From 0a2e3bdb207a6710bba1e109a72a2b109d9ba18a Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 25 May 2022 16:19:51 +0200 Subject: [PATCH 1/9] chore: add reports when workflow fails --- .github/workflows/nightly.yml | 51 +++++++++++++++++++++++++++++++++++ .storybook/main.js | 9 +++---- 2 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/nightly.yml diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 00000000..3e9651bf --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,51 @@ +name: Nightly checks + +on: [push] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Use Node.js 14.x + uses: actions/setup-node@v1 + with: + node-version: 14.x + + - name: Install dependencies + uses: bahmutov/npm-install@v1 + + - name: Upgrade to storybook@next + run: | + npx storybook upgrade --prerelease + + - name: Run test runner + run: | + yarn build + yarn test-storybook:ci + + report_incoming_errors: + runs-on: ubuntu-latest + steps: + - name: Report incoming errors + if: ${{ failure() }} + id: slack + uses: slackapi/slack-github-action@v1.19.0 + with: + channel-id: "${{ secrets.SLACK_CHANNEL_ID }}" + payload: | + { + "text": "[Test Runner] Nightly check has failed: ${{ github.event.pull_request.html_url || github.event.head_commit.url }}", + "blocks": [ + { + "type":"section", + "text":{ + "type":"mrkdwn", + "text":"<${{ github.event.pull_request.html_url }}|View Pull Request>" + } + } + ] + } + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/.storybook/main.js b/.storybook/main.js index 1ce87230..451b70b6 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -1,5 +1,3 @@ -const { STRESS_TEST, STORY_STORE_V7, WITHOUT_DOCS } = process.env; - const stories = [ '../stories/docs/**/*.stories.mdx', // default title prefix @@ -16,13 +14,12 @@ const stories = [ '../stories/pages/**/*.stories.*', ]; - -if (STRESS_TEST) { +if (process.env.STRESS_TEST) { stories.push('../stories/stress-test/*.stories.@(js|jsx|ts|tsx)'); } const addons = [ - WITHOUT_DOCS + process.env.WITHOUT_DOCS ? { name: '@storybook/addon-essentials', options: { @@ -37,7 +34,7 @@ module.exports = { stories, addons, features: { - storyStoreV7: STORY_STORE_V7 ? true : false, + storyStoreV7: process.env.STORY_STORE_V7 ? true : false, buildStoriesJson: true, }, }; From f6f238da5680ba98a945760345679a11db2a02b1 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 25 May 2022 16:20:41 +0200 Subject: [PATCH 2/9] add fake failure for testing purposes --- .storybook/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.storybook/main.js b/.storybook/main.js index 451b70b6..a5070768 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -33,6 +33,7 @@ const addons = [ module.exports = { stories, addons, + webpackFinal: async() => new Error('fail!'), features: { storyStoreV7: process.env.STORY_STORE_V7 ? true : false, buildStoriesJson: true, From 3f909b056cb9482377bcb4cd36dc9adab3c77bb4 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 25 May 2022 19:21:46 +0200 Subject: [PATCH 3/9] chore(CI): add nightly checks --- .github/workflows/nightly.yml | 45 +++++++++++++++++++++++++++-------- .storybook/main.js | 1 - 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3e9651bf..ed62f141 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,9 +1,12 @@ name: Nightly checks -on: [push] +# runs every day at midnight +on: + schedule: + - cron: "0 0 * * *" jobs: - test: + test_storybook_prerelease: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -25,9 +28,11 @@ jobs: yarn build yarn test-storybook:ci - report_incoming_errors: - runs-on: ubuntu-latest - steps: + - name: Get prerelease version of Storybook + if: ${{ failure() }} + run: | + echo "sb_version=$(npm view @storybook/react@prerelease version)" >> $GITHUB_ENV + - name: Report incoming errors if: ${{ failure() }} id: slack @@ -36,13 +41,33 @@ jobs: channel-id: "${{ secrets.SLACK_CHANNEL_ID }}" payload: | { - "text": "[Test Runner] Nightly check has failed: ${{ github.event.pull_request.html_url || github.event.head_commit.url }}", "blocks": [ { - "type":"section", - "text":{ - "type":"mrkdwn", - "text":"<${{ github.event.pull_request.html_url }}|View Pull Request>" + "type": "header", + "text": { + "type": "plain_text", + "text": ":storybook: :runner: [Test Runner] The Nightly check has failed :alert:", + "emoji": true + } + }, + { + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Storybook version:*\n${{ env.sb_version }}" + } + ], + "accessory": { + "type": "button", + "text": { + "type": "plain_text", + "text": "View failure", + "emoji": true + }, + "value": "view_failure", + "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}", + "action_id": "button-action" } } ] diff --git a/.storybook/main.js b/.storybook/main.js index a5070768..451b70b6 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -33,7 +33,6 @@ const addons = [ module.exports = { stories, addons, - webpackFinal: async() => new Error('fail!'), features: { storyStoreV7: process.env.STORY_STORE_V7 ? true : false, buildStoriesJson: true, From 0cada549abc2ebab3d3b454fcb8563b8bcd378b7 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 27 May 2022 19:18:13 +0100 Subject: [PATCH 4/9] Updates readme to lock Jest to version 27 With this pull request, the Readme is updated to lock Jest to version 27.5.1 to prevent issues with the latest release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c35d2f41..802d3a38 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ yarn add @storybook/test-runner -D Jest is a peer dependency. If you don't have it, also install it ```jsx -yarn add jest -D +yarn add jest@27.5.1 -D ```
From f1b428b45fce8059cbd737244250d98205c22cc9 Mon Sep 17 00:00:00 2001 From: jonniebigodes Date: Fri, 27 May 2022 21:16:56 +0100 Subject: [PATCH 5/9] remove specific version --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 802d3a38..0054ccb9 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ yarn add @storybook/test-runner -D Jest is a peer dependency. If you don't have it, also install it ```jsx -yarn add jest@27.5.1 -D +yarn add jest@27 -D ```
From f7145f027c8391382fe847d618ffd78b4eb073b6 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Wed, 1 Jun 2022 11:18:53 +0200 Subject: [PATCH 6/9] feat: support DOM snapshot testing --- .storybook/main.js | 2 +- .storybook/test-runner.ts | 6 + README.md | 24 +- package.json | 1 + src/config/jest-playwright.ts | 1 + .../__snapshots__/Button.stories.js.snap | 39 +++ .../__snapshots__/Header.stories.js.snap | 88 +++++++ .../pages/__snapshots__/Page.stories.js.snap | 226 ++++++++++++++++++ yarn.lock | 61 ++++- 9 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 stories/atoms/__snapshots__/Button.stories.js.snap create mode 100644 stories/molecules/__snapshots__/Header.stories.js.snap create mode 100644 stories/pages/__snapshots__/Page.stories.js.snap diff --git a/.storybook/main.js b/.storybook/main.js index 451b70b6..9b65858b 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -11,7 +11,7 @@ const stories = [ directory: '../stories/molecules', }, // general glob - '../stories/pages/**/*.stories.*', + '../stories/pages/**/*.stories.@(js|jsx|ts|tsx)', ]; if (process.env.STRESS_TEST) { diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index ca9f5bea..d8a03d43 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -9,6 +9,7 @@ const config: TestRunnerConfig = { expect.extend({ toMatchImageSnapshot }); }, async postRender(page, context) { + // Visual snapshot tests const image = await page.screenshot({ fullPage: true }); expect(image).toMatchImageSnapshot({ customSnapshotsDir, @@ -16,6 +17,11 @@ const config: TestRunnerConfig = { failureThreshold: 0.03, failureThresholdType: 'percent', }); + + const elementHandler = await page.$('#root'); + const innerHTML = await elementHandler.innerHTML(); + // HTML snapshot tests + expect(innerHTML).toMatchSnapshot(); }, }; diff --git a/README.md b/README.md index 0054ccb9..dcd96ac9 100644 --- a/README.md +++ b/README.md @@ -19,8 +19,8 @@ Read the announcement: [Interaction Testing with Storybook](https://storybook.js - [Image snapshot recipe](#image-snapshot-recipe) - [Render lifecycle](#render-lifecycle) - [Troubleshooting](#troubleshooting) - - [The test runner seems flaky and keeps timing out](#the-test-runner-seems-flaky-and-keeps-timing-out) - - [Adding the test runner to other CI environments](#adding-the-test-runner-to-other-ci-environments) + - [The test runner seems flaky and keeps timing out](#the-test-runner-seems-flaky-and-keeps-timing-out) + - [Adding the test runner to other CI environments](#adding-the-test-runner-to-other-ci-environments) - [Future work](#future-work) ## Features @@ -170,7 +170,7 @@ module.exports = { }; ``` -Once you have a valid `stories.json` file, your Storybook will be compatible with the "stories.json mode". +Once you have a valid `stories.json` file, your Storybook will be compatible with the "stories.json mode". By default, the test runner will detect whether your Storybook URL is local or remote, and if it is remote, it will run in "stories.json mode" automatically. To disable it, you can pass the `--no-stories-json` flag: @@ -265,9 +265,25 @@ All three functions can be set up in the configuration file `.storybook/test-run > **NOTE:** These test hooks are experimental and may be subject to breaking changes. We encourage you to test as much as possible within the story's play function. +### DOM snapshot recipe + +The `postRender` function provides a [Playwright page](https://playwright.dev/docs/api/class-page) instance, of which you can use for DOM snapshot testing: + +```js +// .storybook/test-runner.js +module.exports = { + async postRender(page, context) { + // the #root element wraps the story + const elementHandler = await page.$('#root'); + const innerHTML = await elementHandler.innerHTML(); + expect(innerHTML).toMatchSnapshot(); + }, +}; +``` + ### Image snapshot recipe -Consider, for example, the following recipe to take image snapshots: +Here's a slightly different recipe for image snapshot testing: ```js // .storybook/test-runner.js diff --git a/package.json b/package.json index 7319a0f6..87079d5b 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "global": "^4.4.0", "is-localhost-ip": "^1.4.0", "jest-playwright-preset": "^1.7.0", + "jest-serializer-html": "^7.1.0", "jest-watch-typeahead": "^1.0.0", "node-fetch": "^2", "playwright": "^1.14.0", diff --git a/src/config/jest-playwright.ts b/src/config/jest-playwright.ts index f8c2a58b..e2dad3b6 100644 --- a/src/config/jest-playwright.ts +++ b/src/config/jest-playwright.ts @@ -14,6 +14,7 @@ export const getJestConfig = () => { globalTeardown: '@storybook/test-runner/playwright/global-teardown.js', testEnvironment: '@storybook/test-runner/playwright/custom-environment.js', setupFilesAfterEnv: ['@storybook/test-runner/playwright/jest-setup.js'], + snapshotSerializers: ['jest-serializer-html'], testEnvironmentOptions: { 'jest-playwright': { browsers: TEST_BROWSERS.split(',') diff --git a/stories/atoms/__snapshots__/Button.stories.js.snap b/stories/atoms/__snapshots__/Button.stories.js.snap new file mode 100644 index 00000000..dab58ccb --- /dev/null +++ b/stories/atoms/__snapshots__/Button.stories.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Atoms/Button Demo play-test 2`] = ` + +`; + +exports[`Atoms/Button FindBy play-test 2`] = ` + +`; + +exports[`Atoms/Button Primary smoke-test 2`] = ` + +`; + +exports[`Atoms/Button WaitFor play-test 2`] = ` + +`; + +exports[`Atoms/Button WaitForElementToBeRemoved play-test 2`] = ` + +`; + +exports[`Atoms/Button WithLoaders play-test 2`] = ` + +`; diff --git a/stories/molecules/__snapshots__/Header.stories.js.snap b/stories/molecules/__snapshots__/Header.stories.js.snap new file mode 100644 index 00000000..2953a50a --- /dev/null +++ b/stories/molecules/__snapshots__/Header.stories.js.snap @@ -0,0 +1,88 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Molecules/Header LoggedIn smoke-test 2`] = ` +
+
+
+ + + + + + + + + + +

+ Acme +

+
+
+ +
+
+
+`; + +exports[`Molecules/Header LoggedOut smoke-test 2`] = ` +
+
+
+ + + + + + + + + + +

+ Acme +

+
+
+ + +
+
+
+`; diff --git a/stories/pages/__snapshots__/Page.stories.js.snap b/stories/pages/__snapshots__/Page.stories.js.snap new file mode 100644 index 00000000..88e8e0b0 --- /dev/null +++ b/stories/pages/__snapshots__/Page.stories.js.snap @@ -0,0 +1,226 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Pages/Page LoggedIn smoke-test 2`] = ` +
+
+
+
+ + + + + + + + + + +

+ Acme +

+
+
+ +
+
+
+
+

+ Pages in Storybook +

+

+ We recommend building UIs with a + + + component-driven + + + process starting with atomic components and ending with pages. +

+

+ Render pages with mock data. This makes it easy to build and review page states without needing to navigate to them in your app. Here are some handy patterns for managing page data in Storybook: +

+
    +
  • + Use a higher-level connected component. Storybook helps you compose such data from the "args" of child component stories +
  • +
  • + Assemble data in the page component from your services. You can mock these services out using Storybook. +
  • +
+

+ Get a guided tutorial on component-driven development at + + Learn Storybook + + . Read more in the + + docs + + . +

+
+ + Tip + + Adjust the width of the canvas with the + + + + + + + Viewports addon in the toolbar +
+
+
+`; + +exports[`Pages/Page LoggedOut smoke-test 2`] = ` +
+
+
+
+ + + + + + + + + + +

+ Acme +

+
+
+ + +
+
+
+
+

+ Pages in Storybook +

+

+ We recommend building UIs with a + + + component-driven + + + process starting with atomic components and ending with pages. +

+

+ Render pages with mock data. This makes it easy to build and review page states without needing to navigate to them in your app. Here are some handy patterns for managing page data in Storybook: +

+
    +
  • + Use a higher-level connected component. Storybook helps you compose such data from the "args" of child component stories +
  • +
  • + Assemble data in the page component from your services. You can mock these services out using Storybook. +
  • +
+

+ Get a guided tutorial on component-driven development at + + Learn Storybook + + . Read more in the + + docs + + . +

+
+ + Tip + + Adjust the width of the canvas with the + + + + + + + Viewports addon in the toolbar +
+
+
+`; diff --git a/yarn.lock b/yarn.lock index 868fc507..33fd05cf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5665,6 +5665,13 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +diffable-html@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/diffable-html/-/diffable-html-4.1.0.tgz#e7a2d1de187c4e23a59751b4e4c17483a058c696" + integrity sha512-++kyNek+YBLH8cLXS+iTj/Hiy2s5qkRJEJ8kgu/WHbFrVY2vz9xPFUT+fii2zGF0m1CaojDlQJjkfrCt7YWM1g== + dependencies: + htmlparser2 "^3.9.2" + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5707,6 +5714,14 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -5726,6 +5741,11 @@ domain-browser@^1.1.1: resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" @@ -5738,6 +5758,13 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" @@ -5745,6 +5772,14 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + domutils@^2.5.2, domutils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" @@ -5879,6 +5914,11 @@ enquirer@^2.3.4: dependencies: ansi-colors "^4.1.1" +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + entities@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" @@ -7212,6 +7252,18 @@ html-webpack-plugin@^4.0.0: tapable "^1.1.3" util.promisify "1.0.0" +htmlparser2@^3.9.2: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -8336,6 +8388,13 @@ jest-runtime@^27.5.1: slash "^3.0.0" strip-bom "^4.0.0" +jest-serializer-html@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/jest-serializer-html/-/jest-serializer-html-7.1.0.tgz#0cfea8a03b9b82bc420fd2cb969bd76713a87c08" + integrity sha512-xYL2qC7kmoYHJo8MYqJkzrl/Fdlx+fat4U1AqYg+kafqwcKPiMkOcjWHPKhueuNEgr+uemhGc+jqXYiwCyRyLA== + dependencies: + diffable-html "^4.1.0" + jest-serializer@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-26.6.2.tgz#d139aafd46957d3a448f3a6cdabe2919ba0742d1" @@ -10579,7 +10638,7 @@ read-pkg@^5.2.0: string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@^3.6.0: +readable-stream@^3.1.1, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== From fedd38658749f44c717fb54c6f0bc96ddce4f1d2 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Mon, 6 Jun 2022 16:00:18 +0200 Subject: [PATCH 7/9] chore(CI): add sb versions to report [skip release] --- .github/workflows/nightly.yml | 30 ++++++++++++++++++++++++------ .github/workflows/release.yml | 2 +- .storybook/main.js | 3 +++ 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ed62f141..1956f2f3 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -19,20 +19,34 @@ jobs: - name: Install dependencies uses: bahmutov/npm-install@v1 + - name: Get current version of Storybook + run: | + echo "prev_sb_version=$(yarn list @storybook/react --depth=0 2> /dev/null | grep @storybook/react | awk -F'@' '{print $3}')" >> $GITHUB_ENV + echo "prev_sb_csf_version=$(yarn list @storybook/csf --depth=0 2> /dev/null | grep @storybook/csf | awk -F'@' '{print $3}')" >> $GITHUB_ENV + - name: Upgrade to storybook@next run: | npx storybook upgrade --prerelease - - name: Run test runner + # TODO: This should not be necessary once @storybook/csf is properly updated + - name: Fix local @storybook/csf version run: | - yarn build - yarn test-storybook:ci + yarn add @storybook/csf@0.0.2--canary.4566f4d.1 + + - name: Run test runner + uses: mathiasvr/command-output@v1 + id: tests + with: + run: | + yarn build + yarn test-storybook:ci - name: Get prerelease version of Storybook if: ${{ failure() }} run: | - echo "sb_version=$(npm view @storybook/react@prerelease version)" >> $GITHUB_ENV - + echo "sb_version=$(yarn list @storybook/react --depth=0 2> /dev/null | grep @storybook/react | awk -F'@' '{print $3}')" >> $GITHUB_ENV + echo "sb_csf_version=$(yarn list @storybook/csf --depth=0 2> /dev/null | grep @storybook/csf | awk -F'@' '{print $3}')" >> $GITHUB_ENV + - name: Report incoming errors if: ${{ failure() }} id: slack @@ -55,7 +69,11 @@ jobs: "fields": [ { "type": "mrkdwn", - "text": "*Storybook version:*\n${{ env.sb_version }}" + "text": "*@storybook/react version:*\n${{ env.prev_sb_version }} >> ${{ env.sb_version }}" + }, + { + "type": "mrkdwn", + "text": "*@storybook/csf version:*\n${{ env.prev_sb_csf_version }} >> ${{ env.sb_csf_version }}" } ], "accessory": { diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2b0f6e2e..3d84250a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,7 +5,7 @@ on: [push] jobs: release: runs-on: ubuntu-latest - if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci')" + if: "!contains(github.event.head_commit.message, 'ci skip') && !contains(github.event.head_commit.message, 'skip ci') && !contains(github.event.head_commit.message, 'skip release')" steps: - uses: actions/checkout@v2 diff --git a/.storybook/main.js b/.storybook/main.js index 451b70b6..93500bc4 100644 --- a/.storybook/main.js +++ b/.storybook/main.js @@ -37,4 +37,7 @@ module.exports = { storyStoreV7: process.env.STORY_STORE_V7 ? true : false, buildStoriesJson: true, }, + core: { + disableTelemetry: true + } }; From 7c42f3f14963b2a4acffcc725777522beb9a3ab6 Mon Sep 17 00:00:00 2001 From: Bodo Graumann Date: Tue, 7 Jun 2022 11:23:23 +0200 Subject: [PATCH 8/9] chore(DEPS): update jest-playwright-preset The new version 1.7.1 has limited the version range for jest to <28. Cf. https://github.com/playwright-community/jest-playwright/issues/793 --- package.json | 2 +- yarn.lock | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 87079d5b..46d0f6ca 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "commander": "^9.0.0", "global": "^4.4.0", "is-localhost-ip": "^1.4.0", - "jest-playwright-preset": "^1.7.0", + "jest-playwright-preset": "^1.7.2", "jest-serializer-html": "^7.1.0", "jest-watch-typeahead": "^1.0.0", "node-fetch": "^2", diff --git a/yarn.lock b/yarn.lock index 33fd05cf..f7c4a4ab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6198,10 +6198,10 @@ expand-tilde@^1.2.2: dependencies: os-homedir "^1.0.1" -expect-playwright@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/expect-playwright/-/expect-playwright-0.7.2.tgz#e1f2c31cd0d13d04e1ba4136019613ad01f6ea43" - integrity sha512-5o9si+8SUi68QVI0CRVv8tvTjZinpJWRSfQ3GP6v0DvlK55lDgFvD79r6A/NU+EUawrBc62qP30MxzOUnXNJZQ== +expect-playwright@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/expect-playwright/-/expect-playwright-0.8.0.tgz#6d4ebe0bdbdd3c1693d880d97153b96a129ae4e8" + integrity sha512-+kn8561vHAY+dt+0gMqqj1oY+g5xWrsuGMk4QGxotT2WS545nVqqjs37z6hrYfIuucwqthzwJfCJUEYqixyljg== expect@^27.5.1: version "27.5.1" @@ -8260,12 +8260,12 @@ jest-mock@^27.0.6, jest-mock@^27.3.0, jest-mock@^27.5.1: "@jest/types" "^27.5.1" "@types/node" "*" -jest-playwright-preset@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/jest-playwright-preset/-/jest-playwright-preset-1.7.0.tgz#bd5998d1a9090fdf68a9dc4421da4c0d45f9156c" - integrity sha512-G25Nik+By0SNniMDdkouDL/yA1LdqjzsXNSVU4xnRX1typjXRmzRE0aSgqxas2sRi8cwG3M1ioHdkLLsp6sang== +jest-playwright-preset@^1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/jest-playwright-preset/-/jest-playwright-preset-1.7.2.tgz#708942c4dcc1edc85429079d2b47a9382298c454" + integrity sha512-0M7M3z342bdKQLnS70cIptlJsW+uuGptbPnqIMg4K5Vp/L/DhqdTKZK7WM4n6miAUnZdUcjXKOdQWfZW/aBo7w== dependencies: - expect-playwright "^0.7.0" + expect-playwright "^0.8.0" jest-process-manager "^0.3.1" nyc "^15.1.0" playwright-core ">=1.2.0" From f7e708ec886ab0a9f83b328d0e870bcef62871e3 Mon Sep 17 00:00:00 2001 From: Yann Braga Date: Tue, 21 Jun 2022 10:42:46 +0200 Subject: [PATCH 9/9] feat: provide global getStoryContext utility --- .storybook/test-runner.ts | 9 ++++++ README.md | 40 +++++++++++++++++++++++++++ src/playwright/hooks.ts | 8 ++++++ src/setup-page.ts | 6 +++- src/util/getStorybookMetadata.test.ts | 2 +- stories/atoms/Button.stories.js | 11 ++++++++ 6 files changed, 74 insertions(+), 2 deletions(-) diff --git a/.storybook/test-runner.ts b/.storybook/test-runner.ts index d8a03d43..c624eaee 100644 --- a/.storybook/test-runner.ts +++ b/.storybook/test-runner.ts @@ -1,4 +1,5 @@ import { toMatchImageSnapshot } from 'jest-image-snapshot'; +import { getStoryContext } from '../dist/cjs/playwright/hooks'; import type { TestRunnerConfig } from '../dist/ts'; const snapshotsDir = process.env.SNAPSHOTS_DIR || '__snapshots__'; @@ -9,6 +10,14 @@ const config: TestRunnerConfig = { expect.extend({ toMatchImageSnapshot }); }, async postRender(page, context) { + // Get entire context of a story, including parameters, args, argTypes, etc. + const { parameters } = await getStoryContext(page, context); + + if (parameters?.tests?.disableSnapshots) { + console.log('skipping story ', context.id); + return; + } + // Visual snapshot tests const image = await page.screenshot({ fullPage: true }); expect(image).toMatchImageSnapshot({ diff --git a/README.md b/README.md index dcd96ac9..d689dccd 100644 --- a/README.md +++ b/README.md @@ -330,6 +330,46 @@ it('button--basic', async () => { }); ``` +### Global utility functions + +While running tests using the hooks, you might want to get information from a story, such as the parameters passed to it, or its args. The test runner now provides a `getStoryContext` utility function that fetches the story context for the current story: + +```js +await getStoryContext(page, context); +``` + +You can use it for multiple use cases, and here's an example that combines the story context and accessibility testing: + +```js +// .storybook/test-runner.js +const { getStoryContext } = require('@storybook/test-runner'); +const { injectAxe, checkA11y } = require('axe-playwright'); + +module.exports = { + async preRender(page, context) { + await injectAxe(page); + }, + async postRender(page, context) { + // Get entire context of a story, including parameters, args, argTypes, etc. + const storyContext = await getStoryContext(page, context); + + // Do not test a11y for stories that disable a11y + if (storyContext.parameters?.a11y?.disable) { + return; + } + + await checkA11y(page, '#root', { + detailedReport: true, + detailedReportOptions: { + html: true, + }, + // pass axe options defined in @storybook/addon-a11y + axeOptions: storyContext.parameters?.a11y?.options + }) + }, +}; +``` + ## Troubleshooting #### Errors with Jest 28 diff --git a/src/playwright/hooks.ts b/src/playwright/hooks.ts index 518a9b91..52cc1930 100644 --- a/src/playwright/hooks.ts +++ b/src/playwright/hooks.ts @@ -1,5 +1,6 @@ import global from 'global'; import type { Page } from 'playwright'; +import type { StoryContext } from '@storybook/csf'; export type TestContext = { id: string; @@ -22,3 +23,10 @@ export const setPreRender = (preRender: TestHook) => { export const setPostRender = (postRender: TestHook) => { global.__sbPostRender = postRender; }; + +export const getStoryContext = async (page: Page, context: TestContext): Promise => { + // @ts-ignore + return page.evaluate(({ storyId }) => globalThis.__getContext(storyId), { + storyId: context.id, + }); +}; diff --git a/src/setup-page.ts b/src/setup-page.ts index 4ea59ecb..9bc85a71 100644 --- a/src/setup-page.ts +++ b/src/setup-page.ts @@ -96,6 +96,10 @@ export const setupPage = async (page) => { }); } + async function __getContext(storyId) { + return globalThis.__STORYBOOK_PREVIEW__.storyStore.loadStory({ storyId }); + } + async function __test(storyId) { try { await __waitForElement('#root'); @@ -104,7 +108,7 @@ export const setupPage = async (page) => { throw new StorybookTestRunnerError(storyId, message); } - const channel = window.__STORYBOOK_ADDONS_CHANNEL__; + const channel = globalThis.__STORYBOOK_ADDONS_CHANNEL__; if(!channel) { throw new StorybookTestRunnerError( storyId, diff --git a/src/util/getStorybookMetadata.test.ts b/src/util/getStorybookMetadata.test.ts index d2fee289..12c0a3c5 100644 --- a/src/util/getStorybookMetadata.test.ts +++ b/src/util/getStorybookMetadata.test.ts @@ -2,7 +2,7 @@ import * as storybookMain from './getStorybookMain'; import { getStorybookMetadata } from './getStorybookMetadata'; -describe.only('getStorybookMetadata', () => { +describe('getStorybookMetadata', () => { afterAll(() => { process.env.STORYBOOK_CONFIG_DIR = undefined; }); diff --git a/stories/atoms/Button.stories.js b/stories/atoms/Button.stories.js index a4689570..f76b7fd2 100644 --- a/stories/atoms/Button.stories.js +++ b/stories/atoms/Button.stories.js @@ -24,6 +24,17 @@ Primary.args = { label: 'Button', }; +export const Secondary = Template.bind({}); +Secondary.args = { + ...Primary.args, + primary: false, +}; +Secondary.parameters = { + tests: { + disableSnapshots: true + } +} + export const Demo = (args) => (