Skip to content

Commit

Permalink
fix(template): testing helper POC 2
Browse files Browse the repository at this point in the history
  • Loading branch information
eunjae-lee authored and BibiSebi committed Nov 14, 2023
1 parent 7f7df22 commit 9330de9
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import {
isModalChangeMessage,
isPluginLoadedMessage,
isValueChangeMessage,
type CreateFieldPlugin,
} from '@storyblok/field-plugin'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { describe, test, expect, vi } from 'vitest'

import FieldPlugin from '.'

const fakeContainer = (sendToFieldPlugin) => {
function fakeContainer() {
const schema = {
field_type: 'test-field-plugin',
options: [],
Expand All @@ -27,6 +28,8 @@ const fakeContainer = (sendToFieldPlugin) => {
let spaceId = 'test-space-id'
let token = 'test-token'

let onMessage

const stateMessage = ({ action, callbackId }) => ({
callbackId: callbackId,
schema,
Expand All @@ -44,8 +47,13 @@ const fakeContainer = (sendToFieldPlugin) => {
action,
})

const sendToFieldPlugin = (message) => {
onMessage!(message)
}

return {
receive: ({ data, origin }) => {
setOnMessage: (_onMessage) => (onMessage = _onMessage),
receive: (data) => {
if (isPluginLoadedMessage(data)) {
sendToFieldPlugin(
stateMessage({
Expand Down Expand Up @@ -105,12 +113,35 @@ const fakeContainer = (sendToFieldPlugin) => {
}
}

const setup = () => {
let handleEvent
const listener = (data) => {
handleEvent({ data })
vi.mock('@storyblok/field-plugin', async () => {
const mod = await vi.importActual<typeof import('@storyblok/field-plugin')>(
'@storyblok/field-plugin',
)
const container = fakeContainer()
const mockedCreateFieldPlugin: CreateFieldPlugin = ({
onUpdateState,
validateContent,
}) =>
mod.internalCreateFieldPlugin({
onUpdateState,
validateContent,
postToContainer: (message) => {
return container.receive(message)
},
listenToContainer: (handleMessage) => {
container.setOnMessage((message) => {
return handleMessage(message)
})
return () => {}
},
})
return {
...mod,
createFieldPlugin: mockedCreateFieldPlugin,
}
const container = fakeContainer(listener)
})

const setup = () => {
global.ResizeObserver = class ResizeObserver {
observe() {
// do nothing
Expand All @@ -124,24 +155,12 @@ const setup = () => {
}
vi.stubGlobal('parent', {
...global.parent,
postMessage: vi.mocked((data: unknown, origin: string) => {
container.receive({ data, origin })
}),
})
vi.stubGlobal('location', {
...window.location,
search:
'?protocol=https%3A&host=plugin-sandbox.storyblok.com&uid=test-uid&preview=1',
})
const addEventListenerFallback = global.addEventListener

vi.stubGlobal('addEventListener', (name: string, callback: EventListener) => {
if (name === 'message') {
handleEvent = callback
} else {
addEventListenerFallback.call(global, name, callback)
}
})
}

describe('FieldPluginExammple', () => {
Expand Down
91 changes: 60 additions & 31 deletions packages/field-plugin/src/createFieldPlugin/createFieldPlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { createHeightChangeListener } from './createHeightChangeListener'
import { disableDefaultStoryblokStyles } from './disableDefaultStoryblokStyles'
import { pluginUrlParamsFromUrl } from '../messaging'
import { FieldPluginResponse } from './FieldPluginResponse'
import { createPluginMessageListener } from './createPluginActions/createPluginMessageListener'
import { sandboxUrl } from './sandboxUrl'
import { isCloneable } from '../utils/isCloneable'
import { handlePluginMessage } from './createPluginActions/createPluginMessageListener/handlePluginMessage'

export type CreateFieldPluginOptions<Content> = {
onUpdateState: (state: FieldPluginResponse<Content>) => void
Expand All @@ -16,12 +16,67 @@ export type CreateFieldPlugin = <Content = unknown>(
options: CreateFieldPluginOptions<Content>,
) => () => void

export const createFieldPlugin: CreateFieldPlugin = ({
onUpdateState,
validateContent,
}) =>
internalCreateFieldPlugin({
onUpdateState,
validateContent,
listenToContainer: (handleMessage) => {
const handleEvent = (event: MessageEvent<unknown>) => {
handleMessage(event.data)
}
window.addEventListener('message', handleEvent, false)

return () => {
window.removeEventListener('message', handleEvent, false)
}
},
postToContainer: (message: unknown) => {
try {
// TODO specify https://app.storyblok.com/ in production mode, * in dev mode
const origin = '*'
window.parent.postMessage(message, origin)
} catch (err) {
if (isCloneable(message)) {
// eslint-disable-next-line functional/no-throw-statement
throw err
}

// eslint-disable-next-line functional/no-throw-statement
throw new Error(
'The argument could not be cloned. ' +
'The argument must be cloneable with structuredClone(), so that it can be sent to other windows with window.postMessage(). ' +
'Does your object contain functions, getters, setters, proxies, or any other value that is not cloneable? Did you try to pass a reactive object? ' +
'For a full description on the structuredClone algorithm, see: ' +
'https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm',
{
cause: err,
},
)
}
},
})

type InternalCreateFieldPlugin = <Content = unknown>(
options: InternalCreateFieldPluginOptions<Content>,
) => () => void

type InternalCreateFieldPluginOptions<Content> =
CreateFieldPluginOptions<Content> & {
listenToContainer: (handleMessage: (data: unknown) => void) => () => void
postToContainer: (message: unknown) => void
}

/**
* @returns cleanup function for side effects
*/
export const createFieldPlugin: CreateFieldPlugin = ({
export const internalCreateFieldPlugin: InternalCreateFieldPlugin = ({
onUpdateState,
validateContent,
listenToContainer,
postToContainer,
}) => {
const isEmbedded = window.parent !== window

Expand Down Expand Up @@ -49,31 +104,6 @@ export const createFieldPlugin: CreateFieldPlugin = ({

const { uid } = params

const postToContainer = (message: unknown) => {
try {
// TODO specify https://app.storyblok.com/ in production mode, * in dev mode
const origin = '*'
window.parent.postMessage(message, origin)
} catch (err) {
if (isCloneable(message)) {
// eslint-disable-next-line functional/no-throw-statement
throw err
}

// eslint-disable-next-line functional/no-throw-statement
throw new Error(
'The argument could not be cloned. ' +
'The argument must be cloneable with structuredClone(), so that it can be sent to other windows with window.postMessage(). ' +
'Does your object contain functions, getters, setters, proxies, or any other value that is not cloneable? Did you try to pass a reactive object? ' +
'For a full description on the structuredClone algorithm, see: ' +
'https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm',
{
cause: err,
},
)
}
}

const cleanupStyleSideEffects = disableDefaultStoryblokStyles()

// This is basically the `Content` inferred from the `validateContent`.
Expand All @@ -99,10 +129,9 @@ export const createFieldPlugin: CreateFieldPlugin = ({

const cleanupHeightChangeListener = createHeightChangeListener(onHeightChange)

const cleanupMessageListenerSideEffects = createPluginMessageListener(
params.uid,
messageCallbacks,
)
const cleanupMessageListenerSideEffects = listenToContainer((data) => {
handlePluginMessage(data, params.uid, messageCallbacks)
})

void initialize()

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import {
OnAssetSelectMessage,
OnContextRequestMessage,
OnLoadedMessage,
OnStateChangeMessage,
OnUnknownPluginMessage,
isAssetSelectedMessage,
isMessageToPlugin,
isLoadedMessage,
} from '../../../messaging'
import { isContextRequestMessage } from '../../../messaging'
import { PluginMessageCallbacks } from './createPluginMessageListener'
import { isStateMessage } from '../../../messaging/pluginMessage/containerToPluginMessage/StateChangedMessage'

export type PluginMessageCallbacks = {
onStateChange: OnStateChangeMessage
onLoaded: OnLoadedMessage
onContextRequest: OnContextRequestMessage
onAssetSelect: OnAssetSelectMessage
onUnknownMessage: OnUnknownPluginMessage
}

export const handlePluginMessage = (
data: unknown,
uid: string,
Expand Down

0 comments on commit 9330de9

Please sign in to comment.