ice/packages/plugin-pha/tests/manifestHelper.test.ts

844 lines
21 KiB
TypeScript

import * as path from 'path';
import { fileURLToPath } from 'url';
import { expect, it, describe } from 'vitest';
import { transformManifestKeys, parseManifest, getAppWorkerUrl, rewriteAppWorker, getMultipleManifest } from '../src/manifestHelpers';
import * as mockServer from './mockServer.mjs';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
describe('get app work url', () => {
it('app worker config as remote url', () => {
const manifest = {
appWorker: {
url: 'http://remote/app-worker.js',
},
};
const appWorkerPath = getAppWorkerUrl(manifest, __dirname);
expect(appWorkerPath).toBeUndefined();
});
it('app worker config which do not exist', () => {
const manifest = {
appWorker: {
url: 'app-worker.js',
},
};
const appWorkerPath = getAppWorkerUrl(manifest, __dirname);
expect(appWorkerPath).toBeUndefined();
});
it('app worker config which exists', () => {
const manifest = {
appWorker: {
url: 'pha-work.js',
},
};
const appWorkerPath = getAppWorkerUrl(manifest, __dirname);
expect(appWorkerPath).toBe(path.join(__dirname, 'pha-work.js'));
});
it('found default app worker', () => {
const manifest = {};
const appWorkerPath = getAppWorkerUrl(manifest, __dirname);
expect(appWorkerPath).toBe(path.join(__dirname, 'app-worker.ts'));
});
});
describe('rewrite app worker url', () => {
it('over write appWorker.url', () => {
expect(rewriteAppWorker({
appWorker: {
url: 'pha-worker.js',
source: 'test',
},
})).toMatchObject({
appWorker: {
url: 'app-worker.js',
source: 'test',
},
});
});
it('config appWorker', () => {
expect(rewriteAppWorker({})).toMatchObject({
appWorker: {
url: 'app-worker.js',
},
});
});
});
describe('transform config keys', () => {
it('should transform decamelize keys fields', () => {
const manifestJSON = transformManifestKeys(
{
offlineResources: ['//g.alicdn.com/.*'],
name: 'name',
pages: [
{
pageHeader: {
includedSafeArea: true,
},
downgradeUrl: 'http://www.taobao.com',
},
],
},
{ isRoot: true },
);
expect(manifestJSON.name).toStrictEqual('name');
expect(manifestJSON?.pages![0].downgrade_url).toStrictEqual('http://www.taobao.com');
expect(manifestJSON.offline_resources).toStrictEqual(['//g.alicdn.com/.*']);
expect(manifestJSON?.pages![0].tab_header).toStrictEqual({ included_safe_area: true });
});
it('should transform dataPrefetch to data_prefetch', () => {
const manifestJSON = transformManifestKeys(
{
dataPrefetch: [
{
api: '/a.com',
data: {
id: 123,
taskId: 233,
cId: {
dId: true,
},
},
header: {
taskId: 455,
},
extHeaders: {
id: 123,
test_id: 234,
},
dataType: 'json',
appKey: '12345',
LoginRequest: true,
},
],
},
{ isRoot: true },
);
expect(manifestJSON?.data_prefetch?.length).toBe(1);
expect(manifestJSON?.data_prefetch![0].data).toMatchObject({
id: 123,
taskId: 233,
cId: {
dId: true,
},
});
expect(manifestJSON?.data_prefetch![0].header).toMatchObject({
taskId: 455,
});
expect(manifestJSON?.data_prefetch![0].ext_headers).toMatchObject({ id: 123, test_id: 234 });
expect(manifestJSON?.data_prefetch![0].dataType).toBe('json');
expect(manifestJSON?.data_prefetch![0].appKey).toBe('12345');
expect(manifestJSON?.data_prefetch![0].LoginRequest).toBe(true);
expect(manifestJSON?.data_prefetch![0].prefetch_key).toBe('mtop');
});
it('should transform tabBar to tab_bar', () => {
const manifestJSON = transformManifestKeys(
{
tabBar: {
textColor: '',
selectedColor: '',
backgroundColor: '',
items: [
{
path: 'tab1',
name: '主会场',
icon: '',
activeIcon: '',
},
{
// transform text to name
text: 'text-name',
icon: '',
activeIcon: '',
},
],
},
},
{ isRoot: true },
);
expect(manifestJSON.tab_bar).toBeTruthy();
expect(manifestJSON?.tab_bar?.items![0]).toMatchObject({ path: 'tab1', name: '主会场', icon: '', active_icon: '' });
});
it('should transform pages keys', () => {
const manifestJSON = transformManifestKeys(
{
pages: [
{
path: '/',
name: 'home',
source: 'pages/Home/index',
dataPrefetch: [
{
url: '/a.com',
data: {
id: 123,
},
},
],
},
{
path: '/home1',
name: 'home1',
source: 'pages/Home1/index',
},
],
},
{ isRoot: true },
);
expect(manifestJSON?.pages?.length).toBe(2);
expect(manifestJSON?.pages![0].data_prefetch).toMatchObject([
{
url: '/a.com',
data: {
id: 123,
},
header: {},
prefetch_key: 'mtop',
},
]);
});
it('should not filter whitelist fields', () => {
const manifestJSON = transformManifestKeys(
{
a: 123,
},
{ isRoot: false },
);
expect(manifestJSON).toMatchObject({ a: 123 });
});
it('should transform enableExpiredManifest', () => {
const manifestJSON = transformManifestKeys(
{
enableExpiredManifest: true,
},
{ isRoot: false },
);
expect(manifestJSON).toMatchObject({ enable_expired_manifest: true });
});
it('should filter unknown fields in root', () => {
const manifestJSON = transformManifestKeys(
{
a: 123,
},
{ isRoot: true },
);
expect(manifestJSON).toMatchObject({});
});
it('should not transform requestHeaders', () => {
const manifestJSON = transformManifestKeys(
{
requestHeaders: {
'U-Tag': '${storage.uTag}',
},
},
{ isRoot: false },
);
expect(manifestJSON).toMatchObject({ request_headers: { 'U-Tag': '${storage.uTag}' } });
});
});
describe('parse manifest', async () => {
const options = {
publicPath: 'https://cdn-path.com/',
urlPrefix: 'https://url-prefix.com/',
routesConfig: (await import(path.join(__dirname, './mockConfig.mjs')))?.default,
excuteServerEntry: async () => mockServer,
routeManifest: path.join(__dirname, './route-manifest.json'),
};
it('should add urlPrefix to manifest', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
routes: [
'home',
'about',
'app/nest',
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].path).toBe('https://url-prefix.com/home');
expect(manifest?.pages![0].key).toBe('home');
expect(manifest?.pages![0].title).toBe('title-home');
expect(manifest?.pages![0].priority).toBe('low');
expect(manifest?.pages![1].path).toBe('https://url-prefix.com/about?c=123');
expect(manifest?.pages![2]?.frames![1].path).toBe('https://m.taobao.com');
expect(manifest?.app_worker?.url).toBe('https://cdn-path.com/pha-worker.js');
});
it('multiple should work with frames', async () => {
const phaManifest = {
routes: [
'home',
'about',
{
defaultFrameIndex: 0,
frames: ['home', 'about'],
},
],
};
const manifest = await parseManifest(phaManifest, options);
const remultipleManifests = await getMultipleManifest(manifest);
expect(remultipleManifests['home']?.pages![0]?.frames?.length).toBe(2);
});
it('should work with enable pull refresh', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
pullRefresh: {
reload: false,
},
routes: [
{
path: '/',
name: 'home',
},
{
path: '/about',
name: 'about',
},
{
path: '/',
name: 'frames',
frames: [
{
name: 'frame1',
url: 'https://m.taobao.com',
},
{
name: 'frame2',
url: 'https://m.taobao.com',
},
],
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].enable_pull_refresh).toBe(true);
expect(manifest?.pages![1].enable_pull_refresh).toBe(true);
expect(manifest?.pages![2].enable_pull_refresh).toBe(true);
});
it('should work with enable pull refresh', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
pullRefresh: {
reload: true,
},
routes: [
{
path: '/',
name: 'home',
pullRefresh: {
reload: true,
},
},
{
path: '/about',
name: 'about',
},
{
path: '/',
name: 'frames',
frames: [
{
name: 'frame1',
url: 'https://m.taobao.com',
pullRefresh: {
reload: true,
},
},
{
name: 'frame2',
url: 'https://m.taobao.com',
pullRefresh: {
reload: true,
},
},
],
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].pull_refresh).toBe(true);
expect(manifest?.pages![1].pull_refresh).toBe(true);
expect(manifest?.pages![2].frames![0].pull_refresh).toBe(true);
expect(manifest?.pages![2].frames![1].pull_refresh).toBe(true);
});
it('pull refresh of manifest should be default value of page', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
pullRefresh: {
reload: true,
},
routes: [
{
path: '/',
name: 'home',
},
{
path: '/about',
name: 'about',
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].pull_refresh).toBe(true);
expect(manifest?.pages![1].pull_refresh).toBe(true);
});
it('enable pull refresh of manifest should be default value of page', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
pullRefresh: true,
routes: [
{
path: '/',
name: 'home',
},
{
path: '/about',
name: 'about',
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].enable_pull_refresh).toBe(true);
expect(manifest?.pages![1].enable_pull_refresh).toBe(true);
});
it('enable pull refresh of page should cover value of page', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
pullRefresh: {
reload: true,
},
routes: [
{
path: '/',
name: 'home',
pullRefresh: {
reload: true,
},
},
{
path: '/about',
name: 'about',
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].pull_refresh).toBe(true);
expect(manifest?.pages![1].pull_refresh).toBe(true);
});
it('should work with enable pull refresh', async () => {
const phaManifest = {
appWorker: {
url: 'pha-worker.js',
},
routes: [
{
path: '/',
name: 'home',
pullRefresh: true,
},
],
};
const manifest = await parseManifest(phaManifest, options);
expect(manifest?.pages![0].enable_pull_refresh).toBe(true);
});
it('should set document to manifest', async () => {
const phaManifest = {
routes: [
'home',
'about',
'app/nest',
],
};
const manifest = await parseManifest(phaManifest, {
...options,
template: true,
});
expect(manifest?.pages![0].document).toBe('<html><body>/home-document</body></html>');
expect(manifest?.pages![1].document).toBe('<html><body>/about-document</body></html>');
expect(manifest?.pages![2]?.frames![0].document).toBe('<html><body>/home-document</body></html>');
});
it('should not support template', async () => {
const phaManifest = {
routes: [
'home',
'about',
'app/nest',
'404',
],
};
const manifest = await parseManifest(phaManifest, {
...options,
template: false,
});
expect(manifest.pages![0].script).toBeUndefined();
expect(manifest.pages![0].stylesheet).toBeUndefined();
expect(manifest.pages![0].document).toBeUndefined();
});
it('should config url to path when user config page.url', async () => {
const phaManifest = {
routes: [
{
path: '/',
name: 'home',
url: 'https://m.taobao.com',
},
{
pageHeader: {
url: 'https://m.taobao.com',
source: 'pages/Header',
},
frames: [
{
path: '/frame1',
name: 'frame1',
url: 'https://m.taobao.com',
},
],
},
'about',
],
tabBar: {
custom: true,
items: ['home', 'frame1'],
url: 'https://m.taobao.com',
},
};
// @ts-ignore ignore type error with mix config key
const manifest = await parseManifest(phaManifest, options);
expect(manifest.pages![0].path).toBe('https://m.taobao.com');
// @ts-ignore
expect(manifest.pages![0].url).toBeUndefined();
expect(manifest.pages![0].script).toBeUndefined();
expect(manifest.pages![0].stylesheet).toBeUndefined();
expect(manifest.pages![1]?.tab_header?.url).toBe('https://m.taobao.com');
// @ts-ignore
expect(manifest.pages![1]?.tab_header?.source).toBeUndefined();
expect(manifest.pages![1]?.frames![0].path).toBe('https://m.taobao.com');
// @ts-ignore
expect(manifest.pages![1]?.frames![0].url).toBeUndefined();
expect(manifest.pages![1]?.frames![0].script).toBeUndefined();
expect(manifest.pages![1]?.frames![0].stylesheet).toBeUndefined();
expect(manifest?.tab_bar?.url).toBe('https://m.taobao.com');
// @ts-ignore
expect(manifest?.tab_bar?.source).toBeUndefined();
expect(manifest?.tab_bar?.items).toHaveLength(2);
expect(manifest.pages![2].path).toBe('https://url-prefix.com/about?c=123');
});
it('should not cover url by pageUrl', async () => {
const phaManifest = {
routes: [
{
pageHeader: {
source: 'pages/header',
},
frames: [
{
name: 'frame1',
url: 'https://m.taobao.com',
},
],
},
],
tabBar: {
custom: true,
source: 'pages/CustomTabBar',
items: ['home', 'frame1'],
},
};
const manifest = await parseManifest(phaManifest, {
...options,
template: true,
});
expect(manifest.pages![0]?.tab_header?.url).toBe('https://url-prefix.com/header');
expect(manifest.pages![0]?.tab_header?.html).toBe('<html><body>/header-document</body></html>');
expect(manifest?.tab_bar?.url).toBe('https://url-prefix.com/CustomTabBar');
});
it('should not set html to tabBar when template is false', async () => {
const phaManifest = {
routes: [
{
pageHeader: {
source: 'pages/header',
},
frames: [
{
name: 'frame1',
url: 'https://m.taobao.com',
},
],
},
],
tabBar: {
custom: true,
source: 'pages/CustomTabBar',
items: ['home', 'frame1'],
},
};
const manifest = await parseManifest(phaManifest, {
...options,
template: false,
});
expect(manifest.pages![0]?.tab_header?.html).toBeUndefined();
});
it('error source without pages', async () => {
const phaManifest = {
tabBar: {
source: 'components/CustomTabBar',
},
};
try {
await parseManifest(phaManifest, options);
expect(true).toBe(false);
} catch (err) {
expect(true).toBe(true);
}
});
it('url failed with new URL', async () => {
const phaManifest = {
tabBar: {
source: 'pages/tabBar',
},
};
const manifest = await parseManifest(phaManifest, {
...options,
urlPrefix: '{{xxx}}/',
});
expect(manifest.tab_bar?.url).toBe('{{xxx}}/tabBar');
expect(manifest.tab_bar?.name).toBe('{{xxx}}');
});
it('should not inject html when tabHeader & tabBar have url field', async () => {
const phaManifest = {
routes: [
{
pageHeader: {
source: 'pages/Header',
url: 'https://m.taobao.com',
},
},
],
tabBar: {
custom: true,
source: 'pages/CustomTabBar',
items: ['home', 'frame1'],
},
};
const manifest = await parseManifest(phaManifest, {
...options,
template: true,
});
expect(manifest.pages![0].tab_header?.url).toBe('https://m.taobao.com');
expect(manifest.pages![0].tab_header?.html).toBeUndefined();
expect(manifest.tab_bar?.url).toBe('https://url-prefix.com/CustomTabBar');
});
it('should work with static dataloader', async () => {
const phaManifest = {
title: 'test',
routes: [
{
pageHeader: {},
frames: [
'blog',
'home',
'about',
],
},
'home',
'about',
],
};
const staticDataloader = {
key: 'dataLoader222',
prefetch_type: 'mtop',
api: 'query222',
v: '0.0.1',
data: {
aaa: 111,
},
ext_headers: {},
};
const manifest = await parseManifest(phaManifest, {
...options,
dataloaderConfig: {
home: {
loader: [
() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'About',
});
}, 1 * 100);
});
},
staticDataloader,
],
},
},
});
expect(manifest.pages![0].frames![1].data_prefetch?.length).toBe(1);
expect(manifest.pages![1].data_prefetch?.length).toBe(2);
expect(manifest.pages![2].data_prefetch).toBeUndefined();
});
it('prefetch of dataloader should not be decamelized', async () => {
const phaManifest = {
title: 'test',
routes: [
{
pageHeader: {},
frames: [
'blog',
'home',
'about',
],
},
'home',
'about',
],
};
const staticDataloader = {
key: 'dataLoader222',
prefetch_type: 'mtop',
api: 'query222',
v: '0.0.1',
data: {
aaa: 111,
aaB: 222,
},
ext_headers: {},
};
const manifest = await parseManifest(phaManifest, {
...options,
dataloaderConfig: {
home: {
loader: [
() => {
return new Promise((resolve) => {
setTimeout(() => {
resolve({
name: 'About',
});
}, 1 * 100);
});
},
staticDataloader,
],
},
},
});
expect(manifest.pages![1].data_prefetch![1]?.data.aaB).toBe(222);
});
});
describe('get multiple manifest', async () => {
const options = {
publicPath: 'https://cdn-path.com/',
urlPrefix: 'https://url-prefix.com/',
routesConfig: (await import(path.join(__dirname, './mockConfig.mjs')))?.default,
excuteServerEntry: async () => mockServer,
routeManifest: path.join(__dirname, './route-manifest.json'),
};
it('simple routes', async () => {
const phaManifest = {
routes: [
'home',
'about',
],
};
const manifest = await parseManifest(phaManifest, options);
const multipleManifest = getMultipleManifest(manifest);
expect(multipleManifest?.home?.pages?.length).toBe(1);
expect(multipleManifest?.home?.data_prefetch).toMatchObject([{ api: 'test/api' }]);
expect(multipleManifest?.about?.pages?.length).toBe(1);
});
it('routes with frame', async () => {
const phaManifest = {
routes: [
{
frames: [
'app/nest',
{
url: 'https://m.taobao.com',
},
],
},
'about',
],
};
const manifest = await parseManifest(phaManifest, options);
const multipleManifest = getMultipleManifest(manifest);
expect(multipleManifest?.['app/nest']?.pages?.length).toBe(1);
expect(multipleManifest?.['app/nest']?.pages![0].frames?.length).toBe(2);
expect(multipleManifest?.about?.pages?.length).toBe(1);
});
});