diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 63c9c05843e9a8..28c7b262bf13af 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -247,6 +247,7 @@ packages/react-components/react-tabs/library @microsoft/cxe-prg @dmytrokirpa packages/react-components/react-tabs/stories @microsoft/cxe-prg @dmytrokirpa packages/react-components/react-text/library @microsoft/cxe-prg @marcosmoura packages/react-components/react-text/stories @microsoft/cxe-prg @marcosmoura +packages/react-components/react-text/visual-regression @microsoft/cxe-prg @marcosmoura packages/react-components/react-textarea/library @microsoft/cxe-prg packages/react-components/react-textarea/stories @microsoft/cxe-prg packages/react-components/react-tooltip/library @microsoft/cxe-prg diff --git a/.github/workflows/pr-vrt-poc-comment.yml b/.github/workflows/pr-vrt-poc-comment.yml new file mode 100644 index 00000000000000..9edd374eebb79a --- /dev/null +++ b/.github/workflows/pr-vrt-poc-comment.yml @@ -0,0 +1,67 @@ +name: VRT POC | Comment on PR +on: + workflow_run: + workflows: ['VRT POC'] + types: + - completed + +concurrency: + # see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + NX_PARALLEL: 4 # ubuntu-latest = 4-core CPU / 16 GB of RAM | macos-14-xlarge (arm) = 6-core CPU / 14 GB of RAM + NX_PREFER_TS_NODE: true + NX_VERBOSE_LOGGING: true + +jobs: + run_vr_diff: + runs-on: ubuntu-latest + if: ${{ github.repository_owner == 'microsoft' }} && ${{ github.event.workflow_run.event == 'pull_request' }} + outputs: + pr_number: ${{ steps.pr_number.outputs.result }} + permissions: + # necessary to write comments to the PR from the vr-approval-cli + pull-requests: write + # id-token: write + steps: + # - uses: actions/checkout@v4 + # with: + # sparse-checkout: | + # .github + + # - uses: actions/download-artifact@v4 + # with: + # name: vrt_report + # path: ./vrt_report + # run-id: ${{ github.event.workflow_run.id }} + # github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Debug + run: | + echo $W_RUN + env: + W_RUN: ${{ toJSON(github.event.workflow_run) }} + + - uses: actions/download-artifact@v4 + with: + name: pr-number + path: ./results + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Load PR number + id: pr_number + run: echo "id=$(cat pr.txt)" >> $GITHUB_OUTPUT + working-directory: ./results + + - name: 'Comment on PR' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: vr-report-comment + number: ${{ steps.pr_number.outputs.id }} + message: | + VR Regression Report: + - status: ${{ github.event.workflow_run.conclusion }} + - check whole [report](${{github.event.workflow_run.html_url}}) to review visual differences diff --git a/.github/workflows/pr-vrt-poc.yml b/.github/workflows/pr-vrt-poc.yml new file mode 100644 index 00000000000000..e9989d15e6a8c1 --- /dev/null +++ b/.github/workflows/pr-vrt-poc.yml @@ -0,0 +1,165 @@ +name: VRT POC +on: + push: + branches: + - hotell/fhl-spring-25 + pull_request: + # TODO: once testing is done enable pull_request on all branches again + branches: + - hotell/fhl-spring-25 + issue_comment: + types: [created] + +concurrency: + # see https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#example-only-cancel-in-progress-jobs-or-runs-for-the-current-workflow + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +env: + NX_PARALLEL: 6 # ubuntu-latest = 4-core CPU / 16 GB of RAM | macos-14-xlarge (arm) = 6-core CPU / 14 GB of RAM + NX_PREFER_TS_NODE: true + NX_VERBOSE_LOGGING: true + +jobs: + # TODO: it's not possible to update another JOB result to "success" in order to make PR mergeable + # + approve_vrt_diff: + if: github.event.issue.pull_request && contains(github.event.comment.body,'/approve-snapshots') + runs-on: ubuntu-latest + # permissions: + # statuses: write + steps: + - run: echo "Hello" + # - name: Approve VRT Diff + # uses: actions/github-script@v7 + # with: + # script: | + # const run = require('./.github/scripts/approve-pr-vrt-diff'); + # const config = { prId: context.payload.issue.number }; + # await run({github,context,core,config}); + + vrt_baseline: + if: ${{ github.repository_owner == 'microsoft' && github.event_name == 'push' && github.event_name!='pull_request' }} + # NOTE: there is an issue on GH atm obtaining large runner - undo this change once resolved + # runs-on: macos-14-xlarge + runs-on: ubuntu-latest + permissions: + contents: 'write' + actions: 'write' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Derive appropriate SHAs for base and head for `nx affected` commands + uses: nrwl/nx-set-shas@v4 + with: + main-branch-name: 'hotell/fhl-spring-25' + + - uses: actions/setup-node@v4 + with: + cache: 'yarn' + node-version: '20' + + - run: yarn install --frozen-lockfile + - run: yarn playwright install --with-deps + - run: yarn nx run visual-regression-assert:build + + - name: Update VR tests baseline + run: yarn nx affected -t test-vr-cli -u --nxBail + + - name: Affected Projects + id: affected_projects_count + run: | + yarn --silent nx show projects -t test-vr-cli --affected --verbose false + affected_count=$(yarn --silent nx show projects -t test-vr-cli --affected --verbose false | wc -l | tr -d ' ') + echo "value=$affected_count" >> $GITHUB_OUTPUT + + - name: Affected Projects Count + run: echo ${{steps.affected_projects_count.outputs.value}} + + - name: Update Baseline + if: steps.affected_projects_count.outputs.value > 0 + run: | + git config user.name "Fluent UI Build" + git config user.email "fluentui-internal@service.microsoft.com" + git add *.png + if [[ -n $(git status --porcelain) ]]; then + git commit -m "chore: update vrt baseline" + git push origin HEAD:${{ github.ref_name }} + else + echo "No PNG changes to commit." + fi + + - name: Report Updated Baseline + if: steps.affected_projects_count.outputs.value > 0 + run: | + yarn visual-regression-assert report --outputPath dist/vrt + cat dist/vrt/vrt-report.md >> $GITHUB_STEP_SUMMARY + + vrt_pr: + if: ${{ github.repository_owner == 'microsoft' && github.event_name == 'pull_request' }} + # NOTE: there is an issue on GH atm obtaining large runner - undo this change once resolved + # runs-on: macos-14-xlarge + runs-on: ubuntu-latest + permissions: + contents: 'read' + actions: 'read' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Derive appropriate SHAs for base and head for `nx affected` commands + uses: nrwl/nx-set-shas@v4 + with: + main-branch-name: 'hotell/fhl-spring-25' + + - uses: actions/setup-node@v4 + with: + cache: 'yarn' + node-version: '20' + + - run: yarn install --frozen-lockfile + - run: yarn playwright install --with-deps + - run: yarn nx run visual-regression-assert:build + + - name: Run VR tests + run: yarn nx affected -t test-vr-cli + + - name: Report + if: always() + id: report + run: | + yarn visual-regression-assert report --outputPath dist/vrt + cat dist/vrt/vrt-report.md >> $GITHUB_STEP_SUMMARY + echo "value=true" >> $GITHUB_OUTPUT + + - name: Upload Report + if: ${{ always() && steps.report.outputs.value == 'true' }} + uses: actions/upload-artifact@v4 + with: + name: vrt_report + retention-days: 1 + if-no-files-found: error + path: | + dist/vrt/ + + - name: Save PR number + if: always() + run: echo ${{ github.event.number }} > pr.txt + - uses: actions/upload-artifact@v4 + if: always() + with: + name: pr-number + retention-days: 1 + if-no-files-found: error + path: | + pr.txt + + # - name: Upload VR screenshots + # uses: actions/upload-artifact@v4 + # with: + # name: vrscreenshot + # retention-days: 1 + # path: ${{steps.screenshots_root.outputs.result}} diff --git a/.gitignore b/.gitignore index 2775862c30b47e..8d2ae8e017fe8f 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,7 @@ $RECYCLE.BIN/ # Generated files *.scss.ts +playwright-report # Files that might appear on external disk .Spotlight-V100 diff --git a/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx b/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx deleted file mode 100644 index d7cd382029b1e6..00000000000000 --- a/apps/vr-tests-react-components/src/stories/Tabs.stories.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import * as React from 'react'; -import type { Meta } from '@storybook/react'; -import { Steps } from 'storywright'; -import { TabList, Tab } from '@fluentui/react-tabs'; - -import { DARK_MODE, getStoryVariant, HIGH_CONTRAST, RTL, withStoryWrightSteps } from '../utilities'; - -export default { - title: 'TabList and Tab Converged', - - decorators: [ - story => - withStoryWrightSteps({ - story, - steps: new Steps() - .snapshot('default') - .hover('.mouse-target') - .snapshot('hover') - .mouseDown('.mouse-target') - .snapshot('pressed') - .mouseUp('.mouse-target') - .end(), - }), - ], -} satisfies Meta; - -export const Default = () => ( - - First - - Second - - Third - -); - -export const DefaultRTL = getStoryVariant(Default, RTL); - -export const DefaultHighContrast = getStoryVariant(Default, HIGH_CONTRAST); - -export const DefaultDarkMode = getStoryVariant(Default, DARK_MODE); - -export const Vertical = () => ( - - First - - Second - - Third - -); - -export const SubtleAppearance = () => ( - - First - - Second - - Third - -); -SubtleAppearance.storyName = 'Subtle appearance'; - -export const SubtleAppearanceHighContrast = getStoryVariant(SubtleAppearance, HIGH_CONTRAST); - -export const SubtleAppearanceDarkMode = getStoryVariant(SubtleAppearance, DARK_MODE); - -export const SmallSize = () => ( - - First - - Second - - Third - -); -SmallSize.storyName = 'Small size'; - -export const SmallSizeHighContrast = getStoryVariant(SmallSize, HIGH_CONTRAST); - -export const SmallSizeDarkMode = getStoryVariant(SmallSize, DARK_MODE); - -export const VerticalAndSmallSize = () => ( - - First - - Second - - Third - -); -VerticalAndSmallSize.storyName = 'Vertical and small size'; - -export const VerticalAndSmallSizeHighContrast = getStoryVariant(VerticalAndSmallSize, HIGH_CONTRAST); - -export const VerticalAndSmallSizeDarkMode = getStoryVariant(VerticalAndSmallSize, DARK_MODE); - -export const LargeSize = () => ( - - First - - Second - - Third - -); -LargeSize.storyName = 'Large size'; - -export const LargeSizeHighContrast = getStoryVariant(LargeSize, HIGH_CONTRAST); - -export const LargeSizeDarkMode = getStoryVariant(LargeSize, DARK_MODE); - -export const VerticalAndLargeSize = () => ( - - First - - Second - - Third - -); -VerticalAndLargeSize.storyName = 'Vertical and large size'; - -export const VerticalAndLargeSizeHighContrast = getStoryVariant(VerticalAndLargeSize, HIGH_CONTRAST); - -export const VerticalAndLargeSizeDarkMode = getStoryVariant(VerticalAndLargeSize, DARK_MODE); - -export const TabSelectedDefault = () => ( - - First - - Second - - Third - -); -TabSelectedDefault.storyName = 'Tab Selected (default)'; - -export const TabSelectedDefaultHighContrast = getStoryVariant(TabSelectedDefault, HIGH_CONTRAST); - -export const TabSelectedDefaultDarkMode = getStoryVariant(TabSelectedDefault, DARK_MODE); - -export const TabSelected = () => ( - - First - - Second - - Third - -); - -export const TabSelectedHighContrast = getStoryVariant(TabSelected, HIGH_CONTRAST); - -export const TabSelectedDarkMode = getStoryVariant(TabSelected, DARK_MODE); - -export const WithIcon = () => ( - - - First - - - Second - - - Third - - -); -WithIcon.storyName = 'With icon'; - -export const WithIconRTL = getStoryVariant(WithIcon, RTL); - -export const WithIconAndVertical = () => ( - - - First - - - Second - - - Third - - -); -WithIconAndVertical.storyName = 'With icon and vertical'; - -export const WithIconAndVerticalRTL = getStoryVariant(WithIconAndVertical, RTL); - -export const WithIconOnly = () => ( - - - - - -); -WithIconOnly.storyName = 'With icon only'; - -export const WithIconOnlyAndVertical = () => ( - - - - - -); - -WithIconOnlyAndVertical.storyName = 'With icon only and vertical'; - -export const SubtleCircularAppearance = () => ( - - First - - Second - - Third - -); - -export const SubtleCircularAppearanceDarkMode = getStoryVariant(SubtleCircularAppearance, DARK_MODE); - -export const SubtleCircularAppearanceHighContrast = getStoryVariant(SubtleCircularAppearance, HIGH_CONTRAST); - -export const FilledCircularAppearance = () => ( - - First - - Second - - Third - -); - -export const FilledCircularAppearanceDarkMode = getStoryVariant(FilledCircularAppearance, DARK_MODE); - -export const FilledCircularAppearanceHighContrast = getStoryVariant(FilledCircularAppearance, HIGH_CONTRAST); diff --git a/apps/vr-tests-react-components/src/stories/Text.stories.tsx b/apps/vr-tests-react-components/src/stories/Text.stories.tsx deleted file mode 100644 index 2f44cb6f30c088..00000000000000 --- a/apps/vr-tests-react-components/src/stories/Text.stories.tsx +++ /dev/null @@ -1,180 +0,0 @@ -import * as React from 'react'; -import type { Meta } from '@storybook/react'; -import { Steps } from 'storywright'; -import { - Body1, - Body1Strong, - Body1Stronger, - Body2, - Caption1, - Caption1Strong, - Caption1Stronger, - Caption2, - Caption2Strong, - Display, - LargeTitle, - Subtitle1, - Subtitle2, - Subtitle2Stronger, - Text, - Title1, - Title2, - Title3, -} from '@fluentui/react-text'; - -import { DARK_MODE, getStoryVariant, HIGH_CONTRAST, RTL, withStoryWrightSteps } from '../utilities'; - -export default { - title: 'Text Converged', - - decorators: [ - (storyFn, context) => ( -
- {storyFn(context)} -
- ), - story => withStoryWrightSteps({ story, steps: new Steps().snapshot('normal', { cropTo: '.testWrapper' }).end() }), - ], -} satisfies Meta; - -export const Default = () => ( - <> -

- Default - Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. -

-

- No wrapping - - Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. - -

-

- Truncate - - Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. - -

-

- Italic - - Hello, world - -

-

- Underline - - Hello, world - -

-

- Strikethrough - - Hello, world - -

-

- Sizes - - 100 - - - 200 - - - 300 - - - 400 - - - 500 - - - 600 - - - 700 - - - 800 - - - 900 - - - 1000 - -

-

- Fonts - - Base - - - Monospace - - - Numeric - -

-

- Weights - - Regular - - - Medium - - - Semibold - -

-

- Alignments - - Start - - - Center - - - End - - - Justified lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. - -

- -); - -export const DefaultRTL = getStoryVariant(Default, RTL); - -export const DefaultHighContrast = getStoryVariant(Default, HIGH_CONTRAST); - -export const DefaultDarkMode = getStoryVariant(Default, DARK_MODE); - -export const TypographyPresets = () => ( - <> - Body1 - Body1Strong - Body1Stronger - Body2 - Caption1 - Caption1Strong - Caption1Stronger - Caption2 - Caption2Strong - Display - LargeTitle - Subtitle1 - Subtitle2 - Subtitle2Stronger - Title1 - Title2 - Title3 - -); -TypographyPresets.storyName = 'Typography presets'; - -export const TypographyPresetsRTL = getStoryVariant(TypographyPresets, RTL); diff --git a/package.json b/package.json index a3b4883509a9cd..31150079ac4a17 100644 --- a/package.json +++ b/package.json @@ -132,7 +132,7 @@ "@types/d3-time": "3.0.3", "@types/d3-time-format": "3.0.4", "@types/doctrine": "0.0.5", - "@types/ejs": "3.1.2", + "@types/ejs": "3.1.5", "@types/enzyme": "3.10.7", "@types/eslint": "8.56.10", "@types/express": "4.17.21", @@ -154,6 +154,7 @@ "@types/markdown-table": "2.0.0", "@types/micromatch": "4.0.2", "@types/node": "20.12.12", + "@types/pngjs": "6.0.5", "@types/prettier": "2.7.2", "@types/progress": "2.0.5", "@types/react": "17.0.44", @@ -286,7 +287,9 @@ "p-queue": "6.6.2", "parse-diff": "0.7.1", "path-browserify": "1.0.1", + "pixelmatch": "7.1.0", "plop": "2.6.0", + "pngjs": "7.0.0", "portfinder": "1.0.28", "postcss": "8.4.31", "postcss-loader": "4.1.0", @@ -325,7 +328,7 @@ "source-map-loader": "4.0.0", "storybook": "7.6.20", "storybook-addon-performance": "0.17.3", - "storywright": "0.0.27-storybook7.11", + "storywright": "0.0.27-storybook7.13", "strip-ansi": "6.0.0", "style-loader": "2.0.0", "swc-loader": "0.2.6", diff --git a/packages/react-components/react-tabs/visual-regression/.eslintrc.json b/packages/react-components/react-tabs/visual-regression/.eslintrc.json new file mode 100644 index 00000000000000..7c4a0ec7ee8ecc --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"] + } + ] + } + } + ] +} diff --git a/packages/react-components/react-tabs/visual-regression/.storybook/main.js b/packages/react-components/react-tabs/visual-regression/.storybook/main.js new file mode 100644 index 00000000000000..ec3017dba08569 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/.storybook/main.js @@ -0,0 +1,58 @@ +const path = require('path'); + +const { registerTsPaths, registerRules, rules, loadWorkspaceAddon } = require('@fluentui/scripts-storybook'); +const tsConfigPath = path.resolve(__dirname, '../../../../../tsconfig.base.json'); + +module.exports = /** @type {import('@storybook/react-webpack5').StorybookConfig} */ ({ + addons: [loadWorkspaceAddon('@fluentui/react-storybook-addon', { tsConfigPath })], + stories: ['../src/**/*.stories.tsx'], + core: { + disableTelemetry: true, + }, + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + lazyCompilation: false, + }, + }, + }, + typescript: { + // disable react-docgen-typescript (totally not needed here, slows things down a lot) + reactDocgen: false, + }, + webpackFinal: (config, options) => { + if (options.configType === 'PRODUCTION') { + // Disable source maps + config.devtool = false; + } + registerTsPaths({ config, configFile: tsConfigPath }); + registerRules({ config, rules: [rules.griffelRule] }); + + return config; + }, + swc() { + return { + jsc: { + target: 'es2019', + parser: { + syntax: 'typescript', + tsx: true, + decorators: true, + dynamicImport: true, + }, + transform: { + decoratorMetadata: true, + legacyDecorator: true, + }, + keepClassNames: true, + externalHelpers: true, + loose: true, + minify: { + mangle: false, + }, + }, + }; + }, +}); diff --git a/packages/react-components/react-tabs/visual-regression/.storybook/preview.js b/packages/react-components/react-tabs/visual-regression/.storybook/preview.js new file mode 100644 index 00000000000000..1851443859d182 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/.storybook/preview.js @@ -0,0 +1,8 @@ +/** @type {import("@fluentui/react-storybook-addon").FluentParameters} */ +export const parameters = { + layout: 'none', + mode: 'vr-test', + reactStorybookAddon: { + disabledDecorators: ['AriaLive'], + }, +}; diff --git a/packages/react-components/react-tabs/visual-regression/.storybook/tsconfig.json b/packages/react-components/react-tabs/visual-regression/.storybook/tsconfig.json new file mode 100644 index 00000000000000..4cdd1ce9d006f1 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment"] + }, + "include": ["*.js"] +} diff --git a/packages/react-components/react-tabs/visual-regression/README.md b/packages/react-components/react-tabs/visual-regression/README.md new file mode 100644 index 00000000000000..23a402071f6754 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/README.md @@ -0,0 +1,43 @@ +# react-tabs-visual-regression + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build react-tabs-visual-regression` to build the library. + +## Running unit tests + +Run `nx test react-tabs-visual-regression` to execute the unit tests via [Jest](https://jestjs.io). + +## About + +Free, Secure and OSS VR testing solution based on: + +- Storybook to author VR scenarios +- StoryWright for capturing Stories and their interactions +- ~PlayWright~ Custom VR Regression CLI for running diffing and updating baseline + +**Demo:** + +> ## NOTE: needs new StoryWright features: +> +> - [x] https://github.com/microsoft/storywright/pull/73 +> - [x] https://github.com/microsoft/storywright/pull/74 + +**Usage:** + +- uses new VRT CLI with configured caching: + +`yarn react-tabs-visual-regression:test-vr-cli` + +- DEPRECATED: uses custom nx generator ( under the hood uses playwright ) + +`yarn react-tabs-visual-regression:test-vr` + +## TODO + +- [x] check raw [pixelmatch](https://github.com/mapbox/pixelmatch) usage instead PW/test / custom NX Executor +- [ ] check using [pixelmatch](https://github.com/mapbox/pixelmatch) within storywright with implementing new API `--mode= assert | screenshot` +- [x] check using PW/test instead just PW within storywright +- [] setup CI and approval pipeline diff --git a/packages/react-components/react-tabs/visual-regression/package.json b/packages/react-components/react-tabs/visual-regression/package.json new file mode 100644 index 00000000000000..59d8f0f7f72b32 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/package.json @@ -0,0 +1,19 @@ +{ + "name": "@fluentui/react-tabs-visual-regression", + "version": "0.0.0", + "private": true, + "dependencies": { + "@fluentui/react-tabs": "*", + "@fluentui/visual-regression-utilities": "*" + }, + "peerDependencies": { + "@storybook/react": "^7.6.20" + }, + "devDependencies": { + "@fluentui/react-storybook-addon": "*", + "@fluentui/scripts-storybook": "*" + }, + "type": "commonjs", + "main": "./src/index.js", + "typings": "./src/index.d.ts" +} diff --git a/packages/react-components/react-tabs/visual-regression/project.json b/packages/react-components/react-tabs/visual-regression/project.json new file mode 100644 index 00000000000000..8645f5d293cc06 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/project.json @@ -0,0 +1,53 @@ +{ + "name": "react-tabs-visual-regression", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/react-components/react-tabs/visual-regression/src", + "projectType": "library", + "tags": ["vNext", "platform:web", "visual-regression"], + "targets": { + "generate-image-for-vrt": { + "command": "rm -rf dist/vrt/actual && storywright --browsers chromium --url dist/storybook --destpath dist/vrt/actual --waitTimeScreenshot 500 --concurrency 4 --headless true --bailOnStoriesError", + "options": { + "cwd": "{projectRoot}" + }, + "metadata": { + "help": { + "command": "yarn storywright --help", + "example": {} + } + }, + "dependsOn": ["build-storybook"], + "inputs": ["{projectRoot}/src/**/*.stories.tsx"], + "outputs": ["{projectRoot}/dist/vrt/actual/**"], + "cache": true + }, + "test-vr": { + "executor": "@fluentui/workspace-plugin:visual-regression", + "dependsOn": ["build-storybook"] + }, + "test-vr-cli": { + "command": "visual-regression-assert assert --baselineDir src/__snapshots__ --outputPath dist/vrt", + "options": { + "cwd": "{projectRoot}" + }, + "dependsOn": [ + "build-storybook", + "generate-image-for-vrt", + { "projects": ["visual-regression-assert"], "target": "build" } + ], + "inputs": ["{projectRoot}/dist/vrt/screenshots/**", "{projectRoot}/src/**/*.stories.tsx"], + "metadata": { + "help": { + "command": "yarn visual-regression-assert --help", + "example": {} + } + } + }, + "build-storybook": { + "command": "storybook build -o dist/storybook --quiet", + "options": { + "cwd": "{projectRoot}" + } + } + } +} diff --git a/packages/react-components/react-tabs/visual-regression/src/Tabs.stories.tsx b/packages/react-components/react-tabs/visual-regression/src/Tabs.stories.tsx new file mode 100644 index 00000000000000..2d20193c8da2ba --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/src/Tabs.stories.tsx @@ -0,0 +1,282 @@ +import * as React from 'react'; +import type { Meta, StoryObj } from '@storybook/react'; +import { Steps, type StoryParameters } from 'storywright'; +import { TabList, Tab } from '@fluentui/react-tabs'; + +import { DARK_MODE, getStoryVariant, HIGH_CONTRAST, RTL } from '@fluentui/visual-regression-utilities'; + +export default { + title: 'TabList and Tab Converged', + decorators: [ + Story => ( +
+ +
+ ), + ], + parameters: { + storyWright: { + steps: new Steps() + .snapshot('default', { cropTo: '.testWrapper' }) + .hover('.mouse-target') + .snapshot('hover', { cropTo: '.testWrapper' }) + .click('.mouse-target') + .snapshot('pressed', { cropTo: '.testWrapper' }) + .end(), + }, + } satisfies StoryParameters, +} satisfies Meta; + +export const Default = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Default', +} satisfies StoryObj; + +export const DefaultRTL = getStoryVariant(Default, RTL); + +export const DefaultHighContrast = getStoryVariant(Default, HIGH_CONTRAST); + +export const DefaultDarkMode = getStoryVariant(Default, DARK_MODE); + +export const Vertical = () => ( + + First + + Second + + Third + +); + +export const SubtleAppearance = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Subtle appearance', +} satisfies StoryObj; + +export const SubtleAppearanceHighContrast = getStoryVariant(SubtleAppearance, HIGH_CONTRAST); + +export const SubtleAppearanceDarkMode = getStoryVariant(SubtleAppearance, DARK_MODE); + +export const SmallSize = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Small size', +} satisfies StoryObj; + +export const SmallSizeHighContrast = getStoryVariant(SmallSize, HIGH_CONTRAST); + +export const SmallSizeDarkMode = getStoryVariant(SmallSize, DARK_MODE); + +export const VerticalAndSmallSize = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Vertical and small size', +} satisfies StoryObj; + +export const VerticalAndSmallSizeHighContrast = getStoryVariant(VerticalAndSmallSize, HIGH_CONTRAST); + +export const VerticalAndSmallSizeDarkMode = getStoryVariant(VerticalAndSmallSize, DARK_MODE); + +export const LargeSize = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Large size', +} satisfies StoryObj; + +export const LargeSizeHighContrast = getStoryVariant(LargeSize, HIGH_CONTRAST); + +export const LargeSizeDarkMode = getStoryVariant(LargeSize, DARK_MODE); + +export const VerticalAndLargeSize = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Vertical and large size', +} satisfies StoryObj; + +export const VerticalAndLargeSizeHighContrast = getStoryVariant(VerticalAndLargeSize, HIGH_CONTRAST); + +export const VerticalAndLargeSizeDarkMode = getStoryVariant(VerticalAndLargeSize, DARK_MODE); + +export const TabSelectedDefault = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Tab Selected (default)', +} satisfies StoryObj; + +export const TabSelectedDefaultHighContrast = getStoryVariant(TabSelectedDefault, HIGH_CONTRAST); + +export const TabSelectedDefaultDarkMode = getStoryVariant(TabSelectedDefault, DARK_MODE); + +export const TabSelected = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Tab Selected', +}; + +export const TabSelectedHighContrast = getStoryVariant(TabSelected, HIGH_CONTRAST); + +export const TabSelectedDarkMode = getStoryVariant(TabSelected, DARK_MODE); + +export const WithIcon = { + render: () => ( + + + First + + + Second + + + Third + + + ), + name: 'With icon', +} satisfies StoryObj; + +export const WithIconRTL = getStoryVariant(WithIcon, RTL); + +export const WithIconAndVertical = { + render: () => ( + + + First + + + Second + + + Third + + + ), + name: 'With icon and vertical', +} satisfies StoryObj; + +export const WithIconAndVerticalRTL = getStoryVariant(WithIconAndVertical, RTL); + +export const WithIconOnly = { + render: () => ( + + + + + + ), + name: 'With icon only', +} satisfies StoryObj; + +export const WithIconOnlyAndVertical = { + render: () => ( + + + + + + ), + name: 'With icon only and vertical', +} satisfies StoryObj; + +export const SubtleCircularAppearance = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Subtle circular appearance', +}; + +export const SubtleCircularAppearanceDarkMode = getStoryVariant(SubtleCircularAppearance, DARK_MODE); + +export const SubtleCircularAppearanceHighContrast = getStoryVariant(SubtleCircularAppearance, HIGH_CONTRAST); + +export const FilledCircularAppearance = { + render: () => ( + + First + + Second + + Third + + ), + name: 'Filled circular appearance', +}; + +export const FilledCircularAppearanceDarkMode = getStoryVariant(FilledCircularAppearance, DARK_MODE); + +export const FilledCircularAppearanceHighContrast = getStoryVariant(FilledCircularAppearance, HIGH_CONTRAST); + +export const WelcomeToContinental = { + render: () => ( + + Reception + + Bar + + Spa + + ), +} satisfies StoryObj; diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..023a5bfe376ee6 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..98069b2debaa24 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..ceb559edac6f64 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..be864062ee84fe Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..ed6609f45a73b3 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.default.chromium.png new file mode 100644 index 00000000000000..212852eab181c1 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.hover.chromium.png new file mode 100644 index 00000000000000..0244f722c08607 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.pressed.chromium.png new file mode 100644 index 00000000000000..5459475e196ac9 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default - RTL.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.default.chromium.png new file mode 100644 index 00000000000000..11c8b587426e0e Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.hover.chromium.png new file mode 100644 index 00000000000000..2b0e642818121f Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.pressed.chromium.png new file mode 100644 index 00000000000000..12d5eb8db86c80 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Default.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..bf198be828fa5a Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..1d4c01bd9879ba Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..7ac4c3b9b7f436 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..787751a7bffd4d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..84838837ed633d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..1fff6346d02aef Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.default.chromium.png new file mode 100644 index 00000000000000..81eb54bb532074 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.hover.chromium.png new file mode 100644 index 00000000000000..ccbbac596f567b Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.pressed.chromium.png new file mode 100644 index 00000000000000..7a044e1991ec56 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Filled circular appearance.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..d8b7413003754d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..f99927f6c446b3 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..e44202e29c9018 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..92ae208ced4d13 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..28286014be77f4 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..7b5f6fa008622c Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.default.chromium.png new file mode 100644 index 00000000000000..7d09d6adc61219 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.hover.chromium.png new file mode 100644 index 00000000000000..b036863f4bae0b Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.pressed.chromium.png new file mode 100644 index 00000000000000..6eda629e2f589c Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Large size.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..1c989f98318f24 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..a853e0d5f9aeda Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..6caaf9a0be7be6 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..7b47ece7d8fc69 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..a2eb6947215d95 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..f7003c409a3036 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.default.chromium.png new file mode 100644 index 00000000000000..33d97d6b803adc Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.hover.chromium.png new file mode 100644 index 00000000000000..10dd22c7c2c977 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.pressed.chromium.png new file mode 100644 index 00000000000000..a5a29d534b2fa1 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Small size.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..023a5bfe376ee6 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..12930dbc396acd Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..f2b8a36298423e Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..be864062ee84fe Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..ed6609f45a73b3 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.default.chromium.png new file mode 100644 index 00000000000000..11c8b587426e0e Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.hover.chromium.png new file mode 100644 index 00000000000000..e3d7f9fd4ba011 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.pressed.chromium.png new file mode 100644 index 00000000000000..4919b7ab923a52 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle appearance.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..d4e45354ee4483 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..c369731dffd690 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..c6368b49a5b68d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..962d668a73eaa7 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..d4490bd90ef79a Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..ab449fd94e071b Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.default.chromium.png new file mode 100644 index 00000000000000..ef2102622fbb7f Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.hover.chromium.png new file mode 100644 index 00000000000000..1059a404f49ed8 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.pressed.chromium.png new file mode 100644 index 00000000000000..5172384214c0ce Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Subtle circular appearance.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..a805494eeb240f Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..653d2f8fb3f6c0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..d442684e4a5533 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..00f58ff35ccaea Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default) - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).default.chromium.png new file mode 100644 index 00000000000000..b10d996941ee51 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).hover.chromium.png new file mode 100644 index 00000000000000..d7f1c2f373fa7d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).pressed.chromium.png new file mode 100644 index 00000000000000..77074e53e113bf Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected (default).pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..a805494eeb240f Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..653d2f8fb3f6c0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..d442684e4a5533 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..00f58ff35ccaea Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..ab9c458460bea0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.default.chromium.png new file mode 100644 index 00000000000000..b10d996941ee51 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.hover.chromium.png new file mode 100644 index 00000000000000..d7f1c2f373fa7d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.pressed.chromium.png new file mode 100644 index 00000000000000..77074e53e113bf Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Tab Selected.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..e23505f606f452 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..18b888816704a5 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..d0591e1b944985 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..5449be0c833ae0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..6bbbbce1f1f6e2 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..b207c25e393396 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.default.chromium.png new file mode 100644 index 00000000000000..8c86fecb44c337 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.hover.chromium.png new file mode 100644 index 00000000000000..3e6e8dfcbcfa77 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.pressed.chromium.png new file mode 100644 index 00000000000000..e13be9e19936fd Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and large size.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.default.chromium.png new file mode 100644 index 00000000000000..b79b65456528dc Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.hover.chromium.png new file mode 100644 index 00000000000000..5e4b886136b715 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.pressed.chromium.png new file mode 100644 index 00000000000000..a4f2265fd42668 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - Dark Mode.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.default.chromium.png new file mode 100644 index 00000000000000..cdeb7e3f764874 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.hover.chromium.png new file mode 100644 index 00000000000000..ac06542ba0a5f7 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.pressed.chromium.png new file mode 100644 index 00000000000000..d8b94e3a0cd4f9 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size - High Contrast.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.default.chromium.png new file mode 100644 index 00000000000000..733e96d0bb6c6d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.hover.chromium.png new file mode 100644 index 00000000000000..e2537090caec5d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.pressed.chromium.png new file mode 100644 index 00000000000000..13897c85f27339 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical and small size.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.default.chromium.png new file mode 100644 index 00000000000000..1f2581030cccc9 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.hover.chromium.png new file mode 100644 index 00000000000000..acc965cfc713ff Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.pressed.chromium.png new file mode 100644 index 00000000000000..e513fe2c737024 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.Vertical.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.default.chromium.png new file mode 100644 index 00000000000000..e69450bae23a9f Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.hover.chromium.png new file mode 100644 index 00000000000000..46bd0e483220ec Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.pressed.chromium.png new file mode 100644 index 00000000000000..8faf90963a8576 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon - RTL.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.default.chromium.png new file mode 100644 index 00000000000000..55d6c975b21da0 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.hover.chromium.png new file mode 100644 index 00000000000000..621e4e572c887b Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.pressed.chromium.png new file mode 100644 index 00000000000000..d1010e1497640a Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical - RTL.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.default.chromium.png new file mode 100644 index 00000000000000..e43208cef44352 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.hover.chromium.png new file mode 100644 index 00000000000000..af7c1f238305f7 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.pressed.chromium.png new file mode 100644 index 00000000000000..ffa37d82578e4c Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon and vertical.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.default.chromium.png new file mode 100644 index 00000000000000..3965b71406c837 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.hover.chromium.png new file mode 100644 index 00000000000000..5aba2c78463d1d Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.pressed.chromium.png new file mode 100644 index 00000000000000..2ce2496bacf57c Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only and vertical.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.default.chromium.png new file mode 100644 index 00000000000000..3a5d9e359c7904 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.hover.chromium.png new file mode 100644 index 00000000000000..4d434af46f5b18 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.pressed.chromium.png new file mode 100644 index 00000000000000..e86e9fe4517bc9 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon only.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.default.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.default.chromium.png new file mode 100644 index 00000000000000..ee07dd6506ae06 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.default.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.hover.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.hover.chromium.png new file mode 100644 index 00000000000000..d6d9f123650703 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.hover.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.pressed.chromium.png b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.pressed.chromium.png new file mode 100644 index 00000000000000..5330b98b1da2d3 Binary files /dev/null and b/packages/react-components/react-tabs/visual-regression/src/__snapshots__/TabList and Tab Converged.With icon.pressed.chromium.png differ diff --git a/packages/react-components/react-tabs/visual-regression/src/index.ts b/packages/react-components/react-tabs/visual-regression/src/index.ts new file mode 100644 index 00000000000000..cb0ff5c3b541f6 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/react-components/react-tabs/visual-regression/tsconfig.json b/packages/react-components/react-tabs/visual-regression/tsconfig.json new file mode 100644 index 00000000000000..be0a513aa78c5b --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/react-tabs/visual-regression/tsconfig.lib.json b/packages/react-components/react-tabs/visual-regression/tsconfig.lib.json new file mode 100644 index 00000000000000..e19d2b095980c0 --- /dev/null +++ b/packages/react-components/react-tabs/visual-regression/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2019", "dom"], + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/packages/react-components/react-text/visual-regression/.eslintrc.json b/packages/react-components/react-text/visual-regression/.eslintrc.json new file mode 100644 index 00000000000000..7c4a0ec7ee8ecc --- /dev/null +++ b/packages/react-components/react-text/visual-regression/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["../../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"] + } + ] + } + } + ] +} diff --git a/packages/react-components/react-text/visual-regression/.storybook/main.js b/packages/react-components/react-text/visual-regression/.storybook/main.js new file mode 100644 index 00000000000000..ec3017dba08569 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/.storybook/main.js @@ -0,0 +1,58 @@ +const path = require('path'); + +const { registerTsPaths, registerRules, rules, loadWorkspaceAddon } = require('@fluentui/scripts-storybook'); +const tsConfigPath = path.resolve(__dirname, '../../../../../tsconfig.base.json'); + +module.exports = /** @type {import('@storybook/react-webpack5').StorybookConfig} */ ({ + addons: [loadWorkspaceAddon('@fluentui/react-storybook-addon', { tsConfigPath })], + stories: ['../src/**/*.stories.tsx'], + core: { + disableTelemetry: true, + }, + framework: { + name: '@storybook/react-webpack5', + options: { + builder: { + useSWC: true, + lazyCompilation: false, + }, + }, + }, + typescript: { + // disable react-docgen-typescript (totally not needed here, slows things down a lot) + reactDocgen: false, + }, + webpackFinal: (config, options) => { + if (options.configType === 'PRODUCTION') { + // Disable source maps + config.devtool = false; + } + registerTsPaths({ config, configFile: tsConfigPath }); + registerRules({ config, rules: [rules.griffelRule] }); + + return config; + }, + swc() { + return { + jsc: { + target: 'es2019', + parser: { + syntax: 'typescript', + tsx: true, + decorators: true, + dynamicImport: true, + }, + transform: { + decoratorMetadata: true, + legacyDecorator: true, + }, + keepClassNames: true, + externalHelpers: true, + loose: true, + minify: { + mangle: false, + }, + }, + }; + }, +}); diff --git a/packages/react-components/react-text/visual-regression/.storybook/preview.js b/packages/react-components/react-text/visual-regression/.storybook/preview.js new file mode 100644 index 00000000000000..1851443859d182 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/.storybook/preview.js @@ -0,0 +1,8 @@ +/** @type {import("@fluentui/react-storybook-addon").FluentParameters} */ +export const parameters = { + layout: 'none', + mode: 'vr-test', + reactStorybookAddon: { + disabledDecorators: ['AriaLive'], + }, +}; diff --git a/packages/react-components/react-text/visual-regression/.storybook/tsconfig.json b/packages/react-components/react-text/visual-regression/.storybook/tsconfig.json new file mode 100644 index 00000000000000..4cdd1ce9d006f1 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/.storybook/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "", + "allowJs": true, + "checkJs": true, + "types": ["static-assets", "environment"] + }, + "include": ["*.js"] +} diff --git a/packages/react-components/react-text/visual-regression/README.md b/packages/react-components/react-text/visual-regression/README.md new file mode 100644 index 00000000000000..8e03e82f526ad8 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/README.md @@ -0,0 +1,43 @@ +# react-text-visual-regression + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build react-text-visual-regression` to build the library. + +## Running unit tests + +Run `nx test react-text-visual-regression` to execute the unit tests via [Jest](https://jestjs.io). + +## About + +Free, Secure and OSS VR testing solution based on: + +- Storybook to author VR scenarios +- StoryWright for capturing Stories and their interactions +- ~PlayWright~ Custom VR Regression CLI for running diffing and updating baseline + +**Demo:** + +> ## NOTE: needs new StoryWright features: +> +> - [x] https://github.com/microsoft/storywright/pull/73 +> - [x] https://github.com/microsoft/storywright/pull/74 + +**Usage:** + +- uses new VRT CLI with configured caching: + +`yarn react-text-visual-regression:test-vr-cli` + +- DEPRECATED: uses custom nx generator ( under the hood uses playwright ) + +`yarn react-text-visual-regression:test-vr` + +## TODO + +- [x] check raw [pixelmatch](https://github.com/mapbox/pixelmatch) usage instead PW/test / custom NX Executor +- [ ] check using [pixelmatch](https://github.com/mapbox/pixelmatch) within storywright with implementing new API `--mode= assert | screenshot` +- [x] check using PW/test instead just PW within storywright +- [] setup CI and approval pipeline diff --git a/packages/react-components/react-text/visual-regression/package.json b/packages/react-components/react-text/visual-regression/package.json new file mode 100644 index 00000000000000..eea52076de4d6c --- /dev/null +++ b/packages/react-components/react-text/visual-regression/package.json @@ -0,0 +1,19 @@ +{ + "name": "@fluentui/react-text-visual-regression", + "version": "0.0.0", + "private": true, + "dependencies": { + "@fluentui/react-text": "*", + "@fluentui/visual-regression-utilities": "*" + }, + "peerDependencies": { + "@storybook/react": "^7.6.20" + }, + "devDependencies": { + "@fluentui/react-storybook-addon": "*", + "@fluentui/scripts-storybook": "*" + }, + "type": "commonjs", + "main": "./src/index.js", + "typings": "./src/index.d.ts" +} diff --git a/packages/react-components/react-text/visual-regression/project.json b/packages/react-components/react-text/visual-regression/project.json new file mode 100644 index 00000000000000..bb6a459aace404 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/project.json @@ -0,0 +1,53 @@ +{ + "name": "react-text-visual-regression", + "$schema": "../../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "packages/react-components/react-text/visual-regression/src", + "projectType": "library", + "tags": ["vNext", "platform:web", "visual-regression"], + "targets": { + "generate-image-for-vrt": { + "command": "rm -rf dist/vrt/actual && storywright --browsers chromium --url dist/storybook --destpath dist/vrt/actual --waitTimeScreenshot 500 --concurrency 4 --headless true", + "options": { + "cwd": "{projectRoot}" + }, + "metadata": { + "help": { + "command": "yarn storywright --help", + "example": {} + } + }, + "dependsOn": ["build-storybook"], + "inputs": ["{projectRoot}/src/**/*.stories.tsx"], + "outputs": ["{projectRoot}/dist/vrt/actual/**"], + "cache": true + }, + "test-vr": { + "executor": "@fluentui/workspace-plugin:visual-regression", + "dependsOn": ["build-storybook"] + }, + "test-vr-cli": { + "command": "visual-regression-assert assert --baselineDir src/__snapshots__ --outputPath dist/vrt", + "options": { + "cwd": "{projectRoot}" + }, + "dependsOn": [ + "build-storybook", + "generate-image-for-vrt", + { "projects": ["visual-regression-assert"], "target": "build" } + ], + "inputs": ["{projectRoot}/dist/vrt/screenshots/**", "{projectRoot}/src/**/*.stories.tsx"], + "metadata": { + "help": { + "command": "yarn visual-regression-assert --help", + "example": {} + } + } + }, + "build-storybook": { + "command": "storybook build -o dist/storybook --quiet", + "options": { + "cwd": "{projectRoot}" + } + } + } +} diff --git a/packages/react-components/react-text/visual-regression/src/Text.stories.tsx b/packages/react-components/react-text/visual-regression/src/Text.stories.tsx new file mode 100644 index 00000000000000..6619f070400254 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/src/Text.stories.tsx @@ -0,0 +1,191 @@ +import * as React from 'react'; +import { StoryObj, type Meta } from '@storybook/react'; +import { Steps, type StoryParameters } from 'storywright'; +import { + Body1, + Body1Strong, + Body1Stronger, + Body2, + Caption1, + Caption1Strong, + Caption1Stronger, + Caption2, + Caption2Strong, + Display, + LargeTitle, + Subtitle1, + Subtitle2, + Subtitle2Stronger, + Text, + Title1, + Title2, + Title3, +} from '@fluentui/react-text'; + +import { DARK_MODE, getStoryVariant, HIGH_CONTRAST, RTL } from '@fluentui/visual-regression-utilities'; + +export default { + title: 'Text Converged', + decorators: [ + Story => ( +
+ +
+ ), + ], + parameters: { + storyWright: { steps: new Steps().snapshot('normal', { cropTo: '.testWrapper' }).end() }, + } satisfies StoryParameters, +} satisfies Meta; + +export const Default = { + name: 'Default', + render: () => ( + <> +

+ Default + Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. +

+

+ No wrapping + + Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. + +

+

+ Truncate + + Lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. + +

+

+ Italic + + Hello, world + +

+

+ Underline + + Hello, world + +

+

+ Strikethrough + + Hello, world + +

+

+ Sizes + + 100 + + + 200 + + + 300 + + + 400 + + + 500 + + + 600 + + + 700 + + + 800 + + + 900 + + + 1000 + +

+

+ Fonts + + Base + + + Monospace + + + Numeric + +

+

+ Weights + + Regular + + + Medium + + + Semibold + +

+

+ Alignments + + Start + + + Center + + + End + + + Justified lorem ipsum dolor sit amet, nullam rhoncus tristique tellus in Portucale. + +

+ + ), +} satisfies StoryObj; + +export const DefaultRTL = getStoryVariant(Default, RTL); + +export const DefaultHighContrast = getStoryVariant(Default, HIGH_CONTRAST); + +export const DefaultDarkMode = getStoryVariant(Default, DARK_MODE); + +export const TypographyPresets = { + render: () => ( + <> + Body1 + Body1Strong + Body1Stronger + Body2 + Caption1 + Caption1Strong + Caption1Stronger + Caption2 + Caption2Strong + Display + LargeTitle + Subtitle1 + Subtitle2 + Subtitle2Stronger + Title1 + Title2 + Title3 + + ), + + name: 'Typography presets', +} satisfies StoryObj; + +export const TypographyPresetsRTL = getStoryVariant(TypographyPresets, RTL); + +export const HelloMrWick = { + render: () => Hello Jonathan, +} satisfies StoryObj; diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - Dark Mode.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - Dark Mode.normal.chromium.png new file mode 100644 index 00000000000000..c64734acb93853 Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - Dark Mode.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - High Contrast.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - High Contrast.normal.chromium.png new file mode 100644 index 00000000000000..4572e980c630d6 Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - High Contrast.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - RTL.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - RTL.normal.chromium.png new file mode 100644 index 00000000000000..4e2e242300848d Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default - RTL.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default.normal.chromium.png new file mode 100644 index 00000000000000..3359651166d152 Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Default.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets - RTL.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets - RTL.normal.chromium.png new file mode 100644 index 00000000000000..6bbbab390f2733 Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets - RTL.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets.normal.chromium.png b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets.normal.chromium.png new file mode 100644 index 00000000000000..44d149bce88fb4 Binary files /dev/null and b/packages/react-components/react-text/visual-regression/src/__snapshots__/Text Converged.Typography presets.normal.chromium.png differ diff --git a/packages/react-components/react-text/visual-regression/src/index.ts b/packages/react-components/react-text/visual-regression/src/index.ts new file mode 100644 index 00000000000000..cb0ff5c3b541f6 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/packages/react-components/react-text/visual-regression/tsconfig.json b/packages/react-components/react-text/visual-regression/tsconfig.json new file mode 100644 index 00000000000000..be0a513aa78c5b --- /dev/null +++ b/packages/react-components/react-text/visual-regression/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./.storybook/tsconfig.json" + } + ] +} diff --git a/packages/react-components/react-text/visual-regression/tsconfig.lib.json b/packages/react-components/react-text/visual-regression/tsconfig.lib.json new file mode 100644 index 00000000000000..e19d2b095980c0 --- /dev/null +++ b/packages/react-components/react-text/visual-regression/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "lib": ["ES2019", "dom"], + "outDir": "../../../../dist/out-tsc", + "inlineSources": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"] +} diff --git a/tools/visual-regression-assert/.eslintrc.json b/tools/visual-regression-assert/.eslintrc.json new file mode 100644 index 00000000000000..703b92b1c10f3c --- /dev/null +++ b/tools/visual-regression-assert/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"] + } + ] + } + } + ] +} diff --git a/tools/visual-regression-assert/.swcrc b/tools/visual-regression-assert/.swcrc new file mode 100644 index 00000000000000..0d9b8baada46d0 --- /dev/null +++ b/tools/visual-regression-assert/.swcrc @@ -0,0 +1,30 @@ +{ + "jsc": { + "target": "es2017", + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": false + }, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true + }, + "keepClassNames": true, + "externalHelpers": true, + "loose": true + }, + "module": { + "type": "commonjs", + "ignoreDynamic": true + }, + "sourceMaps": true, + "exclude": [ + "jest.config.ts", + ".*\\.spec.tsx?$", + ".*\\.test.tsx?$", + "./src/jest-setup.ts$", + "./**/jest-setup.ts$", + ".*.js$" + ] +} diff --git a/tools/visual-regression-assert/README.md b/tools/visual-regression-assert/README.md new file mode 100644 index 00000000000000..e9147a44142e5a --- /dev/null +++ b/tools/visual-regression-assert/README.md @@ -0,0 +1,11 @@ +# visual-regression-assert + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build visual-regression-assert` to build the library. + +## Running unit tests + +Run `nx test visual-regression-assert` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/tools/visual-regression-assert/bin/visual-regression-assert.js b/tools/visual-regression-assert/bin/visual-regression-assert.js new file mode 100755 index 00000000000000..3901985fe63664 --- /dev/null +++ b/tools/visual-regression-assert/bin/visual-regression-assert.js @@ -0,0 +1,13 @@ +#!/usr/bin/env node + +// @ts-check + +const { join } = require('node:path'); +const { registerTsProject } = require('@nx/js/src/internal'); + +// TODO: we need SWC in memory transpile as TSC doesn't support ignoring dynamic import during transpilations which is crucial for CJS module that needs to consume ESM ( in our case pixelmatch ) + +// registerTsProject(join(__dirname, '../tsconfig.lib.json')); +// require('../src/cli'); + +require('../dist/src/cli'); diff --git a/tools/visual-regression-assert/jest.config.ts b/tools/visual-regression-assert/jest.config.ts new file mode 100644 index 00000000000000..79d514ea8f92f6 --- /dev/null +++ b/tools/visual-regression-assert/jest.config.ts @@ -0,0 +1,29 @@ +/* eslint-disable */ +import { readFileSync } from 'node:fs'; +const { join } = require('node:path'); + +// Reading the SWC compilation config and remove the "exclude" +// for the test files to be compiled by SWC +const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(join(__dirname, '.swcrc'), 'utf-8')); + +// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. +// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" +if (swcJestConfig.swcrc === undefined) { + swcJestConfig.swcrc = false; +} + +// Uncomment if using global setup/teardown files being transformed via swc +// https://nx.dev/nx-api/jest/documents/overview#global-setupteardown-with-nx-libraries +// jest needs EsModule Interop to find the default exported setup/teardown functions +// swcJestConfig.module.noInterop = false; + +export default { + displayName: 'visual-regression-assert', + preset: '../../jest.preset.js', + transform: { + '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], + }, + moduleFileExtensions: ['ts', 'tsx', 'js'], + testEnvironment: 'node', + coverageDirectory: '../../coverage/tools/visual-regression-assert', +}; diff --git a/tools/visual-regression-assert/package.json b/tools/visual-regression-assert/package.json new file mode 100644 index 00000000000000..ab95fe4c75db83 --- /dev/null +++ b/tools/visual-regression-assert/package.json @@ -0,0 +1,29 @@ +{ + "name": "@fluentui/visual-regression-assert", + "private": true, + "version": "0.0.0", + "type": "commonjs", + "bin": { + "visual-regression-assert": "./bin/visual-regression-assert.js" + }, + "main": "./src/index.js", + "typings": "./src/index.d.ts", + "files": [ + "*.md", + "dist/*.d.ts", + "lib", + "lib-commonjs" + ], + "dependencies": { + "@swc/helpers": "^0.5.1", + "pixelmatch": "^7.1.0", + "ejs": "^3.1.7", + "glob": "^11.0.1", + "find-up": "5.0.0", + "cli-table3": "^0.6.5" + }, + "devDependencies": { + "@types/ejs": "3.1.5" + }, + "peerDependencies": {} +} diff --git a/tools/visual-regression-assert/project.json b/tools/visual-regression-assert/project.json new file mode 100644 index 00000000000000..4c8cd93bc97df5 --- /dev/null +++ b/tools/visual-regression-assert/project.json @@ -0,0 +1,43 @@ +{ + "name": "visual-regression-assert", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "tools/visual-regression-assert/src", + "projectType": "library", + "release": { + "version": { + "generatorOptions": { + "packageRoot": "dist/{projectRoot}", + "currentVersionResolver": "git-tag" + } + } + }, + "tags": ["platform:any", "tools"], + "targets": { + "build": { + "executor": "@nx/js:swc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "{projectRoot}/dist", + "main": "tools/visual-regression-assert/src/index.ts", + "tsConfig": "tools/visual-regression-assert/tsconfig.lib.json", + "assets": ["tools/visual-regression-assert/*.md", "tools/visual-regression-assert/src/template/*"] + } + }, + "nx-release-publish": { + "options": { + "packageRoot": "dist/{projectRoot}" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "command": "jest", + "options": { + "cwd": "tools/visual-regression-assert", + "passWithNoTests": true + }, + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"] + } + } +} diff --git a/tools/visual-regression-assert/src/assert.ts b/tools/visual-regression-assert/src/assert.ts new file mode 100644 index 00000000000000..550fb5d2073757 --- /dev/null +++ b/tools/visual-regression-assert/src/assert.ts @@ -0,0 +1,185 @@ +import { join } from 'node:path'; +import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync, writeFileSync } from 'node:fs'; +import { cwd } from 'node:process'; + +import { PNG } from 'pngjs'; + +import { getPackageMetadata, loadPixelmatch } from './utils'; +import { Metadata, Result } from './types'; +import { generateCliReport, generateHtmlReport, generateJsonReport, generateMarkdownReport } from './reporters'; + +async function compareSnapshots( + baselinePath: string, + actualPath: string, + diffPath: string, +): Promise> { + try { + const baselineImg = PNG.sync.read(readFileSync(baselinePath)); + const actualImg = PNG.sync.read(readFileSync(actualPath)); + const { width, height } = baselineImg; + + if (actualImg.width !== width || actualImg.height !== height) { + return { passed: false, error: 'Image dimensions mismatch', changeType: 'diff' }; + } + + const diff = new PNG({ width, height }); + const pixelmatch = await loadPixelmatch(); + const numDiffPixels = pixelmatch(baselineImg.data, actualImg.data, diff.data, width, height, { threshold: 0.1 }); + + if (numDiffPixels > 0) { + writeFileSync(diffPath, PNG.sync.write(diff)); + return { + passed: false, + changeType: 'diff', + diffPixels: numDiffPixels, + diffPath: diffPath, + }; + } + + if (existsSync(diffPath)) { + unlinkSync(diffPath); + } + + return { passed: true }; + } catch (error) { + return { passed: false, error: (error as Error).message }; + } +} + +export async function runSnapshotTests(options: { + baselineDir: string; + outputPath: string; + reportFileName: string; + updateSnapshots: boolean; +}) { + const { updateSnapshots, reportFileName, baselineDir, outputPath } = options; + + const relativePaths = { + baselineDir, + outputPath, + outputBaselineDir: join(outputPath, 'baseline'), + actualDir: join(outputPath, 'actual'), + diffDir: join(outputPath, 'diff'), + }; + + const normalizedPaths = { + outputPath: join(cwd(), outputPath), + baselineDir: join(cwd(), relativePaths.baselineDir), + outputBaselineDir: join(cwd(), relativePaths.outputBaselineDir), + actualDir: join(cwd(), relativePaths.actualDir), + diffDir: join(cwd(), relativePaths.diffDir), + }; + + if (!existsSync(normalizedPaths.actualDir)) { + throw new Error( + `actualDir "${normalizedPaths.actualDir}" doesn't exist. Make sure to provide images for assertion`, + ); + } + + const metadata: Metadata = { + paths: normalizedPaths, + project: getPackageMetadata(normalizedPaths.outputPath), + }; + + if (updateSnapshots) { + console.info('======================'); + console.info('๐Ÿ’ก UPDATING SNAPSHOTS!'); + console.info('======================'); + } + + if (!existsSync(normalizedPaths.outputBaselineDir)) { + mkdirSync(normalizedPaths.outputBaselineDir, { recursive: true }); + } + + if (!existsSync(normalizedPaths.baselineDir)) { + mkdirSync(normalizedPaths.baselineDir, { recursive: true }); + } + + const baselineFiles = readdirSync(normalizedPaths.baselineDir); + const actualFiles = readdirSync(normalizedPaths.actualDir); + let allPassed = true; + const results: Result[] = []; + + if (!existsSync(normalizedPaths.diffDir)) { + mkdirSync(normalizedPaths.diffDir, { recursive: true }); + } + + const removedFilesFromBaseline = baselineFiles.filter(file => { + if (!file.endsWith('.png')) { + throw new Error(`Only png files are supported - ${file}`); + } + + if (!actualFiles.includes(file)) { + const baselinePath = join(normalizedPaths.baselineDir, file); + if (!updateSnapshots) { + copyFileSync(baselinePath, join(normalizedPaths.outputBaselineDir, file)); + results.push({ + file, + passed: updateSnapshots ? true : false, + error: updateSnapshots ? undefined : 'Remove Snapshot', + changeType: 'remove', + }); + allPassed = false; + } else { + unlinkSync(baselinePath); + } + } + }); + + if (removedFilesFromBaseline.length > 0) { + console.error(`๐Ÿงน Removed snapshots: ${removedFilesFromBaseline.join(', ')}`); + } + + for (const file of actualFiles) { + if (!file.endsWith('.png')) { + throw new Error(`Only png files are supported - ${file}`); + } + + const baselinePath = join(normalizedPaths.baselineDir, file); + const actualPath = join(normalizedPaths.actualDir, file); + const diffPath = join(normalizedPaths.diffDir, file); + + if (!existsSync(baselinePath)) { + results.push({ + file, + passed: updateSnapshots ? true : false, + error: updateSnapshots ? undefined : 'New Snapshot', + changeType: 'add', + }); + if (!updateSnapshots) { + allPassed = false; + } else { + copyFileSync(actualPath, baselinePath); + } + continue; + } + + if (!updateSnapshots) { + const result = await compareSnapshots(baselinePath, actualPath, diffPath); + if (!result.passed) { + copyFileSync(baselinePath, join(normalizedPaths.outputBaselineDir, file)); + allPassed = false; + } + results.push({ file, ...result }); + } else { + results.push({ file, passed: true }); + } + } + + const reportConfig = { + metadata, + reportFileName, + paths: { absolute: normalizedPaths, relative: relativePaths }, + }; + + generateCliReport(results, reportConfig); + generateJsonReport(results, reportConfig); + generateHtmlReport(results, reportConfig); + generateMarkdownReport(results, reportConfig); + + if (!allPassed) { + return { passed: false }; + } + + return { passed: true }; +} diff --git a/tools/visual-regression-assert/src/cli.ts b/tools/visual-regression-assert/src/cli.ts new file mode 100644 index 00000000000000..28ca45c3e19e84 --- /dev/null +++ b/tools/visual-regression-assert/src/cli.ts @@ -0,0 +1,85 @@ +import yargs from 'yargs'; +import { runSnapshotTests } from './assert'; +import { prepareReport } from './report'; +import { reporterFileNames } from './shared'; + +main() + // .then(_ => { + // process.exit(0); + // }) + .catch(reason => { + console.error(reason); + process.exit(1); + }); + +async function main() { + const reportFileName = reporterFileNames.json; + const reportFilesGlob = `**/${reportFileName}`; + + yargs(process.argv.slice(2)) + .usage('$0', 'Run VR tests for Images') + .command( + 'report', + 'prepare diff report for CI', + y => { + return y.option('outputPath', { + type: 'string', + demandOption: true, + description: 'relative path all projects report output should be created', + }); + }, + async argv => { + const outputPath = argv.outputPath; + + return await prepareReport(reportFilesGlob, outputPath); + }, + ) + .command( + 'assert', + 'run assertion', + y => { + return y + .option('baselineDir', { + type: 'string', + demandOption: true, + description: 'relative path to baseline folder', + }) + .option('outputPath', { + type: 'string', + demandOption: true, + description: 'relative path where report output should be created', + }) + .option('updateSnapshots', { type: 'boolean', default: false, alias: 'u' }); + }, + async argv => { + const { baselineDir, outputPath, updateSnapshots } = argv; + + const result = await runSnapshotTests({ + baselineDir, + outputPath, + reportFileName, + updateSnapshots, + }); + if (!result.passed) { + console.error('==============================================='); + console.error('๐Ÿšจ Snapshots changed! Please Review VR Report ๐Ÿšจ'); + console.error('==============================================='); + process.exit(1); + } + }, + ) + // .option('baselineDir', { type: 'string', demandOption: true }) + // .option('actualDir', { type: 'string', demandOption: true }) + // .option('diffDir', { type: 'string', demandOption: true }) + // .option('reportPath', { type: 'string', demandOption: true }) + // .option('updateSnapshots', { type: 'boolean', default: false, alias: 'u' }) + .strict().argv; + + // Example usage: + // const baselineDir = './snapshots/baseline'; + // const actualDir = './snapshots/actual'; + // const diffDir = './snapshots/diff'; + // const reportPath = './snapshot-report.html'; + + // runSnapshotTests(baselineDir, actualDir, diffDir, reportPath); +} diff --git a/tools/visual-regression-assert/src/index.ts b/tools/visual-regression-assert/src/index.ts new file mode 100644 index 00000000000000..cb0ff5c3b541f6 --- /dev/null +++ b/tools/visual-regression-assert/src/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/tools/visual-regression-assert/src/report.ts b/tools/visual-regression-assert/src/report.ts new file mode 100644 index 00000000000000..e63750c260b2a3 --- /dev/null +++ b/tools/visual-regression-assert/src/report.ts @@ -0,0 +1,55 @@ +import { glob } from 'glob'; + +import { cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { findGitRoot } from './utils'; +import { Report } from './types'; +import { reporterFileNames } from './shared'; + +export async function prepareReport(reportFilesGlob: string, outputPath: string) { + const rootReportName = 'vrt-report.json'; + const root = findGitRoot(process.cwd()); + const absoluteRootPath = join(root, outputPath); + + const reportFiles = glob.sync(reportFilesGlob, { absolute: true, cwd: root }); + const reports = reportFiles.reduce>((acc, reportFile) => { + const data: Report = JSON.parse(readFileSync(reportFile, 'utf-8')); + acc[data.metadata.project.name] = data; + + return acc; + }, {}); + + if (!existsSync(absoluteRootPath)) { + mkdirSync(absoluteRootPath, { recursive: true }); + } else { + rmSync(absoluteRootPath, { recursive: true }); + mkdirSync(absoluteRootPath, { recursive: true }); + } + + const reportEntries = Object.entries(reports); + let markdownReport = '# Visual Regression Repo Report\n\n'; + + if (reportEntries.length) { + reportEntries.forEach(([project, report]) => { + const projectNameWithoutScope = project.replace(/^@[a-z-]+\//, ''); + // copy project report + console.log(`Copy project report to: ${join(absoluteRootPath, projectNameWithoutScope)}`); + cpSync(report.metadata.paths.outputPath, join(absoluteRootPath, projectNameWithoutScope), { recursive: true }); + + // update markdownReport + const projectMdReport = readFileSync(join(report.metadata.paths.outputPath, reporterFileNames.markdown), 'utf-8'); + markdownReport = markdownReport + `## ${projectNameWithoutScope}\n\n` + projectMdReport + '\n\n'; + }); + } else { + markdownReport += 'No Regressions found โœ…'; + } + + const jsonReportPath = join(absoluteRootPath, rootReportName); + const markdownReportPath = join(absoluteRootPath, rootReportName.replace('.json', '.md')); + console.log('Creating reports:'); + console.log(`- ${jsonReportPath}`); + console.log(`- ${markdownReportPath}`); + + writeFileSync(jsonReportPath, JSON.stringify(reports, null, 2)); + writeFileSync(markdownReportPath, markdownReport); +} diff --git a/tools/visual-regression-assert/src/reporters.ts b/tools/visual-regression-assert/src/reporters.ts new file mode 100644 index 00000000000000..575eea766e0d98 --- /dev/null +++ b/tools/visual-regression-assert/src/reporters.ts @@ -0,0 +1,235 @@ +import { render } from 'ejs'; +const Table = require('cli-table3') as import('cli-table3'); +import { readFileSync, writeFileSync } from 'node:fs'; +import { join, relative } from 'node:path'; +import { stripIndents } from './utils'; +import type { Metadata, Result, Report } from './types'; +import { reporterFileNames } from './shared'; + +type Options = { + metadata: Metadata; + reportFileName: string; + paths: { + absolute: { + baselineDir: string; + actualDir: string; + diffDir: string; + outputBaselineDir: string; + outputPath: string; + }; + relative: { + baselineDir: string; + actualDir: string; + diffDir: string; + outputBaselineDir: string; + outputPath: string; + }; + }; +}; + +export function generateMarkdownReport(results: Result[], options: Options) { + const template = readFileSync(join(__dirname, 'template/report.md__tmpl__'), 'utf-8'); + + const { changedEntries, unchangedEntries } = getChangedEntriesInReport(results); + const changedOutput = changedEntries + .map(result => { + let generatedContent = stripIndents`| ${result.file} | โŒ Failed |`; + + if (result.error) { + generatedContent += result.error; + } + if (result.diffPixels) { + generatedContent += `
Diff pixels: ${result.diffPixels}`; + // TODO: this is impossible to do in GitHub MD context without either uploading images to some cloud or inlining them via BASE64 which would be catastrophic for GH GUI + // generatedContent += `
`; + // generatedContent += `
BaselineBaseline
`; + // generatedContent += `
ActualActual
`; + // generatedContent += `
DiffDiff
`; + } + + generatedContent += `|`; + + return generatedContent; + }) + .join('\n'); + + const unchangedOutput = unchangedEntries + .map(result => { + const generatedContent = stripIndents`| ${result.file} | โœ… Passed |`; + + return generatedContent; + }) + .join('\n'); + + const renderedMD = render(template, { changed: changedOutput, unchanged: unchangedOutput }); + const reportPath = join(options.paths.absolute.outputPath, reporterFileNames.markdown); + writeFileSync(reportPath, renderedMD, 'utf-8'); + + console.log(`Markdown report generated: ${reportPath}`); +} + +export function generateHtmlReport(results: Result[], options: Options) { + const template = readFileSync(join(__dirname, 'template/report.html__tmpl__'), 'utf-8'); + + const { changedEntries, unchangedEntries } = getChangedEntriesInReport(results); + + const changedOutput = changedEntries + .map(result => { + let generatedContent = stripIndents` + + ${result.file} + โŒ Failed + `; + + if (result.error) { + generatedContent += `

${result.error}

`; + generatedContent += `
`; + if (result.changeType === 'add') { + generatedContent += renderImage( + result.file, + createRelativeImagePath(options.paths.relative.outputPath, options.paths.relative.actualDir), + 'actual', + ); + } + if (result.changeType === 'remove') { + generatedContent += renderImage( + result.file, + createRelativeImagePath(options.paths.relative.outputPath, options.paths.relative.outputBaselineDir), + 'baseline', + ); + } + generatedContent += `
`; + } + + if (result.diffPixels) { + generatedContent += `

Diff pixels: ${result.diffPixels}

`; + generatedContent += `
`; + generatedContent += renderImage( + result.file, + createRelativeImagePath(options.paths.relative.outputPath, options.paths.relative.outputBaselineDir), + 'baseline', + ); + generatedContent += renderImage( + result.file, + createRelativeImagePath(options.paths.relative.outputPath, options.paths.relative.actualDir), + 'actual', + ); + generatedContent += renderImage( + result.file, + createRelativeImagePath(options.paths.relative.outputPath, options.paths.relative.diffDir), + 'diff', + ); + + generatedContent += `
`; + } + + generatedContent += ``; + + return generatedContent; + }) + .join('\n'); + + const unchangedOutput = unchangedEntries + .map(result => { + const generatedContent = stripIndents` + + ${result.file} + + `; + } +} + +export function generateJsonReport(results: Result[], options: Options) { + const reportPathFile = join(options.paths.absolute.outputPath, options.reportFileName); + const report: Report = { results, metadata: options.metadata }; + writeFileSync(reportPathFile, JSON.stringify(report, null, 2), 'utf-8'); + + console.log(`JSON report generated: ${reportPathFile}`); +} + +export function generateCliReport(results: Result[], options: Options) { + const { changedEntries } = getChangedEntriesInReport(results); + const table = new Table({ + colAligns: ['left', 'left', 'right'], + head: ['File', 'Status', 'Details'], + }); + + changedEntries.forEach(result => { + let details = ''; + + if (!result.passed) { + if (result.error) { + details += result.error; + } + if (result.diffPixels) { + details += `Diff pixels: ${result.diffPixels}`; + } + } + + table.push([ + result.file, + result.passed ? 'โœ… Passed' : 'โŒ Failed', + details.trim(), // Remove trailing newline + ]); + }); + + // const footer = `๐Ÿค– This report was generated against '${repository}/commit/${commitSHA}'`; + const footer = `๐Ÿค– Report was generated`; + + console.log(table.toString()); + console.log(''); + console.log(footer); +} + +function createRelativeImagePath(outputRootDir: string, imageDirectory: string): string { + try { + const relativePath = relative(outputRootDir, imageDirectory); + // Add a trailing slash for directory access + return join(relativePath, '/'); + } catch (error) { + console.error('Error creating relative path:', error); + return ''; + } +} + +function getChangedEntriesInReport(results: Result[]): { changedEntries: Result[]; unchangedEntries: Result[] } { + const { changedEntries, unchangedEntries } = results.reduce<{ + changedEntries: Result[]; + unchangedEntries: Result[]; + }>( + (acc, reportEntry) => { + if (reportEntry.passed) { + acc.unchangedEntries.push(reportEntry); + return acc; + } + + acc.changedEntries.push(reportEntry); + return acc; + }, + { changedEntries: [], unchangedEntries: [] }, + ); + + return { + changedEntries, + unchangedEntries, + }; +} diff --git a/tools/visual-regression-assert/src/shared.ts b/tools/visual-regression-assert/src/shared.ts new file mode 100644 index 00000000000000..f23b15c0c579f4 --- /dev/null +++ b/tools/visual-regression-assert/src/shared.ts @@ -0,0 +1,5 @@ +export const reporterFileNames = { + json: 'visual-regression-assert.json', + html: 'report.html', + markdown: 'report.md', +}; diff --git a/tools/visual-regression-assert/src/template/report.html__tmpl__ b/tools/visual-regression-assert/src/template/report.html__tmpl__ new file mode 100644 index 00000000000000..d251ee6ec068e6 --- /dev/null +++ b/tools/visual-regression-assert/src/template/report.html__tmpl__ @@ -0,0 +1,118 @@ + + + + Visual Regression Report + + + +

Snapshot Comparison Report

+ +
+ + + + + + + + + + <%- changed %> + +
FileStatusDetails
+
+ +
+
+ Unchanged Scenarios + + + + + + + + + + <%- unchanged %> + +
FileStatusDetails
+
+
+ + diff --git a/tools/visual-regression-assert/src/template/report.md__tmpl__ b/tools/visual-regression-assert/src/template/report.md__tmpl__ new file mode 100644 index 00000000000000..82a21627307059 --- /dev/null +++ b/tools/visual-regression-assert/src/template/report.md__tmpl__ @@ -0,0 +1,15 @@ +### Snapshot Comparison Report + +| File | Status | Details | +| ---- | ------ | ------- | +<%- changed %> + + +
+Unchanged Scenarios + +| File | Status | Details | +| ---- | ------ | ------- | +<%- unchanged %> + +
diff --git a/tools/visual-regression-assert/src/types.ts b/tools/visual-regression-assert/src/types.ts new file mode 100644 index 00000000000000..42c7c0395bdc16 --- /dev/null +++ b/tools/visual-regression-assert/src/types.ts @@ -0,0 +1,33 @@ +export interface Result { + passed: boolean; + diffPixels?: any; + diffPath?: string; + file: any; + changeType?: 'add' | 'diff' | 'remove'; + error?: string; +} + +export interface Metadata { + /** + * absolute paths + */ + paths: { + baselineDir: string; + actualDir: string; + diffDir: string; + outputBaselineDir: string; + outputPath: string; + }; + project: { + root: string; + /** + * package.json#name + */ + name: string; + }; +} + +export interface Report { + results: Result[]; + metadata: Metadata; +} diff --git a/tools/visual-regression-assert/src/utils.ts b/tools/visual-regression-assert/src/utils.ts new file mode 100644 index 00000000000000..ceb331de0cbf98 --- /dev/null +++ b/tools/visual-regression-assert/src/utils.ts @@ -0,0 +1,55 @@ +import { execSync } from 'node:child_process'; + +import { dirname, join } from 'node:path'; +import { readFileSync } from 'node:fs'; +import { sync as findUpSync } from 'find-up'; + +export function findGitRoot(cwd: string) { + const output = execSync('git rev-parse --show-toplevel', { cwd }); + + return output.toString().trim(); +} + +/** + * + * @param outputRoot- absolute root path to output assets + * @returns + */ +export function getPackageMetadata(outputRoot: string) { + const root = getPackageRoot(outputRoot); + const json: { name: string } = JSON.parse(readFileSync(join(root, 'package.json'), 'utf-8')); + + return { + root, + name: json.name, + }; +} + +export function getPackageRoot(outputRoot: string) { + const rootConfig = findUpSync(['package.json', 'project.json'], { cwd: outputRoot }); + + if (!rootConfig) { + throw new Error( + [ + 'Failed to find a package root (directory that contains "package.json" or "project.json" file)', + `Report file location: ${outputRoot}`, + `Tip: You can override package root resolution by providing "packageRoot" function in the configuration`, + ].join('\n'), + ); + } + + return dirname(rootConfig); +} + +export async function loadPixelmatch() { + const pixelmatch = (await import('pixelmatch')).default; + return pixelmatch; +} + +export function stripIndents(strings: { raw: readonly string[] }, ...values: string[]) { + return String.raw(strings, ...values) + .split('\n') + .map(line => line.trim()) + .join('\n') + .trim(); +} diff --git a/tools/visual-regression-assert/tsconfig.json b/tools/visual-regression-assert/tsconfig.json new file mode 100644 index 00000000000000..e79f06ab2ee48c --- /dev/null +++ b/tools/visual-regression-assert/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/tools/visual-regression-assert/tsconfig.lib.json b/tools/visual-regression-assert/tsconfig.lib.json new file mode 100644 index 00000000000000..9ef3243fd2d77c --- /dev/null +++ b/tools/visual-regression-assert/tsconfig.lib.json @@ -0,0 +1,15 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../dist/out-tsc/types", + "outDir": "../../dist/out-tsc", + "inlineSources": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx"] +} diff --git a/tools/visual-regression-assert/tsconfig.spec.json b/tools/visual-regression-assert/tsconfig.spec.json new file mode 100644 index 00000000000000..ac9bcb7a8f905e --- /dev/null +++ b/tools/visual-regression-assert/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "../../dist/out-tsc", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.d.ts" + ] +} diff --git a/tools/visual-regression-utilities/.eslintrc.json b/tools/visual-regression-utilities/.eslintrc.json new file mode 100644 index 00000000000000..703b92b1c10f3c --- /dev/null +++ b/tools/visual-regression-utilities/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "extends": ["../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.ts", "*.tsx"], + "rules": {} + }, + { + "files": ["*.js", "*.jsx"], + "rules": {} + }, + { + "files": ["*.json"], + "parser": "jsonc-eslint-parser", + "rules": { + "@nx/dependency-checks": [ + "error", + { + "ignoredFiles": ["{projectRoot}/eslint.config.{js,cjs,mjs}"] + } + ] + } + } + ] +} diff --git a/tools/visual-regression-utilities/.swcrc b/tools/visual-regression-utilities/.swcrc new file mode 100644 index 00000000000000..d54df2b947bb84 --- /dev/null +++ b/tools/visual-regression-utilities/.swcrc @@ -0,0 +1,29 @@ +{ + "jsc": { + "target": "es2017", + "parser": { + "syntax": "typescript", + "decorators": true, + "dynamicImport": true + }, + "transform": { + "decoratorMetadata": true, + "legacyDecorator": true + }, + "keepClassNames": true, + "externalHelpers": true, + "loose": true + }, + "module": { + "type": "commonjs" + }, + "sourceMaps": true, + "exclude": [ + "jest.config.ts", + ".*\\.spec.tsx?$", + ".*\\.test.tsx?$", + "./src/jest-setup.ts$", + "./**/jest-setup.ts$", + ".*.js$" + ] +} diff --git a/tools/visual-regression-utilities/README.md b/tools/visual-regression-utilities/README.md new file mode 100644 index 00000000000000..86c3217fa3dd30 --- /dev/null +++ b/tools/visual-regression-utilities/README.md @@ -0,0 +1,11 @@ +# visual-regression-utilities + +This library was generated with [Nx](https://nx.dev). + +## Building + +Run `nx build visual-regression-utilities` to build the library. + +## Running unit tests + +Run `nx test visual-regression-utilities` to execute the unit tests via [Jest](https://jestjs.io). diff --git a/tools/visual-regression-utilities/jest.config.ts b/tools/visual-regression-utilities/jest.config.ts new file mode 100644 index 00000000000000..12191d5c87ae98 --- /dev/null +++ b/tools/visual-regression-utilities/jest.config.ts @@ -0,0 +1,29 @@ +/* eslint-disable */ +import { readFileSync } from 'node:fs'; +const { join } = require('node:path'); + +// Reading the SWC compilation config and remove the "exclude" +// for the test files to be compiled by SWC +const { exclude: _, ...swcJestConfig } = JSON.parse(readFileSync(join(__dirname, '.swcrc'), 'utf-8')); + +// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves. +// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude" +if (swcJestConfig.swcrc === undefined) { + swcJestConfig.swcrc = false; +} + +// Uncomment if using global setup/teardown files being transformed via swc +// https://nx.dev/nx-api/jest/documents/overview#global-setupteardown-with-nx-libraries +// jest needs EsModule Interop to find the default exported setup/teardown functions +// swcJestConfig.module.noInterop = false; + +export default { + displayName: 'visual-regression-utilities', + preset: '../../jest.preset.js', + transform: { + '^.+\\.tsx?$': ['@swc/jest', swcJestConfig], + }, + moduleFileExtensions: ['ts', 'tsx', 'js'], + testEnvironment: 'node', + coverageDirectory: '../../coverage/tools/visual-regression-utilities', +}; diff --git a/tools/visual-regression-utilities/package.json b/tools/visual-regression-utilities/package.json new file mode 100644 index 00000000000000..7a7e5f69dcf98a --- /dev/null +++ b/tools/visual-regression-utilities/package.json @@ -0,0 +1,24 @@ +{ + "name": "@fluentui/visual-regression-utilities", + "private": true, + "version": "0.0.0", + "type": "commonjs", + "main": "./src/index.js", + "typings": "./src/index.d.ts", + "files": [ + "*.md", + "dist/*.d.ts", + "lib", + "lib-commonjs" + ], + "dependencies": { + "@fluentui/react-provider": "^9.20.0", + "@fluentui/react-theme": "^9.1.24", + "@swc/helpers": "^0.5.1" + }, + "peerDependencies": { + "@storybook/react": "^7.6.20", + "react": ">=16.14.0 <19.0.0", + "@types/react": ">=16.14.0 <19.0.0" + } +} diff --git a/tools/visual-regression-utilities/project.json b/tools/visual-regression-utilities/project.json new file mode 100644 index 00000000000000..51b4559d829edc --- /dev/null +++ b/tools/visual-regression-utilities/project.json @@ -0,0 +1,43 @@ +{ + "name": "visual-regression-utilities", + "$schema": "../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "tools/visual-regression-utilities/src", + "projectType": "library", + "release": { + "version": { + "generatorOptions": { + "packageRoot": "dist/{projectRoot}", + "currentVersionResolver": "git-tag" + } + } + }, + "tags": ["platform:any", "tools"], + "targets": { + "build": { + "executor": "@nx/js:swc", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/tools/visual-regression-utilities", + "main": "tools/visual-regression-utilities/src/index.ts", + "tsConfig": "tools/visual-regression-utilities/tsconfig.lib.json", + "assets": ["tools/visual-regression-utilities/*.md"] + } + }, + "nx-release-publish": { + "options": { + "packageRoot": "dist/{projectRoot}" + } + }, + "lint": { + "executor": "@nx/eslint:lint" + }, + "test": { + "command": "jest", + "options": { + "cwd": "tools/visual-regression-utilities", + "passWithNoTests": true + }, + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"] + } + } +} diff --git a/tools/visual-regression-utilities/src/TestWrapperDecorator.tsx b/tools/visual-regression-utilities/src/TestWrapperDecorator.tsx new file mode 100644 index 00000000000000..1662f69d268876 --- /dev/null +++ b/tools/visual-regression-utilities/src/TestWrapperDecorator.tsx @@ -0,0 +1,42 @@ +import * as React from 'react'; +import type { Decorator } from '@storybook/react'; + +export const TestWrapperDecorator: Decorator = story => ( +
+
+ {story()} +
+
+); + +export const TestWrapperDecoratorTall: Decorator = story => ( +
+
+ {story()} +
+
+); + +export const TestWrapperDecoratorTallFixedWidth: Decorator = story => ( +
+
+ {story()} +
+
+); + +export const TestWrapperDecoratorFixedWidth: Decorator = story => ( +
+
+ {story()} +
+
+); + +export const TestWrapperDecoratorFullWidth: Decorator = story => ( +
+
+ {story()} +
+
+); diff --git a/tools/visual-regression-utilities/src/getStoryVariant.spec.tsx b/tools/visual-regression-utilities/src/getStoryVariant.spec.tsx new file mode 100644 index 00000000000000..0e05486240c6e3 --- /dev/null +++ b/tools/visual-regression-utilities/src/getStoryVariant.spec.tsx @@ -0,0 +1,46 @@ +import * as React from 'react'; + +import type { StoryFn } from '@storybook/react'; +import { Button } from '@fluentui/react-button'; +import { webDarkTheme, teamsHighContrastTheme } from '@fluentui/react-theme'; + +import { getStoryVariant, DARK_MODE, HIGH_CONTRAST, RTL } from './getStoryVariant'; + +describe('getStoryVariant', () => { + const DefaultStory: StoryFn = () => ; + + it('should set the correct direction for story', () => { + const ltrStory = getStoryVariant(DefaultStory, DARK_MODE); + const rtlStory = getStoryVariant(DefaultStory, RTL); + + expect(ltrStory.parameters.dir).toBe('ltr'); + expect(rtlStory.parameters.dir).toBe('rtl'); + }); + + it('should set the correct theme for story', () => { + const darkModeStory = getStoryVariant(DefaultStory, DARK_MODE); + const highContrastStory = getStoryVariant(DefaultStory, HIGH_CONTRAST); + + expect(darkModeStory.parameters.theme).toEqual(webDarkTheme); + expect(highContrastStory.parameters.theme).toEqual(teamsHighContrastTheme); + }); + + it('should set the correct name for story', () => { + const darkModeStory = getStoryVariant(DefaultStory, DARK_MODE); + const highContrastStory = getStoryVariant(DefaultStory, HIGH_CONTRAST); + const rtlStory = getStoryVariant(DefaultStory, RTL); + expect(darkModeStory.storyName).toEqual('Default Story - Dark Mode'); + expect(highContrastStory.storyName).toEqual('Default Story - High Contrast'); + expect(rtlStory.storyName).toEqual('Default Story - RTL'); + + const buttonStory: StoryFn = (...args) => DefaultStory(...args); + buttonStory.storyName = 'button'; + + const buttonDarkModeStory = getStoryVariant(buttonStory, DARK_MODE); + const buttonHighContrastStory = getStoryVariant(buttonStory, HIGH_CONTRAST); + const buttonRtlStory = getStoryVariant(buttonStory, RTL); + expect(buttonDarkModeStory.storyName).toEqual('button - Dark Mode'); + expect(buttonHighContrastStory.storyName).toEqual('button - High Contrast'); + expect(buttonRtlStory.storyName).toEqual('button - RTL'); + }); +}); diff --git a/tools/visual-regression-utilities/src/getStoryVariant.tsx b/tools/visual-regression-utilities/src/getStoryVariant.tsx new file mode 100644 index 00000000000000..35c6012d826fbf --- /dev/null +++ b/tools/visual-regression-utilities/src/getStoryVariant.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import type { Args, Decorator, StoryObj } from '@storybook/react'; +import { FluentProvider } from '@fluentui/react-provider'; +import { webLightTheme, webDarkTheme, teamsHighContrastTheme } from '@fluentui/react-theme'; + +export const DARK_MODE = 'Dark Mode'; +export const HIGH_CONTRAST = 'High Contrast'; +export const RTL = 'RTL'; + +type StoryVariant = typeof DARK_MODE | typeof HIGH_CONTRAST | typeof RTL; + +/** Helper function that returns RTL, Dark Mode or High Contrast variant of an existing story. */ +export function getStoryVariant(story: StoryObj & { name: string }, variant: StoryVariant): StoryObj { + const theme = getTheme(variant); + const dir = getDir(variant); + const decorators = story.decorators ?? []; + + return { + ...story, + name: `${getStoryName(story)} - ${variant}`, + parameters: { + ...story.parameters, + dir, + mode: 'vr-test', + theme, + }, + decorators: [...(Array.isArray(decorators) ? decorators : [decorators]), StoryVariantDecorator], + }; +} + +const StoryVariantDecorator: Decorator = (Story, context) => { + return ( + + + + ); +}; + +/** A mapping of story variants to Fluent themes. */ +const STORY_VARIANT_THEME = { + [RTL]: webLightTheme, + [DARK_MODE]: webDarkTheme, + [HIGH_CONTRAST]: teamsHighContrastTheme, +} as const; + +function getTheme(variant: StoryVariant) { + return STORY_VARIANT_THEME[variant]; +} + +function getDir(variant: StoryVariant) { + return variant === RTL ? 'rtl' : 'ltr'; +} + +function getStoryName({ name }: StoryObj) { + return name?.replace(/([a-z])([A-Z])/g, '$1 $2'); +} diff --git a/tools/visual-regression-utilities/src/index.ts b/tools/visual-regression-utilities/src/index.ts new file mode 100644 index 00000000000000..0199a8bff9f0f5 --- /dev/null +++ b/tools/visual-regression-utilities/src/index.ts @@ -0,0 +1,8 @@ +export { DARK_MODE, HIGH_CONTRAST, RTL, getStoryVariant } from './getStoryVariant'; +export { + TestWrapperDecorator, + TestWrapperDecoratorFixedWidth, + TestWrapperDecoratorFullWidth, + TestWrapperDecoratorTall, + TestWrapperDecoratorTallFixedWidth, +} from './TestWrapperDecorator'; diff --git a/tools/visual-regression-utilities/tsconfig.json b/tools/visual-regression-utilities/tsconfig.json new file mode 100644 index 00000000000000..e79f06ab2ee48c --- /dev/null +++ b/tools/visual-regression-utilities/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2019", + "noEmit": true, + "isolatedModules": true, + "importHelpers": true, + "jsx": "react", + "noUnusedLocals": true, + "preserveConstEnums": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + }, + { + "path": "./tsconfig.spec.json" + } + ] +} diff --git a/tools/visual-regression-utilities/tsconfig.lib.json b/tools/visual-regression-utilities/tsconfig.lib.json new file mode 100644 index 00000000000000..43f8dbc53072e9 --- /dev/null +++ b/tools/visual-regression-utilities/tsconfig.lib.json @@ -0,0 +1,14 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "noEmit": false, + "lib": ["ES2019", "dom"], + "declaration": true, + "declarationDir": "../../../dist/out-tsc/types", + "outDir": "../../dist/out-tsc", + "inlineSources": true, + "types": ["node"] + }, + "include": ["src/**/*.ts", "src/**/*.tsx"], + "exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx"] +} diff --git a/tools/visual-regression-utilities/tsconfig.spec.json b/tools/visual-regression-utilities/tsconfig.spec.json new file mode 100644 index 00000000000000..ac9bcb7a8f905e --- /dev/null +++ b/tools/visual-regression-utilities/tsconfig.spec.json @@ -0,0 +1,16 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "module": "CommonJS", + "outDir": "../../dist/out-tsc", + "types": ["jest", "node"] + }, + "include": [ + "jest.config.ts", + "src/**/*.test.ts", + "src/**/*.test.tsx", + "src/**/*.spec.ts", + "src/**/*.spec.tsx", + "src/**/*.d.ts" + ] +} diff --git a/tools/workspace-plugin/executors.json b/tools/workspace-plugin/executors.json index 28e40ee1c81e5d..f70f49b61c573b 100644 --- a/tools/workspace-plugin/executors.json +++ b/tools/workspace-plugin/executors.json @@ -24,6 +24,11 @@ "implementation": "./src/executors/clean/executor", "schema": "./src/executors/clean/schema.json", "description": "clean executor - remove build artifacts." + }, + "visual-regression": { + "implementation": "./src/executors/visual-regression/executor", + "schema": "./src/executors/visual-regression/schema.json", + "description": "visual-regression executor" } } } diff --git a/tools/workspace-plugin/src/executors/visual-regression/executor.spec.ts b/tools/workspace-plugin/src/executors/visual-regression/executor.spec.ts new file mode 100644 index 00000000000000..76f23ca2718c5c --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/executor.spec.ts @@ -0,0 +1,18 @@ +import { ExecutorContext } from '@nx/devkit'; + +import { VisualRegressionExecutorSchema } from './schema'; +import executor from './executor'; + +const options: VisualRegressionExecutorSchema = {}; +const context: ExecutorContext = { + root: '', + cwd: process.cwd(), + isVerbose: false, +}; + +describe('VisualRegression Executor', () => { + it('can run', async () => { + const output = await executor(options, context); + expect(output.success).toBe(true); + }); +}); diff --git a/tools/workspace-plugin/src/executors/visual-regression/executor.ts b/tools/workspace-plugin/src/executors/visual-regression/executor.ts new file mode 100644 index 00000000000000..bc4c8df54606ad --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/executor.ts @@ -0,0 +1,83 @@ +import { ExecutorContext, PromiseExecutor, generateFiles, workspaceRoot } from '@nx/devkit'; +import { spawnSync, exec } from 'node:child_process'; +import { join } from 'node:path'; +import { FsTree, flushChanges } from 'nx/src/generators/tree'; + +import { VisualRegressionExecutorSchema } from './schema'; +import { rm } from 'node:fs'; + +interface NormalizedOptions extends ReturnType {} + +const runExecutor: PromiseExecutor = async (schema, context) => { + const options = normalizeOptions(schema, context); + + prepareSetup(options); + + const storywrightCmdArgs = Object.entries(options.storywright) + .map(([flag, value]) => { + return [`--${flag}`, `${value}`]; + }) + .flat(); + + try { + const rmResult = spawnSync('rm', ['-rf', options.storywright.destpath], { + cwd: options.project.root, + stdio: 'inherit', + }); + + const swResult = spawnSync('storywright', [...storywrightCmdArgs], { cwd: options.project.root, stdio: 'inherit' }); + if (swResult.error) { + return { success: false }; + } + + // spawnSync('rm', ['-rf', options.configRoot], { cwd: options.project.root, stdio: 'inherit' }); + const pwResult = spawnSync('playwright', ['test', '--config', options.configRoot], { + cwd: options.project.root, + stdio: 'inherit', + }); + if (pwResult.error) { + return { success: false }; + } + } catch { + return { success: false }; + } + + return { + success: true, + }; +}; + +function prepareSetup(options: NormalizedOptions) { + const tree = new FsTree(workspaceRoot, false); + generateFiles(tree, join(__dirname, 'templates'), join(options.project.root, options.configRoot), { + tmpl: '', + playwright: options.playwright, + }); + flushChanges(workspaceRoot, tree.listChanges()); +} + +function normalizeOptions(schema: VisualRegressionExecutorSchema, context: ExecutorContext) { + const project = context.projectsConfigurations!.projects[context.projectName!]; + const defaults = { + // storywright + storywright: { + browsers: 'chromium', + url: 'dist/storybook', + destpath: 'dist/screenshots', + waitTimeScreenshot: 500, + concurrency: 4, + headless: true, + }, + playwright: { + baselinePath: '__snapshots__', + }, + // playwright + // config: join(__dirname, 'playwright.config.ts'), + // misc + configRoot: 'dist/visual-regression', + }; + + return { ...defaults, ...schema, project }; +} + +export default runExecutor; diff --git a/tools/workspace-plugin/src/executors/visual-regression/schema.d.ts b/tools/workspace-plugin/src/executors/visual-regression/schema.d.ts new file mode 100644 index 00000000000000..6e045f14ade198 --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/schema.d.ts @@ -0,0 +1 @@ +export interface VisualRegressionExecutorSchema {} // eslint-disable-line diff --git a/tools/workspace-plugin/src/executors/visual-regression/schema.json b/tools/workspace-plugin/src/executors/visual-regression/schema.json new file mode 100644 index 00000000000000..b579312ce97390 --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/schema.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://json-schema.org/schema", + "version": 2, + "title": "VisualRegression executor", + "description": "", + "type": "object", + "properties": {}, + "required": [] +} diff --git a/tools/workspace-plugin/src/executors/visual-regression/templates/playwright.config.ts__tmpl__ b/tools/workspace-plugin/src/executors/visual-regression/templates/playwright.config.ts__tmpl__ new file mode 100644 index 00000000000000..093484982018a0 --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/templates/playwright.config.ts__tmpl__ @@ -0,0 +1,43 @@ +/* eslint-disable import/no-extraneous-dependencies */ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +const config: PlaywrightTestConfig = { + reporter: 'html', + retries: 3, + fullyParallel: process.env.CI ? false : true, + timeout: process.env.CI ? 10000 : 30000, + snapshotPathTemplate: '{testDir}/<%= playwright.baselinePath %>/{arg}{ext}', + // use: { + // baseURL: 'http://localhost:6006', + // viewport: { + // height: 720, + // width: 1280, + // }, + // }, + testMatch: /spec-vr.ts$/, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + // testMatch: /spec-vr.ts$/, + }, + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // testMatch: [/set-theme\.spec\.ts$/], + // }, + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // testMatch: [/set-theme\.spec\.ts$/], + // }, + ], + // webServer: { + // command: `node scripts/server.js --port 6006`, + // port: 6006, + // reuseExistingServer: process.env.CI ? false : true, + // }, +}; + +export default config; diff --git a/tools/workspace-plugin/src/executors/visual-regression/templates/spec-vr.ts__tmpl__ b/tools/workspace-plugin/src/executors/visual-regression/templates/spec-vr.ts__tmpl__ new file mode 100644 index 00000000000000..27179b9208ca40 --- /dev/null +++ b/tools/workspace-plugin/src/executors/visual-regression/templates/spec-vr.ts__tmpl__ @@ -0,0 +1,37 @@ +import { readdirSync, readFileSync, existsSync } from 'node:fs'; +import { join } from 'node:path'; +import { expect, test } from '@playwright/test'; + +// Paths for snapshots +const actualRoot = join(__dirname, '../screenshots'); // storywright generated images root +const baselineRoot = join(__dirname, '__snapshots__'); // baseline root + +const actualSnapshots = existsSync(actualRoot) ? readdirSync(actualRoot) : []; +const baselineSnapshots = existsSync(baselineRoot) ? readdirSync(baselineRoot) : []; + +if (!existsSync(baselineRoot)) { + console.warn(`No baseline snapshots exist yet! - ${baselineRoot}`); +} + +const snapshotsToAssert = [...actualSnapshots]; + +// Run tests for actual snapshots +snapshotsToAssert.forEach(actualSnapshotFileName => { + const actualImgPath = join(actualRoot, actualSnapshotFileName); + + test(`${actualSnapshotFileName}`, async ({ page }) => { + const actualImg = readFileSync(actualImgPath, 'base64'); + + // Render the image on a test page + await page.setContent(` + + + + + + `); + + // Compare the rendered image with the baseline + await expect(page.locator('#image')).toHaveScreenshot(actualSnapshotFileName); + }); +}); diff --git a/tools/workspace-plugin/src/plugins/workspace-plugin.ts b/tools/workspace-plugin/src/plugins/workspace-plugin.ts index 27a2f365b69ca9..60a547e090818f 100644 --- a/tools/workspace-plugin/src/plugins/workspace-plugin.ts +++ b/tools/workspace-plugin/src/plugins/workspace-plugin.ts @@ -151,6 +151,17 @@ function buildWorkspaceTargets( // react v9 lib if (projectJSON.projectType === 'library' && tags.includes('vNext')) { + // *-visual-regression projects + if (tags.includes('visual-regression')) { + // TODO: add VRT targets + + if (storybookTarget) { + targets.start = { command: `nx run ${config.projectJSON.name}:storybook`, cache: true }; + } + + return targets; + } + // *-stories projects if (tags.includes('type:stories')) { const testSsrTarget = buildTestSsrTarget(projectRoot, options, context, config); diff --git a/tsconfig.base.all.json b/tsconfig.base.all.json index 259394b5d3b167..fcb4704ae2343e 100644 --- a/tsconfig.base.all.json +++ b/tsconfig.base.all.json @@ -157,6 +157,8 @@ "@fluentui/react-label-stories": ["packages/react-components/react-label/stories/src/index.ts"], "@fluentui/react-link": ["packages/react-components/react-link/library/src/index.ts"], "@fluentui/react-link-stories": ["packages/react-components/react-link/stories/src/index.ts"], + "@fluentui/react-list": ["packages/react-components/react-list/library/src/index.ts"], + "@fluentui/react-list-stories": ["packages/react-components/react-list/stories/src/index.ts"], "@fluentui/react-menu": ["packages/react-components/react-menu/library/src/index.ts"], "@fluentui/react-menu-stories": ["packages/react-components/react-menu/stories/src/index.ts"], "@fluentui/react-message-bar": ["packages/react-components/react-message-bar/library/src/index.ts"], @@ -225,6 +227,7 @@ "@fluentui/react-table-stories": ["packages/react-components/react-table/stories/src/index.ts"], "@fluentui/react-tabs": ["packages/react-components/react-tabs/library/src/index.ts"], "@fluentui/react-tabs-stories": ["packages/react-components/react-tabs/stories/src/index.ts"], + "@fluentui/react-tabs-visual-regression": ["packages/react-components/react-tabs/visual-regression/src/index.ts"], "@fluentui/react-tabster": ["packages/react-components/react-tabster/src/index.ts"], "@fluentui/react-tag-picker": ["packages/react-components/react-tag-picker/library/src/index.ts"], "@fluentui/react-tag-picker-stories": ["packages/react-components/react-tag-picker/stories/src/index.ts"], @@ -236,6 +239,7 @@ ], "@fluentui/react-text": ["packages/react-components/react-text/library/src/index.ts"], "@fluentui/react-text-stories": ["packages/react-components/react-text/stories/src/index.ts"], + "@fluentui/react-text-visual-regression": ["packages/react-components/react-text/visual-regression/src/index.ts"], "@fluentui/react-textarea": ["packages/react-components/react-textarea/library/src/index.ts"], "@fluentui/react-textarea-stories": ["packages/react-components/react-textarea/stories/src/index.ts"], "@fluentui/react-theme": ["packages/react-components/react-theme/library/src/index.ts"], @@ -263,9 +267,9 @@ "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], - "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"], - "@fluentui/react-list-stories": ["packages/react-components/react-list/stories/src/index.ts"], - "@fluentui/react-list": ["packages/react-components/react-list/library/src/index.ts"] + "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], + "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], + "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] } } } diff --git a/tsconfig.base.json b/tsconfig.base.json index 8646fc887ddcb0..d7edada53ca90d 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -162,6 +162,7 @@ "@fluentui/react-table-stories": ["packages/react-components/react-table/stories/src/index.ts"], "@fluentui/react-tabs": ["packages/react-components/react-tabs/library/src/index.ts"], "@fluentui/react-tabs-stories": ["packages/react-components/react-tabs/stories/src/index.ts"], + "@fluentui/react-tabs-visual-regression": ["packages/react-components/react-tabs/visual-regression/src/index.ts"], "@fluentui/react-tabster": ["packages/react-components/react-tabster/src/index.ts"], "@fluentui/react-tag-picker": ["packages/react-components/react-tag-picker/library/src/index.ts"], "@fluentui/react-tag-picker-stories": ["packages/react-components/react-tag-picker/stories/src/index.ts"], @@ -173,6 +174,7 @@ ], "@fluentui/react-text": ["packages/react-components/react-text/library/src/index.ts"], "@fluentui/react-text-stories": ["packages/react-components/react-text/stories/src/index.ts"], + "@fluentui/react-text-visual-regression": ["packages/react-components/react-text/visual-regression/src/index.ts"], "@fluentui/react-textarea": ["packages/react-components/react-textarea/library/src/index.ts"], "@fluentui/react-textarea-stories": ["packages/react-components/react-textarea/stories/src/index.ts"], "@fluentui/react-theme": ["packages/react-components/react-theme/library/src/index.ts"], @@ -200,6 +202,8 @@ "@fluentui/recipes": ["packages/react-components/recipes/src/index.ts"], "@fluentui/theme-designer": ["packages/react-components/theme-designer/src/index.ts"], "@fluentui/tokens": ["packages/tokens/src/index.ts"], + "@fluentui/visual-regression-assert": ["tools/visual-regression-assert/src/index.ts"], + "@fluentui/visual-regression-utilities": ["tools/visual-regression-utilities/src/index.ts"], "@fluentui/workspace-plugin": ["tools/workspace-plugin/src/index.ts"] } }, diff --git a/yarn.lock b/yarn.lock index 5b1932bdd48aef..9f4a432f13d3f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5201,10 +5201,10 @@ resolved "https://registry.yarnpkg.com/@types/doctrine/-/doctrine-0.0.9.tgz#d86a5f452a15e3e3113b99e39616a9baa0f9863f" integrity sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA== -"@types/ejs@3.1.2", "@types/ejs@^3.1.1": - version "3.1.2" - resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.2.tgz#75d277b030bc11b3be38c807e10071f45ebc78d9" - integrity sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g== +"@types/ejs@3.1.5", "@types/ejs@^3.1.1": + version "3.1.5" + resolved "https://registry.yarnpkg.com/@types/ejs/-/ejs-3.1.5.tgz#49d738257cc73bafe45c13cb8ff240683b4d5117" + integrity sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg== "@types/emscripten@^1.39.6": version "1.39.13" @@ -5639,6 +5639,13 @@ resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-5.0.3.tgz#e7b5aebbac150f8b5fdd4a46e7f0bd8e65e19109" integrity sha512-kUNnecmtkunAoQ3CnjmMkzNU/gtxG8guhi+Fk2U/kOpIKjIMKnXGp4IJCgQJrXSgMsWYimYG4TGjz/UzbGEBTw== +"@types/pngjs@6.0.5": + version "6.0.5" + resolved "https://registry.yarnpkg.com/@types/pngjs/-/pngjs-6.0.5.tgz#6dec2f7eb8284543ca4e423f3c09b119fa939ea3" + integrity sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ== + dependencies: + "@types/node" "*" + "@types/prettier@*", "@types/prettier@2.7.2": version "2.7.2" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0" @@ -8460,6 +8467,15 @@ cli-table3@^0.6.1, cli-table3@^0.6.3, cli-table3@~0.6.1: optionalDependencies: "@colors/colors" "1.5.0" +cli-table3@^0.6.5: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-table@0.3.11, cli-table@^0.3.1: version "0.3.11" resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" @@ -12079,6 +12095,14 @@ find-up@3.0.0, find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@5.0.0, find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -12095,14 +12119,6 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-up@^6.2.0, find-up@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-6.3.0.tgz#2abab3d3280b2dc7ac10199ef324c4e002c8c790" @@ -12818,6 +12834,18 @@ glob@^10.0.0: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" +glob@^11.0.1: + version "11.0.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-11.0.1.tgz#1c3aef9a59d680e611b53dcd24bb8639cef064d9" + integrity sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw== + dependencies: + foreground-child "^3.1.0" + jackspeak "^4.0.1" + minimatch "^10.0.0" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^2.0.0" + glob@^8.0.3: version "8.1.0" resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" @@ -14813,6 +14841,13 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-4.1.0.tgz#c489c079f2b636dc4cbe9b0312a13ff1282e561b" + integrity sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw== + dependencies: + "@isaacs/cliui" "^8.0.2" + jake@^10.8.5: version "10.8.5" resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" @@ -16419,6 +16454,11 @@ lru-cache@^10.0.1, lru-cache@^10.2.0: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== +lru-cache@^11.0.0: + version "11.0.2" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-11.0.2.tgz#fbd8e7cf8211f5e7e5d91905c415a3f55755ca39" + integrity sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA== + lru-cache@^4.0.1: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -17409,6 +17449,13 @@ minimatch@9.0.3: dependencies: brace-expansion "^2.0.1" +minimatch@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.0.1.tgz#ce0521856b453c86e25f2c4c0d03e6ff7ddc440b" + integrity sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ== + dependencies: + brace-expansion "^2.0.1" + minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -18824,6 +18871,14 @@ path-scurry@^1.11.1: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-2.0.0.tgz#9f052289f23ad8bf9397a2a0425e7b8615c58580" + integrity sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg== + dependencies: + lru-cache "^11.0.0" + minipass "^7.1.2" + path-to-regexp@0.1.10: version "0.1.10" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b" @@ -18968,6 +19023,13 @@ piscina@^4.3.0: optionalDependencies: nice-napi "^1.0.2" +pixelmatch@7.1.0, pixelmatch@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-7.1.0.tgz#9d59bddc8c779340e791106c0f245ac33ae4d113" + integrity sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng== + dependencies: + pngjs "^7.0.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -19083,6 +19145,11 @@ plugin-error@^1.0.1: arr-union "^3.1.0" extend-shallow "^3.0.2" +pngjs@7.0.0, pngjs@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-7.0.0.tgz#a8b7446020ebbc6ac739db6c5415a65d17090e26" + integrity sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow== + polished@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/polished/-/polished-4.2.2.tgz#2529bb7c3198945373c52e34618c8fe7b1aa84d1" @@ -21741,10 +21808,10 @@ storybook@7.6.20: dependencies: "@storybook/cli" "7.6.20" -storywright@0.0.27-storybook7.11: - version "0.0.27-storybook7.11" - resolved "https://registry.yarnpkg.com/storywright/-/storywright-0.0.27-storybook7.11.tgz#c032deedd83c82895b09fc17b7404d397b1ebedf" - integrity sha512-B/tte/PyJ6MATFvMsGlv3+Yy3dSmzmWYyLYx/ijWwbUQ8O6BqDkkmmgVlsFrgydfsCu3CX2qVXPsNEQjYW8cEQ== +storywright@0.0.27-storybook7.13: + version "0.0.27-storybook7.13" + resolved "https://registry.yarnpkg.com/storywright/-/storywright-0.0.27-storybook7.13.tgz#1b5a557e463da56d1496efbe47d9db7fbd1c2531" + integrity sha512-NDeQRdaN7xasQE8NdfjX8NEyQ6V73IR9QUt8Ouq815Egy/4N7MaglbkeXCO/1WNwLkqCBDbrQgtndJnuEcHeyg== dependencies: playwright "^1.34.3" prop-types "^15.6.0" @@ -21791,7 +21858,7 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -21826,15 +21893,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -21935,7 +21993,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -21970,13 +22028,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -24306,7 +24357,7 @@ workspace-tools@^0.27.0: js-yaml "^4.1.0" micromatch "^4.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -24341,15 +24392,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"