salsify-experiences-sdk
Version:
SDK to be used by commerce websites to implement product experiences.
133 lines (115 loc) • 4.2 kB
text/typescript
/**
* @jest-environment jsdom
* @jest-environment-options {"url": "https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/existing-product/index.html"}
*/
import SdkApi from '../api'
import { MessageChannel as WorkerThreadsMessageChannel } from 'worker_threads'
import { SDK_VERSION } from '../version'
const uuidRegex = /^[0-9a-f]{8}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{4}\b-[0-9a-f]{12}$/
const defaultOptions = {
clientId: 'client-id',
}
describe('SdkApi', () => {
let sdk: SdkApi
beforeEach(() => {
sdk = new SdkApi('npm')
})
describe('SDK context communication', () => {
let contextReceived: (value?: unknown) => void
const waitForContext = (): Promise<unknown> => new Promise(resolve => (contextReceived = resolve))
let messageListener: ((event: MessageEvent) => void) | undefined
let channel: MessageChannel | undefined
// workaround for https://github.com/jsdom/jsdom/issues/2745
const messageMonkeyPatch = (event: MessageEvent): void => {
if (event.origin === '' && event.data.messageType === 'contextRequest') {
event.stopImmediatePropagation()
const eventWithOriginInitDict: MessageEventInit = {
data: event.data,
origin: 'https://salsify-ecdn.com',
source: window,
}
if (channel) {
eventWithOriginInitDict.ports = [channel.port2]
}
const eventWithOrigin: MessageEvent = new MessageEvent('message', eventWithOriginInitDict)
window.dispatchEvent(eventWithOrigin)
}
}
beforeAll(() => {
window.addEventListener('message', messageMonkeyPatch)
})
afterAll(() => {
window.removeEventListener('message', messageMonkeyPatch)
})
beforeEach(() => {
messageListener = jest.fn().mockImplementation((event: MessageEvent) => {
if (event.data.clientId) {
contextReceived()
}
})
sdk.init(defaultOptions)
})
afterEach(() => {
messageListener = undefined
})
test('Using MessageChannel', async () => {
channel = new WorkerThreadsMessageChannel() as unknown as MessageChannel
channel!.port1.onmessage = messageListener!
window.postMessage({ messageType: 'contextRequest' }, '*', [channel!.port2])
await waitForContext()
expect(messageListener).toHaveBeenCalledTimes(1)
expect(messageListener).toHaveBeenCalledWith(
expect.objectContaining({
data: {
url: 'https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/existing-product/index.html',
sessionId: expect.stringMatching(uuidRegex),
pageSessionId: expect.stringMatching(uuidRegex),
tracking: true,
clientId: defaultOptions.clientId,
languageCode: 'en-US',
enhancedContent: {
idType: 'SDKID',
},
version: SDK_VERSION,
jsSource: 'npm',
},
})
)
channel!.port1.onmessage = null
channel = undefined
})
test('Using postMessage', async () => {
window.addEventListener('message', messageListener!)
window.postMessage({ messageType: 'contextRequest' }, '*')
await waitForContext()
expect(messageListener).toHaveBeenCalledTimes(2)
expect(messageListener).toHaveBeenNthCalledWith(
1,
expect.objectContaining({
data: {
messageType: 'contextRequest',
},
})
)
expect(messageListener).toHaveBeenNthCalledWith(
2,
expect.objectContaining({
data: {
url: 'https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/existing-product/index.html',
sessionId: expect.stringMatching(uuidRegex),
pageSessionId: expect.stringMatching(uuidRegex),
tracking: true,
clientId: defaultOptions.clientId,
languageCode: 'en-US',
enhancedContent: {
idType: 'SDKID',
},
version: SDK_VERSION,
jsSource: 'npm',
},
})
)
window.removeEventListener('message', messageListener!)
})
})
})