mirror of https://gitee.com/antv-l7/antv-l7
540 lines
16 KiB
JavaScript
540 lines
16 KiB
JavaScript
// This is by no means a full test file for loader.js so feel free to add more tests.
|
|
import mock from "xhr-mock"
|
|
import DevLoader from "../dev-loader"
|
|
import emitter from "../emitter"
|
|
|
|
jest.mock(`../emitter`)
|
|
jest.mock(`../socketIo`, () => {
|
|
return {
|
|
default: jest.fn(),
|
|
getPageData: jest.fn().mockResolvedValue(),
|
|
}
|
|
})
|
|
|
|
describe(`Dev loader`, () => {
|
|
describe(`loadPageDataJson`, () => {
|
|
let originalBasePath
|
|
let originalPathPrefix
|
|
let xhrCount
|
|
|
|
/**
|
|
* @param {string} path
|
|
* @param {number} status
|
|
* @param {string|Object?} responseText
|
|
* @param {boolean?} json
|
|
*/
|
|
const mockPageData = (path, status, responseText = ``, json = false) => {
|
|
mock.get(`/page-data${path}/page-data.json`, (req, res) => {
|
|
xhrCount++
|
|
if (json) {
|
|
res.header(`content-type`, `application/json`)
|
|
}
|
|
|
|
return res
|
|
.status(status)
|
|
.body(
|
|
typeof responseText === `string`
|
|
? responseText
|
|
: JSON.stringify(responseText)
|
|
)
|
|
})
|
|
}
|
|
|
|
const defaultPayload = {
|
|
path: `/mypage/`,
|
|
}
|
|
|
|
// replace the real XHR object with the mock XHR object before each test
|
|
beforeEach(() => {
|
|
originalBasePath = global.__BASE_PATH__
|
|
originalPathPrefix = global.__PATH_PREFIX__
|
|
global.__BASE_PATH__ = ``
|
|
global.__PATH_PREFIX__ = ``
|
|
xhrCount = 0
|
|
mock.setup()
|
|
})
|
|
|
|
// put the real XHR object back and clear the mocks after each test
|
|
afterEach(() => {
|
|
global.__BASE_PATH__ = originalBasePath
|
|
global.__PATH_PREFIX__ = originalPathPrefix
|
|
mock.teardown()
|
|
})
|
|
|
|
it(`should return a pageData json on success`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/mypage`,
|
|
payload: defaultPayload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(1)
|
|
})
|
|
|
|
it(`should return a pageData json on success without contentType`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
mockPageData(`/mypage`, 200, defaultPayload)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/mypage`,
|
|
payload: defaultPayload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(1)
|
|
})
|
|
|
|
it(`should return a pageData json with an empty compilation hash (gatsby develop)`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
const payload = { ...defaultPayload, webpackCompilationHash: `` }
|
|
mockPageData(`/mypage`, 200, payload)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/mypage`,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toEqual(expectation)
|
|
expect(devLoader.pageDataDb.get(`/mypage`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(1)
|
|
})
|
|
|
|
it(`should load a 404 page when page-path file is not a gatsby json`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
|
mockPageData(`/unknown-page`, 200, { random: `string` }, true)
|
|
mockPageData(`/404.html`, 200, payload, true)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/404.html`,
|
|
notFound: true,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(2)
|
|
})
|
|
|
|
it(`should load a 404 page when page-path file is not a json`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
|
mockPageData(`/unknown-page`, 200)
|
|
mockPageData(`/404.html`, 200, payload, true)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/404.html`,
|
|
notFound: true,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(2)
|
|
})
|
|
|
|
it(`should load a 404 page when path returns a 404`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
const payload = { ...defaultPayload, path: `/404.html/` }
|
|
mockPageData(`/unknown-page`, 200)
|
|
mockPageData(`/404.html`, 200, payload, true)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/404.html`,
|
|
notFound: true,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(2)
|
|
})
|
|
|
|
it(`should return the dev-404-page when no 404 page can be found`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
const payload = { ...defaultPayload, path: `/dev-404-page/` }
|
|
mockPageData(`/unknown-page`, 404)
|
|
mockPageData(`/404.html`, 404)
|
|
mockPageData(`/dev-404-page`, 200, payload, true)
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
pagePath: `/dev-404-page`,
|
|
notFound: true,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/unknown-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
|
|
expect(devLoader.pageDataDb.get(`/unknown-page`)).toEqual({
|
|
notFound: true,
|
|
pagePath: `/404.html`,
|
|
status: `failure`,
|
|
})
|
|
expect(xhrCount).toBe(3)
|
|
})
|
|
|
|
it(`should return an error when status is 500`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
mockPageData(`/error-page`, 500)
|
|
|
|
const expectation = {
|
|
status: `error`,
|
|
pagePath: `/error-page`,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/error-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/error-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(1)
|
|
})
|
|
|
|
it(`should retry 3 times before returning an error`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
mockPageData(`/blocked-page`, 0)
|
|
|
|
const expectation = {
|
|
status: `error`,
|
|
retries: 3,
|
|
pagePath: `/blocked-page`,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(4)
|
|
})
|
|
|
|
it(`should recover if we get 1 failure`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
const payload = {
|
|
path: `/blocked-page/`,
|
|
}
|
|
|
|
let xhrCount = 0
|
|
mock.get(`/page-data/blocked-page/page-data.json`, (req, res) => {
|
|
if (xhrCount++ === 0) {
|
|
return res.status(0).body(``)
|
|
} else {
|
|
res.header(`content-type`, `application/json`)
|
|
return res.status(200).body(JSON.stringify(payload))
|
|
}
|
|
})
|
|
|
|
const expectation = {
|
|
status: `success`,
|
|
retries: 1,
|
|
pagePath: `/blocked-page`,
|
|
payload,
|
|
}
|
|
expect(await devLoader.loadPageDataJson(`/blocked-page/`)).toEqual(
|
|
expectation
|
|
)
|
|
expect(devLoader.pageDataDb.get(`/blocked-page`)).toEqual(expectation)
|
|
expect(xhrCount).toBe(2)
|
|
})
|
|
|
|
it(`shouldn't load pageData multiple times`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
mockPageData(`/mypage`, 200, defaultPayload, true)
|
|
|
|
const expectation = await devLoader.loadPageDataJson(`/mypage/`)
|
|
expect(await devLoader.loadPageDataJson(`/mypage/`)).toBe(expectation)
|
|
expect(xhrCount).toBe(1)
|
|
})
|
|
})
|
|
|
|
describe(`loadPage`, () => {
|
|
const createSyncRequires = components => {
|
|
return {
|
|
components,
|
|
}
|
|
}
|
|
|
|
let originalPathPrefix
|
|
|
|
beforeEach(() => {
|
|
originalPathPrefix = global.__PATH_PREFIX__
|
|
global.__PATH_PREFIX__ = ``
|
|
mock.setup()
|
|
mock.get(`/page-data/app-data.json`, (req, res) =>
|
|
res
|
|
.status(200)
|
|
.header(`content-type`, `application/json`)
|
|
.body(
|
|
JSON.stringify({
|
|
webpackCompilationHash: `123`,
|
|
})
|
|
)
|
|
)
|
|
emitter.emit.mockReset()
|
|
})
|
|
|
|
afterEach(() => {
|
|
global.__PATH_PREFIX__ = originalPathPrefix
|
|
mock.teardown()
|
|
})
|
|
|
|
it(`should be successful when component can be loaded`, async () => {
|
|
const syncRequires = createSyncRequires({
|
|
chunk: `instance`,
|
|
})
|
|
const devLoader = new DevLoader(syncRequires, [])
|
|
const pageData = {
|
|
path: `/mypage/`,
|
|
componentChunkName: `chunk`,
|
|
result: {
|
|
pageContext: `something something`,
|
|
},
|
|
}
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
payload: pageData,
|
|
status: `success`,
|
|
})
|
|
)
|
|
|
|
const expectation = await devLoader.loadPage(`/mypage/`)
|
|
expect(expectation).toMatchSnapshot()
|
|
expect(Object.keys(expectation)).toEqual([`component`, `json`, `page`])
|
|
expect(devLoader.pageDb.get(`/mypage`)).toEqual(
|
|
expect.objectContaining({
|
|
payload: expectation,
|
|
status: `success`,
|
|
})
|
|
)
|
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
|
page: expectation,
|
|
pageResources: expectation,
|
|
})
|
|
})
|
|
|
|
it(`should set not found on finalResult`, async () => {
|
|
const syncRequires = createSyncRequires({
|
|
chunk: `instance`,
|
|
})
|
|
const devLoader = new DevLoader(syncRequires, [])
|
|
const pageData = {
|
|
path: `/mypage/`,
|
|
componentChunkName: `chunk`,
|
|
}
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
payload: pageData,
|
|
status: `success`,
|
|
notFound: true,
|
|
})
|
|
)
|
|
|
|
await devLoader.loadPage(`/mypage/`)
|
|
const expectation = devLoader.pageDb.get(`/mypage`)
|
|
expect(expectation).toHaveProperty(`notFound`, true)
|
|
expect(emitter.emit).toHaveBeenCalledTimes(1)
|
|
expect(emitter.emit).toHaveBeenCalledWith(`onPostLoadPageResources`, {
|
|
page: expectation.payload,
|
|
pageResources: expectation.payload,
|
|
})
|
|
})
|
|
|
|
it(`should return an error when component cannot be loaded`, async () => {
|
|
const syncRequires = createSyncRequires({
|
|
chunk: false,
|
|
})
|
|
const devLoader = new DevLoader(syncRequires, [])
|
|
const pageData = {
|
|
path: `/mypage/`,
|
|
componentChunkName: `chunk`,
|
|
}
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
payload: pageData,
|
|
status: `success`,
|
|
})
|
|
)
|
|
|
|
await devLoader.loadPage(`/mypage/`)
|
|
const expectation = devLoader.pageDb.get(`/mypage`)
|
|
expect(expectation).toHaveProperty(`status`, `error`)
|
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
|
})
|
|
|
|
it(`should return an error pageData contains an error`, async () => {
|
|
const syncRequires = createSyncRequires({
|
|
chunk: `instance`,
|
|
})
|
|
const devLoader = new DevLoader(syncRequires, [])
|
|
const pageData = {
|
|
path: `/mypage/`,
|
|
componentChunkName: `chunk`,
|
|
}
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
payload: pageData,
|
|
status: `error`,
|
|
})
|
|
)
|
|
|
|
expect(await devLoader.loadPage(`/mypage/`)).toEqual({ status: `error` })
|
|
expect(devLoader.pageDb.size).toBe(0)
|
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
|
})
|
|
|
|
it(`should throw an error when 404 cannot be fetched`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
status: `failure`,
|
|
})
|
|
)
|
|
|
|
try {
|
|
await devLoader.loadPage(`/404.html/`)
|
|
} catch (err) {
|
|
expect(err.message).toEqual(
|
|
expect.stringContaining(`404 page could not be found`)
|
|
)
|
|
}
|
|
expect(devLoader.pageDb.size).toBe(0)
|
|
expect(emitter.emit).toHaveBeenCalledTimes(0)
|
|
})
|
|
|
|
it(`should cache the result of loadPage`, async () => {
|
|
const syncRequires = createSyncRequires({
|
|
chunk: `instance`,
|
|
})
|
|
const devLoader = new DevLoader(syncRequires, [])
|
|
devLoader.loadPageDataJson = jest.fn(() =>
|
|
Promise.resolve({
|
|
payload: {
|
|
componentChunkName: `chunk`,
|
|
},
|
|
status: `success`,
|
|
})
|
|
)
|
|
|
|
const expectation = await devLoader.loadPage(`/mypage/`)
|
|
expect(await devLoader.loadPage(`/mypage/`)).toBe(expectation)
|
|
expect(devLoader.loadPageDataJson).toHaveBeenCalledTimes(1)
|
|
})
|
|
})
|
|
|
|
describe(`loadPageSync`, () => {
|
|
it(`returns page resources when already fetched`, () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
devLoader.pageDb.set(`/mypage`, { payload: true })
|
|
expect(devLoader.loadPageSync(`/mypage/`)).toBe(true)
|
|
})
|
|
|
|
it(`returns page resources when already fetched`, () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
|
|
expect(devLoader.loadPageSync(`/mypage/`)).toBeUndefined()
|
|
})
|
|
})
|
|
|
|
describe(`prefetch`, () => {
|
|
const flushPromises = () => new Promise(resolve => setImmediate(resolve))
|
|
|
|
it(`shouldn't prefetch when shouldPrefetch is false`, () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
devLoader.shouldPrefetch = jest.fn(() => false)
|
|
devLoader.doPrefetch = jest.fn()
|
|
devLoader.apiRunner = jest.fn()
|
|
|
|
expect(devLoader.prefetch(`/mypath/`)).toBe(false)
|
|
expect(devLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
|
expect(devLoader.apiRunner).not.toHaveBeenCalled()
|
|
expect(devLoader.doPrefetch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it(`should trigger custom prefetch logic when core is disabled`, () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
|
devLoader.doPrefetch = jest.fn()
|
|
devLoader.apiRunner = jest.fn()
|
|
devLoader.prefetchDisabled = true
|
|
|
|
expect(devLoader.prefetch(`/mypath/`)).toBe(false)
|
|
expect(devLoader.shouldPrefetch).toHaveBeenCalledWith(`/mypath/`)
|
|
expect(devLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
|
pathname: `/mypath/`,
|
|
})
|
|
expect(devLoader.doPrefetch).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it(`should prefetch when not yet triggered`, async () => {
|
|
jest.useFakeTimers()
|
|
const devLoader = new DevLoader(null, [])
|
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
|
devLoader.apiRunner = jest.fn()
|
|
devLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
|
|
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
|
|
|
// wait for doPrefetchPromise
|
|
await flushPromises()
|
|
|
|
expect(devLoader.apiRunner).toHaveBeenCalledWith(`onPrefetchPathname`, {
|
|
pathname: `/mypath/`,
|
|
})
|
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
|
2,
|
|
`onPostPrefetchPathname`,
|
|
{
|
|
pathname: `/mypath/`,
|
|
}
|
|
)
|
|
})
|
|
|
|
it(`should only run apis once`, async () => {
|
|
const devLoader = new DevLoader(null, [])
|
|
devLoader.shouldPrefetch = jest.fn(() => true)
|
|
devLoader.apiRunner = jest.fn()
|
|
devLoader.doPrefetch = jest.fn(() => Promise.resolve({}))
|
|
|
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
|
expect(devLoader.prefetch(`/mypath/`)).toBe(true)
|
|
|
|
// wait for doPrefetchPromise
|
|
await flushPromises()
|
|
|
|
expect(devLoader.apiRunner).toHaveBeenCalledTimes(2)
|
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
|
1,
|
|
`onPrefetchPathname`,
|
|
expect.anything()
|
|
)
|
|
expect(devLoader.apiRunner).toHaveBeenNthCalledWith(
|
|
2,
|
|
`onPostPrefetchPathname`,
|
|
expect.anything()
|
|
)
|
|
})
|
|
})
|
|
})
|