mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-09 00:19:13 +08:00
it works
This commit is contained in:
parent
16aa7229be
commit
ee122a471e
@ -1,54 +1,99 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
import type { Page, FrameLocator } from 'playwright';
|
||||
import type { Page, FrameLocator, Browser } from 'playwright';
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
import type { Config } from './types';
|
||||
import { now } from './utils';
|
||||
|
||||
const now = () => new Date().getTime();
|
||||
const TIMEOUT = 20000;
|
||||
|
||||
export const browse = async (url: string, config: Config) => {
|
||||
if (!config.managerLoaded && !config.previewLoadedText) return undefined;
|
||||
interface Result {
|
||||
managerHeaderVisible?: number;
|
||||
managerIndexVisible?: number;
|
||||
storyVisible?: number;
|
||||
docsVisible?: number;
|
||||
}
|
||||
|
||||
export const browse = async (url: string) => {
|
||||
const start = now();
|
||||
let managerLoaded;
|
||||
let previewLoaded;
|
||||
const result: Result = {};
|
||||
|
||||
const browser = await chromium.launch(/* { headless: false } */);
|
||||
const page = await browser.newPage();
|
||||
page.on('console', (msg: any) => {
|
||||
const type = msg.type();
|
||||
console.log(type, msg.text());
|
||||
});
|
||||
|
||||
await page.goto(url);
|
||||
|
||||
if (config.managerLoaded) {
|
||||
await page.waitForSelector(config.managerLoaded, { state: 'attached' });
|
||||
managerLoaded = now() - start;
|
||||
console.log('manager', new Date(), config.managerLoaded, managerLoaded);
|
||||
}
|
||||
|
||||
if (config.previewLoadedText) {
|
||||
let previewFrame: FrameLocator | Page;
|
||||
previewFrame = page;
|
||||
if (config.previewFrameLocator) {
|
||||
previewFrame = await page.frameLocator(config.previewFrameLocator);
|
||||
}
|
||||
|
||||
let actualText;
|
||||
while (now() - start < TIMEOUT && (!actualText || !actualText.length)) {
|
||||
const preview = await previewFrame.getByText(config.previewLoadedText);
|
||||
actualText = await preview.innerText();
|
||||
}
|
||||
console.log({ actualText });
|
||||
if (!actualText?.includes(config.previewLoadedText)) {
|
||||
throw new Error('previewLoadedText not found');
|
||||
}
|
||||
previewLoaded = now() - start;
|
||||
console.log('preview', new Date(), config.previewLoadedText, previewLoaded);
|
||||
}
|
||||
Object.assign(result, await benchStory(browser, url, start));
|
||||
Object.assign(result, await benchDocs(browser, url, start));
|
||||
|
||||
await browser.close();
|
||||
return { managerLoaded, previewLoaded };
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
async function benchDocs(browser: Browser, url: string, start: number) {
|
||||
const page = await browser.newPage();
|
||||
const result: Result = {};
|
||||
await page.goto(`${url}?path=/docs/example-button--docs`);
|
||||
|
||||
const tasks = [
|
||||
async () => {
|
||||
let previewFrame: FrameLocator | Page = page;
|
||||
previewFrame = await page.frameLocator('#storybook-preview-iframe');
|
||||
|
||||
let actualText;
|
||||
while (now() - start < TIMEOUT && (!actualText || !actualText.length)) {
|
||||
const preview = await previewFrame.getByText('Primary UI component for user interaction');
|
||||
actualText = await preview.innerText();
|
||||
}
|
||||
|
||||
if (!actualText?.includes('Primary UI component for user interaction')) {
|
||||
throw new Error('docs not visible in time');
|
||||
}
|
||||
|
||||
result.docsVisible = now() - start;
|
||||
},
|
||||
];
|
||||
|
||||
await Promise.all(tasks.map((t) => t()));
|
||||
|
||||
await page.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function benchStory(browser: Browser, url: string, start: number) {
|
||||
const page = await browser.newPage();
|
||||
const result: Result = {};
|
||||
await page.goto(`${url}?path=/story/example-button--primary`);
|
||||
|
||||
const tasks = [
|
||||
//
|
||||
async () => {
|
||||
await page.waitForSelector('.sidebar-header', { state: 'attached' });
|
||||
result.managerHeaderVisible = now() - start;
|
||||
},
|
||||
async () => {
|
||||
await page.waitForSelector('#example-button--primary', { state: 'attached' });
|
||||
result.managerIndexVisible = now() - start;
|
||||
},
|
||||
async () => {
|
||||
let previewFrame: FrameLocator | Page = page;
|
||||
previewFrame = await page.frameLocator('#storybook-preview-iframe');
|
||||
|
||||
let actualText;
|
||||
while (now() - start < TIMEOUT && (!actualText || !actualText.length)) {
|
||||
const preview = await previewFrame.getByText('Button');
|
||||
actualText = await preview.innerText();
|
||||
}
|
||||
|
||||
if (!actualText?.includes('Button')) {
|
||||
throw new Error('preview not visible in time');
|
||||
}
|
||||
|
||||
result.storyVisible = now() - start;
|
||||
},
|
||||
];
|
||||
|
||||
await Promise.all(tasks.map((t) => t()));
|
||||
|
||||
await page.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
import type { Config } from './types';
|
||||
|
||||
export const storybookConfig: Config = {
|
||||
managerLoaded: '.sidebar-header',
|
||||
previewFrameLocator: '#storybook-preview-iframe',
|
||||
previewLoadedText: 'Button',
|
||||
};
|
@ -1,4 +1,3 @@
|
||||
export * from './types';
|
||||
export * from './config';
|
||||
export * from './utils';
|
||||
export { browse } from './browse';
|
||||
|
@ -86,9 +86,11 @@
|
||||
"@types/escodegen": "^0.0.6",
|
||||
"@types/express": "^4.17.11",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/http-server": "^0.12.1",
|
||||
"@types/lodash": "^4",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
"@types/pretty-hrtime": "^1.0.0",
|
||||
"@types/prompts": "2.0.11",
|
||||
"@types/react": "^16.14.34",
|
||||
"@types/react-dom": "^16.9.17",
|
||||
@ -154,7 +156,9 @@
|
||||
"playwright": "^1.35.0",
|
||||
"playwright-core": "^1.35.0",
|
||||
"prettier": "^2.8.0",
|
||||
"pretty-bytes": "^6.1.0",
|
||||
"pretty-hrtime": "^1.0.0",
|
||||
"pretty-ms": "^8.0.0",
|
||||
"process": "^0.11.10",
|
||||
"prompts": "^2.4.0",
|
||||
"react": "16.14.0",
|
||||
|
@ -3,6 +3,9 @@ import type { Task } from '../task';
|
||||
import { PORT, dev } from './dev';
|
||||
import { serve } from './serve';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
||||
const dynamicImport = new Function('specifier', 'return import(specifier)');
|
||||
|
||||
export const bench: Task = {
|
||||
description: 'Run benchmarks against a sandbox in dev mode',
|
||||
dependsOn: ['build'],
|
||||
@ -11,35 +14,60 @@ export const bench: Task = {
|
||||
},
|
||||
|
||||
async run(details, options) {
|
||||
const { browse, saveBench, storybookConfig } = await import('../bench');
|
||||
const url = `http://localhost:${PORT}?path=/story/example-button--primary`;
|
||||
const controllers: AbortController[] = [];
|
||||
try {
|
||||
const { browse, saveBench, loadBench } = await import('../bench');
|
||||
const { default: prettyBytes } = await dynamicImport('pretty-bytes');
|
||||
const { default: prettyTime } = await dynamicImport('pretty-ms');
|
||||
|
||||
const devController = await dev.run(details, options);
|
||||
if (!devController) {
|
||||
throw new Error('dev: controller is null');
|
||||
}
|
||||
const url = `http://localhost:${PORT}`;
|
||||
|
||||
const devBrowseResult = await browse(url, storybookConfig);
|
||||
devController.abort();
|
||||
|
||||
const serveController = await serve.run(details, options);
|
||||
if (!serveController) {
|
||||
throw new Error('serve: controller is null');
|
||||
}
|
||||
|
||||
const buildBrowseResult = await browse(url, storybookConfig);
|
||||
serveController.abort();
|
||||
|
||||
await saveBench(
|
||||
{
|
||||
devManagerLoaded: devBrowseResult.managerLoaded,
|
||||
devPreviewLoaded: devBrowseResult.previewLoaded,
|
||||
buildManagerLoaded: buildBrowseResult.managerLoaded,
|
||||
buildPreviewLoaded: buildBrowseResult.previewLoaded,
|
||||
},
|
||||
{
|
||||
rootDir: details.sandboxDir,
|
||||
const devController = await dev.run(details, { ...options, debug: false });
|
||||
if (!devController) {
|
||||
throw new Error('dev: controller is null');
|
||||
}
|
||||
);
|
||||
controllers.push(devController);
|
||||
|
||||
const devBrowseResult = await browse(url);
|
||||
devController.abort();
|
||||
|
||||
const serveController = await serve.run(details, { ...options, debug: false });
|
||||
if (!serveController) {
|
||||
throw new Error('serve: controller is null');
|
||||
}
|
||||
controllers.push(serveController);
|
||||
|
||||
const buildBrowseResult = await browse(url);
|
||||
serveController.abort();
|
||||
|
||||
await saveBench(
|
||||
{
|
||||
devManagerHeaderVisible: devBrowseResult.managerHeaderVisible,
|
||||
devManagerIndexVisible: devBrowseResult.managerIndexVisible,
|
||||
devStoryVisible: devBrowseResult.storyVisible,
|
||||
devDocsVisible: devBrowseResult.docsVisible,
|
||||
|
||||
buildManagerHeaderVisible: buildBrowseResult.managerHeaderVisible,
|
||||
buildManagerIndexVisible: buildBrowseResult.managerIndexVisible,
|
||||
buildPreviewVisible: buildBrowseResult.storyVisible,
|
||||
buildDocsVisible: buildBrowseResult.docsVisible,
|
||||
},
|
||||
{
|
||||
rootDir: details.sandboxDir,
|
||||
}
|
||||
);
|
||||
|
||||
const data = await loadBench({ rootDir: details.sandboxDir });
|
||||
Object.entries(data).forEach(([key, value]) => {
|
||||
if (key.includes('Size')) {
|
||||
console.log(`${key}: ${prettyBytes(value)}`);
|
||||
} else {
|
||||
console.log(`${key}: ${prettyTime(value)}`);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
controllers.forEach((c) => c.abort());
|
||||
throw e;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -6,6 +6,7 @@ import { exec } from '../utils/exec';
|
||||
export const PORT = process.env.STORYBOOK_SERVE_PORT
|
||||
? parseInt(process.env.STORYBOOK_SERVE_PORT, 10)
|
||||
: 8001;
|
||||
|
||||
export const serve: Task = {
|
||||
description: 'Serve the build storybook for a sandbox',
|
||||
service: true,
|
||||
@ -16,12 +17,14 @@ export const serve: Task = {
|
||||
async run({ builtSandboxDir, codeDir }, { debug, dryRun }) {
|
||||
const controller = new AbortController();
|
||||
exec(
|
||||
`yarn http-server ${builtSandboxDir} --port ${PORT}`,
|
||||
`yarn http-server ${builtSandboxDir} --port ${PORT} -s`,
|
||||
{ cwd: codeDir },
|
||||
{ dryRun, debug, signal: controller.signal as AbortSignal }
|
||||
).catch((err) => {
|
||||
// If aborted, we want to make sure the rejection is handled.
|
||||
if (!err.killed) throw err;
|
||||
if (!err.killed) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
const { default: waitOn } = await import('wait-on');
|
||||
await waitOn({ resources: [`http://localhost:${PORT}`], interval: 16 });
|
||||
|
1
scripts/typings.d.ts
vendored
1
scripts/typings.d.ts
vendored
@ -1 +0,0 @@
|
||||
declare module 'pretty-hrtime';
|
@ -2951,9 +2951,11 @@ __metadata:
|
||||
"@types/escodegen": ^0.0.6
|
||||
"@types/express": ^4.17.11
|
||||
"@types/fs-extra": ^11.0.1
|
||||
"@types/http-server": ^0.12.1
|
||||
"@types/lodash": ^4
|
||||
"@types/node": ^16.0.0
|
||||
"@types/node-fetch": ^2.5.7
|
||||
"@types/pretty-hrtime": ^1.0.0
|
||||
"@types/prompts": 2.0.11
|
||||
"@types/react": ^16.14.34
|
||||
"@types/react-dom": ^16.9.17
|
||||
@ -3020,7 +3022,9 @@ __metadata:
|
||||
playwright: ^1.35.0
|
||||
playwright-core: ^1.35.0
|
||||
prettier: ^2.8.0
|
||||
pretty-bytes: ^6.1.0
|
||||
pretty-hrtime: ^1.0.0
|
||||
pretty-ms: ^8.0.0
|
||||
process: ^0.11.10
|
||||
prompts: ^2.4.0
|
||||
react: 16.14.0
|
||||
@ -3530,6 +3534,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/http-server@npm:^0.12.1":
|
||||
version: 0.12.1
|
||||
resolution: "@types/http-server@npm:0.12.1"
|
||||
dependencies:
|
||||
"@types/connect": "*"
|
||||
checksum: 9b2397640b961589b23c98c5b1ed9f507fe07413f5723b3b4f447740d70ed702a356ec32dfa7e77fc477505f65758e423ee8745b879efb1fdddc23a840c29534
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/is-empty@npm:^1.0.0":
|
||||
version: 1.2.1
|
||||
resolution: "@types/is-empty@npm:1.2.1"
|
||||
@ -3711,6 +3724,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/pretty-hrtime@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "@types/pretty-hrtime@npm:1.0.1"
|
||||
checksum: e990110a3626e987319092c5149d5ea244785b83fbbd8e62605714ec1fa4317a3524ae0b6381cdc2ca92619d9a451b3fe9ff4085c42826f5398e3380d3031bff
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/prompts@npm:2.0.11":
|
||||
version: 2.0.11
|
||||
resolution: "@types/prompts@npm:2.0.11"
|
||||
@ -12787,6 +12807,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-ms@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "parse-ms@npm:3.0.0"
|
||||
checksum: 056b4a32a9d3749f3f4cfffefb45c45540491deaa8e1d8ad43c2ddde7ba04edd076bd1b298f521238bb5fb084a9b2c4a2ebb78aefa651afbc4c2b0af4232fc54
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-passwd@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "parse-passwd@npm:1.0.0"
|
||||
@ -13159,6 +13186,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-bytes@npm:^6.1.0":
|
||||
version: 6.1.0
|
||||
resolution: "pretty-bytes@npm:6.1.0"
|
||||
checksum: 717ed82f8d4bbf038b623062d360bf71fe9107c3f2c43de7d0fd3e9610780306e38059d8d8356bc2c151d2929fada934c051f7d51be2587ebe8f426268b43112
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-format@npm:^27.0.2":
|
||||
version: 27.5.1
|
||||
resolution: "pretty-format@npm:27.5.1"
|
||||
@ -13188,6 +13222,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-ms@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "pretty-ms@npm:8.0.0"
|
||||
dependencies:
|
||||
parse-ms: ^3.0.0
|
||||
checksum: e960d633ecca45445cf5c6dffc0f5e4bef6744c92449ab0e8c6c704800675ab71e181c5e02ece5265e02137a33e313d3f3e355fbf8ea30b4b5b23de423329f8d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"prettyjson@npm:^1.2.1":
|
||||
version: 1.2.5
|
||||
resolution: "prettyjson@npm:1.2.5"
|
||||
|
Loading…
x
Reference in New Issue
Block a user