Skip to content

Commit

Permalink
feat: add CLI wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
yannbf committed Feb 3, 2022
1 parent b1997ee commit 9b77c29
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 15 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,23 @@ yarn test-storybook
> TARGET_URL=http://localhost:9009 yarn test-storybook
> ```
## CLI Options
```plaintext
Usage: test-storybook [options]
```
| Options | Description |
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--help` | Output usage information <br/>`test-storybook --help` |
| `-s`, `--stories-json` | Run in stories json mode (requires a compatible Storybook) <br/>`test-storybook --stories-json` |
| `-c`, `--config-dir [dir-name]` | Directory where to load Storybook configurations from <br/>`test-storybook -c .storybook` |
| `--watch` | Run in watch mode <br/>`test-storybook --watch` |
| `--maxWorkers [amount]` | Specifies the maximum number of workers the worker-pool will spawn for running tests <br/>`test-storybook --maxWorkers=2` |
| `--no-cache` | Disable the cache <br/>`test-storybook --no-cache` |
| `--clearCache` | Deletes the Jest cache directory and then exits without running tests <br/>`test-storybook --clearCache` |
| `--verbose` | Display individual test results with the test suite hierarchy <br/>`test-storybook --verbose` |

## Configuration

The test runner is based on [Jest](https://jestjs.io/) and will accept the [CLI options](https://jestjs.io/docs/cli) that Jest does, like `--watch`, `--maxWorkers`, etc.
Expand Down
12 changes: 9 additions & 3 deletions bin/test-storybook.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const fetch = require('node-fetch');
const fs = require('fs');
const path = require('path');
const tempy = require('tempy');
const { getCliOptions, getStorybookMain } = require('../dist/cjs/util/cli');
const { transformPlaywrightJson } = require('../dist/cjs/playwright/transformPlaywrightJson');

// Do this as the first thing so that any code reading it knows the right env.
Expand Down Expand Up @@ -103,15 +104,20 @@ async function fetchStoriesJson(url) {
const main = async () => {
const targetURL = sanitizeURL(process.env.TARGET_URL || `http://localhost:6006`);
await checkStorybook(targetURL);
let args = process.argv.filter((arg) => arg !== '--stories-json');

if (args.length !== process.argv.length) {
const { jestOptions, runnerOptions } = getCliOptions()

if (runnerOptions.storiesJson) {
storiesJsonTmpDir = await fetchStoriesJson(targetURL);
process.env.TEST_ROOT = storiesJsonTmpDir;
process.env.TEST_MATCH = '**/*.test.js';
}

await executeJestPlaywright(args);
// check if main.js exists, throw an error if not
getStorybookMain(runnerOptions.configDir);
process.env.STORYBOOK_CONFIG_DIR = runnerOptions.configDir;

await executeJestPlaywright(jestOptions);
};

main().catch((e) => console.log(`[test-storybook] ${e}`));
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
"buildTsc": "tsc --declaration --emitDeclarationOnly --outDir ./dist/ts",
"prebuild": "yarn clean",
"build": "concurrently \"yarn buildBabel\" \"yarn buildTsc\"",
"build:watch": "concurrently \"yarn buildBabel:esm -- --watch\" \"yarn buildTsc -- --watch\"",
"build:watch": "concurrently \"yarn buildBabel:cjs -- --watch\" \"yarn buildTsc -- --watch\"",
"test": "jest",
"storybook": "start-storybook -p 6006",
"start": "concurrently \"yarn build:watch\" \"yarn storybook -- --no-manager-cache --quiet\"",
Expand Down Expand Up @@ -106,6 +106,7 @@
"dependencies": {
"@storybook/csf": "0.0.2--canary.87bc651.0",
"@storybook/csf-tools": "^6.4.14",
"commander": "^9.0.0",
"jest-playwright-preset": "^1.7.0",
"node-fetch": "^2",
"playwright": "^1.14.0",
Expand Down
4 changes: 3 additions & 1 deletion src/playwright/transformPlaywright.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import dedent from 'ts-dedent';
import path from 'path';
import * as coreCommon from '@storybook/core-common';
import * as cli from '../util/cli';

import { transformPlaywright } from './transformPlaywright';

jest.mock('@storybook/core-common');
jest.mock('../util/cli');

expect.addSnapshotSerializer({
print: (val: any) => val.trim(),
Expand All @@ -15,7 +17,7 @@ describe('Playwright', () => {
beforeEach(() => {
const relativeSpy = jest.spyOn(path, 'relative');
relativeSpy.mockReturnValueOnce('stories/basic/Header.stories.js');
jest.spyOn(coreCommon, 'serverRequire').mockImplementation(() => ({
jest.spyOn(cli, 'getStorybookMain').mockImplementation(() => ({
stories: [
{
directory: '../stories/basic',
Expand Down
15 changes: 5 additions & 10 deletions src/playwright/transformPlaywright.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { resolve, join, relative } from 'path';
import { resolve, relative } from 'path';
import template from '@babel/template';
import { serverRequire, normalizeStories } from '@storybook/core-common';
import { normalizeStories } from '@storybook/core-common';
import { autoTitle } from '@storybook/store';

import { getStorybookMain } from '../util/cli';
import { transformCsf } from '../csf/transformCsf';

export const testPrefixer = template(
Expand All @@ -24,16 +25,10 @@ export const testPrefixer = template(
);

const getDefaultTitle = (filename: string) => {
// we'll need to figure this out for different cases
// e.g. --config-dir
const configDir = resolve('.storybook');
const workingDir = resolve();
const configDir = process.env.STORYBOOK_CONFIG_DIR;

const main = serverRequire(join(configDir, 'main'));

if (!main) {
throw new Error(`Could not load main.js in ${configDir}`);
}
const main = getStorybookMain(configDir);

const normalizedStoriesEntries = normalizeStories(main.stories, {
configDir,
Expand Down
44 changes: 44 additions & 0 deletions src/util/cli.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import * as coreCommon from '@storybook/core-common';

import * as cliHelper from './helpers';
import { getCliOptions, getStorybookMain, defaultRunnerOptions } from './cli';

jest.mock('@storybook/core-common');

describe('CLI', () => {
describe('getCliOptions', () => {
it('returns default options if no extra option is passed', () => {
const opts = getCliOptions();
expect(opts.runnerOptions).toMatchObject(defaultRunnerOptions);
});

it('returns custom options if passed', () => {
const customConfig = { configDir: 'custom', storiesJson: true };
jest.spyOn(cliHelper, 'getParsedCliOptions').mockReturnValue(customConfig);
const opts = getCliOptions();
expect(opts.runnerOptions).toMatchObject(customConfig);
});
});

describe('getStorybookMain', () => {
it('should throw an error if no configuration is found', () => {
expect(() => getStorybookMain('.storybook')).toThrow();
});

it('should return mainjs', () => {
const mockedMain = {
stories: [
{
directory: '../stories/basic',
titlePrefix: 'Example',
},
],
};

jest.spyOn(coreCommon, 'serverRequire').mockImplementation(() => mockedMain);

const res = getStorybookMain('.storybook');
expect(res).toMatchObject(mockedMain);
});
});
});
63 changes: 63 additions & 0 deletions src/util/cli.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { join, resolve } from 'path';
import { serverRequire, StorybookConfig } from '@storybook/core-common';
import { getParsedCliOptions } from './helpers';

type CliOptions = {
runnerOptions: {
storiesJson: boolean;
configDir: string;
};
jestOptions: string[];
};

type StorybookRunnerCommand = keyof CliOptions['runnerOptions'];

const STORYBOOK_RUNNER_COMMANDS: StorybookRunnerCommand[] = ['storiesJson', 'configDir'];

export const defaultRunnerOptions: CliOptions['runnerOptions'] = {
configDir: '.storybook',
storiesJson: false,
};

let storybookMainConfig: StorybookConfig;

export const getCliOptions = () => {
const allOptions = getParsedCliOptions();

const defaultOptions: CliOptions = {
runnerOptions: { ...defaultRunnerOptions },
jestOptions: process.argv.splice(0, 2),
};

return Object.keys(allOptions).reduce((acc, key: any) => {
if (STORYBOOK_RUNNER_COMMANDS.includes(key)) {
//@ts-ignore
acc.runnerOptions[key] = allOptions[key];
} else {
if (allOptions[key] === true) {
acc.jestOptions.push(`--${key}`);
} else if (allOptions[key] === false) {
acc.jestOptions.push(`--no-${key}`);
} else {
acc.jestOptions.push(`--${key}`, allOptions[key]);
}
}

return acc;
}, defaultOptions);
};

export const getStorybookMain = (configDir: string) => {
if (storybookMainConfig) {
return storybookMainConfig;
}

storybookMainConfig = serverRequire(join(resolve(configDir), 'main'));
if (!storybookMainConfig) {
throw new Error(
`Could not load main.js in ${configDir}. Is the config directory correct? You can change it by using --config-dir <path-to-dir>`
);
}

return storybookMainConfig;
};
41 changes: 41 additions & 0 deletions src/util/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export const getParsedCliOptions = () => {
const { program } = require('commander');

program
.option('-s, --stories-json', 'Run in stories json mode (requires a compatible Storybook)')
.option('-c, --config-dir <directory>', 'Directory where to load Storybook configurations from')
.option('--watch', 'Run in watch mode')
.option(
'--maxWorkers <amount>',
'Specifies the maximum number of workers the worker-pool will spawn for running tests'
)
.option('--no-cache', 'Disable the cache')
.option('--clearCache', 'Deletes the Jest cache directory and then exits without running tests')
.option('--verbose', 'Display individual test results with the test suite hierarchy');

program.exitOverride();

try {
program.parse();
} catch (err) {
switch (err.code) {
case 'commander.unknownOption': {
program.outputHelp();
console.warn(
`\nIf you'd like this option to be supported, please open an issue at https://github.com/storybookjs/test-runner/issues/new\n`
);
process.exit(1);
}

case 'commander.helpDisplayed': {
process.exit(0);
}

default: {
throw err;
}
}
}

return program.opts();
};
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5124,6 +5124,11 @@ commander@^8.2.0:
resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==

commander@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.0.0.tgz#86d58f24ee98126568936bd1d3574e0308a99a40"
integrity sha512-JJfP2saEKbQqvW+FI93OYUB4ByV5cizMpFMiiJI8xDbBvQvSkIk0VvQdn1CZ8mqAO8Loq2h0gYTYtDFUZUeERw==

common-path-prefix@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/common-path-prefix/-/common-path-prefix-3.0.0.tgz#7d007a7e07c58c4b4d5f433131a19141b29f11e0"
Expand Down

0 comments on commit 9b77c29

Please sign in to comment.