Skip to content

Commit

Permalink
Merge pull request #47 from storybookjs/fix/reset-page-between-tests
Browse files Browse the repository at this point in the history
fix: reset page between tests
  • Loading branch information
yannbf authored Feb 23, 2022
2 parents 84f9be0 + e38bd00 commit 75acd03
Show file tree
Hide file tree
Showing 9 changed files with 464 additions and 290 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ build-storybook.log
.DS_Store
.env
.cache
stories/stress-test
stories/atoms/StressTest.stories.js
yarn-error.log
98 changes: 3 additions & 95 deletions playwright/custom-environment.js
Original file line number Diff line number Diff line change
@@ -1,104 +1,12 @@
const { setupPage } = require('../dist/cjs/setup-page');

require('regenerator-runtime/runtime');
const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvironment').default;

class CustomEnvironment extends PlaywrightEnvironment {
async setup() {
await super.setup();
const page = this.global.page;
const start = new Date();
const { TARGET_URL: targetUrl, REFERENCE_URL: referenceUrl } = process.env

await page.goto(`${targetUrl}iframe.html`, { waitUntil: 'load' }).catch((err) => {
if (err.message?.includes('ERR_CONNECTION_REFUSED')) {
const errorMessage = `Could not access the Storybook instance at ${targetUrl}. Are you sure it's running?\n\n${err.message}`;
throw new Error(errorMessage)
}

throw err;
}); // FIXME: configure
console.log(`page loaded in ${new Date() - start}ms.`);

// if we ever want to log something from the browser to node
await page.exposeBinding('logToPage', (_, message) => console.log(message));

await page.addScriptTag({
content: `
class StorybookTestRunnerError extends Error {
constructor(storyId, errorMessage) {
super(errorMessage);
this.name = 'StorybookTestRunnerError';
const storyUrl = \`${referenceUrl || targetUrl}?path=/story/\${storyId}\`;
const finalStoryUrl = \`\${storyUrl}&addonPanel=storybook/interactions/panel\`;
this.message = \`\nAn error occurred in the following story:\n\${finalStoryUrl}\n\nMessage:\n \${errorMessage}\`;
}
}
async function __throwError(storyId, errorMessage) {
throw new StorybookTestRunnerError(storyId, errorMessage);
}
async function __waitForElement(selector) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject();
}, 10000);
if (document.querySelector(selector)) {
clearTimeout(timeout);
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
clearTimeout(timeout);
resolve(document.querySelector(selector));
observer.disconnect();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
async function __test(storyId) {
try {
await __waitForElement('#root');
} catch(err) {
const message = \`Timed out waiting for Storybook to load after 10 seconds. Are you sure the Storybook is running correctly in that URL? Is the Storybook private (e.g. under authentication layers)?\n\n\nHTML: \${document.body.innerHTML}\`;
throw new StorybookTestRunnerError(storyId, message);
}
const channel = window.__STORYBOOK_ADDONS_CHANNEL__;
if(!channel) {
throw new StorybookTestRunnerError(
storyId,
'The test runner could not access the Storybook channel. Are you sure the Storybook is running correctly in that URL?'
);
}
return new Promise((resolve, reject) => {
channel.on('storyRendered', () => resolve(document.getElementById('root')));
channel.on('storyUnchanged', () => resolve(document.getElementById('root')));
channel.on('storyErrored', ({ description }) => reject(
new StorybookTestRunnerError(storyId, description))
);
channel.on('storyThrewException', (error) => reject(
new StorybookTestRunnerError(storyId, error.message))
);
channel.on('storyMissing', (id) => id === storyId && reject(
new StorybookTestRunnerError(storyId, 'The story was missing when trying to access it.'))
);
channel.emit('setCurrentStory', { storyId });
});
};
`,
});
await setupPage(this.global.page)
}

async teardown() {
Expand Down
4 changes: 2 additions & 2 deletions scripts/generate-dynamic-stories.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const fs = require('fs');

const storiesFolderPath = 'stories/stress-test';
const storiesFolderPath = 'stories/atoms';

if (!fs.existsSync(storiesFolderPath)) {
fs.mkdirSync(storiesFolderPath);
Expand All @@ -11,7 +11,7 @@ const storyCount = Number(process.env.DYNAMIC_STORIES_COUNT) || 500;
let content = `
import React from 'react';
import { Button } from '../basic/Button';
import { Button } from './Button';
export default {
title: 'StressTest',
Expand Down
23 changes: 20 additions & 3 deletions src/csf/transformCsf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type TestPrefixer = (context: TestContext) => TemplateResult;
interface TransformOptions {
clearBody?: boolean;
filePrefixer?: FilePrefixer;
beforeEachPrefixer?: FilePrefixer;
testPrefixer?: TestPrefixer;
insertTestIfEmpty?: boolean;
defaultTitle?: string;
Expand Down Expand Up @@ -60,15 +61,26 @@ const makePlayTest = (
];
};

const makeDescribe = (key: string, tests: t.Statement[]): t.Statement | null => {
const makeDescribe = (
key: string,
tests: t.Statement[],
beforeEachBlock?: t.ExpressionStatement
): t.Statement | null => {
const blockStatements = beforeEachBlock ? [beforeEachBlock, ...tests] : tests;
return t.expressionStatement(
t.callExpression(t.identifier('describe'), [
t.stringLiteral(key),
t.arrowFunctionExpression([], t.blockStatement(tests)),
t.arrowFunctionExpression([], t.blockStatement(blockStatements)),
])
);
};

const makeBeforeEach = (beforeEachPrefixer: FilePrefixer) => {
const stmt = beforeEachPrefixer() as t.ExpressionStatement;

return t.expressionStatement(t.callExpression(t.identifier('beforeEach'), [stmt.expression]));
};

const makeArray = (templateResult: TemplateResult) =>
Array.isArray(templateResult) ? templateResult : [templateResult];

Expand All @@ -78,6 +90,7 @@ export const transformCsf = (
filePrefixer,
clearBody = false,
testPrefixer,
beforeEachPrefixer,
insertTestIfEmpty,
defaultTitle,
}: TransformOptions = {}
Expand Down Expand Up @@ -118,7 +131,11 @@ export const transformCsf = (
}
if (!clearBody) result = `${result}${code}\n`;
if (allTests.length) {
const describe = makeDescribe(csf.meta.title, allTests);
const describe = makeDescribe(
csf.meta.title,
allTests,
beforeEachPrefixer ? makeBeforeEach(beforeEachPrefixer) : undefined
);
const { code: describeCode } = generate(describe, {});
result = dedent`
${result}
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './playwright/hooks';
export * from './config/jest-playwright';
export * from './setup-page';
export * from './util/getTestRunnerConfig';
Loading

0 comments on commit 75acd03

Please sign in to comment.