Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 0.13.0 #341

Merged
merged 16 commits into from
Aug 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ const addons = [
: '@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
'@storybook/addon-mdx-gfm',
];
module.exports = {
stories,
Expand All @@ -40,7 +39,7 @@ module.exports = {
disableTelemetry: true,
},
framework: {
name: '@storybook/react-webpack5',
name: '@storybook/react-vite',
options: {},
},
docs: {
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ Usage: test-storybook [options]
| `--watch` | Watch files for changes and rerun tests related to changed files.<br/>`test-storybook --watch` |
| `--watchAll` | Watch files for changes and rerun all tests when something changes.<br/>`test-storybook --watchAll` |
| `--coverage` | Indicates that test coverage information should be collected and reported in the output <br/>`test-storybook --coverage` |
| `--coverageDirectory` | Directory where to write coverage report output <br/>`test-storybook --coverage --coverageDirectory coverage/ui/storybook` |
| `--url` | Define the URL to run tests in. Useful for custom Storybook URLs <br/>`test-storybook --url http://the-storybook-url-here.com` |
| `--browsers` | Define browsers to run tests in. One or multiple of: chromium, firefox, webkit <br/>`test-storybook --browsers firefox chromium` |
| `--maxWorkers [amount]` | Specifies the maximum number of workers the worker-pool will spawn for running tests <br/>`test-storybook --maxWorkers=2` |
Expand All @@ -150,6 +151,7 @@ Usage: test-storybook [options]
| `--junit` | Indicates that test information should be reported in a junit file. <br/>`test-storybook --**junit**` |
| `--ci` | Instead of the regular behavior of storing a new snapshot automatically, it will fail the test and require Jest to be run with `--updateSnapshot`. <br/>`test-storybook --ci` |
| `--shard [shardIndex/shardCount]` | Splits your test suite across different machines to run in CI. <br/>`test-storybook --shard=1/3` |
| `--failOnConsole` | Makes tests fail on browser console errors<br/>`test-storybook --failOnConsole` |

## Ejecting configuration

Expand Down
34 changes: 17 additions & 17 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"build-storybook": "storybook build",
"release": "yarn build && auto shipit",
"test-storybook": "node dist/test-storybook",
"test-storybook:failures": "SKIP_SNAPSHOTS=true TEST_FAILURES=1 yarn test-storybook --json --outputFile test-results.json",
"test-storybook:failures": "SKIP_SNAPSHOTS=true TEST_FAILURES=1 yarn test-storybook --json --failOnConsole --outputFile test-results.json",
"test-storybook:no-cache": "yarn test-storybook --no-cache",
"test-storybook:json": "yarn test-storybook --stories-json",
"test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx serve storybook-static -l 6006\" \"wait-on tcp:6006 && yarn test-storybook\"",
Expand All @@ -55,15 +55,15 @@
"@babel/preset-typescript": "^7.18.6",
"@jest/types": "^28.1.3",
"@storybook/addon-coverage": "^0.0.7",
"@storybook/addon-essentials": "^7.0.0",
"@storybook/addon-interactions": "^7.0.0",
"@storybook/addon-mdx-gfm": "^7.0.0",
"@storybook/addon-essentials": "^7.2.0",
"@storybook/addon-interactions": "^7.2.0",
"@storybook/jest": "^0.1.0",
"@storybook/react": "^7.0.0",
"@storybook/react-webpack5": "^7.0.0",
"@storybook/testing-library": "^0.1.0",
"@storybook/react": "^7.2.0",
"@storybook/react-vite": "^7.2.0",
"@storybook/testing-library": "^0.2.0",
"@types/jest": "^27.0.3",
"@types/node": "^16.4.1",
"@vitejs/plugin-react": "^4.0.3",
"auto": "^10.3.0",
"babel-jest": "^28.1.3",
"babel-loader": "^8.1.0",
Expand All @@ -73,30 +73,23 @@
"jest-image-snapshot": "^5.1.0",
"lint-staged": "^13.0.3",
"prettier": "^2.8.1",
"prop-types": "^15.7.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rimraf": "^3.0.2",
"semver": "^7.3.7",
"storybook": "^7.0.0",
"storybook": "^7.2.0",
"ts-jest": "^28.0.8",
"tsup": "^6.5.0",
"typescript": "~4.9.4",
"wait-on": "^6.0.0"
"wait-on": "^6.0.0",
"vite": "^4.4.5"
},
"lint-staged": {
"*.{ts,js,tsx,jsx,css,md}": "prettier --write"
},
"publishConfig": {
"access": "public"
},
"storybook": {
"displayName": "Storybook test runner",
"unsupportedFrameworks": [
"react-native"
],
"icon": "https://user-images.githubusercontent.com/321738/63501763-88dbf600-c4cc-11e9-96cd-94adadc2fd72.png"
},
"dependencies": {
"@babel/core": "^7.22.5",
"@babel/generator": "^7.22.5",
Expand Down Expand Up @@ -136,5 +129,12 @@
"npm",
"released"
]
},
"storybook": {
"displayName": "Storybook test runner",
"unsupportedFrameworks": [
"react-native"
],
"icon": "https://user-images.githubusercontent.com/321738/63501763-88dbf600-c4cc-11e9-96cd-94adadc2fd72.png"
}
}
19 changes: 12 additions & 7 deletions src/config/jest-playwright.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import path from 'path';
import { getProjectRoot } from '@storybook/core-common';

const TEST_RUNNER_PATH = process.env.STORYBOOK_TEST_RUNNER_PATH || '@storybook/test-runner';

/**
* IMPORTANT NOTE:
Expand All @@ -25,11 +28,11 @@ const getJestPlaywrightConfig = () => {
);
return {
runner: path.join(presetBasePath, 'runner.js'),
globalSetup: '@storybook/test-runner/playwright/global-setup.js',
globalTeardown: '@storybook/test-runner/playwright/global-teardown.js',
testEnvironment: '@storybook/test-runner/playwright/custom-environment.js',
globalSetup: require.resolve(TEST_RUNNER_PATH + '/playwright/global-setup.js'),
globalTeardown: require.resolve(TEST_RUNNER_PATH + '/playwright/global-teardown.js'),
testEnvironment: require.resolve(TEST_RUNNER_PATH + '/playwright/custom-environment.js'),
setupFilesAfterEnv: [
'@storybook/test-runner/playwright/jest-setup.js',
require.resolve(TEST_RUNNER_PATH + '/playwright/jest-setup.js'),
expectPlaywrightPath,
path.join(presetBasePath, 'lib', 'extends.js'),
],
Expand Down Expand Up @@ -66,13 +69,15 @@ export const getJestConfig = () => {

const reporters = STORYBOOK_JUNIT ? ['default', jestJunitPath] : ['default'];

const testMatch = (STORYBOOK_STORIES_PATTERN && STORYBOOK_STORIES_PATTERN.split(';')) || [];

let config = {
rootDir: process.cwd(),
rootDir: getProjectRoot(),
roots: TEST_ROOT ? [TEST_ROOT] : undefined,
reporters,
testMatch: STORYBOOK_STORIES_PATTERN && STORYBOOK_STORIES_PATTERN.split(';'),
testMatch,
transform: {
'^.+\\.stories\\.[jt]sx?$': '@storybook/test-runner/playwright/transform',
'^.+\\.stories\\.[jt]sx?$': require.resolve(TEST_RUNNER_PATH + '/playwright/transform'),
'^.+\\.[jt]sx?$': swcJestPath,
},
snapshotSerializers: [jestSerializerHtmlPath],
Expand Down
24 changes: 13 additions & 11 deletions src/playwright/transformPlaywright.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
import dedent from 'ts-dedent';
import path from 'path';
import * as coreCommon from '@storybook/core-common';
import * as storybookMain from '../util/getStorybookMain';

import { transformPlaywright } from './transformPlaywright';

jest.mock('@storybook/core-common');
jest.mock('@storybook/core-common', () => ({
...jest.requireActual('@storybook/core-common'),
getProjectRoot: jest.fn(() => '/foo/bar'),
normalizeStories: jest.fn(() => [
{
titlePrefix: 'Example',
files: '**/*.stories.@(mdx|tsx|ts|jsx|js)',
directory: './stories/basic',
importPathMatcher:
/^\.[\\/](?:stories\/basic(?:\/(?!\.)(?:(?:(?!(?:^|\/)\.).)*?)\/|\/|$)(?!\.)(?=.)[^/]*?\.stories\.(mdx|tsx|ts|jsx|js))$/,
},
]),
}));

expect.addSnapshotSerializer({
print: (val: any) => val.trim(),
Expand All @@ -24,15 +35,6 @@ describe('Playwright', () => {
},
],
}));
jest.spyOn(coreCommon, 'normalizeStories').mockImplementation(() => [
{
titlePrefix: 'Example',
files: '**/*.stories.@(mdx|tsx|ts|jsx|js)',
directory: './stories/basic',
importPathMatcher:
/^\.[\\/](?:stories\/basic(?:\/(?!\.)(?:(?:(?!(?:^|\/)\.).)*?)\/|\/|$)(?!\.)(?=.)[^/]*?\.stories\.(mdx|tsx|ts|jsx|js))$/,
},
]);
});

const filename = './stories/basic/Header.stories.js';
Expand Down
15 changes: 14 additions & 1 deletion src/setup-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ const sanitizeURL = (url: string) => {

export const setupPage = async (page: Page, browserContext: BrowserContext) => {
const targetURL = process.env.TARGET_URL;
const failOnConsole = process.env.TEST_CHECK_CONSOLE;

const viewMode = process.env.VIEW_MODE || 'story';
const renderedEvent = viewMode === 'docs' ? 'docsRendered' : 'storyRendered';
Expand Down Expand Up @@ -215,6 +216,9 @@ export const setupPage = async (page: Page, browserContext: BrowserContext) => {
// end of fast-safe-stringify code

function composeMessage(args) {
if (args instanceof Error) {
return \`\${args.name}: \${args.message}\\n\${args.stack}\`;
}
if (typeof args === 'undefined') return "undefined";
if (typeof args === 'string') return args;
return stringify(args, null, null, { depthLimit: 5, edgesLimit: 100 });
Expand Down Expand Up @@ -306,10 +310,14 @@ export const setupPage = async (page: Page, browserContext: BrowserContext) => {

// collect logs to show upon test error
let logs = [];
let hasErrors = false;

const spyOnConsole = (method, name) => {
const originalFn = console[method];
return function () {
if (\`${failOnConsole}\`==='true' && method==='error') {
hasErrors = true;
}
const message = [...arguments].map(composeMessage).join(', ');
const prefix = \`\${bold(name)}: \`;
logs.push(prefix + message);
Expand All @@ -332,7 +340,12 @@ export const setupPage = async (page: Page, browserContext: BrowserContext) => {
})

return new Promise((resolve, reject) => {
channel.on('${renderedEvent}', () => resolve(document.getElementById('root')));
channel.on('${renderedEvent}', () => {
if (hasErrors) {
return reject(new StorybookTestRunnerError(storyId, 'Browser console errors', logs));
}
return resolve(document.getElementById('root'));
});
channel.on('storyUnchanged', () => resolve(document.getElementById('root')));
channel.on('storyErrored', ({ description }) => reject(
new StorybookTestRunnerError(storyId, description, logs))
Expand Down
13 changes: 12 additions & 1 deletion src/test-storybook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ const cleanup = () => {

async function reportCoverage() {
const coverageFolderE2E = path.resolve(process.cwd(), '.nyc_output');
const coverageFolder = path.resolve(process.cwd(), 'coverage/storybook');
const coverageFolder = path.resolve(
process.cwd(),
process.env.STORYBOOK_COVERAGE_DIRECTORY ?? 'coverage/storybook'
);

// in case something goes wrong and .nyc_output does not exist, bail
if (!fs.existsSync(coverageFolderE2E)) {
Expand Down Expand Up @@ -260,6 +263,10 @@ const main = async () => {
process.env.STORYBOOK_COLLECT_COVERAGE = 'true';
}

if (runnerOptions.coverageDirectory) {
process.env.STORYBOOK_COVERAGE_DIRECTORY = runnerOptions.coverageDirectory;
}

if (runnerOptions.junit) {
process.env.STORYBOOK_JUNIT = 'true';
}
Expand Down Expand Up @@ -297,6 +304,10 @@ const main = async () => {
const { storiesPaths, lazyCompilation } = getStorybookMetadata();
process.env.STORYBOOK_STORIES_PATTERN = storiesPaths;

if (runnerOptions.failOnConsole) {
process.env.TEST_CHECK_CONSOLE = 'true';
}

if (lazyCompilation && isLocalStorybookIp) {
log(
`You're running Storybook with lazy compilation enabled, and will likely cause issues with the test runner locally. Consider disabling 'lazyCompilation' in ${runnerOptions.configDir}/main.js when running 'test-storybook' locally.`
Expand Down
9 changes: 9 additions & 0 deletions src/util/getCliOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ describe('getCliOptions', () => {
expect(opts.runnerOptions).toMatchObject(customConfig);
});

it('returns failOnConsole option if passed', () => {
const customConfig = { failOnConsole: true };
jest
.spyOn(cliHelper, 'getParsedCliOptions')
.mockReturnValue({ options: customConfig, extraArgs: [] });
const opts = getCliOptions();
expect(opts.runnerOptions).toMatchObject(customConfig);
});

it('returns extra args if passed', () => {
const extraArgs = ['TestName', 'AnotherTestName'];
// mock argv to avoid side effect from running tests e.g. jest --coverage,
Expand Down
4 changes: 4 additions & 0 deletions src/util/getCliOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@ export type CliOptions = {
configDir?: string;
eject?: boolean;
coverage?: boolean;
coverageDirectory?: string;
junit?: boolean;
browsers?: BrowserType | BrowserType[];
failOnConsole?: boolean;
};
jestOptions: JestOptions;
};
Expand All @@ -25,7 +27,9 @@ const STORYBOOK_RUNNER_COMMANDS: StorybookRunnerCommand[] = [
'eject',
'url',
'coverage',
'coverageDirectory',
'junit',
'failOnConsole',
];

function copyOption<ObjType extends object, KeyType extends keyof ObjType>(
Expand Down
8 changes: 7 additions & 1 deletion src/util/getParsedCliOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ export const getParsedCliOptions = (): ParsedCliOptions => {
'--coverage',
'Indicates that test coverage information should be collected and reported in the output'
)
.option(
'--coverageDirectory <directory>',
'Directory where to write coverage report output',
'coverage/storybook'
)
.option('--junit', 'Indicates that test information should be reported in a junit file')
.option(
'--eject',
Expand All @@ -70,7 +75,8 @@ export const getParsedCliOptions = (): ParsedCliOptions => {
.option(
'--shard <shardIndex/shardCount>',
'Splits your test suite across different machines to run in CI.'
);
)
.option('--failOnConsole', 'Makes tests fail on browser console errors');

program.exitOverride();

Expand Down
22 changes: 19 additions & 3 deletions src/util/getStorybookMetadata.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ import * as storybookMain from './getStorybookMain';

import { getStorybookMetadata } from './getStorybookMetadata';

jest.mock('@storybook/core-common', () => ({
...jest.requireActual('@storybook/core-common'),
getProjectRoot: jest.fn(() => '/foo/bar'),
normalizeStories: jest.fn(() => [
{
titlePrefix: 'Example',
files: '**/*.stories.@(mdx|tsx|ts|jsx|js)',
directory: './stories/basic',
importPathMatcher:
/^\.[\\/](?:stories\/basic(?:\/(?!\.)(?:(?:(?!(?:^|\/)\.).)*?)\/|\/|$)(?!\.)(?=.)[^/]*?\.stories\.(mdx|tsx|ts|jsx|js))$/,
},
]),
}));

describe('getStorybookMetadata', () => {
afterAll(() => {
process.env.STORYBOOK_CONFIG_DIR = undefined;
Expand Down Expand Up @@ -32,7 +46,7 @@ describe('getStorybookMetadata', () => {
process.env.STORYBOOK_CONFIG_DIR = '.storybook';
const { storiesPaths } = getStorybookMetadata();
expect(storiesPaths).toMatchInlineSnapshot(
`"<rootDir>/stories/basic/**/*.@(mdx|stories.@(tsx|ts|jsx|js))"`
`"/foo/bar/stories/basic/**/*.stories.@(mdx|tsx|ts|jsx|js)"`
);
});

Expand All @@ -44,7 +58,9 @@ describe('getStorybookMetadata', () => {
jest.spyOn(storybookMain, 'getStorybookMain').mockReturnValueOnce(mockedMain);
process.env.STORYBOOK_CONFIG_DIR = '.storybook';
const { storiesPaths } = getStorybookMetadata();
expect(storiesPaths).toMatchInlineSnapshot(`"<rootDir>/**/stories/*.stories.@(js|ts)"`);
expect(storiesPaths).toMatchInlineSnapshot(
`"/foo/bar/stories/basic/**/*.stories.@(mdx|tsx|ts|jsx|js)"`
);
});

it('should return storiesPath from mixed CSF2 and CSF3 style config', () => {
Expand All @@ -62,7 +78,7 @@ describe('getStorybookMetadata', () => {
process.env.STORYBOOK_CONFIG_DIR = '.storybook';
const { storiesPaths } = getStorybookMetadata();
expect(storiesPaths).toMatchInlineSnapshot(
`"<rootDir>/stories/basic/**/*.@(mdx|stories.@(tsx|ts|jsx|js));<rootDir>/stories/complex/*.stories.@(js|ts)"`
`"/foo/bar/stories/basic/**/*.stories.@(mdx|tsx|ts|jsx|js)"`
);
});

Expand Down
Loading
Loading