UNPKG

salsify-experiences-sdk

Version:

SDK to be used by commerce websites to implement product experiences.

198 lines (174 loc) 8.48 kB
import EnhancedContentApi from '../index' import request from '../../utils/request' import { PerProductConfigCache, selectSource } from '../perProductConfig' import { makeContext, makeSettings, makeResponse } from '../../__tests__/helpers' jest.mock('../../utils/request') const clientId = 'client-id' const languageCode = 'lang-code' const enhancedContent = {} const productExistingPath = 'https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/existing-product' const productMissingPath = 'https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/missing-product' const exampleContent = '<div>enhanced-content</div>' const emptyContent = '' const PerProductConfigCacheMock = PerProductConfigCache as jest.Mock const headMock = request.head as jest.Mock const selectSourceMock = selectSource as jest.Mock let ecApi: EnhancedContentApi const log = jest.fn() const settings = makeSettings({ clientId, languageCode, enhancedContent, tracking: true }) const settingsNoTrack = makeSettings({ clientId, languageCode, enhancedContent, tracking: false }) export default function commonSuite(runtime: string): void { describe(`EnhancedContentApi (${runtime})`, () => { beforeEach(() => { ecApi = new EnhancedContentApi(settings, makeContext(), { log }) PerProductConfigCacheMock.mock.instances[0].getConfig.mockImplementation(() => undefined) }) afterEach(() => { jest.clearAllMocks() }) describe('exists', () => { test('should throw error when no product ID type is specified when calling', async () => { await expect(ecApi.exists('product')).rejects.toStrictEqual(new Error('No ID type specified.')) }) test('should return true when content length is greater than zero', async () => { headMock.mockImplementation(() => makeResponse(exampleContent)) const exists = await ecApi.exists('existing-product', 'id-type') expect(exists).toBe(true) expect(headMock).toBeCalledTimes(1) expect(headMock).toBeCalledWith(`${productExistingPath}/index.html`) expect(PerProductConfigCacheMock.mock.instances.length).toBe(1) expect(PerProductConfigCacheMock.mock.instances[0].getConfig).toHaveBeenCalledTimes(1) expect(PerProductConfigCacheMock.mock.instances[0].getConfig).toHaveBeenCalledWith(productExistingPath) const renderConfig = { idType: 'id-type', productId: 'existing-product', content: null, allContentExists: false, source: 'index.html', sourceExists: true, } expect(ecApi.lastRenderConfig).toMatchObject(renderConfig) expect(log).toHaveBeenCalledWith('ec_exists', renderConfig) }) test('should return false when content length is zero', async () => { headMock.mockImplementation(() => makeResponse(emptyContent)) const exists = await ecApi.exists('missing-product', 'id-type') expect(exists).toBe(false) expect(headMock).toBeCalledTimes(1) expect(headMock).toBeCalledWith(`${productMissingPath}/index.html`) expect(PerProductConfigCacheMock.mock.instances.length).toBe(1) expect(PerProductConfigCacheMock.mock.instances[0].getConfig).toHaveBeenCalledTimes(1) expect(PerProductConfigCacheMock.mock.instances[0].getConfig).toHaveBeenCalledWith(productMissingPath) const renderConfig = { idType: 'id-type', productId: 'missing-product', content: null, allContentExists: false, source: 'index.html', sourceExists: false, } expect(ecApi.lastRenderConfig).toMatchObject(renderConfig) expect(log).toHaveBeenCalledWith('ec_exists', renderConfig) }) test('should cache results', async () => { headMock.mockImplementation(() => makeResponse(emptyContent)) await ecApi.exists('new-product', 'id-type') await ecApi.exists('new-product', 'id-type') expect(headMock).toHaveBeenCalledTimes(1) }) test('it should handle rejection', async () => { headMock.mockRejectedValue(Error('network error')) const exists = await ecApi.exists('rejected-product', 'id-type') expect(exists).toBe(false) expect(headMock).toHaveBeenCalledTimes(1) expect(log).toBeCalledWith('error', { errorContext: 'exists', errorType: 'fetch', errorMessage: 'Error on HEAD request of https://salsify-ecdn.com/sdk/client-id/lang-code/BTF/id-type/rejected-product/index.html: network error', }) }) describe('with config content', () => { const content = [ { source: 'foo.html', weight: 0.4 }, { source: 'bar.html', weight: 0.4 }, { source: null, weight: 0.2 }, ] beforeEach(() => { PerProductConfigCacheMock.mock.instances[0].getConfig.mockImplementation(() => ({ content, })) }) test('it checks a content source from config', async () => { selectSourceMock.mockImplementation(() => content[1]) headMock.mockImplementation(() => makeResponse(exampleContent)) const exists = await ecApi.exists('existing-product', 'id-type') expect(exists).toBe(true) expect(headMock).toBeCalledTimes(2) expect(headMock).toBeCalledWith(`${productExistingPath}/foo.html`) expect(headMock).toBeCalledWith(`${productExistingPath}/bar.html`) const renderConfig = { idType: 'id-type', productId: 'existing-product', content, allContentExists: true, source: 'bar.html', sourceExists: true, } expect(ecApi.lastRenderConfig).toMatchObject(renderConfig) expect(log).toHaveBeenCalledWith('ec_exists', renderConfig) }) test('it returns false when a null source is selected', async () => { selectSourceMock.mockImplementation(() => content[2]) headMock.mockImplementation(() => makeResponse(exampleContent)) const exists = await ecApi.exists('existing-product', 'id-type') expect(exists).toBe(false) expect(headMock).toBeCalledTimes(2) expect(headMock).toBeCalledWith(`${productExistingPath}/foo.html`) expect(headMock).toBeCalledWith(`${productExistingPath}/bar.html`) const renderConfig = { idType: 'id-type', productId: 'existing-product', content, allContentExists: true, source: null, sourceExists: false, } expect(ecApi.lastRenderConfig).toMatchObject(renderConfig) expect(log).toHaveBeenCalledWith('ec_exists', renderConfig) }) describe('when tracking is disabled', () => { beforeEach(() => { ecApi = new EnhancedContentApi(settingsNoTrack, makeContext(), { log }) PerProductConfigCacheMock.mock.instances[1].getConfig.mockImplementation(() => ({ content, })) }) test('it falls back to default source', async () => { selectSourceMock.mockImplementation(() => content[1]) headMock.mockImplementation(() => makeResponse(exampleContent)) const exists = await ecApi.exists('existing-product', 'id-type') expect(exists).toBe(true) expect(headMock).toBeCalledTimes(3) expect(headMock).toBeCalledWith(`${productExistingPath}/foo.html`) expect(headMock).toBeCalledWith(`${productExistingPath}/bar.html`) expect(headMock).toBeCalledWith(`${productExistingPath}/index.html`) expect(PerProductConfigCacheMock.mock.instances.length).toBe(2) expect(PerProductConfigCacheMock.mock.instances[1].getConfig).toHaveBeenCalledTimes(1) expect(PerProductConfigCacheMock.mock.instances[1].getConfig).toHaveBeenCalledWith(productExistingPath) const renderConfig = { idType: 'id-type', productId: 'existing-product', content, allContentExists: true, source: 'index.html', sourceExists: true, } expect(ecApi.lastRenderConfig).toMatchObject(renderConfig) expect(log).toHaveBeenCalledWith('ec_exists', renderConfig) }) }) }) }) }) }