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

✨ add storybook v7 support #829

Merged
merged 28 commits into from
Oct 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
d24e398
feat: uncache stories before extracting them (#828)
nilshah98 Oct 18, 2023
5a5762c
✨ add fetching env support for storybook v7+ (#827)
nilshah98 Oct 18, 2023
ad6b64c
fix: evalStorybookEnvironmentInfo
nilshah98 Oct 19, 2023
af828f1
feat: update package.json and lockfile for storybook v7
nilshah98 Oct 19, 2023
63f8c26
fix: convert start-storybook to storybook dev
nilshah98 Oct 19, 2023
e51b598
fix: add framework option to storybook config
nilshah98 Oct 19, 2023
35c5e61
feat: update babel and package config to node 16
nilshah98 Oct 19, 2023
19995f5
feat: update github actions to use node 16
nilshah98 Oct 19, 2023
3d6922c
chore: lint fix
nilshah98 Oct 19, 2023
9b5ccc9
feat: add storybook-v6 to support start-storybook and build-storybook…
nilshah98 Oct 19, 2023
7abd3a5
wip: use storybook 6 as default
nilshah98 Oct 24, 2023
87e77b3
fix: make storybook6 default, and keep storybook7 separate
nilshah98 Oct 24, 2023
9289b33
fix: inherit stdio to run tests
nilshah98 Oct 24, 2023
2e37163
fix: support storybook6 tests
nilshah98 Oct 24, 2023
8fbccec
wip: add flag support for switching between storybook v6 <> v7 tests
nilshah98 Oct 24, 2023
c252c87
fix: build storybook command in pretest
nilshah98 Oct 25, 2023
c23158d
wip: add utils and support for storybook v7 tests only
nilshah98 Oct 25, 2023
0076956
test: fix case where storybook had not loaded yet for live url testing
nilshah98 Oct 25, 2023
a5dc440
wip: run storybook6 tests by default
nilshah98 Oct 25, 2023
1d67241
fix: change babel build target version to 14
nilshah98 Oct 25, 2023
4c3d42c
feat: add storybook v6 and v7 test workflows, also remove default tes…
nilshah98 Oct 25, 2023
8f6230d
feat: downgrade node 16 to node 14 in github workflows and package js…
nilshah98 Oct 25, 2023
bd76e43
feat: upgrade node 14 to 16 for lint and release workflows, since the…
nilshah98 Oct 25, 2023
6f2f9fa
chore: update yarn.lock as per storybook v7, node 16
nilshah98 Oct 25, 2023
e39df90
fix: pin action sha, use inputs.branch and use yarn test in v6
nilshah98 Oct 26, 2023
6e3e2ff
fix: check if error exists before checking for message attribute
nilshah98 Oct 26, 2023
891a553
docs: add and update `Development` section in readme
nilshah98 Oct 26, 2023
f649e03
chore: add EOF
nilshah98 Oct 26, 2023
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
6 changes: 3 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ jobs:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}/node-14/${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}/node-14/
key: ${{ runner.os }}/node-16/${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}/node-16/
- run: yarn
env:
PERCY_POSTINSTALL_BROWSER: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
- uses: actions/setup-node@v3
with:
node-version: 14
node-version: 16
registry-url: 'https://registry.npmjs.org'
- run: yarn
- run: yarn build
Expand Down
58 changes: 58 additions & 0 deletions .github/workflows/test-storybook-v6.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Test
on:
push:
workflow_dispatch:
inputs:
branch:
required: false
type: string
default: master
jobs:
test:
name: Test Storybook v6
strategy:
matrix:
os: [ubuntu-latest]
node: [14]
runs-on: ${{ matrix.os }}
steps:
- uses: actions-ecosystem/action-regex-match@9e6c4fb3d5e898f505be7a1fb6e7b0a278f6665b
id: regex-match
if: ${{ github.event_name == 'workflow_dispatch' }}
with:
text: ${{ inputs.branch }}
regex: '^[a-zA-Z0-9_/\-]+$'
- name: Break on invalid branch name
run: exit 1
if: ${{ github.event_name == 'workflow_dispatch' && steps.regex-match.outputs && steps.regex-match.outputs.match == '' }}
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
- run: ./prepare-storybook-v6-tests.sh
- uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}/node-${{ matrix.node }}/${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}/node-${{ matrix.node }}/
- run: yarn
env:
PERCY_POSTINSTALL_BROWSER: true
- run: yarn build
- name: Set up @percy/cli from git
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
cd /tmp
git clone --branch ${{ inputs.branch }} --depth 1 https://github.com/percy/cli
cd cli
PERCY_PACKAGES=`find packages -type d -mindepth 1 -maxdepth 1 | sed -e 's/packages/@percy/g' | tr '\n' ' '`
echo "Packages: $PERCY_PACKAGES"
git log -1
yarn
yarn build
yarn global:link
cd ${{ github.workspace }}
yarn link `echo $PERCY_PACKAGES`
npx percy --version

- run: yarn test
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@ on:
default: master
jobs:
test:
name: Test
name: Test Storybook v7
strategy:
matrix:
os: [ubuntu-latest]
node: [14]
node: [16]
runs-on: ${{ matrix.os }}
steps:
- uses: actions-ecosystem/action-regex-match@v2
- uses: actions-ecosystem/action-regex-match@9e6c4fb3d5e898f505be7a1fb6e7b0a278f6665b
id: regex-match
if: ${{ github.event_name == 'workflow_dispatch' }}
with:
text: ${{ github.event.inputs.branch }}
text: ${{ inputs.branch }}
regex: '^[a-zA-Z0-9_/\-]+$'
- name: Break on invalid branch name
run: exit 1
Expand All @@ -42,7 +42,7 @@ jobs:
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
cd /tmp
git clone --branch ${{ github.event.inputs.branch }} --depth 1 https://github.com/percy/cli
git clone --branch ${{ inputs.branch }} --depth 1 https://github.com/percy/cli
cd cli
PERCY_PACKAGES=`find packages -type d -mindepth 1 -maxdepth 1 | sed -e 's/packages/@percy/g' | tr '\n' ' '`
echo "Packages: $PERCY_PACKAGES"
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,7 @@ JavaScript is disabled by default to prevent flakey diffs caused by animations o
running on the page. With the new SDK and real DOM snapshots, JS is disabled by default. If you
upgrade and experience diffs due to the lack of JavaScript, it can be re-enabled using the matching
Percy config file or per-snapshot option, [`enableJavaScript`](https://docs.percy.io/docs/cli-configuration#snapshot).

## Development
- Current package.json and yarn.lock install storybook v7 as devDependency and hence require node 16 for development.
- There are separate package.json and config files for storybook v6, for testing purposes. Please check `prepare-storybook-v6-tests.sh` file for more details around changes for storybook v6 testing.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"clean": "git clean -Xdf -e !node_modules -e !node_modules/**",
"lint": "eslint --ignore-path .gitignore .",
"readme": "percy-cli-readme",
"pretest": "build-storybook --config-dir=./test/.storybook --output-dir=./test/.storybook-build --loglevel error",
"pretest": "storybook build --config-dir=./test/.storybook --output-dir=./test/.storybook-build --loglevel error",
"test": "yarn test:env jasmine --config=./test/jasmine.json",
"test:env": "cross-env NODE_ENV=test NODE_OPTIONS='--loader=./test/loader.js'",
"test:coverage": "nyc yarn test"
Expand All @@ -45,7 +45,8 @@
"@babel/eslint-parser": "^7.19.1",
"@babel/eslint-plugin": "^7.19.1",
"@babel/preset-env": "^7.22.9",
"@storybook/react": "^6.5.13",
"@storybook/react": "^7.5.0",
"@storybook/react-webpack5": "^7.5.0",
"babel-eslint": "^10.0.3",
"babel-plugin-istanbul": "^6.1.1",
"cross-env": "^7.0.3",
Expand All @@ -61,6 +62,7 @@
"nock": "^13.2.9",
"nyc": "^15.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"storybook": "^7.5.0"
}
}
66 changes: 66 additions & 0 deletions packageV6.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "@percy/storybook",
"version": "4.3.7",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/percy/percy-storybook.git"
},
"engine": {
"node": ">=14"
},
"files": [
"bin",
"dist"
],
"bin": {
"percy-storybook": "./bin/percy-storybook.cjs"
},
"main": "./dist/index.js",
"exports": "./dist/index.js",
"type": "module",
"scripts": {
"build": "babel src --out-dir dist",
"clean": "git clean -Xdf -e !node_modules -e !node_modules/**",
"lint": "eslint --ignore-path .gitignore .",
"readme": "percy-cli-readme",
"pretest": "build-storybook --config-dir=./test/.storybook --output-dir=./test/.storybook-build --loglevel error",
"test": "yarn test:env jasmine --config=./test/jasmine.json",
"test:env": "cross-env NODE_ENV=test NODE_OPTIONS='--loader=./test/loader.js'",
"test:coverage": "nyc yarn test"
},
"@percy/cli": {
"commands": [
"./dist/storybook.js"
]
},
"dependencies": {
"@percy/cli-command": "^1.24.0",
"cross-spawn": "^7.0.3",
"qs": "^6.11.0"
},
"devDependencies": {
"@babel/cli": "^7.23.0",
"@babel/core": "^7.23.2",
"@babel/eslint-parser": "^7.19.1",
"@babel/eslint-plugin": "^7.19.1",
"@babel/preset-env": "^7.22.9",
"@storybook/react": "^6.5.13",
"babel-eslint": "^10.0.3",
"babel-plugin-istanbul": "^6.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.51.0",
"eslint-config-standard": "^17.0.0",
"eslint-plugin-import": "^2.28.1",
"eslint-plugin-n": "^15.4.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^6.1.1",
"jasmine": "^4.5.0",
"jasmine-spec-reporter": "^7.0.0",
"mock-require": "^3.0.3",
"nock": "^13.2.9",
"nyc": "^15.1.0",
"react": "^18.2.0",
"react-dom": "^18.2.0"
}
}
5 changes: 5 additions & 0 deletions prepare-storybook-v6-tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/bin/bash

# move package and storybook configs
mv ./packageV6.json ./package.json
mv ./test/.storybook/mainV6.js ./test/.storybook/main.js
12 changes: 9 additions & 3 deletions src/start.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import command from '@percy/cli-command';
import * as common from './common.js';
import { checkStorybookVersion } from './utils.js';

export const start = command('start', {
description: 'Run start-storybook to snapshot stories',
Expand Down Expand Up @@ -31,12 +32,17 @@ export const start = command('start', {
let { takeStorybookSnapshots } = yield import('./snapshots.js');
let { default: { spawn } } = yield import('cross-spawn');
let { host, port } = flags;
let storybookVersion = await checkStorybookVersion();

let args = ['--ci', `--host=${host}`, `--port=${port}`, ...argv];
log.info(`Running "start-storybook ${args.join(' ')}"`);
let args = storybookVersion === 7 ? ['dev'] : [];
args = args.concat(['--ci', `--host=${host}`, `--port=${port}`, ...argv]);

let storybookBinary = storybookVersion === 7 ? 'storybook' : 'start-storybook';

log.info(`Running "${storybookBinary} ${args.join(' ')}"`);

let proc = yield new Promise((resolve, reject) => resolve(
spawn('start-storybook', args, { stdio: 'inherit' }).on('error', reject)
spawn(storybookBinary, args, { stdio: 'inherit' }).on('error', reject)
));

/* istanbul ignore next: this is a storybook flag we don't need to test */
Expand Down
25 changes: 23 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
import { request, createRootResource, yieldTo } from '@percy/cli-command/utils';
import spawn from 'cross-spawn';

// check storybook version
export function checkStorybookVersion() {
return new Promise((resolve, reject) => {
spawn('storybook', ['--version'])
.on('exit', (code) => { if (code === 0) resolve(7); })
.on('error', (err) => {
if (err.code === 'ENOENT') {
resolve(6);
}
reject(err);
});
});
}

// Transforms authorization credentials into a basic auth header and returns all config request
// headers with the additional authorization header if not already set.
Expand Down Expand Up @@ -127,7 +142,7 @@ export async function* withPage(percy, url, callback, retry) {
return yield* yieldTo(callback(page));
} catch (error) {
// if the page crashed and retry returns truthy, try again
if (error.message?.includes('crashed') && retry?.()) {
if (error?.message?.includes('crashed') && retry?.()) {
return yield* withPage(...arguments);
}

Expand All @@ -147,7 +162,10 @@ export async function* withPage(percy, url, callback, retry) {
// Evaluate and return Storybook environment information from the about page
/* istanbul ignore next: no instrumenting injected code */
export function evalStorybookEnvironmentInfo({ waitForXPath }) {
return waitForXPath("//header[starts-with(text(), 'Storybook ')]", 5000)
let possibleEnvs = [];
itsjwala marked this conversation as resolved.
Show resolved Hide resolved
possibleEnvs.push(waitForXPath("//header[starts-with(text(), 'Storybook ')]", 5000));
possibleEnvs.push(waitForXPath("//strong[starts-with(text(), 'You are on Storybook ')]", 5000));
return Promise.any(possibleEnvs)
.then(el => `storybook/${el.innerText.match(/-?\d*\.?\d+/g).join('')}`)
.catch(() => 'storybook/unknown');
}
Expand Down Expand Up @@ -180,6 +198,9 @@ export function evalStorybookStorySnapshots({ waitFor }) {
};

return waitFor(async () => {
// uncache stories, if cached via storyStorev7: true
itsjwala marked this conversation as resolved.
Show resolved Hide resolved
await (window.__STORYBOOK_PREVIEW__?.cacheAllCSFFiles?.() ||
window.__STORYBOOK_STORY_STORE__?.cacheAllCSFFiles?.());
// use newer storybook APIs before old APIs
await (window.__STORYBOOK_PREVIEW__?.extract?.() ||
window.__STORYBOOK_STORY_STORE__?.extract?.());
Expand Down
4 changes: 4 additions & 0 deletions test/.storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,9 @@ module.exports = {
stories: ['*.stories.js'],
features: {
postcss: false
},
framework: {
name: '@storybook/react-webpack5',
options: {}
}
};
6 changes: 6 additions & 0 deletions test/.storybook/mainV6.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
stories: ['*.stories.js'],
features: {
postcss: false
}
};
4 changes: 2 additions & 2 deletions test/storybook-start.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('percy storybook:start', () => {
] : []);

expect(logger.stdout).toEqual(jasmine.arrayContaining([
`[percy] Running "start-storybook --ci --host=localhost --port=9000 ${args.join(' ')}"`,
jasmine.stringMatching(`\\[percy\\] Running "(start-storybook|storybook dev) --ci --host=localhost --port=9000 ${args.join(' ').replace('.', '\\.')}"`),
'[percy] Percy has started!',
'[percy] Snapshot taken: Snapshot: First',
'[percy] Snapshot taken: Snapshot: Second',
Expand All @@ -51,7 +51,7 @@ describe('percy storybook:start', () => {
await expectAsync(start([...args])).toBeRejectedWithError('FAKE ENOENT');

expect(logger.stdout).toEqual([
`[percy] Running "start-storybook --ci --host=localhost --port=9000 ${args.join(' ')}"`
jasmine.stringMatching(`\\[percy\\] Running "(start-storybook|storybook dev) --ci --host=localhost --port=9000 ${args.join(' ').replace('.', '\\.')}"`)
]);
expect(logger.stderr).toEqual([
'[percy] Error: FAKE ENOENT'
Expand Down
14 changes: 12 additions & 2 deletions test/storybook.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import spawn from 'cross-spawn';
import { request } from '@percy/cli-command/utils';
import { api, logger, setupTest, createTestServer } from '@percy/cli-command/test/helpers';
import { storybook } from '../src/index.js';
import { checkStorybookVersion } from '../src/utils.js';

describe('percy storybook', () => {
let server, proc;
Expand All @@ -16,11 +17,16 @@ describe('percy storybook', () => {
default: () => [200, 'text/html', '<p>Not Storybook</p>']
});

proc = spawn('start-storybook', [
let storybookVersion = await checkStorybookVersion();
let args = storybookVersion === 7 ? ['dev'] : [];
args = args.concat([
'--config-dir=./test/.storybook',
'--port=9000',
'--ci'
]);
let storybookBinary = storybookVersion === 7 ? 'storybook' : 'start-storybook';

proc = spawn(storybookBinary, args, { stdio: 'inherit' });

// wait for storybook to become available
await request('http://localhost:9000', {
Expand Down Expand Up @@ -49,7 +55,11 @@ describe('percy storybook', () => {
it('snapshots live urls', async () => {
await storybook(['http://localhost:9000']);

expect(logger.stderr).toEqual([]);
// if there are stderr logs ensure it is only an acceptable warning
expect(logger.stderr).toEqual(logger.stderr.length ? [
'[percy] Waiting on a response from Storybook...'
itsjwala marked this conversation as resolved.
Show resolved Hide resolved
] : []);

expect(logger.stdout).toEqual(jasmine.arrayContaining([
'[percy] Percy has started!',
'[percy] Snapshot taken: Snapshot: First',
Expand Down
Loading