2022-11-14 15:59:22 +08:00
|
|
|
/**
|
|
|
|
* @vitest-environment jsdom
|
|
|
|
*/
|
|
|
|
|
2022-09-07 14:49:54 +08:00
|
|
|
import React from 'react';
|
|
|
|
import { renderToString } from 'react-dom/server';
|
|
|
|
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
|
2023-04-13 12:01:16 +08:00
|
|
|
import { fetch, Request, Response } from '@remix-run/web-fetch';
|
|
|
|
import runClientApp from '../src/runClientApp';
|
|
|
|
import { useAppData } from '../src/AppContext';
|
2022-09-07 14:49:54 +08:00
|
|
|
|
|
|
|
describe('run client app', () => {
|
|
|
|
let windowSpy;
|
|
|
|
let documentSpy;
|
2023-04-13 12:01:16 +08:00
|
|
|
if (!globalThis.fetch) {
|
|
|
|
// @ts-expect-error
|
|
|
|
globalThis.fetch = fetch;
|
|
|
|
// @ts-expect-error
|
|
|
|
globalThis.Request = Request;
|
|
|
|
// @ts-expect-error
|
|
|
|
globalThis.Response = Response;
|
|
|
|
}
|
2022-09-07 14:49:54 +08:00
|
|
|
const mockData = {
|
|
|
|
location: new URL('http://localhost:4000/'),
|
|
|
|
history: {
|
|
|
|
replaceState: vi.fn(),
|
|
|
|
},
|
|
|
|
addEventListener: vi.fn(),
|
2023-04-13 12:01:16 +08:00
|
|
|
removeEventListener: vi.fn(),
|
2022-09-07 14:49:54 +08:00
|
|
|
};
|
|
|
|
beforeEach(() => {
|
|
|
|
process.env.ICE_CORE_ROUTER = 'true';
|
|
|
|
windowSpy = vi.spyOn(global, 'window', 'get');
|
|
|
|
documentSpy = vi.spyOn(global, 'document', 'get');
|
|
|
|
|
|
|
|
windowSpy.mockImplementation(() => mockData);
|
|
|
|
documentSpy.mockImplementation(() => ({
|
|
|
|
head: {
|
|
|
|
querySelector: () => ({
|
|
|
|
content: '',
|
|
|
|
}),
|
|
|
|
},
|
2022-10-14 14:45:34 +08:00
|
|
|
body: {
|
|
|
|
appendChild: () => null,
|
|
|
|
},
|
2022-09-07 14:49:54 +08:00
|
|
|
getElementById: () => null,
|
2022-10-14 14:45:34 +08:00
|
|
|
createElement: () => ({
|
|
|
|
id: '',
|
|
|
|
}),
|
2022-09-07 14:49:54 +08:00
|
|
|
querySelectorAll: () => [],
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
afterEach(() => {
|
2022-11-14 15:59:22 +08:00
|
|
|
(window as any).__ICE_DATA_LOADER__ = undefined;
|
2022-09-07 14:49:54 +08:00
|
|
|
windowSpy.mockRestore();
|
|
|
|
documentSpy.mockRestore();
|
|
|
|
});
|
|
|
|
|
|
|
|
let domstring = '';
|
|
|
|
|
|
|
|
const serverRuntime = async ({ setRender }) => {
|
|
|
|
setRender((container, element) => {
|
|
|
|
try {
|
|
|
|
domstring = renderToString(element as any);
|
2023-04-13 12:01:16 +08:00
|
|
|
} catch (err) {
|
|
|
|
domstring = '';
|
|
|
|
}
|
2022-09-07 14:49:54 +08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2022-10-24 15:17:16 +08:00
|
|
|
let staticMsg = '';
|
|
|
|
|
|
|
|
const staticRuntime = async () => {
|
|
|
|
staticMsg = 'static';
|
|
|
|
};
|
|
|
|
|
2022-09-07 14:49:54 +08:00
|
|
|
const providerRuntmie = async ({ addProvider }) => {
|
|
|
|
const Provider = ({ children }) => {
|
|
|
|
return <div>{children}</div>;
|
|
|
|
};
|
|
|
|
addProvider(Provider);
|
|
|
|
// Add twice.
|
|
|
|
addProvider(Provider);
|
|
|
|
};
|
|
|
|
|
2023-04-13 12:01:16 +08:00
|
|
|
const homeItem = {
|
|
|
|
default: () => {
|
|
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
|
|
const appData = useAppData();
|
|
|
|
return (
|
|
|
|
<div>home{appData?.msg || ''}</div>
|
|
|
|
);
|
|
|
|
},
|
|
|
|
pageConfig: () => ({ title: 'home' }),
|
2023-04-25 10:47:40 +08:00
|
|
|
dataLoader: {
|
|
|
|
loader: async () => ({ data: 'test' }),
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
};
|
2022-09-07 14:49:54 +08:00
|
|
|
const basicRoutes = [
|
|
|
|
{
|
|
|
|
id: 'home',
|
|
|
|
path: '/',
|
|
|
|
componentName: 'Home',
|
2023-04-13 12:01:16 +08:00
|
|
|
lazy: () => {
|
|
|
|
return {
|
|
|
|
Component: homeItem.default,
|
|
|
|
};
|
|
|
|
},
|
2022-09-07 14:49:54 +08:00
|
|
|
},
|
|
|
|
];
|
|
|
|
|
2022-10-24 15:17:16 +08:00
|
|
|
it('run with static runtime', async () => {
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
2023-04-25 10:47:40 +08:00
|
|
|
dataLoader: {
|
|
|
|
loader: async () => {
|
|
|
|
return { msg: staticMsg };
|
|
|
|
},
|
2022-10-24 15:17:16 +08:00
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime], statics: [staticRuntime] },
|
2023-04-13 12:01:16 +08:00
|
|
|
hydrate: true,
|
2022-10-24 15:17:16 +08:00
|
|
|
});
|
|
|
|
expect(domstring).toBe('<div>home<!-- -->static</div>');
|
|
|
|
});
|
|
|
|
|
2022-09-07 14:49:54 +08:00
|
|
|
it('run client basic', async () => {
|
|
|
|
windowSpy.mockImplementation(() => ({
|
|
|
|
...mockData,
|
|
|
|
location: new URL('http://localhost:4000/?test=1&runtime=true&baisc'),
|
|
|
|
}));
|
|
|
|
await runClientApp({
|
|
|
|
app: {},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: false,
|
|
|
|
});
|
|
|
|
expect(domstring).toBe('<div>home</div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client single-router', async () => {
|
2023-04-13 12:01:16 +08:00
|
|
|
const sigleRoutes = [
|
|
|
|
{
|
|
|
|
id: 'home',
|
|
|
|
path: '/',
|
|
|
|
lazy: () => {
|
|
|
|
return {
|
|
|
|
Component: homeItem.default,
|
|
|
|
loader: () => {},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
];
|
2022-09-07 14:49:54 +08:00
|
|
|
process.env.ICE_CORE_ROUTER = '';
|
|
|
|
await runClientApp({
|
|
|
|
app: {},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => sigleRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: false,
|
|
|
|
});
|
|
|
|
process.env.ICE_CORE_ROUTER = 'true';
|
|
|
|
expect(domstring).toBe('<div>home</div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with app provider', async () => {
|
|
|
|
await runClientApp({
|
|
|
|
app: {},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime, providerRuntmie] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: true,
|
|
|
|
});
|
|
|
|
expect(domstring).toBe('<div><div><div>home</div></div></div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with memory router', async () => {
|
|
|
|
const routes = [...basicRoutes, {
|
|
|
|
id: 'about',
|
|
|
|
path: '/about',
|
|
|
|
componentName: 'About',
|
2023-04-13 12:01:16 +08:00
|
|
|
Component: () => {
|
|
|
|
return (
|
|
|
|
<div>about</div>
|
|
|
|
);
|
|
|
|
} }];
|
2022-09-07 14:49:54 +08:00
|
|
|
await runClientApp({
|
|
|
|
app: {
|
|
|
|
default: {
|
|
|
|
router: {
|
|
|
|
type: 'memory',
|
|
|
|
initialEntries: ['/about'],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => routes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(domstring).toBe('<div>about</div>');
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
|
|
|
default: {
|
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: true,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with memory router - from context', async () => {
|
|
|
|
windowSpy.mockImplementation(() => ({
|
|
|
|
...mockData,
|
|
|
|
__ICE_APP_CONTEXT__: {
|
|
|
|
routePath: '/about',
|
|
|
|
},
|
|
|
|
}));
|
|
|
|
const routes = [...basicRoutes, {
|
|
|
|
id: 'about',
|
|
|
|
path: '/about',
|
|
|
|
componentName: 'About',
|
2023-04-13 12:01:16 +08:00
|
|
|
Component: () => {
|
|
|
|
return (
|
|
|
|
<div>about</div>
|
|
|
|
);
|
|
|
|
} }];
|
2022-09-07 14:49:54 +08:00
|
|
|
await runClientApp({
|
|
|
|
app: {
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => routes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: true,
|
|
|
|
memoryRouter: true,
|
|
|
|
});
|
|
|
|
|
|
|
|
expect(domstring).toBe('<div>about</div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with hash router', async () => {
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
|
|
|
default: {
|
|
|
|
router: {
|
|
|
|
type: 'hash',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: true,
|
|
|
|
});
|
|
|
|
expect(domstring).toBe('<div>home</div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with app data', async () => {
|
|
|
|
let executed = false;
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
2023-04-25 10:47:40 +08:00
|
|
|
dataLoader: {
|
|
|
|
loader: async () => {
|
|
|
|
executed = true;
|
|
|
|
return { msg: '-getAppData' };
|
|
|
|
},
|
2022-09-07 14:49:54 +08:00
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: false,
|
|
|
|
});
|
|
|
|
expect(domstring).toBe('<div>home<!-- -->-getAppData</div>');
|
|
|
|
expect(executed).toBe(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with app data', async () => {
|
|
|
|
let useGlobalLoader = false;
|
|
|
|
let executed = false;
|
|
|
|
windowSpy.mockImplementation(() => ({
|
|
|
|
...mockData,
|
2022-11-14 15:59:22 +08:00
|
|
|
__ICE_DATA_LOADER__: {
|
|
|
|
getData: async () => {
|
|
|
|
useGlobalLoader = true;
|
|
|
|
return { msg: '-globalData' };
|
|
|
|
},
|
2022-09-07 14:49:54 +08:00
|
|
|
},
|
|
|
|
}));
|
|
|
|
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
2023-04-25 10:47:40 +08:00
|
|
|
dataLoader: {
|
|
|
|
loader: async () => {
|
|
|
|
executed = true;
|
|
|
|
return { msg: 'app' };
|
|
|
|
},
|
2022-09-07 14:49:54 +08:00
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: false,
|
|
|
|
});
|
|
|
|
expect(executed).toBe(false);
|
|
|
|
expect(useGlobalLoader).toBe(true);
|
|
|
|
expect(domstring).toBe('<div>home<!-- -->-globalData</div>');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('run client with AppErrorBoundary', async () => {
|
|
|
|
await runClientApp({
|
|
|
|
app: {
|
|
|
|
default: {
|
|
|
|
app: {
|
|
|
|
errorBoundary: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-04-13 12:01:16 +08:00
|
|
|
// @ts-ignore don't need to pass params in test case.
|
|
|
|
createRoutes: () => basicRoutes,
|
2022-10-24 15:17:16 +08:00
|
|
|
runtimeModules: { commons: [serverRuntime] },
|
2022-09-07 14:49:54 +08:00
|
|
|
hydrate: false,
|
|
|
|
});
|
2023-04-13 12:01:16 +08:00
|
|
|
expect(domstring).toBe('<div>home</div>');
|
2022-09-07 14:49:54 +08:00
|
|
|
});
|
2023-03-15 17:47:25 +08:00
|
|
|
});
|