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 = '
enhanced-content
'
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)
})
})
})
})
})
}