diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 5593984949..36964ddc1f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -53,6 +53,13 @@ jobs: name: dev-pages-react${{ matrix.react }} path: pages/lib/static-default + - name: Upload test utils selectors artifact + if: matrix.react == 18 + uses: actions/upload-artifact@v4 + with: + name: test-utils-selectors + path: lib/components + deploy: needs: quick-build name: deploy${{ matrix.react != 16 && format(' (React {0})', matrix.react) || '' }} @@ -65,3 +72,14 @@ jobs: with: artifact-name: dev-pages-react${{ matrix.react }} deployment-path: pages/lib/static-default + + visual: + name: Visual regression + needs: quick-build + if: ${{ github.event.pull_request.head.repo.full_name == github.repository }} + uses: ./.github/workflows/visual-regression.yml + secrets: inherit + with: + pr-artifact-name: dev-pages-react18 + test-utils-artifact-name: test-utils-selectors + caller-run-id: ${{ github.run_id }} diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml new file mode 100644 index 0000000000..f3f792b8d4 --- /dev/null +++ b/.github/workflows/visual-regression.yml @@ -0,0 +1,197 @@ +name: Visual Regression Tests + +on: + workflow_call: + inputs: + pr-artifact-name: + description: 'Name of the artifact containing PR pages (built by the caller workflow).' + required: true + type: string + test-utils-artifact-name: + description: 'Name of the artifact containing test-utils selectors.' + required: true + type: string + caller-run-id: + description: 'The run ID of the calling workflow, used to download artifacts it uploaded.' + required: true + type: string + +defaults: + run: + shell: bash + +permissions: + id-token: write + contents: read + actions: read + deployments: write + +jobs: + # Build the baseline (main branch) pages once and share them across all browser jobs. + build-baseline: + name: Build baseline pages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm i + + # Use a git worktree so the baseline has its own directory and its own + # node_modules. This means a PR that changes package-lock.json will still + # produce a correct baseline: the baseline installs from main's lockfile + # and the PR build installs from the PR's lockfile, so both sides use the + # dependency versions that are correct for their respective source trees. + - name: Create baseline worktree from origin/main + run: git worktree add /tmp/baseline origin/main + + - name: Install baseline dependencies + run: npm i + working-directory: /tmp/baseline + + - name: Build baseline pages + run: npx gulp quick-build + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Bundle baseline pages + run: node_modules/.bin/webpack --config pages/webpack.config.integ.cjs --output-path ${{ github.workspace }}/pages/lib/static-visual-baseline + working-directory: /tmp/baseline + env: + NODE_ENV: production + + - name: Upload baseline artifact + uses: actions/upload-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + retention-days: 1 + + visual: + name: Visual regression (shard ${{ matrix.shard }}) + needs: [build-baseline] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30] + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Setup Chrome and ChromeDriver + uses: browser-actions/setup-chrome@v1 + with: + chrome-version: stable + + - name: Install dependencies + run: npm i + + - name: Download PR pages artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.pr-artifact-name }} + path: pages/lib/static-default + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} + + - name: Download baseline artifact + uses: actions/download-artifact@v4 + with: + name: visual-baseline-pages + path: pages/lib/static-visual-baseline + + - name: Download test utils artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.test-utils-artifact-name }} + path: lib/components + github-token: ${{ github.token }} + run-id: ${{ inputs.caller-run-id }} + + # ── Run tests ───────────────────────────────────────────────────────── + + - name: Generate visual test files + run: node build-tools/visual/generate-tests.js + + - name: Start test server (port 8080) + run: npx --yes serve --no-clipboard --listen 8080 pages/lib/static-default & + + - name: Start baseline server (port 8081) + run: npx --yes serve --no-clipboard --listen 8081 pages/lib/static-visual-baseline & + + - name: Wait for servers to be ready + run: node_modules/.bin/wait-on http://localhost:8080 http://localhost:8081 + + - name: Run visual regression tests + run: NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard=${{ matrix.shard }}/30 + env: + TZ: UTC + + - name: Upload diff artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: visual-regression-diffs-shard-${{ matrix.shard }} + path: visual-regression-output/ + retention-days: 14 + + - name: Upload Allure results + if: always() + uses: actions/upload-artifact@v4 + with: + name: allure-results-shard-${{ matrix.shard }} + path: allure-results/ + retention-days: 3 + + report: + name: Generate Allure Report + if: always() + needs: [visual] + runs-on: ubuntu-latest + steps: + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Download all Allure results + uses: actions/download-artifact@v4 + with: + pattern: allure-results-shard-* + path: allure-results + merge-multiple: true + + - name: Generate Allure HTML report + run: npx --yes allure generate allure-results -o allure-report + + - name: Upload Allure report artifact + uses: actions/upload-artifact@v4 + with: + name: allure-report + path: allure-report/ + retention-days: 14 + + deploy-report: + name: Deploy Allure Report + if: always() + needs: [report] + uses: cloudscape-design/actions/.github/workflows/deploy.yml@main + secrets: inherit + with: + artifact-name: allure-report + deployment-path: allure-report diff --git a/.gitignore b/.gitignore index db3d62b944..1f46343d58 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ coverage lib # generated sources src/index.ts +test/visual +test/definitions/index.ts +allure-results +allure-report src/test-utils/dom/index.ts src/test-utils/selectors src/icon/generated diff --git a/build-tools/tasks/package-json.js b/build-tools/tasks/package-json.js index a7c69a53f5..2c447682a1 100644 --- a/build-tools/tasks/package-json.js +++ b/build-tools/tasks/package-json.js @@ -103,6 +103,10 @@ const devPagesPackageJson = generatePackageJson(path.join(workspace.targetPath, const testDefinitionsPackageJson = generatePackageJson(path.join(workspace.targetPath, 'test-definitions'), { name: '@cloudscape-design/test-definitions', + exports: { + '.': './index.js', + './types': './types.js', + }, }); module.exports = parallel([ diff --git a/build-tools/tasks/test-definitions.js b/build-tools/tasks/test-definitions.js index db82381589..1d66b13b84 100644 --- a/build-tools/tasks/test-definitions.js +++ b/build-tools/tasks/test-definitions.js @@ -1,8 +1,17 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +const { series } = require('gulp'); const execa = require('execa'); const { task } = require('../utils/gulp-utils'); -module.exports = task('test-definitions', () => +// Generate test/definitions/index.ts from the visual definition files before +// compiling, so that downstream consumers always get a complete barrel export. +const generate = task('test-definitions:generate', () => + execa('node', ['build-tools/visual/generate-tests.js'], { stdio: 'inherit' }) +); + +const compile = task('test-definitions:compile', () => execa('tsc', ['-p', 'tsconfig.test-definitions.json'], { stdio: 'inherit' }) ); + +module.exports = series(generate, compile); diff --git a/build-tools/visual/generate-tests.js b/build-tools/visual/generate-tests.js new file mode 100644 index 0000000000..f82b54d037 --- /dev/null +++ b/build-tools/visual/generate-tests.js @@ -0,0 +1,106 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** + * Auto-generates: + * 1. test/visual/*.test.ts — one test runner per definition file (for Jest sharding) + * 2. test/definitions/index.ts — barrel that exports allSuites from all definitions + * + * Each definition file that exports a default TestSuite gets included. + * Helper files (those without a default export, e.g. shared utils like + * app-layout-responsive-tests.ts) are skipped. + * + * Run this script before executing the visual test suite: + * node build-tools/visual/generate-tests.js + */ +const fs = require('fs'); +const path = require('path'); + +const definitionsDir = path.resolve(__dirname, '../../test/definitions/visual'); +const testOutputDir = path.resolve(__dirname, '../../test/visual'); +const indexOutputPath = path.resolve(__dirname, '../../test/definitions/index.ts'); + +// Files that are shared helpers (export named functions, not a default suite). +const HELPER_SUFFIXES = ['-tests']; + +function isHelperFile(basename) { + return HELPER_SUFFIXES.some(suffix => basename.endsWith(suffix)); +} + +function toCamelCase(basename) { + return basename.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase()); +} + +function generate() { + // Ensure output directory exists + if (!fs.existsSync(testOutputDir)) { + fs.mkdirSync(testOutputDir, { recursive: true }); + } + + const files = fs.readdirSync(definitionsDir).filter(f => f.endsWith('.ts') && !f.endsWith('.d.ts')); + + const suiteFiles = []; + + for (const file of files) { + const basename = file.replace(/\.ts$/, ''); + + if (isHelperFile(basename)) { + continue; + } + + // Verify the file has a default export by scanning for the pattern + const content = fs.readFileSync(path.join(definitionsDir, file), 'utf-8'); + if (!content.includes('export default')) { + continue; + } + + suiteFiles.push(basename); + + // Generate test/visual/.test.ts + const testContent = [ + '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', + '// SPDX-License-Identifier: Apache-2.0', + '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', + `import { runTestSuites } from '../definitions/utils';`, + `import suite from '../definitions/visual/${basename}';`, + '', + 'runTestSuites([suite]);', + '', + ].join('\n'); + + fs.writeFileSync(path.join(testOutputDir, `${basename}.test.ts`), testContent); + } + + // Generate test/definitions/index.ts + // Sort by module path for consistent import ordering + suiteFiles.sort(); + + const imports = suiteFiles.map(basename => { + const varName = toCamelCase(basename); + return `import ${varName} from './visual/${basename}';`; + }); + + const arrayEntries = suiteFiles.map(basename => ` ${toCamelCase(basename)},`); + + const indexContent = [ + '// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.', + '// SPDX-License-Identifier: Apache-2.0', + '// Auto-generated by build-tools/visual/generate-tests.js — do not edit manually.', + '', + `import { TestSuite } from './types';`, + `export { TestSuite, TestDefinition, ScreenshotType, ScreenshotTestConfiguration } from './types';`, + ...imports, + '', + 'export const allSuites: TestSuite[] = [', + ...arrayEntries, + '];', + '', + ].join('\n'); + + fs.writeFileSync(indexOutputPath, indexContent); + + console.log(`Generated ${suiteFiles.length} visual test files in test/visual/`); + console.log(`Generated test/definitions/index.ts with ${suiteFiles.length} suites`); +} + +generate(); diff --git a/build-tools/visual/global-setup.js b/build-tools/visual/global-setup.js new file mode 100644 index 0000000000..52ce2f271c --- /dev/null +++ b/build-tools/visual/global-setup.js @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = async () => { + const { startWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + await startWebdriver(); +}; diff --git a/build-tools/visual/global-teardown.js b/build-tools/visual/global-teardown.js new file mode 100644 index 0000000000..0fa05eebfe --- /dev/null +++ b/build-tools/visual/global-teardown.js @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +module.exports = () => { + const { shutdownWebdriver } = require('@cloudscape-design/browser-test-tools/chrome-launcher'); + shutdownWebdriver(); +}; diff --git a/build-tools/visual/setup.js b/build-tools/visual/setup.js new file mode 100644 index 0000000000..d52cd606fb --- /dev/null +++ b/build-tools/visual/setup.js @@ -0,0 +1,16 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +/* global jest */ +const { configure } = require('@cloudscape-design/browser-test-tools/use-browser'); + +configure({ + browserName: 'ChromeHeadlessIntegration', + browserCreatorOptions: { + seleniumUrl: 'http://localhost:9515', + }, + webdriverOptions: { + baseUrl: 'http://localhost:8080', + }, +}); + +jest.retryTimes(2, { logErrorsBeforeRetry: true }); diff --git a/docs/RUNNING_TESTS.md b/docs/RUNNING_TESTS.md index 525cdf181d..4db82e361b 100644 --- a/docs/RUNNING_TESTS.md +++ b/docs/RUNNING_TESTS.md @@ -60,11 +60,60 @@ TZ=UTC npx jest -u -c jest.unit.config.js src/ ``` ## Visual Regression Tests -> **Note:** The components repository does not have visual regression tests on GitHub. This section applies to other repositories such as chat-components, code-view, chart-components, and board-components. +Visual regression tests run automatically when opening a pull request in GitHub (see `.github/workflows/visual-regression.yml`). -Visual regression tests for permutation pages run automatically when opening a pull request in GitHub. +They compare permutation pages between the PR build and a baseline build of `main`, both served locally in the same CI job. Each side installs from its own `package-lock.json` via a git worktree, so dependency changes in the PR are handled correctly and unpinned updates in sister repositories affect both sides equally. -To check results: look at the "Visual Regression Tests" action in the PR. The "Test for regressions" step logs which pages failed. For a full report, download the `visual-regression-snapshots-results` artifact from the action summary. +### How it works -If there are unexpected regressions, fix your pull request. -If the changes are expected, call this out in your pull request comments. +1. The PR pages are built and served on port 8080. +2. A git worktree of `origin/main` is created, its dependencies installed, and its pages built and served on port 8081. +3. The single test runner (`test/visual.test.ts`) iterates over all test definitions, captures the `.screenshot-area` element from both servers for each test, and fails if any pixels differ. + +### Running locally + +``` +npm run test:visual +``` + +This handles the full build and comparison in one command. If both outputs are already built, skip the build step: + +``` +NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js +``` + +(Requires both servers to be running — start the PR build with `npm run start:integ` on port 8080 and the baseline build on port 8081, or set `NEW_HOST` / `OLD_HOST` env vars to point at different hosts.) + +### Adding tests for a new component + +Create `test/definitions/visual/.ts`: + +```ts +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'my-component', + tests: [ + { + description: 'permutations', + path: 'my-component/permutations', + }, + ], +}; + +export default suite; +``` + +Then run the generation script to pick it up automatically: + +```bash +node build-tools/visual/generate-tests.js +``` + +This generates both the test runner (`test/visual/my-component.test.ts`) and updates `test/definitions/index.ts`. No manual imports needed. + +### Reviewing failures + +If the CI job fails, download the `visual-regression-diffs` artifact from the Actions summary. + +If the diff is expected (intentional visual change), note it in your PR description. diff --git a/eslint.config.mjs b/eslint.config.mjs index 0d9423aa5b..b92a169696 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -225,7 +225,7 @@ export default tsEslint.config( }, }, { - files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**'], + files: ['**/__integ__/**', '**/__motion__/**', '**/__a11y__/**', 'test/definitions/**'], rules: { // useBrowser is not a hook 'react-hooks/rules-of-hooks': 'off', diff --git a/jest.visual.config.js b/jest.visual.config.js new file mode 100644 index 0000000000..98938768b3 --- /dev/null +++ b/jest.visual.config.js @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +const path = require('path'); +const os = require('os'); + +module.exports = { + verbose: true, + testEnvironment: 'allure-jest/node', + testEnvironmentOptions: { + resultsDir: 'allure-results', + }, + transform: { + '^.+\\.tsx?$': [ + 'ts-jest', + { + tsconfig: 'tsconfig.visual.json', + }, + ], + }, + reporters: ['default', 'github-actions'], + testTimeout: 240_000, // 4min — pages can be tall and slow to capture + maxWorkers: os.cpus().length * (process.env.GITHUB_ACTION ? 3 : 1), + globalSetup: '/build-tools/visual/global-setup.js', + globalTeardown: '/build-tools/visual/global-teardown.js', + setupFilesAfterEnv: [path.join(__dirname, 'build-tools', 'visual', 'setup.js')], + moduleFileExtensions: ['js', 'ts'], + testMatch: ['/test/visual/**/*.test.ts'], +}; diff --git a/package-lock.json b/package-lock.json index 1c7a32ecfd..22ac706458 100644 --- a/package-lock.json +++ b/package-lock.json @@ -52,6 +52,7 @@ "@types/jest": "^29.5.13", "@types/lodash": "^4.14.176", "@types/node": "^20.17.14", + "@types/pixelmatch": "^5.2.6", "@types/react": "^16.14.20", "@types/react-dom": "^16.9.14", "@types/react-is": "^18.2.0", @@ -60,6 +61,7 @@ "@types/react-test-renderer": "^16.9.12", "@types/react-transition-group": "^4.4.4", "@types/webpack-env": "^1.16.3", + "allure-jest": "^3.9.0", "axe-core": "^4.7.2", "babel-jest": "^29.7.0", "change-case": "^4.1.2", @@ -96,6 +98,7 @@ "mockdate": "^3.0.5", "npm-run-all": "^4.1.5", "prettier": "^3.6.1", + "puppeteer-core": "^24.43.1", "react": "^16.14.0", "react-dom": "^16.14.0", "react-dom18": "npm:react-dom@^18.3.1", @@ -3521,9 +3524,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.0.tgz", - "integrity": "sha512-46BZJYJjc/WwmKjsvDFykHtXrtomsCIrwYQPOP7VfMJoZY2bsDF9oROBABR3paDjDcmkUye1Pb1BqdcdiipaWA==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.13.2.tgz", + "integrity": "sha512-5EUZSUIc37H6aIXyWO0Z4y8NlF8NnjgmqeQgOGiswAU7pY0HOo16ho4+alIWmSfdZnjqBRawMsP3I5YqLSn6kw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4846,6 +4849,16 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/pixelmatch": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@types/pixelmatch/-/pixelmatch-5.2.6.tgz", + "integrity": "sha512-wC83uexE5KGuUODn6zkm9gMzTwdY5L0chiK+VrKcDfEjzxh1uadlWTvOmAbCpnM9zx/Ww3f8uKlYQVnO/TrqVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/pngjs": { "version": "6.0.5", "dev": true, @@ -5892,6 +5905,58 @@ "dev": true, "license": "MIT" }, + "node_modules/allure-jest": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/allure-jest/-/allure-jest-3.9.0.tgz", + "integrity": "sha512-hEW4DKjvb3engGoHUPQaDEdyrFkUxQnqULiSQAehL1eDEggqdPbQro86Nch8Cj1yuIqUTn9UP1FMuuuwl/5jnQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "allure-js-commons": "3.9.0" + }, + "peerDependencies": { + "jest": ">=24.8.0", + "jest-circus": ">=24.8.0", + "jest-cli": ">=24.8.0", + "jest-environment-jsdom": ">=24.8.0", + "jest-environment-node": ">=24.8.0" + }, + "peerDependenciesMeta": { + "jest": { + "optional": true + }, + "jest-circus": { + "optional": true + }, + "jest-cli": { + "optional": true + }, + "jest-environment-jsdom": { + "optional": true + }, + "jest-environment-node": { + "optional": true + } + } + }, + "node_modules/allure-js-commons": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/allure-js-commons/-/allure-js-commons-3.9.0.tgz", + "integrity": "sha512-uVQcGE6MWIvGR/zW1XEUwHXUQa1EJKY0Cah+0TZK1qKuw6ptyhftDr34XE3wExTyCZirRrI98dbRtPeYYuyI+g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "md5": "^2.3.0" + }, + "peerDependencies": { + "allure-playwright": "3.9.0" + }, + "peerDependenciesMeta": { + "allure-playwright": { + "optional": true + } + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "dev": true, @@ -7154,6 +7219,16 @@ "node": ">=10" } }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/cheerio": { "version": "1.1.0", "dev": true, @@ -7262,6 +7337,20 @@ "node": ">=6.0" } }, + "node_modules/chromium-bidi": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", + "integrity": "sha512-9gYlLtS6tStdRWzrtXaTMnqcM4dudNegMXJxkR0I/CXObHalYeYcAMPrL19eroNZHtJ8DQmu1E+ZNOYu/IXMXw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^3.24.1" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, "node_modules/ci-info": { "version": "3.9.0", "dev": true, @@ -7848,6 +7937,16 @@ "node": ">= 8" } }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": "*" + } + }, "node_modules/css-declaration-sorter": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-7.3.1.tgz", @@ -8778,6 +8877,13 @@ "dev": true, "license": "MIT" }, + "node_modules/devtools-protocol": { + "version": "0.0.1608973", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1608973.tgz", + "integrity": "sha512-Tpm17fxYzt+J7VrGdc1k8YdRqS3YV7se/M6KeemEqvUbq/n7At1rWVuXMxQgpWkdwSdIEKYbU//Bve+Shm4YNQ==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/diff-sequences": { "version": "29.6.3", "dev": true, @@ -12559,6 +12665,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true, + "license": "MIT" + }, "node_modules/is-builtin-module": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-5.0.0.tgz", @@ -15238,6 +15351,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, "node_modules/mdn-data": { "version": "2.12.2", "dev": true, @@ -17735,6 +17860,25 @@ "node": ">=6" } }, + "node_modules/puppeteer-core": { + "version": "24.43.1", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.43.1.tgz", + "integrity": "sha512-T5ScUMAsmhdNbgDR41AGESYeS6V9MSgetkSnVhhW+gXvzC42VesKCn5ld87gAZDJ6vLHL9GkRvY9WtQWSnwFbw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "2.13.2", + "chromium-bidi": "14.0.0", + "debug": "^4.4.3", + "devtools-protocol": "0.0.1608973", + "typed-query-selector": "^2.12.2", + "webdriver-bidi-protocol": "0.4.1", + "ws": "^8.20.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/pure-rand": { "version": "6.1.0", "dev": true, @@ -21147,6 +21291,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typed-query-selector": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.2.tgz", + "integrity": "sha512-EOPFbyIub4ngnEdqi2yOcNeDLaX/0jcE1JoAXQDDMIthap7FoN795lc/SHfIq2d416VufXpM8z/lD+WRm2gfOQ==", + "dev": true, + "license": "MIT" + }, "node_modules/typedarray": { "version": "0.0.6", "dev": true, @@ -21664,6 +21815,13 @@ "node": ">=18.20.0" } }, + "node_modules/webdriver-bidi-protocol": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.1.tgz", + "integrity": "sha512-ARrjNjtWRRs2w4Tk7nqrf2gBI0QXWuOmMCx2hU+1jUt6d00MjMxURrhxhGbrsoiZKJrhTSTzbIrc554iKI10qw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/webdriver/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -22441,6 +22599,16 @@ "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index fda995324a..b672732db3 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@types/jest": "^29.5.13", "@types/lodash": "^4.14.176", "@types/node": "^20.17.14", + "@types/pixelmatch": "^5.2.6", "@types/react": "^16.14.20", "@types/react-dom": "^16.9.14", "@types/react-is": "^18.2.0", @@ -83,6 +84,7 @@ "@types/react-test-renderer": "^16.9.12", "@types/react-transition-group": "^4.4.4", "@types/webpack-env": "^1.16.3", + "allure-jest": "^3.9.0", "axe-core": "^4.7.2", "babel-jest": "^29.7.0", "change-case": "^4.1.2", @@ -119,6 +121,7 @@ "mockdate": "^3.0.5", "npm-run-all": "^4.1.5", "prettier": "^3.6.1", + "puppeteer-core": "^24.43.1", "react": "^16.14.0", "react-dom": "^16.14.0", "react-dom18": "npm:react-dom@^18.3.1", diff --git a/test/definitions/index.ts b/test/definitions/index.ts deleted file mode 100644 index 91e90a89f7..0000000000 --- a/test/definitions/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Each component has its own test definition file. -// Import them here manually to form the full test suite. -import { TestSuite } from './types'; -import actionCard from './visual/action-card'; -import alert from './visual/alert'; - -export const allSuites: TestSuite[] = [actionCard, alert]; diff --git a/test/definitions/types.ts b/test/definitions/types.ts index 8cdca9996a..b56a5dbc98 100644 --- a/test/definitions/types.ts +++ b/test/definitions/types.ts @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import type { ScreenshotPageObject } from '@cloudscape-design/browser-test-tools/page-objects'; +import type { ElementWrapper } from '../../lib/components/test-utils/selectors'; + export interface ScreenshotTestConfiguration { width?: number; height?: number; @@ -9,7 +11,7 @@ export interface ScreenshotTestConfiguration { // 'screenshotArea' — captures the .screenshot-area element on the page. // 'permutations' — captures the entire page and crops permutations out of it. -export type ScreenshotType = 'screenshotArea' | 'permutations'; +export type ScreenshotType = 'screenshotArea' | 'permutations' | 'viewport'; export interface TestDefinition { description: string; @@ -17,7 +19,7 @@ export interface TestDefinition { screenshotType: ScreenshotType; queryParams?: Record; configuration?: ScreenshotTestConfiguration; - setup?: (page: ScreenshotPageObject) => Promise; + setup?: (page: ScreenshotPageObject, wrapper: ElementWrapper) => Promise; } export interface TestSuite { diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts new file mode 100644 index 0000000000..4b4bf96f57 --- /dev/null +++ b/test/definitions/utils.ts @@ -0,0 +1,198 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { attachment, ContentType } from 'allure-js-commons'; + +import { cropAndCompare, parsePng } from '@cloudscape-design/browser-test-tools/image-utils'; +import { ScreenshotPageObject, ScreenshotWithOffset } from '@cloudscape-design/browser-test-tools/page-objects'; + +import createWrapper from '../../lib/components/test-utils/selectors'; +import { TestDefinition, TestSuite } from './types'; + +const screenshotAreaSelector = '.screenshot-area'; +const defaultWindowSize = { width: 1600, height: 800 }; + +// NEW_HOST serves the PR's pages, OLD_HOST serves the baseline (main) pages. +const newHost = process.env.NEW_HOST || 'http://localhost:8080'; +const oldHost = process.env.OLD_HOST || 'http://localhost:8081'; + +const wrapper = createWrapper(); +interface RawCapture { + /** The raw base64-encoded PNG string from WebDriver (before decoding). */ + rawBase64: string; + /** The fully parsed screenshot with offset metadata (lazily resolved). */ + screenshot: () => Promise; +} + +function buildUrl(host: string, path: string, queryParams?: Record): string { + const params = new URLSearchParams(queryParams); + const qs = params.toString(); + return `${host}/#/${path}${qs ? `?${qs}` : ''}`; +} + +function isTestDefinition(item: TestDefinition | TestSuite): item is TestDefinition { + return (item as TestDefinition).path !== undefined; +} + +/** + * Attaches visual diff images (new, baseline, diff) to the Allure report + * via the allure-js-commons runtime API. + */ +async function attachDiffImages( + result: { firstImage: Buffer; secondImage: Buffer; diffImage: Buffer | null }, + testName: string +): Promise { + await attachment(`${testName} — new (PR)`, result.firstImage, ContentType.PNG); + await attachment(`${testName} — baseline (main)`, result.secondImage, ContentType.PNG); + if (result.diffImage) { + await attachment(`${testName} — diff`, result.diffImage, ContentType.PNG); + } +} + +/** + * Registers all test suites with a single shared browser session per worker. + * This avoids the per-test session creation overhead. + */ +export function runTestSuites(suites: Array) { + let browser: WebdriverIO.Browser; + + beforeAll(async () => { + const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); + const creator = getBrowserCreator('ChromeHeadlessIntegration', 'local', { + seleniumUrl: 'http://localhost:9515', + }); + browser = await creator.getBrowser({ width: defaultWindowSize.width, height: defaultWindowSize.height }); + }); + + afterAll(async () => { + await browser?.deleteSession(); + }); + + registerSuites(suites, () => browser); +} + +function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { + for (const item of suites) { + if (isTestDefinition(item)) { + registerTest(item, getBrowser); + } else { + describe(item.description, () => { + registerSuites(item.tests, getBrowser); + }); + } + } +} + +/** + * Captures a screenshot and returns both the raw PNG base64 and the parsed result. + * Having the raw base64 allows a fast byte-equality check before expensive pixel decoding. + */ +async function captureRaw( + browser: WebdriverIO.Browser, + page: ScreenshotPageObject, + url: string, + testDef: TestDefinition, + windowSize: { width: number; height: number } | undefined +): Promise { + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } + await browser.url(url); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page, wrapper); + } + + if (testDef.screenshotType === 'viewport') { + const { height, width } = await page.getViewportSize(); + const rawBase64 = await browser.takeScreenshot(); + return { + rawBase64, + screenshot: async () => { + const image = await parsePng(rawBase64); + return { image, offset: { top: 0, left: 0 }, height, width }; + }, + }; + } + + // screenshotArea / permutations — capture by selector with viewportOnly + const box = await page.getBoundingBox(screenshotAreaSelector); + const rawBase64 = await browser.takeScreenshot(); + return { + rawBase64, + screenshot: async () => { + const image = await parsePng(rawBase64); + return { image, offset: { top: box.top, left: box.left }, height: box.height, width: box.width }; + }, + }; +} + +function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { + test(testDef.description, async () => { + const browser = getBrowser(); + const windowSize = testDef.configuration ? { ...defaultWindowSize, ...testDef.configuration } : undefined; + const page = new ScreenshotPageObject(browser); + + const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); + const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + + const newCapture = await captureRaw(browser, page, newUrl, testDef, windowSize); + const oldCapture = await captureRaw(browser, page, oldUrl, testDef, windowSize); + + // Fast path: if the raw PNG bytes are identical, the images are guaranteed + // to be the same. This skips the expensive crop + pixelmatch decode path + // for the common case (no visual difference). + if (newCapture.rawBase64 === oldCapture.rawBase64) { + return; + } + + // Raw bytes differ — could be a real diff or just offset/crop differences. + // Fall through to full pixel comparison. + const result = await cropAndCompare(await newCapture.screenshot(), await oldCapture.screenshot()); + + if (result.diffPixels === 0) { + return; + } + + // For permutations pages, a screenshot-area diff might be a false positive + // caused by content extending beyond the viewport. Re-capture using the + // full capturePermutations strategy which resizes the window to fit all + // content and returns individual permutation crops for precise comparison. + if (testDef.screenshotType === 'permutations') { + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } + await browser.url(newUrl); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page, wrapper); + } + const newPermutations = await page.capturePermutations(); + + if (windowSize) { + await browser.setWindowSize(windowSize.width, windowSize.height); + } + await browser.url(oldUrl); + await page.waitForVisible(screenshotAreaSelector); + if (testDef.setup) { + await testDef.setup(page, wrapper); + } + const oldPermutations = await page.capturePermutations(); + + expect(newPermutations.length).toBe(oldPermutations.length); + for (let i = 0; i < newPermutations.length; i++) { + const permResult = await cropAndCompare(newPermutations[i], oldPermutations[i]); + if (permResult.diffPixels !== 0) { + await attachDiffImages(permResult, `${testDef.description} [permutation ${i}]`); + } + expect(permResult.diffPixels).toBe(0); + } + return; + } + + // Attach diff images to Allure report for visual inspection. + await attachDiffImages(result, testDef.description); + + // For screenshotArea and viewport types, the diff is a real failure. + expect(result.diffPixels).toBe(0); + }); +} diff --git a/test/definitions/visual/app-layout-content-paddings.ts b/test/definitions/visual/app-layout-content-paddings.ts new file mode 100644 index 0000000000..e76d055749 --- /dev/null +++ b/test/definitions/visual/app-layout-content-paddings.ts @@ -0,0 +1,30 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Content paddings', + componentName: 'app-layout', + tests: [ + ...(['true', 'false'] as const).flatMap(toolsEnabled => + (['true', 'false'] as const).flatMap(splitPanelEnabled => + (['bottom', 'side'] as const).map(splitPanelPosition => ({ + description: `toolsEnabled=${toolsEnabled} splitPanelEnabled=${splitPanelEnabled} splitPanelPosition=${splitPanelPosition}`, + path: 'app-layout/with-split-panel', + screenshotType: 'viewport' as const, + queryParams: { toolsEnabled, splitPanelEnabled, splitPanelPosition }, + })) + ) + ), + ...[1500, 600].map(width => ({ + description: `with split panel and disabled content paddings - width=${width}`, + path: 'app-layout/disable-paddings-with-split-panel', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { splitPanelOpen: 'true', splitPanelPosition: 'side' }, + })), + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-drawers.ts b/test/definitions/visual/app-layout-drawers.ts new file mode 100644 index 0000000000..5fe2f12b5d --- /dev/null +++ b/test/definitions/visual/app-layout-drawers.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Drawers', + componentName: 'app-layout', + tests: [ + { + description: 'with split panel', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with tooltip on hover', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.hoverElement(wrapper.findAppLayout().findDrawerTriggerById('pro-help').toSelector()); + }, + }, + { + description: 'with custom scrollable drawer content', + path: 'app-layout/with-drawers-scrollable', + screenshotType: 'viewport', + queryParams: { sideNavFill: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findAppLayout().findDrawerTriggerById('chat').toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-flashbar.ts b/test/definitions/visual/app-layout-flashbar.ts new file mode 100644 index 0000000000..5849a5cbf5 --- /dev/null +++ b/test/definitions/visual/app-layout-flashbar.ts @@ -0,0 +1,39 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar', + componentName: 'app-layout', + tests: [true, false].flatMap(disableContentPaddings => + [true, false].flatMap(stickyNotifications => + [true, false].flatMap(stickyTableHeader => + [true, false].map(stackNotifications => ({ + description: `disableContentPaddings: ${disableContentPaddings}, stickyNotifications: ${stickyNotifications}, stickyTableHeader: ${stickyTableHeader}, stackNotifications: ${stackNotifications}`, + path: 'app-layout/with-stacked-notifications-and-table', + screenshotType: 'screenshotArea' as const, + configuration: { width: 1280, height: 900 }, + setup: async (page: import('@cloudscape-design/browser-test-tools/page-objects').ScreenshotPageObject) => { + if (!disableContentPaddings) { + await page.click('[data-id="toggle-content-paddings"]'); + } + if (stickyNotifications) { + await page.click('[data-id="toggle-sticky-notifications"]'); + } + if (!stickyTableHeader) { + await page.click('[data-id="toggle-sticky-table-header"]'); + } + if (!stackNotifications) { + await page.click('[data-id="toggle-stack-items"]'); + } + await page.click('[data-id="add-notification"]'); + await page.click('[data-id="add-notification"]'); + }, + })) + ) + ) + ), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-header.ts b/test/definitions/visual/app-layout-header.ts new file mode 100644 index 0000000000..3a128a266e --- /dev/null +++ b/test/definitions/visual/app-layout-header.ts @@ -0,0 +1,85 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Headers', + componentName: 'app-layout', + tests: [ + // ── Headers ─────────────────────────────────────────────────────────── + { + description: 'Headers', + tests: [600, 1280].flatMap(width => [ + { + description: `alignment with full-page table (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + { + description: `alignment with full-page table in sticky state (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `alignment with full-page table in sticky state with sticky notifications (${width}px)`, + path: 'app-layout/with-table', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { stickyNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `high contrast header variant in landing page (${width}px)`, + path: 'app-layout/landing-page', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), + }, + + // ── High contrast header variant ────────────────────────────────────── + { + description: 'High contrast header variant', + tests: [ + ...[1400, 600].flatMap(width => [ + { + description: `with breadcrumbs and notifications at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', hasContainer: 'true' }, + } as TestDefinition, + { + description: `without overlap at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { disableOverlap: 'true' }, + } as TestDefinition, + { + description: `with content layout at ${width}px`, + path: 'app-layout/high-contrast-header-variant', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + hasContainer: 'true', + hasContentLayout: 'true', + }, + } as TestDefinition, + ]), + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-multi.ts b/test/definitions/visual/app-layout-multi.ts new file mode 100644 index 0000000000..babf9733cf --- /dev/null +++ b/test/definitions/visual/app-layout-multi.ts @@ -0,0 +1,25 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Multiple instances', + componentName: 'app-layout', + tests: [600, 1280].flatMap(width => [ + { + description: `simple (${width}px)`, + path: 'app-layout/multi-layout-simple', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + { + description: `iframe (${width}px)`, + path: 'app-layout/multi-layout-iframe', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1280.ts b/test/definitions/visual/app-layout-responsive-1280.ts new file mode 100644 index 0000000000..d5fe823f54 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1280.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1280); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1400.ts b/test/definitions/visual/app-layout-responsive-1400.ts new file mode 100644 index 0000000000..1cb519005c --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1400.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1400); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-1920.ts b/test/definitions/visual/app-layout-responsive-1920.ts new file mode 100644 index 0000000000..88b8c6caf3 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-1920.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(1920); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-2540.ts b/test/definitions/visual/app-layout-responsive-2540.ts new file mode 100644 index 0000000000..9dd62c00db --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-2540.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(2540); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-600.ts b/test/definitions/visual/app-layout-responsive-600.ts new file mode 100644 index 0000000000..f6fd3665cc --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-600.ts @@ -0,0 +1,7 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { responsiveTests } from './app-layout-responsive-tests'; + +const suite = responsiveTests(600); +export default suite; diff --git a/test/definitions/visual/app-layout-responsive-tests.ts b/test/definitions/visual/app-layout-responsive-tests.ts new file mode 100644 index 0000000000..f0ac91d121 --- /dev/null +++ b/test/definitions/visual/app-layout-responsive-tests.ts @@ -0,0 +1,159 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +/** + * Shared responsive test scenarios for app-layout. Each width gets its own + * definition file and test runner so that Jest sharding can parallelize them. + */ +export function responsiveTests(width: number): TestSuite { + return { + description: `AppLayout responsive width ${width}px`, + componentName: 'app-layout', + tests: [ + { + description: 'default', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'navigation drawer is open', + path: 'app-layout/with-wizard', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Open navigation"]'); + }, + }, + { + description: 'wizard', + path: 'app-layout/with-wizard', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with wizard and table', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with wizard, table, and breadcrumbs', + path: 'app-layout/with-wizard-and-table', + screenshotType: 'viewport', + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true' }, + }, + { + description: 'notifications', + path: 'app-layout/with-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'breadcrumbs', + path: 'app-layout/with-breadcrumbs', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'notifications and breadcrumbs', + path: 'app-layout/with-breadcrumbs-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'dashboard content type', + path: 'app-layout/dashboard-content-type', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'fixed header and footer', + path: 'app-layout/with-fixed-header-footer', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - empty', + path: 'app-layout/legacy-nav-empty', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with content', + path: 'app-layout/legacy-nav-scrollable', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disableBodyScroll - with split panel', + path: 'app-layout/legacy-nav-scrollable-with-split-panel', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disable paddings', + path: 'app-layout/disable-paddings', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'disable paddings with breadcrumbs', + path: 'app-layout/disable-paddings-breadcrumbs', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'sticky notifications', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'sticky notifications scrolled down', + path: 'app-layout/with-sticky-notifications', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.windowScrollTo({ top: 2000 }); + }, + }, + { + description: 'layout without panels', + path: 'app-layout/no-panels', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'layout without panels but with notifications', + path: 'app-layout/no-panels-with-notifications', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with drawers', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with empty drawers', + path: 'app-layout/with-drawers-empty', + screenshotType: 'viewport', + configuration: { width }, + }, + { + description: 'with open drawer', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + }, + }, + ], + }; +} diff --git a/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts new file mode 100644 index 0000000000..6a0b899686 --- /dev/null +++ b/test/definitions/visual/app-layout-sticky-table-header-split-panel.ts @@ -0,0 +1,78 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Sticky header with split panel', + componentName: 'app-layout', + tests: [ + { + description: 'scrolling to bottom with closed split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'scrolling to bottom with closed split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (1 table row)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-1"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky with open split panel (30 table rows)', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.scrollToBottom('html'); + }, + }, + { + description: 'header stays sticky when mounting and unmounting a second table', + path: 'app-layout/with-sticky-table-and-split-panel', + screenshotType: 'viewport', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[data-testid="set-item-count-to-30"]'); + await page.click('aria/Open panel'); + await page.windowScrollTo({ top: 0 }); + await page.click('aria/Close panel'); + await page.scrollToBottom('html'); + }, + }, + // ── Max content width ───────────────────────────────────────────────── + { + description: 'maxContentWidth set to Number.MAX_VALUE', + path: 'app-layout/refresh-content-width', + screenshotType: 'viewport', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('[data-test-id="button_width-number-max_value"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-toolbar.ts b/test/definitions/visual/app-layout-toolbar.ts new file mode 100644 index 0000000000..fb174b0a71 --- /dev/null +++ b/test/definitions/visual/app-layout-toolbar.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toolbar', + componentName: 'app-layout', + tests: [ + { + description: 'multiple nested instances (no breadcrumbs dedup)', + path: 'app-layout-toolbar/multi-layout-with-hidden-instances', + screenshotType: 'viewport', + }, + { + description: 'no toolbar', + path: 'app-layout-toolbar/without-toolbar', + screenshotType: 'viewport', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts new file mode 100644 index 0000000000..b0dacf8bc5 --- /dev/null +++ b/test/definitions/visual/app-layout-z-index.ts @@ -0,0 +1,60 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Z-index', + componentName: 'app-layout', + tests: [ + ...[600, 1280].flatMap(width => [ + { + description: `button dropdown (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'viewport' as const, + configuration: { width }, + setup: async page => { + await page.click('button=Button dropdown'); + await page.click('[data-testid="2"]'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `select (${width}px)`, + path: 'app-layout/with-absolute-components', + screenshotType: 'viewport' as const, + configuration: { width, height: 800 }, + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + await page.windowScrollTo({ top: 300 }); + }, + } as TestDefinition, + { + description: `split-panel and full-page table (${width}px)`, + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width }, + }, + ]), + { + description: 'split-panel and full-page with open navigation (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open navigation"]'); + }, + }, + { + description: 'split-panel and full-page with open tools (600px)', + path: 'app-layout/with-full-page-table-and-split-panel', + screenshotType: 'viewport' as const, + configuration: { width: 600 }, + setup: async page => { + await page.click('button[aria-label="Open tools"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/app-layout.ts b/test/definitions/visual/app-layout.ts new file mode 100644 index 0000000000..683efdcc25 --- /dev/null +++ b/test/definitions/visual/app-layout.ts @@ -0,0 +1,104 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'AppLayout', + componentName: 'app-layout', + tests: [ + { + description: 'no scrollbars at 320px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 320 }, + }, + { + description: 'drawer buttons alignment', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('[aria-label="Open tools"]'); + }, + }, + { + description: 'disable paddings - navigation closed', + path: 'app-layout/disable-paddings', + screenshotType: 'viewport', + configuration: { width: 1280 }, + setup: async page => { + await page.click('[aria-label="Close navigation"]'); + }, + }, + { + description: 'panels stacking on mobile', + path: 'app-layout/all-panels-open', + screenshotType: 'viewport', + configuration: { width: 600 }, + }, + { + description: 'wrapping long words', + path: 'app-layout/text-wrap', + screenshotType: 'viewport', + }, + { + description: 'fill content area', + path: 'app-layout/fill-content-area', + screenshotType: 'viewport', + }, + { + description: 'with tools and drawers', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + queryParams: { hasTools: 'true' }, + }, + { + description: 'with open drawer and open side split panel', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + }, + }, + + // regression for https://github.com/cloudscape-design/components/pull/1612 + { + description: 'with open drawer and open side split panel after resize', + path: 'app-layout/with-drawers', + screenshotType: 'viewport', + configuration: { width: 1500 }, + queryParams: { splitPanelPosition: 'side' }, + setup: async page => { + await page.click('[aria-label="Security trigger button"]'); + await page.click('[aria-label="Open panel"]'); + await page.setWindowSize({ width: 1400, height: 800 }); + }, + }, + + // ── Transitions ─────────────────────────────────────────────────────── + { + description: 'transition from 400px to 1800px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 400, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 1800, height: 400 }); + }, + }, + { + description: 'transition from 1800px to 400px', + path: 'app-layout/default', + screenshotType: 'viewport', + configuration: { width: 1800, height: 400 }, + setup: async page => { + await page.setWindowSize({ width: 400, height: 400 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts new file mode 100644 index 0000000000..cfa0b5e47b --- /dev/null +++ b/test/definitions/visual/area-chart.ts @@ -0,0 +1,136 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_FILTER_TRIGGER = '#linear-latency-chart button'; +const TEST_CHART_TOOLTIP_HEADER = '#linear-latency-chart h2'; + +const suite: TestSuite = { + description: 'Area chart', + componentName: 'area-chart', + tests: [ + { + description: 'permutations', + path: 'area-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'fit-height', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'fit-height, no legend', + path: 'area-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideLegend: 'true' }, + }, + { + description: 'chart plot has a focus outline', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate along X axis highlighting all series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can navigate a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'selects correct series when navigated back from legend', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.keys(['Tab']); + await page.keys(['Tab']); + await page.keys(['ArrowRight']); + await page.keys(['Shift', 'Tab']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover for all data points at a given X coordinate with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'can pin popover for a point in a specific series with keyboard', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowDown']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'area-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('[aria-label="Linear latency chart"]', 200, 50); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/attribute-editor.ts b/test/definitions/visual/attribute-editor.ts new file mode 100644 index 0000000000..33da3fad86 --- /dev/null +++ b/test/definitions/visual/attribute-editor.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Attribute Editor', + componentName: 'attribute-editor', + tests: [360, 768, 992].flatMap(width => [ + { + description: `permutations at ${width}px`, + path: 'attribute-editor/permutations', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + { + description: `customizable-footer at ${width}px`, + path: 'attribute-editor/customizable-footer', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `with long select at ${width}px`, + path: 'attribute-editor/select-with-long-value', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/autosuggest.ts b/test/definitions/visual/autosuggest.ts new file mode 100644 index 0000000000..62910a0ed1 --- /dev/null +++ b/test/definitions/visual/autosuggest.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestDefinition, TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Autosuggest', + componentName: 'autosuggest', + tests: [ + { + description: 'permutations', + path: 'autosuggest/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'permutations for async properties', + path: 'autosuggest/permutations-async', + screenshotType: 'permutations', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Displays options with groups correctly', + path: 'autosuggest/scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Correctly displays dropdown regions', + path: 'autosuggest/regions-scenarios', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('input'); + }, + }, + { + description: 'Long virtual list - navigate to last item', + path: 'autosuggest/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowUp']); + }, + }, + ...[true, false].map( + virtualScroll => + ({ + description: `with custom renderOption (virtualScroll=${virtualScroll})`, + path: 'autosuggest/custom-render-option', + screenshotType: 'screenshotArea' as const, + queryParams: { virtualScroll: String(virtualScroll) }, + setup: async (page, wrapper) => { + await page.click(wrapper.findAutosuggest().findNativeInput().toSelector()); + await page.keys(['ArrowDown']); + }, + }) as TestDefinition + ), + ], +}; + +export default suite; diff --git a/test/definitions/visual/badge.ts b/test/definitions/visual/badge.ts new file mode 100644 index 0000000000..7d405ed843 --- /dev/null +++ b/test/definitions/visual/badge.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Badge', + componentName: 'badge', + tests: [ + { + description: 'permutation page', + path: 'badge/permutations', + screenshotType: 'permutations', + }, + { + description: 'style custom page', + path: 'badge/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/bar-chart.ts b/test/definitions/visual/bar-chart.ts new file mode 100644 index 0000000000..e61b6b36c1 --- /dev/null +++ b/test/definitions/visual/bar-chart.ts @@ -0,0 +1,97 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_TOOLTIP_HEADER = '#chart'; + +const suite: TestSuite = { + description: 'Bar chart', + componentName: 'bar-chart', + tests: [ + { + description: 'Horizontal bars permutations', + path: 'bar-chart/horizontal-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Horizontal stacked bars permutations', + path: 'bar-chart/horizontal-stacked-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Other permutations', + path: 'bar-chart/other-permutations', + screenshotType: 'permutations', + }, + { + description: 'Threshold permutations', + path: 'bar-chart/threshold-permutations', + screenshotType: 'permutations', + }, + { + description: 'Vertical bars permutations', + path: 'bar-chart/vertical-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'Vertical stacked bars permutations', + path: 'bar-chart/vertical-stacked-bars-permutations', + screenshotType: 'permutations', + }, + { + description: 'can navigate series with keyboard', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover with keyboard', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('#chart svg[aria-label="Bar chart"]'); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'wrapping long series title 123', + path: 'bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.scrollToBottom('html'); + await page.click('#focus-target-3'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/box.ts b/test/definitions/visual/box.ts new file mode 100644 index 0000000000..0de10d4318 --- /dev/null +++ b/test/definitions/visual/box.ts @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Box', + componentName: 'box', + tests: [ + { + description: 'variants permutations', + path: 'box/variants', + screenshotType: 'permutations', + }, + { + description: 'margins permutations', + path: 'box/margins', + screenshotType: 'permutations', + }, + { + description: 'paddings permutations', + path: 'box/paddings', + screenshotType: 'permutations', + }, + { + description: 'float and textAlign', + path: 'box/float-align', + screenshotType: 'permutations', + }, + { + description: 'with overrides to layout defaults', + path: 'box/elements-with-extra-defaults', + screenshotType: 'screenshotArea', + }, + { + description: 'with icons in content', + path: 'box/iconography', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/breadcrumb-group.ts b/test/definitions/visual/breadcrumb-group.ts new file mode 100644 index 0000000000..72fbddf96f --- /dev/null +++ b/test/definitions/visual/breadcrumb-group.ts @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'BreadcrumbGroup', + componentName: 'breadcrumb-group', + tests: [ + { + description: 'layout at 300px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 300 }, + }, + { + description: 'layout at 680px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 680 }, + }, + { + description: 'layout at 1200px', + path: 'breadcrumb-group/scenarios', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'dropdown', + path: 'breadcrumb-group/scenarios', + screenshotType: 'viewport', + configuration: { width: 300, height: 1000 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findBreadcrumbGroup('[data-testid="breadcrumbs-6"]').findDropdown().toSelector()); + }, + }, + { + description: 'responsive behavior', + path: 'breadcrumb-group/responsive', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button-dropdown.ts b/test/definitions/visual/button-dropdown.ts new file mode 100644 index 0000000000..3dda4a427c --- /dev/null +++ b/test/definitions/visual/button-dropdown.ts @@ -0,0 +1,395 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ButtonDropdown', + componentName: 'button-dropdown', + tests: [ + { + description: 'ButtonDropdown opening top left at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening top right at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-right'); + }, + }, + { + description: 'ButtonDropdown opening bottom left at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom right at 500', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-right'); + }, + }, + { + description: 'ButtonDropdown opening top left at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening top right at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-top-right'); + }, + }, + { + description: 'ButtonDropdown opening bottom left at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom right at 800', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 800 }, + setup: async page => { + await page.click('.bd-bottom-right'); + }, + }, + { + description: 'ButtonDropdown opening top left, width maximum truncated', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 230, height: 400 }, + setup: async page => { + await page.click('.bd-top-left'); + }, + }, + { + description: 'ButtonDropdown opening bottom left, width maximum truncated', + path: 'button-dropdown/scenarios-positioning', + screenshotType: 'viewport', + configuration: { width: 230, height: 400 }, + setup: async page => { + await page.click('.bd-bottom-left'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top left at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top right at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-top-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom left at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom right at 500', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 500 }, + setup: async page => { + await page.click('.bd-bottom-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top left at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-top-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening top right at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-top-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom left at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-bottom-left'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandable groups opening bottom right at 1200', + path: 'button-dropdown/scenarios-expandable', + screenshotType: 'viewport', + configuration: { width: 1200 }, + setup: async page => { + await page.click('.bd-bottom-right'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown in scrollable container', + path: 'button-dropdown/scenarios-container', + screenshotType: 'viewport', + configuration: { width: 600 }, + setup: async page => { + await page.waitForVisible('#scrollable-container'); + const containerBBox = await page.getBoundingBox('#scrollable-container'); + const buttonBBox = await page.getBoundingBox('#ButtonDropdown button'); + await page.elementScrollTo('#scrollable-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#ButtonDropdown'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing a scroll container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#scroll-container'); + const containerBBox = await page.getBoundingBox('#scroll-container'); + const buttonBBox = await page.getBoundingBox('#button-dropdown-scroll button'); + await page.elementScrollTo('#scroll-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#button-dropdown-scroll'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing a hidden container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#hidden-container'); + await page.click('#button-dropdown-hidden'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown with expandToViewport overflowing an auto container', + path: 'button-dropdown/scenarios-overflow-container', + screenshotType: 'viewport', + configuration: { width: 1000 }, + setup: async page => { + await page.waitForVisible('#auto-container'); + const containerBBox = await page.getBoundingBox('#auto-container'); + const buttonBBox = await page.getBoundingBox('#button-dropdown-auto button'); + await page.elementScrollTo('#auto-container', { + top: (containerBBox.height + buttonBBox.width) / 2, + left: (containerBBox.width + buttonBBox.width) / 2, + }); + await page.click('#button-dropdown-auto'); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown permutations', + path: 'button-dropdown/permutations', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown item element permutations', + path: 'button-dropdown/item-element.permutations', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown main action permutations', + path: 'button-dropdown/permutations-main-action', + screenshotType: 'permutations', + }, + { + description: 'ButtonDropdown dimmed category group at width 500', + path: 'button-dropdown/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#ButtonDropdown8'); + await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); + }, + }, + { + description: 'ButtonDropdown dimmed category group at width 800', + path: 'button-dropdown/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#ButtonDropdown8'); + await page.keys(['ArrowDown', 'ArrowDown', 'Enter']); + }, + }, + { + description: 'ButtonDropdown with disabled reason at width 500', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdown"]'); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'ButtonDropdown with disabled reason at width 800', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdown"]'); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'ButtonDropdown with disabled reason for selectable item at width 500', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdownSelectableItems"]'); + }, + }, + { + description: 'ButtonDropdown with disabled reason for selectable item at width 800', + path: 'button-dropdown/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="buttonDropdownSelectableItems"]'); + }, + }, + { + description: 'ButtonDropdown expandable groups show icons at width 500', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups show icons at width 500', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups show icons at width 800', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="category1"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups show icons at width 800', + path: 'button-dropdown/icon-expandable', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups with custom renderItem width 500', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="group"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups with custom renderItem width 500', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + { + description: 'ButtonDropdown expandable groups with custom renderItem width 1000', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 1000 }, + queryParams: { expandableGroups: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + await page.click('[data-testid="group"]'); + }, + }, + { + description: 'ButtonDropdown non-expandable groups with custom renderItem width 1000', + path: 'button-dropdown/custom-render-item', + screenshotType: 'screenshotArea', + configuration: { width: 1000 }, + queryParams: { expandableGroups: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findButtonDropdown().toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button-group.ts b/test/definitions/visual/button-group.ts new file mode 100644 index 0000000000..ecdbc17d06 --- /dev/null +++ b/test/definitions/visual/button-group.ts @@ -0,0 +1,52 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ButtonGroup', + componentName: 'button-group', + tests: [ + { + description: 'item permutations', + path: 'button-group/item-permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations', + path: 'button-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'shows tooltip when hovering item', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="like"]'); + }, + }, + { + description: 'shows tooltip when hovering menu', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="more-actions"]'); + }, + }, + { + description: 'shows feedback when clicking copy', + path: 'button-group/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="copy"]'); + }, + }, + { + description: 'style custom page', + path: 'button-group/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/button.ts b/test/definitions/visual/button.ts new file mode 100644 index 0000000000..198412c3ec --- /dev/null +++ b/test/definitions/visual/button.ts @@ -0,0 +1,73 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Button', + componentName: 'button', + tests: [ + { + description: 'permutations', + path: 'button/permutations', + screenshotType: 'permutations', + }, + { + description: 'style-permutations', + path: 'button/style-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom-types', + path: 'button/style-custom-types', + screenshotType: 'screenshotArea', + }, + { + description: 'external', + path: 'button/external.permutations', + screenshotType: 'permutations', + }, + { + description: 'alignment', + path: 'button/alignment', + screenshotType: 'screenshotArea', + }, + { + description: 'wrapping text', + path: 'button/text-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'wrapping text with icon', + path: 'button/with-icon-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'Button is focused', + path: 'button/tab-navigation', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focusButton'); + await page.focusNextElement(); + }, + }, + { + description: 'shows disabled reason tooltip on hover within modal', + path: 'button/disabled-reason-modal', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="button"]'); + }, + }, + { + description: 'shows disabled reason tooltip on hover over a button with an href', + path: 'button/disabled-reason', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[data-testid="normal-button-with-href"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/cards.ts b/test/definitions/visual/cards.ts new file mode 100644 index 0000000000..25227eedb1 --- /dev/null +++ b/test/definitions/visual/cards.ts @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Cards', + componentName: 'cards', + tests: [ + { + description: 'permutations at 2200', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 2200 }, + }, + { + description: 'permutations at 1920', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1920 }, + }, + { + description: 'permutations at 1400', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1400 }, + }, + { + description: 'permutations at 1200', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'permutations at 992', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 992 }, + }, + { + description: 'permutations at 768', + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width: 768 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/checkbox.ts b/test/definitions/visual/checkbox.ts new file mode 100644 index 0000000000..34e92eff2b --- /dev/null +++ b/test/definitions/visual/checkbox.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Checkbox', + componentName: 'checkbox', + tests: [ + { + description: 'Permutations', + path: 'checkbox/permutations', + screenshotType: 'permutations', + }, + { + description: 'Checkbox is focused', + path: 'checkbox/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Checkbox has label with a correct width', + path: 'checkbox/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Style custom page', + path: 'checkbox/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts new file mode 100644 index 0000000000..051ae5bd92 --- /dev/null +++ b/test/definitions/visual/code-editor.ts @@ -0,0 +1,68 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; + +const suite: TestSuite = { + description: 'Code editor', + componentName: 'code-editor', + tests: [ + { + description: 'simple', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + { + description: 'error', + path: 'code-editor/error', + screenshotType: 'screenshotArea', + }, + { + description: 'loading', + path: 'code-editor/loading', + screenshotType: 'screenshotArea', + }, + { + description: 'theme resolution', + path: 'code-editor/themes', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + { + description: 'permutations', + path: 'code-editor/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); + await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error'); + }, + }, + { + description: 'listens to mode change', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + await page.click('#mode-toggle'); + }, + }, + { + description: 'compare simple on small screen', + path: 'code-editor/simple', + screenshotType: 'screenshotArea', + configuration: { width: 360 }, + setup: async page => { + await page.waitForVisible(ACE_SELECTOR); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts new file mode 100644 index 0000000000..3448633608 --- /dev/null +++ b/test/definitions/visual/collection-preferences.ts @@ -0,0 +1,82 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'CollectionPreferences', + componentName: 'collection-preferences', + tests: [ + { + description: 'complete at 600x1100', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + configuration: { width: 600, height: 1100 }, + setup: async page => { + await page.click('.cp-1 button'); + }, + }, + { + description: 'visible content only at 600x1100', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-4 button'); + }, + }, + { + description: 'complete at 1280x700', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + configuration: { width: 1280, height: 700 }, + setup: async page => { + await page.click('.cp-1 button'); + }, + }, + { + description: 'visible content only at 1280x700', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-4 button'); + }, + }, + { + description: 'custom', + path: 'collection-preferences/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('.cp-2 button'); + }, + }, + { + description: 'Content reordering', + componentName: 'collection-preferences', + tests: [ + { + description: 'drag handle focused', + path: 'collection-preferences/reorder-content', + screenshotType: 'viewport', + configuration: { width: 900, height: 650 }, + setup: async page => { + await page.click('.cp-1 button'); + await page.keys(Array(5).fill('Tab')); + }, + }, + { + description: 'reordering active', + path: 'collection-preferences/reorder-content', + screenshotType: 'viewport', + configuration: { width: 900, height: 650 }, + setup: async page => { + await page.click('.cp-1 button'); + await page.keys(Array(5).fill('Tab')); + await page.keys('Space'); + }, + }, + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/column-layout.ts b/test/definitions/visual/column-layout.ts new file mode 100644 index 0000000000..a889050b9b --- /dev/null +++ b/test/definitions/visual/column-layout.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ColumnLayout', + componentName: 'column-layout', + tests: [ + { + description: 'column-layout at "default"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'permutations at "default"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'column-layout at "xxs"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 500 }, + }, + { + description: 'permutations at "xxs"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 500 }, + }, + { + description: 'column-layout at "xs"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'permutations at "xs"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'column-layout at "m"', + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'permutations at "m"', + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/container-sticky.ts b/test/definitions/visual/container-sticky.ts new file mode 100644 index 0000000000..ef67bd3d09 --- /dev/null +++ b/test/definitions/visual/container-sticky.ts @@ -0,0 +1,125 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Container sticky permutations', + componentName: 'container', + tests: [ + { + description: 'simple - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with notifications - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with breadcrumbs - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 1400px without scroll', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + }, + { + description: 'with high-contrast header - at 1400px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 1400 }, + queryParams: { hasNotifications: 'true', highContrast: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'simple - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with notifications - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with breadcrumbs - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: 'with an alert - at 600px without scroll', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + }, + { + description: 'with high-contrast header - at 600px', + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width: 600 }, + queryParams: { hasNotifications: 'true', highContrast: 'true' }, + setup: async page => { + await page.windowScrollTo({ top: 200 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/container.ts b/test/definitions/visual/container.ts new file mode 100644 index 0000000000..a0ac94e8d0 --- /dev/null +++ b/test/definitions/visual/container.ts @@ -0,0 +1,87 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Container and header', + componentName: 'container', + tests: [ + { + description: 'simple', + path: 'container/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'fit height with footer', + path: 'container/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit height without footer', + path: 'container/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFooters: 'true' }, + }, + { + description: 'correctly displays container with side media', + path: 'container/media', + screenshotType: 'screenshotArea', + queryParams: { position: 'side', width: '33%', content: '16-9' }, + }, + { + description: 'correctly displays container with top media', + path: 'container/media', + screenshotType: 'screenshotArea', + queryParams: { position: 'top', height: '150px', content: '4-3' }, + }, + { + description: 'media with position: side permutations at 465', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 465 }, + }, + { + description: 'media with position: side permutations at 688', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 688 }, + }, + { + description: 'media with position: side permutations at 1120', + path: 'container/media-side-permutations', + screenshotType: 'permutations', + configuration: { width: 1120 }, + }, + { + description: 'media with position: top permutations at 465', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 465 }, + }, + { + description: 'media with position: top permutations at 688', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 688 }, + }, + { + description: 'media with position: top permutations at 1120', + path: 'container/media-top-permutations', + screenshotType: 'permutations', + configuration: { width: 1120 }, + }, + { + description: 'stacked', + path: 'container/stacked-components', + screenshotType: 'screenshotArea', + }, + { + description: 'style-custom', + path: 'container/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/content-layout-permutations.ts b/test/definitions/visual/content-layout-permutations.ts new file mode 100644 index 0000000000..4f58d1a70d --- /dev/null +++ b/test/definitions/visual/content-layout-permutations.ts @@ -0,0 +1,661 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ContentLayout permutations', + componentName: 'content-layout', + tests: [ + // default headerVariant + { + description: 'default headerVariant, none headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 600, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, at 600, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'default headerVariant, none headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // default headerVariant, gradient + { + description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'default headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'default headerVariant, gradient headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'gradient', + maxContentWidth: '1000', + }, + }, + // high-contrast headerVariant, none + { + description: 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: + 'high-contrast headerVariant, none headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'high-contrast headerVariant, none headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // high-contrast headerVariant, gradient + { + description: 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: + 'high-contrast headerVariant, gradient headerBackgroundStyle, at 1400, with notifications and breadcrumbs', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + }, + }, + { + description: 'high-contrast headerVariant, gradient headerBackgroundStyle, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + maxContentWidth: '1000', + }, + }, + // divider headerVariant + { + description: 'divider headerVariant, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'divider headerVariant, at 1400, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + { + description: 'divider headerVariant, at 600, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'divider headerVariant, at 600, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '1000', + }, + }, + // with app layout + { + description: 'with app layout, divider headerVariant, at 1400, simple', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with app layout, high-contrast gradient, at 1400, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + maxContentWidth: '800', + }, + }, + { + description: 'with open navigation, without maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with open navigation, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + maxContentWidth: '700', + }, + }, + // with secondary header + { + description: 'with secondary header, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with secondary header, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with secondary header, with maxContentWidth', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + maxContentWidth: '900', + }, + }, + // without header + { + description: 'without header, at 1400, defaultPadding=true, default headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=true, high-contrast headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=true, divider headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'true', + headerVariant: 'divider', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without header, at 1400, defaultPadding=false, default headerVariant', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'true', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + // without default padding + { + description: 'without default padding, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'without default padding, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'true', + defaultPadding: 'false', + headerVariant: 'default', + headerBackgroundStyle: 'none', + }, + }, + // with disabled overlap + { + description: 'with disabled overlap, at 1400', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + { + description: 'with disabled overlap, at 600', + path: 'content-layout/permutations', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + hasContainer: 'false', + defaultPadding: 'true', + headerVariant: 'high-contrast', + headerBackgroundStyle: 'none', + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/content-layout.ts b/test/definitions/visual/content-layout.ts new file mode 100644 index 0000000000..4b387f5d70 --- /dev/null +++ b/test/definitions/visual/content-layout.ts @@ -0,0 +1,98 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ContentLayout', + componentName: 'content-layout', + tests: [ + { + description: 'fill content area', + path: 'content-layout/fill-content-area', + screenshotType: 'screenshotArea', + }, + { + description: 'standalone', + path: 'content-layout/standalone', + screenshotType: 'screenshotArea', + }, + { + description: 'with absolute components', + path: 'content-layout/with-absolute-components', + screenshotType: 'screenshotArea', + }, + { + description: 'form with form header', + path: 'content-layout/with-header-toggles', + screenshotType: 'screenshotArea', + }, + { + description: 'form with content layout header', + path: 'content-layout/with-header-toggles', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="toggle-form-header"] input'); + await page.click('[data-testid="toggle-content-layout"] input'); + }, + }, + { + description: 'without header - at 1400 without breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 without breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'true', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, + }, + { + description: 'without header - at 1400 without breadcrumbs, without notifications, without overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'true' }, + }, + { + description: 'without header - at 1400 with breadcrumbs, with notifications, without overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'true' }, + }, + { + description: 'without header - at 600 without breadcrumbs, without notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'false', hasNotifications: 'false', disableOverlap: 'false' }, + }, + { + description: 'without header - at 600 with breadcrumbs, with notifications, with overlap', + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + queryParams: { hasBreadcrumbs: 'true', hasNotifications: 'true', disableOverlap: 'false' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts new file mode 100644 index 0000000000..64f967f0b7 --- /dev/null +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'CopyToClipboard', + componentName: 'copy-to-clipboard', + tests: [ + { + description: 'Variants', + path: 'copy-to-clipboard/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'copy-to-clipboard below bottom split panel is not visible', + path: 'copy-to-clipboard/scenario-split-panel', + screenshotType: 'screenshotArea', + configuration: { width: 1280, height: 900 }, + setup: async page => { + await page.click('[aria-label="Copy dummy text"]'); + await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-input.ts b/test/definitions/visual/date-input.ts new file mode 100644 index 0000000000..431b274b22 --- /dev/null +++ b/test/definitions/visual/date-input.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date input', + componentName: 'date-input', + tests: [ + { + description: 'Permutations: states', + path: 'date-input/permutations-states', + screenshotType: 'permutations', + }, + { + description: 'Permutations: formats', + path: 'date-input/permutations-formats', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-picker.ts b/test/definitions/visual/date-picker.ts new file mode 100644 index 0000000000..901c35b469 --- /dev/null +++ b/test/definitions/visual/date-picker.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date picker', + componentName: 'date-picker', + tests: [ + { + description: 'Permutations: states', + path: 'date-picker/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.click('[data-testid="date-picker-expanded-example"] button'); + }, + }, + { + description: 'Permutations: formats', + path: 'date-picker/permutations-formats', + screenshotType: 'permutations', + }, + { + description: 'Month picker', + componentName: 'date-picker', + tests: [ + { + description: 'focus ring on selected month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + }, + }, + { + description: 'focus ring on current month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + await page.keys(['ArrowRight']); + }, + }, + { + description: 'focus ring on non selected, non current month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); + await page.keys(['ArrowRight', 'ArrowRight']); + }, + }, + ], + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts new file mode 100644 index 0000000000..26aa0d2c01 --- /dev/null +++ b/test/definitions/visual/date-range-picker.ts @@ -0,0 +1,121 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Date Range Picker', + componentName: 'date-range-picker', + tests: [ + { + description: 'Absolute range at 450px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 450, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.click('[data-date="2018-01-09"]'); + await page.click('[data-date="2018-01-27"]'); + }, + }, + { + description: 'Absolute range at 1200px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 1200, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.click('[data-date="2018-01-09"]'); + await page.click('[data-date="2018-02-24"]'); + }, + }, + { + description: 'Absolute range input permutations for day granularity', + path: 'date-range-picker/absolute-format-day-picker.permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'Absolute range input permutations for month granularity', + path: 'date-range-picker/absolute-format-month-picker.permutations', + screenshotType: 'permutations', + }, + { + description: 'Relative range at 450px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 450, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['Space']); + await page.keys(['ArrowUp']); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'Relative range at 1200px', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + configuration: { width: 1200, height: 950 }, + setup: async page => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowLeft']); + await page.keys(['Enter']); + await page.focusNextElement(); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + { + description: 'Calendar permutations for day granularity', + path: 'date-range-picker/month-calendar-permutations', + screenshotType: 'permutations', + }, + { + description: 'Calendar permutations for month granularity', + path: 'date-range-picker/year-calendar-permutations', + screenshotType: 'permutations', + }, + { + description: 'selects text when double-clicking calendar header', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const firstCalendarHeaderSelector = wrapper + .findDateRangePicker() + .findDropdown() + .findHeader() + .find('h2 span') + .toSelector(); + await (page as any).doubleClick(firstCalendarHeaderSelector); + }, + }, + { + description: 'does not select text when double-clicking next button', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const nextButtonSelector = wrapper.findDateRangePicker().findDropdown().findNextMonthButton().toSelector(); + await (page as any).doubleClick(nextButtonSelector); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/divider.ts b/test/definitions/visual/divider.ts new file mode 100644 index 0000000000..8dbed83984 --- /dev/null +++ b/test/definitions/visual/divider.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Divider', + componentName: 'divider', + tests: [ + { + description: 'permutations', + path: 'divider/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/drawer.ts b/test/definitions/visual/drawer.ts new file mode 100644 index 0000000000..33fdb8d896 --- /dev/null +++ b/test/definitions/visual/drawer.ts @@ -0,0 +1,105 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Drawer', + componentName: 'drawer', + tests: [ + { + description: 'permutations', + path: 'drawer/permutations', + screenshotType: 'permutations', + }, + { + description: 'short content, with footer, short footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'false', longFooter: 'false', hasFooter: 'true' }, + }, + { + description: 'short content, with footer, long footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'false', longFooter: 'true', hasFooter: 'true' }, + }, + { + description: 'long content, with footer, short footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'true', longFooter: 'false', hasFooter: 'true' }, + }, + { + description: 'long content, with footer, long footer', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + queryParams: { longContent: 'true', longFooter: 'true', hasFooter: 'true' }, + }, + { + description: 'Drawer with small view height', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + configuration: { width: 1280, height: 268 }, + queryParams: { longContent: 'true', hasFooter: 'true', longFooter: 'true' }, + }, + { + description: 'Drawer footer with small view height', + path: 'app-layout/drawer-with-footer', + screenshotType: 'viewport', + configuration: { width: 1280, height: 268 }, + queryParams: { longContent: 'true', hasFooter: 'true' }, + setup: async (page, wrapper) => { + await (page as any).scrollIntoView(wrapper.findDrawer().findFooter().toSelector()); + }, + }, + { + description: 'Drawer with absolute position', + path: 'drawer/drawer-position-absolute', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with absolute position and backdrops', + path: 'drawer/drawer-position-absolute', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { backdrops: 'start,end' }, + }, + { + description: 'Drawer with sticky position', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with sticky position and offsets', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { offsets: 'true' }, + }, + { + description: 'Drawer with sticky position and sticky offsets', + path: 'drawer/drawer-position-sticky', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { stickyOffsets: 'true' }, + }, + { + description: 'Drawer with fixed position', + path: 'drawer/drawer-position-fixed', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + }, + { + description: 'Drawer with fixed position, offsets and backdrop', + path: 'drawer/drawer-position-fixed', + screenshotType: 'viewport', + configuration: { width: 1200, height: 1000 }, + queryParams: { offsets: 'true', backdrop: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/dropdown.ts b/test/definitions/visual/dropdown.ts new file mode 100644 index 0000000000..8d17b837ca --- /dev/null +++ b/test/definitions/visual/dropdown.ts @@ -0,0 +1,83 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Dropdown', + componentName: 'dropdown', + tests: [ + { + description: 'In fixed container', + path: 'dropdown/fixed-container', + screenshotType: 'viewport', + setup: async page => { + const { height: windowHeight } = await page.getViewportSize(); + await page.windowScrollTo({ top: windowHeight }); + await page.click('button=Open dropdown'); + }, + }, + { + description: 'positions select inside modal, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#show-modal'); + await page.click('#in-modal'); + }, + }, + { + description: 'positions select inside modal, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#show-modal'); + await page.click('#in-modal'); + }, + }, + { + description: 'positions select inside popover, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#show-popover'); + await page.click('#in-popover'); + await page.click('[data-test-index="5"]'); + }, + }, + { + description: 'positions select inside popover, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#show-popover'); + await page.click('#in-popover'); + await page.click('[data-test-index="5"]'); + }, + }, + { + description: 'select has bottom borders when opened upwards, expandToViewport=false', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'false' }, + setup: async page => { + await page.click('#bottom-left'); + }, + }, + { + description: 'select has bottom borders when opened upwards, expandToViewport=true', + path: 'dropdown/expandable', + screenshotType: 'viewport', + queryParams: { componentType: 'Select', expandToViewport: 'true' }, + setup: async page => { + await page.click('#bottom-left'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/expandable-section.ts b/test/definitions/visual/expandable-section.ts new file mode 100644 index 0000000000..310a8d1752 --- /dev/null +++ b/test/definitions/visual/expandable-section.ts @@ -0,0 +1,124 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Expandable section', + componentName: 'expandable-section', + tests: [ + { + description: 'permutations', + path: 'expandable-section/permutations', + screenshotType: 'permutations', + }, + { + description: 'container variant', + path: 'expandable-section/container-variant.permutations', + screenshotType: 'permutations', + }, + { + description: 'focus - container variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - default variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'default' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - footer variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'footer' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - navigation variant with only heading', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', variant: 'navigation' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - default variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'default' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - footer variant with heading and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', headerDescription: 'Header description', variant: 'footer' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with interactive elements', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { headerText: 'Header text', hasHeaderInfo: 'true', hasHeaderActions: 'true', variant: 'container' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - container variant with interactive elements and description', + path: 'expandable-section/focus', + screenshotType: 'screenshotArea', + queryParams: { + headerText: 'Header text', + headerDescription: 'Header description', + hasHeaderInfo: 'true', + hasHeaderActions: 'true', + variant: 'container', + }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'stacked variant', + path: 'expandable-section/stacked-variant.permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-dropzone.ts b/test/definitions/visual/file-dropzone.ts new file mode 100644 index 0000000000..5c845fa1ac --- /dev/null +++ b/test/definitions/visual/file-dropzone.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File dropzone', + componentName: 'file-dropzone', + tests: [ + { + description: 'In container', + path: 'file-dropzone/container', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-input.ts b/test/definitions/visual/file-input.ts new file mode 100644 index 0000000000..c2d958d9c9 --- /dev/null +++ b/test/definitions/visual/file-input.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File input', + componentName: 'file-input', + tests: [ + { + description: 'Simple', + path: 'file-input/simple', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-token-group.ts b/test/definitions/visual/file-token-group.ts new file mode 100644 index 0000000000..8aa68908d0 --- /dev/null +++ b/test/definitions/visual/file-token-group.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'File token group', + componentName: 'file-token-group', + tests: [ + { + description: 'Permutations', + path: 'file-token-group/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/file-upload.ts b/test/definitions/visual/file-upload.ts new file mode 100644 index 0000000000..7426504608 --- /dev/null +++ b/test/definitions/visual/file-upload.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'FileUpload', + componentName: 'file-upload', + tests: [ + { + description: 'Permutations', + path: 'file-upload/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts new file mode 100644 index 0000000000..7878754974 --- /dev/null +++ b/test/definitions/visual/flashbar-stacked.ts @@ -0,0 +1,125 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar stacked notifications', + componentName: 'flashbar', + tests: [ + { + description: '380px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + }, + { + description: '380px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '380px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 380 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '450px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + }, + { + description: '450px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '450px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 450 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '600px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + { + description: '600px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '600px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + { + description: '1200px, collapsed', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: '1200px, collapsed, notifications bar button focused', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: '1200px, expanded', + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await new Promise(resolve => setTimeout(resolve, 500)); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts new file mode 100644 index 0000000000..61ce21ea16 --- /dev/null +++ b/test/definitions/visual/flashbar.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Flashbar', + componentName: 'flashbar', + tests: [ + { + description: 'permutations at 600', + path: 'flashbar/permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'permutations at 1280', + path: 'flashbar/permutations', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'runtime-action at 600', + path: 'flashbar/runtime-action', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'runtime-action at 1280', + path: 'flashbar/runtime-action', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'content permutations', + path: 'flashbar/content-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom', + path: 'flashbar/style-custom', + screenshotType: 'screenshotArea', + }, + { + description: 'small screen button layout', + path: 'flashbar/small-screen', + screenshotType: 'screenshotArea', + configuration: { width: 550 }, + }, + { + description: 'stacking of multiple flashbar items', + path: 'flashbar/stacking', + screenshotType: 'screenshotArea', + }, + { + description: 'focus border color', + path: 'flashbar/dismissal', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/form-field.ts b/test/definitions/visual/form-field.ts new file mode 100644 index 0000000000..59a207a24a --- /dev/null +++ b/test/definitions/visual/form-field.ts @@ -0,0 +1,61 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'FormField', + componentName: 'form-field', + tests: [ + { + description: 'Permutations at 576', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 576 }, + }, + { + description: 'Scenarios at 576', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 576 }, + }, + { + description: 'Permutations at 768', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 768 }, + }, + { + description: 'Scenarios at 768', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 768 }, + }, + { + description: 'Permutations at 992', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 992 }, + }, + { + description: 'Scenarios at 992', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 992 }, + }, + { + description: 'Permutations at 1200', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'Scenarios at 1200', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts new file mode 100644 index 0000000000..3a61c6e5a6 --- /dev/null +++ b/test/definitions/visual/form.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Form', + componentName: 'form', + tests: [ + { + description: 'permutations', + path: 'form/permutations', + screenshotType: 'permutations', + }, + { + description: 'simple', + path: 'form/simple', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/grid.ts b/test/definitions/visual/grid.ts new file mode 100644 index 0000000000..299f2bb6e8 --- /dev/null +++ b/test/definitions/visual/grid.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Grid', + componentName: 'grid', + tests: [ + { + description: 'grid at "default"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'grid at "xs"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'grid at "m"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + }, + { + description: 'grid at "l"', + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width: 1400 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/header.ts b/test/definitions/visual/header.ts new file mode 100644 index 0000000000..9643607963 --- /dev/null +++ b/test/definitions/visual/header.ts @@ -0,0 +1,67 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Header', + componentName: 'header', + tests: [ + { + description: 'level-1 at 1500px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-1 at 850px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-1 at 400px', + path: 'header/level-1', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'level-2 at 1500px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-2 at 850px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-2 at 400px', + path: 'header/level-2', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'level-3 at 1500px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 1500 }, + }, + { + description: 'level-3 at 850px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 850 }, + }, + { + description: 'level-3 at 400px', + path: 'header/level-3', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/help-panel.ts b/test/definitions/visual/help-panel.ts new file mode 100644 index 0000000000..69f40a05c5 --- /dev/null +++ b/test/definitions/visual/help-panel.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'HelpPanel', + componentName: 'help-panel', + tests: [ + { + description: 'permutations', + path: 'help-panel/permutations', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + { + description: 'with AppLayout', + path: 'help-panel/with-app-layout', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + { + description: 'loading state - with AppLayout', + path: 'help-panel/loading-with-app-layout', + screenshotType: 'screenshotArea', + setup: async page => { + await (page as any).focusInputs(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/icon.ts b/test/definitions/visual/icon.ts new file mode 100644 index 0000000000..bd8b064b35 --- /dev/null +++ b/test/definitions/visual/icon.ts @@ -0,0 +1,69 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Icon', + componentName: 'icon', + tests: [ + { + description: 'Alignment with text', + path: 'icon/text-align', + screenshotType: 'screenshotArea', + configuration: { width: 300 }, + }, + { + description: 'Icons in normal variant', + path: 'icon/variant-normal', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in disabled variant', + path: 'icon/variant-disabled', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in error variant', + path: 'icon/variant-error', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in inverted variant', + path: 'icon/variant-inverted', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in subtle variant', + path: 'icon/variant-subtle', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in success variant', + path: 'icon/variant-success', + screenshotType: 'screenshotArea', + }, + { + description: 'Icons in warning variant', + path: 'icon/variant-warning', + screenshotType: 'screenshotArea', + }, + { + description: 'Custom icon', + path: 'icon/custom-icon', + screenshotType: 'screenshotArea', + }, + { + description: 'Custom svg icon', + path: 'icon/custom-svg', + screenshotType: 'screenshotArea', + }, + { + description: 'Inherit size property', + path: 'icon/size-inherit', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/input.ts b/test/definitions/visual/input.ts new file mode 100644 index 0000000000..81c0145e88 --- /dev/null +++ b/test/definitions/visual/input.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Input', + componentName: 'input', + tests: [ + { + description: 'permutations', + path: 'input/permutations', + screenshotType: 'permutations', + }, + { + description: 'style-permutations', + path: 'input/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/item-card.ts b/test/definitions/visual/item-card.ts new file mode 100644 index 0000000000..bcf0439635 --- /dev/null +++ b/test/definitions/visual/item-card.ts @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Item card', + componentName: 'item-card', + tests: [ + { + description: 'permutations', + path: 'item-card/permutations', + screenshotType: 'permutations', + }, + { + description: 'padding permutations', + path: 'item-card/padding-permutations', + screenshotType: 'permutations', + }, + { + description: 'variant permutations', + path: 'item-card/variant-permutations', + screenshotType: 'permutations', + }, + { + description: 'style-custom', + path: 'item-card/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/key-value-pairs.ts b/test/definitions/visual/key-value-pairs.ts new file mode 100644 index 0000000000..89dc9ec268 --- /dev/null +++ b/test/definitions/visual/key-value-pairs.ts @@ -0,0 +1,29 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Key-value pairs', + componentName: 'key-value-pairs', + tests: [ + { + description: 'permutations', + path: 'key-value-pairs/permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations on mobile (600px)', + path: 'key-value-pairs/permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'wrapping text', + path: 'key-value-pairs/text-wrap', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/line-chart.ts b/test/definitions/visual/line-chart.ts new file mode 100644 index 0000000000..3b4d56bb09 --- /dev/null +++ b/test/definitions/visual/line-chart.ts @@ -0,0 +1,156 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_FILTER_TRIGGER = '#chart button'; +const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; + +const suite: TestSuite = { + description: 'Line chart', + componentName: 'line-chart', + tests: [ + { + description: 'permutations', + path: 'line-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'can highlight all data points at a given X coordinate with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can navigate series with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover for all data points at a given X coordinate with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'can pin popover for a point in a specific series with keyboard', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click(TEST_CHART_FILTER_TRIGGER); + await page.keys(['Escape']); + await page.focusNextElement(); + await page.keys(['ArrowRight', 'ArrowDown', 'ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'line-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('[aria-label="Line chart"]', 200, 50); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'shows popover with expandable sub-items - no series highlighted, sub-items collapsed', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.waitForVisible('[class*="detail-popover"]'); + }, + }, + { + description: 'shows popover with expandable sub-items - no series highlighted, sub-items expanded', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.waitForVisible('[class*="detail-popover"]'); + await page.keys(['Tab']); + await page.keys(['Enter']); + }, + }, + { + description: 'shows popover with expandable sub-items - one series highlighted, sub-items collapsed', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowUp']); + await page.waitForVisible('[class*="detail-popover"]'); + }, + }, + { + description: 'shows popover with expandable sub-items - one series highlighted, sub-items expanded', + path: 'line-chart/drilldown', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 1000 }, + queryParams: { expandableSubItems: 'true' }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowUp']); + await page.waitForVisible('[class*="detail-popover"]'); + await page.keys(['Tab']); + await page.keys(['Enter']); + }, + }, + { + description: 'correctly renders the chart inside an expandable section - X ticks do not overlap nor overflow', + path: 'line-chart/in-expandable-section-test', + screenshotType: 'screenshotArea', + configuration: { width: 800, height: 800 }, + setup: async (page, wrapper) => { + const expandableSectionWrapper = wrapper.findExpandableSection(); + await page.waitForVisible(expandableSectionWrapper.toSelector()); + await page.click(expandableSectionWrapper.findExpandButton().toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/link.ts b/test/definitions/visual/link.ts new file mode 100644 index 0000000000..c381876e14 --- /dev/null +++ b/test/definitions/visual/link.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Link', + componentName: 'link', + tests: [ + { + description: 'Permutations', + path: 'link/permutations', + screenshotType: 'permutations', + }, + { + description: 'Permutations (long label)', + path: 'link/long-label-permutations', + screenshotType: 'permutations', + }, + { + description: 'Icon overflow permutations', + path: 'link/icon-overflow-permutations', + screenshotType: 'permutations', + }, + { + description: 'Inherit font size permutations', + path: 'link/inherit-permutations', + screenshotType: 'permutations', + }, + { + description: 'Style custom page', + path: 'link/style-custom-types', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/list.ts b/test/definitions/visual/list.ts new file mode 100644 index 0000000000..9dbe349f8c --- /dev/null +++ b/test/definitions/visual/list.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'List', + componentName: 'list', + tests: [ + { + description: 'permutations', + path: 'list/permutations', + screenshotType: 'permutations', + }, + { + description: 'sortable-permutations', + path: 'list/sortable-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/mixed-line-bar-chart.ts b/test/definitions/visual/mixed-line-bar-chart.ts new file mode 100644 index 0000000000..5c7370a979 --- /dev/null +++ b/test/definitions/visual/mixed-line-bar-chart.ts @@ -0,0 +1,108 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const TEST_CHART_TOOLTIP_HEADER = '#chart h2'; + +const suite: TestSuite = { + description: 'Mixed line bar chart', + componentName: 'mixed-line-bar-chart', + tests: [ + { + description: 'permutations', + path: 'mixed-line-bar-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'fit-height', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'fit-height, no legend', + path: 'mixed-line-bar-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideLegend: 'true' }, + }, + { + description: 'chart plot has a focus outline', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate series with keyboard', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'can pin popover with keyboard', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.keys(['ArrowRight']); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + await page.keys(['Enter']); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + { + description: 'shows popover on hover', + path: 'mixed-line-bar-chart/test', + screenshotType: 'viewport', + configuration: { width: 800, height: 800 }, + setup: async page => { + await page.hoverElement('#chart svg[aria-label="Mixed chart 1"]', 200, 100); + await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); + }, + }, + { + description: 'handles long left-labels at width 320px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 320 }, + }, + { + description: 'handles long left-labels at width 400px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 400 }, + }, + { + description: 'handles long left-labels at width 600px', + path: 'mixed-line-bar-chart/with-long-left-labels', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/modal.ts b/test/definitions/visual/modal.ts new file mode 100644 index 0000000000..49c4092c33 --- /dev/null +++ b/test/definitions/visual/modal.ts @@ -0,0 +1,163 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Modal', + componentName: 'modal', + tests: [ + { + description: 'simple', + path: 'modal/simple', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'no-paddings', + path: 'modal/no-paddings', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'vertical-scroll', + path: 'modal/vertical-scroll', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'long-header', + path: 'modal/long-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'unbreakable-header', + path: 'modal/unbreakable-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'size-small', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/small'); + }, + }, + { + description: 'size-medium', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/medium'); + }, + }, + { + description: 'size-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/large'); + }, + }, + { + description: 'size-x-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/x-large'); + }, + }, + { + description: 'size-xx-large', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/xx-large'); + }, + }, + { + description: 'size-max', + path: 'modal/sizes', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/max'); + }, + }, + { + description: 'position-top', + path: 'modal/position-top', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + }, + }, + { + description: 'custom-dimensions with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '600', height: '400', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions without footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '600', height: '400', footer: 'false' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small width', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small height with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { height: '10', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions very small height & width with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10', height: '15', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + { + description: 'custom-dimensions large height & width with footer', + path: 'modal/custom-dimensions', + screenshotType: 'viewport', + queryParams: { width: '10000', height: '10000', footer: 'true' }, + setup: async page => { + await page.click('[data-testid="modal-trigger"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/multiselect.ts b/test/definitions/visual/multiselect.ts new file mode 100644 index 0000000000..45a022e5e5 --- /dev/null +++ b/test/definitions/visual/multiselect.ts @@ -0,0 +1,132 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Multiselect', + componentName: 'multiselect', + tests: [ + { + description: 'permutations', + path: 'multiselect/permutations', + screenshotType: 'permutations', + }, + { + description: 'inlineLabelText-permutations', + path: 'multiselect/inline-label-text-permutations', + screenshotType: 'permutations', + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=manual', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'manual' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=auto', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'auto' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=true virtualScroll=true filteringType=none', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'true', virtualScroll: 'true', filteringType: 'none' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'expandToViewport=false virtualScroll=false filteringType=manual', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { expandToViewport: 'false', virtualScroll: 'false', filteringType: 'manual' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'error status wrapping - normal list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { statusType: 'error' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'item selected (dropdown stays open) - normal list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.click('[data-test-index="4"]'); + }, + }, + { + description: 'item selected (dropdown stays open) - virtual list', + path: 'multiselect/screenshot', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.click('[data-test-index="4"]'); + }, + }, + { + description: 'custom render option - normal list', + path: 'multiselect/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'custom render option - virtual list', + path: 'multiselect/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + }, + }, + { + description: 'Long virtual list - navigate to last item', + path: 'multiselect/virtual-scroll', + screenshotType: 'screenshotArea', + queryParams: { type: 'multiselect' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + await page.waitForJsTimers(); + }, + }, + { + description: 'Long virtual list (select all) - navigate to last item', + path: 'multiselect/virtual-scroll', + screenshotType: 'screenshotArea', + queryParams: { type: 'multiselect-select-all' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findMultiselect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findMultiselect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + await page.waitForJsTimers(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/pagination.ts b/test/definitions/visual/pagination.ts new file mode 100644 index 0000000000..ed8ec25aee --- /dev/null +++ b/test/definitions/visual/pagination.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Pagination', + componentName: 'pagination', + tests: [ + { + description: 'permutations', + path: 'pagination/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/pie-chart.ts b/test/definitions/visual/pie-chart.ts new file mode 100644 index 0000000000..25901670cf --- /dev/null +++ b/test/definitions/visual/pie-chart.ts @@ -0,0 +1,66 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Pie chart', + componentName: 'pie-chart', + tests: [ + { + description: 'permutations', + path: 'pie-chart/permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations in narrow container', + path: 'pie-chart/permutations', + screenshotType: 'permutations', + configuration: { width: 350 }, + }, + { + description: 'fit-height', + path: 'pie-chart/fit-height', + screenshotType: 'screenshotArea', + }, + { + description: 'fit-height no filter, no legend', + path: 'pie-chart/fit-height', + screenshotType: 'screenshotArea', + queryParams: { hideFilter: 'true', hideLegend: 'true' }, + }, + { + description: 'can focus chart plot', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'can navigate segments with keyboard', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.keys(['ArrowRight']); + }, + }, + { + description: 'can pin segments with mouse', + path: 'pie-chart/test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('svg [aria-label~="Apples"] > path'); + await page.waitForVisible('[aria-label="Dismiss"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/popover.ts b/test/definitions/visual/popover.ts new file mode 100644 index 0000000000..b15f2589b4 --- /dev/null +++ b/test/definitions/visual/popover.ts @@ -0,0 +1,197 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Popover', + componentName: 'popover', + tests: [ + { + description: 'text wrapping', + path: 'popover/text-wrap', + screenshotType: 'screenshotArea', + }, + { + description: 'alignment inside text', + path: 'popover/text-align', + screenshotType: 'screenshotArea', + }, + { + description: 'inside modal', + path: 'popover/scenario-in-modal', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Show modal'); + await page.click('#popover button'); + }, + }, + { + description: 'positioning with navigation v1.0', + path: 'popover/nav-v1-0-positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover button'); + }, + }, + { + description: 'close icon positioned inside the popover (no header and fixed width)', + path: 'popover/header-variant', + screenshotType: 'viewport', + setup: async page => { + await page.click('[data-testid="popover-without-title"] button'); + }, + }, + { + description: 'inside table - renderWithPortal=false', + path: 'popover/scenario-in-table', + screenshotType: 'viewport', + setup: async page => { + await page.click('table button'); + }, + }, + { + description: 'inside table - renderWithPortal=true', + path: 'popover/scenario-in-table', + screenshotType: 'viewport', + setup: async page => { + await page.click('#renderWithPortal'); + await page.click('table button'); + }, + }, + { + description: 'scenario - copy - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-copy button'); + }, + }, + { + description: 'scenario - copy - renderWithPortal=true', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#renderWithPortal'); + await page.click('#scenario-copy button'); + }, + }, + { + description: 'scenario - medium-key-value - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-medium-key-value button'); + }, + }, + { + description: 'scenario - large-key-value - renderWithPortal=false', + path: 'popover/scenarios', + screenshotType: 'viewport', + setup: async page => { + await page.click('#scenario-large-key-value button'); + }, + }, + { + description: 'inline popover - closed - renderWithPortal=false', + path: 'popover/inline', + screenshotType: 'screenshotArea', + }, + { + description: 'inline popover - closed - renderWithPortal=true', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#renderWithPortal'); + }, + }, + { + description: 'inline popover - open - renderWithPortal=false', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findPopover().findTrigger().toSelector()); + }, + }, + { + description: 'inline popover - open - renderWithPortal=true', + path: 'popover/inline', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('#renderWithPortal'); + await page.click(wrapper.findPopover().findTrigger().toSelector()); + }, + }, + { + description: 'positioning - opens in the correct position - renderWithPortal=false', + path: 'popover/positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover-2-2 button'); + }, + }, + { + description: 'positioning - flips to the opposite position - renderWithPortal=false', + path: 'popover/positioning', + screenshotType: 'viewport', + setup: async page => { + await page.click('#popover-1-2 button'); + }, + }, + { + description: 'focus - Permutation 1', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'focus - Permutation 2', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 2; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 3', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 3; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 6', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 6; j++) { + await page.focusNextElement(); + } + }, + }, + { + description: 'focus - Permutation 12', + path: 'popover/focus-ring', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + for (let j = 0; j < 12; j++) { + await page.focusNextElement(); + } + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/progress-bar.ts b/test/definitions/visual/progress-bar.ts new file mode 100644 index 0000000000..1280b00599 --- /dev/null +++ b/test/definitions/visual/progress-bar.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ProgressBar', + componentName: 'progress-bar', + tests: [ + { + description: 'permutations - standalone', + path: 'progress-bar/permutations-standalone', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'permutations - flash', + path: 'progress-bar/permutations-flash', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + { + description: 'permutations - key-value', + path: 'progress-bar/permutations-key-value', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/prompt-input.ts b/test/definitions/visual/prompt-input.ts new file mode 100644 index 0000000000..7358e18aea --- /dev/null +++ b/test/definitions/visual/prompt-input.ts @@ -0,0 +1,46 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'PromptInput', + componentName: 'prompt-input', + tests: [ + { + description: 'Permutations at 250', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 250 }, + }, + { + description: 'Permutations at 400', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'Permutations at 800', + path: 'prompt-input/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + { + description: 'Style Permutations', + path: 'prompt-input/style-permutations', + screenshotType: 'permutations', + }, + { + description: 'focus ring on the last secondary action', + path: 'prompt-input/simple', + screenshotType: 'screenshotArea', + queryParams: { hasSecondaryActions: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findPromptInput('[data-testid="prompt-input"]').findNativeTextarea().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/property-filter.ts b/test/definitions/visual/property-filter.ts new file mode 100644 index 0000000000..d9529692e4 --- /dev/null +++ b/test/definitions/visual/property-filter.ts @@ -0,0 +1,144 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Property filter', + componentName: 'property-filter', + tests: [ + { + description: 'token editor popover', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-default [aria-haspopup=dialog]'); + }, + }, + { + description: 'filtering token select', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-default [aria-haspopup=listbox]'); + }, + }, + { + description: 'token editor popover overflow', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-overflow [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor custom property boolean', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-custom-prop-boolean [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor custom property datetime', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('.property-filter-custom-prop-datetime [aria-haspopup=dialog]'); + }, + }, + { + description: 'token editor group string property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); + }, + }, + { + description: 'token editor group datetime property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findTokens().get(1).findLabel().toSelector()); + await page.keys(['Escape']); + await page.keys(['Tab', 'Tab', 'Tab', 'Enter']); + }, + }, + { + description: 'token editor enum property', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('state = s'); + }, + }, + { + description: 'token editor enum property no matches', + path: 'property-filter/token-editor', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter('.property-filter-group-editor'); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('state = x'); + }, + }, + { + description: 'permutations', + path: 'property-filter/permutations', + screenshotType: 'permutations', + }, + { + description: 'tokens permutations', + path: 'property-filter/property-filter-tokens-permutations', + screenshotType: 'permutations', + }, + { + description: 'editor permutations at 400', + path: 'property-filter/property-filter-editor-permutations', + screenshotType: 'permutations', + configuration: { width: 400 }, + }, + { + description: 'editor permutations at 1200', + path: 'property-filter/property-filter-editor-permutations', + screenshotType: 'permutations', + configuration: { width: 1200 }, + }, + { + description: 'split panel integration - main content dropdown', + path: 'property-filter/split-panel-app-layout-integration', + screenshotType: 'viewport', + setup: async page => { + await page.click('.main-content input[aria-label="your choice"]'); + }, + }, + { + description: 'split panel integration - main content popover', + path: 'property-filter/split-panel-app-layout-integration', + screenshotType: 'viewport', + setup: async page => { + await page.click('.main-content button[aria-haspopup=dialog]'); + }, + }, + { + description: 'virtual scroll navigate through 100 items', + path: 'property-filter/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const propertyFilter = wrapper.findPropertyFilter(); + await page.click(propertyFilter.findNativeInput().toSelector()); + await page.keys('Property = '); + for (let i = 0; i < 100; i++) { + await page.keys('ArrowDown'); + } + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/radio-button.ts b/test/definitions/visual/radio-button.ts new file mode 100644 index 0000000000..c97bef2e8f --- /dev/null +++ b/test/definitions/visual/radio-button.ts @@ -0,0 +1,42 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'RadioButton', + componentName: 'radio-button', + tests: [ + { + description: 'Permutations', + path: 'radio-button/permutations', + screenshotType: 'permutations', + }, + { + description: 'Focused and not checked', + path: 'radio-button/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Focused and checked', + path: 'radio-button/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + }, + }, + { + description: 'Custom style permutations', + path: 'radio-button/style-custom', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/radio-group.ts b/test/definitions/visual/radio-group.ts new file mode 100644 index 0000000000..20da0133f1 --- /dev/null +++ b/test/definitions/visual/radio-group.ts @@ -0,0 +1,49 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'RadioGroup', + componentName: 'radio-group', + tests: [ + { + description: 'Permutations', + path: 'radio-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'Radio button is focused', + path: 'radio-group/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Radio button has label with a correct width', + path: 'radio-group/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Horizontal radio group permutations at 600', + path: 'radio-group/horizontal.permutations', + screenshotType: 'permutations', + configuration: { width: 600 }, + }, + { + description: 'Horizontal radio group permutations at 1280', + path: 'radio-group/horizontal.permutations', + screenshotType: 'permutations', + configuration: { width: 1280 }, + }, + { + description: 'Style custom page', + path: 'radio-group/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/s3-resource-selector.ts b/test/definitions/visual/s3-resource-selector.ts new file mode 100644 index 0000000000..0cdaa5bcd9 --- /dev/null +++ b/test/definitions/visual/s3-resource-selector.ts @@ -0,0 +1,38 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'S3 Resource Selector', + componentName: 's3-resource-selector', + tests: [ + { + description: 'Permutations', + path: 's3-resource-selector/permutations', + screenshotType: 'permutations', + }, + { + description: 'Browse dialog - plain', + path: 's3-resource-selector/permutations', + screenshotType: 'viewport', + configuration: { height: 1000 }, + setup: async page => { + await page.click('button=Browse S3'); + await page.waitForVisible('[role="dialog"]'); + }, + }, + { + description: 'Browse dialog - with alert', + path: 's3-resource-selector/with-alert', + screenshotType: 'viewport', + configuration: { height: 1100 }, + setup: async page => { + await page.click('button=Browse S3'); + await page.waitForVisible('[role="dialog"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/segmented-control.ts b/test/definitions/visual/segmented-control.ts new file mode 100644 index 0000000000..5be8493c37 --- /dev/null +++ b/test/definitions/visual/segmented-control.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SegmentedControl', + componentName: 'segmented-control', + tests: [ + { + description: 'Permutations', + path: 'segmented-control/permutations', + screenshotType: 'permutations', + }, + { + description: 'Style Permutations', + path: 'segmented-control/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/select.ts b/test/definitions/visual/select.ts new file mode 100644 index 0000000000..cfd3abae15 --- /dev/null +++ b/test/definitions/visual/select.ts @@ -0,0 +1,96 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Select', + componentName: 'select', + tests: [ + { + description: 'component - dropdown closed', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + }, + { + description: 'component - dropdown open - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo"] button'); + }, + }, + { + description: 'component - dropdown open - virtual list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#toggle-virtual'); + await page.click('[data-testid="select-demo"] button'); + }, + }, + { + description: 'component - dropdown open limited width - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo-with-no-filtering-and-limited-width"] button'); + }, + }, + { + description: 'keyboard interaction - plain list', + path: 'select/screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-testid="select-demo-no-filtering"] button'); + await page.keys(['ArrowDown', 'Space']); + }, + }, + { + description: 'component - custom render option - plain list', + path: 'select/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'false' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'component - custom render option - virtual list', + path: 'select/custom-render-option', + screenshotType: 'screenshotArea', + queryParams: { virtualScroll: 'true' }, + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'item permutations', + path: 'select/item.permutations', + screenshotType: 'permutations', + }, + { + description: 'selectable-item permutations', + path: 'selectable-item/permutations', + screenshotType: 'permutations', + }, + { + description: 'trigger permutations', + path: 'select/trigger.permutations', + screenshotType: 'permutations', + }, + { + description: 'Long virtual list - navigate to last item', + path: 'select/virtual-scroll', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click(wrapper.findSelect().findTrigger().toSelector()); + await page.elementScrollTo(wrapper.findSelect().findDropdown().findOptionsContainer().toSelector(), { + top: 99999, + }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/side-navigation.ts b/test/definitions/visual/side-navigation.ts new file mode 100644 index 0000000000..fce8be8eb1 --- /dev/null +++ b/test/definitions/visual/side-navigation.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SideNavigation', + componentName: 'side-navigation', + tests: [ + { + description: 'Permutations', + path: 'side-navigation/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/slider.ts b/test/definitions/visual/slider.ts new file mode 100644 index 0000000000..49f7df5dfb --- /dev/null +++ b/test/definitions/visual/slider.ts @@ -0,0 +1,19 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Slider', + componentName: 'slider', + tests: [ + { + description: 'permutations - standalone', + path: 'slider/permutations', + screenshotType: 'permutations', + configuration: { width: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/space-between.ts b/test/definitions/visual/space-between.ts new file mode 100644 index 0000000000..636d375d42 --- /dev/null +++ b/test/definitions/visual/space-between.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'SpaceBetween', + componentName: 'space-between', + tests: [ + { + description: 'Permutations', + path: 'space-between/permutations', + screenshotType: 'permutations', + }, + { + description: 'in ColumnLayout and Grid', + path: 'space-between/nested-components', + screenshotType: 'screenshotArea', + }, + { + description: 'Alignment permutations', + path: 'space-between/alignment.permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/spinner.ts b/test/definitions/visual/spinner.ts new file mode 100644 index 0000000000..65aa6c6ca1 --- /dev/null +++ b/test/definitions/visual/spinner.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Spinner', + componentName: 'spinner', + tests: [ + { + description: 'Alignment with text', + path: 'spinner/text-align', + screenshotType: 'screenshotArea', + }, + { + description: 'Permutations', + path: 'spinner/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/split-panel.ts b/test/definitions/visual/split-panel.ts new file mode 100644 index 0000000000..ae94690b69 --- /dev/null +++ b/test/definitions/visual/split-panel.ts @@ -0,0 +1,93 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Split panel', + componentName: 'split-panel', + tests: [ + { + description: 'position bottom', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + }, + }, + { + description: 'position bottom - closed', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + }, + { + description: 'position side', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + }, + }, + { + description: 'position side with tools open', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + await page.waitForVisible('aria/Open tools'); + await page.click('aria/Open tools'); + }, + }, + { + description: 'position side - closed', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('[aria-label="Preferences"]'); + await page.click('aria/Side'); + await page.click('aria/Confirm'); + await page.click('aria/Close panel'); + }, + }, + { + description: 'preferences open', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async page => { + await page.click('aria/Open panel'); + await page.click('aria/Preferences'); + }, + }, + { + description: 'popover in bottom panel', + path: 'app-layout/with-split-panel', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + await page.click('aria/Open panel'); + await page.click(wrapper.findSplitPanel().findOpenPanelBottom().findPopover().findTrigger().toSelector()); + await (page as any).scrollIntoView('[data-testid="scroll-me"]'); + }, + }, + { + description: 'headerBefore, info link, actions and description', + path: 'app-layout/split-panel-with-custom-header', + screenshotType: 'screenshotArea', + queryParams: { renderActionsButtonDropdown: 'true', renderBeforeBadge: 'true', renderInfoLink: 'true' }, + }, + { + description: 'Entire layout defined in headerBefore slot', + path: 'app-layout/split-panel-with-custom-header', + screenshotType: 'screenshotArea', + queryParams: { renderActionsButtonDropdown: 'true', renderBeforeButtons: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/status-indicator.ts b/test/definitions/visual/status-indicator.ts new file mode 100644 index 0000000000..526849f4fa --- /dev/null +++ b/test/definitions/visual/status-indicator.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'StatusIndicator', + componentName: 'status-indicator', + tests: [ + { + description: 'permutations', + path: 'status-indicator/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/steps.ts b/test/definitions/visual/steps.ts new file mode 100644 index 0000000000..3c21b870fb --- /dev/null +++ b/test/definitions/visual/steps.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Steps', + componentName: 'steps', + tests: [ + { + description: 'permutations', + path: 'steps/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-cells.ts b/test/definitions/visual/table-cells.ts new file mode 100644 index 0000000000..38ebde3b8e --- /dev/null +++ b/test/definitions/visual/table-cells.ts @@ -0,0 +1,20 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table cells', + componentName: 'table-cells', + tests: [ + { + description: 'vertical align', + path: 'table/cell-permutations', + screenshotType: 'screenshotArea', + configuration: { width: 1200 }, + queryParams: { verticalAlignTop: 'true' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-embedded.ts b/test/definitions/visual/table-embedded.ts new file mode 100644 index 0000000000..06da54e843 --- /dev/null +++ b/test/definitions/visual/table-embedded.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table embedded', + componentName: 'table-embedded', + tests: [ + { + description: 'in alert', + path: 'table/embedded-in-alert', + screenshotType: 'viewport', + }, + { + description: 'stacked and container variants', + path: 'table/stacked-and-container-variant', + screenshotType: 'viewport', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-inline-editing.ts b/test/definitions/visual/table-inline-editing.ts new file mode 100644 index 0000000000..52dd8e12ec --- /dev/null +++ b/test/definitions/visual/table-inline-editing.ts @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table inline editing', + componentName: 'table-inline-editing', + tests: [ + { + description: 'permutations', + path: 'table/inline-editor.permutations', + screenshotType: 'permutations', + }, + { + description: 'active select editing', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); + }, + }, + { + description: 'active select editing with open dropdown', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + await page.click('[aria-label="Edit EKXAM4L45YPC8 TLS Version"]'); + await page.click(wrapper.findSelect().findTrigger().toSelector()); + }, + }, + { + description: 'hovering over cell, resizableColumns=${resizableColumns}', + path: 'table/editable', + screenshotType: 'screenshotArea', + setup: async page => { + await page.hoverElement('[aria-label="Edit EKXAM4L45YPC8 Domain name"]'); + }, + }, + { + description: 'active select editing with keyboard navigation', + path: 'table/editable', + screenshotType: 'screenshotArea', + configuration: { width: 1600 }, + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async page => { + await page.click('[data-testid="focus"]'); + await page.keys(['Tab', 'ArrowDown', 'ArrowRight', 'Enter']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-resizable-columns.ts b/test/definitions/visual/table-resizable-columns.ts new file mode 100644 index 0000000000..293dc98319 --- /dev/null +++ b/test/definitions/visual/table-resizable-columns.ts @@ -0,0 +1,69 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table', + componentName: 'table-resizable-columns', + tests: [ + { + description: 'resizable columns permutations', + path: 'table/resizable-columns-permutations', + screenshotType: 'permutations', + }, + { + description: 'focused column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + setup: async page => { + await page.buttonDownOnElement('#reset-state'); + await page.keys(['Tab']); + }, + }, + { + description: 'active column resizer on sticky header', + path: 'table/resizable-columns', + screenshotType: 'viewport', + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click('#sticky-header-toggle input'); + await page.windowScrollTo({ top: 600 }); + await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); + }, + }, + { + description: 'active column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.buttonDownOnElement(table.findColumnResizer(3).toSelector()); + }, + }, + { + description: 'pressed column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click(table.findHeaderSlot().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); + }, + }, + { + description: 'pressed last column resizer', + path: 'table/resizable-columns', + screenshotType: 'screenshotArea', + queryParams: { enableKeyboardNavigation: 'true' }, + setup: async (page, wrapper) => { + const table = wrapper.findTable(); + await page.click(table.findHeaderSlot().toSelector()); + await page.keys(['Tab', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'ArrowRight', 'Enter']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-sticky-header.ts b/test/definitions/visual/table-sticky-header.ts new file mode 100644 index 0000000000..ed508f17a7 --- /dev/null +++ b/test/definitions/visual/table-sticky-header.ts @@ -0,0 +1,44 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table sticky header', + componentName: 'table-sticky-header', + tests: [ + { + description: 'initial state', + path: 'table/sticky-header', + screenshotType: 'viewport', + }, + { + description: 'mid-scroll sticky state - container variant', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('#container'); + await page.windowScrollTo({ top: 400 }); + }, + }, + { + description: 'mid-scroll sticky state - embedded variant', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.click('#embedded'); + await page.windowScrollTo({ top: 400 }); + }, + }, + { + description: 'bottom sticky state', + path: 'table/sticky-header', + screenshotType: 'viewport', + setup: async page => { + await page.windowScrollTo({ top: 925 }); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table-sticky-scrollbar.ts b/test/definitions/visual/table-sticky-scrollbar.ts new file mode 100644 index 0000000000..1f661f5a48 --- /dev/null +++ b/test/definitions/visual/table-sticky-scrollbar.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table sticky scrollbar', + componentName: 'table-sticky-scrollbar', + tests: [ + { + description: 'on a plain page is positioned at the bottom', + path: 'table/sticky-scrollbar', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + { + description: 'on a page with app layout is positioned over the footer', + path: 'app-layout/with-table', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + { + description: 'on a page with app layout in a container is positioned over the bottom of the container', + path: 'app-layout/with-table-in-container', + screenshotType: 'screenshotArea', + configuration: { width: 600, height: 800 }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/table.ts b/test/definitions/visual/table.ts new file mode 100644 index 0000000000..9ed8cceced --- /dev/null +++ b/test/definitions/visual/table.ts @@ -0,0 +1,130 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Table', + componentName: 'table', + tests: [ + { + description: 'simple permutations at ${width}', + path: 'table/simple-permutations', + screenshotType: 'permutations', + }, + { + description: 'permutations at ${width}', + path: 'table/permutations', + screenshotType: 'permutations', + }, + { + description: 'expandable rows permutations', + path: 'table/expandable-rows.permutations', + screenshotType: 'permutations', + }, + { + description: 'empty state – default', + path: 'table/empty-state', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + }, + { + description: 'empty state – scrolled horizontally', + path: 'table/empty-state', + screenshotType: 'screenshotArea', + configuration: { width: 600 }, + setup: async page => { + await page.click('#scroll-content'); + }, + }, + { + description: 'with features', + path: 'table/hooks', + screenshotType: 'screenshotArea', + }, + { + description: 'with striped rows', + path: 'table/striped-rows', + screenshotType: 'screenshotArea', + }, + { + description: 'inside stacked container', + path: 'table/sticky-header-in-stacked-container', + screenshotType: 'screenshotArea', + setup: async page => { + await page.windowScrollTo({ top: 10 }); + await page.click('button=Actions'); + }, + }, + { + description: 'sticky header with open action button dropdown', + path: 'table/sticky-header-with-actions', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[data-test-id="actions-button"]'); + }, + }, + { + description: 'variants', + path: 'table/variants', + screenshotType: 'screenshotArea', + }, + { + description: 'focus on last cell inline action', + path: 'table/inline-actions', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); + await page.click(tableWrapper.findRows().get(1).findButtonDropdown().findNativeButton().toSelector()); + await page.keys(['Escape']); + }, + }, + { + description: 'focus on first cell link', + path: 'table/inline-actions', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const tableWrapper = wrapper.findTable('[data-testid="table-with-dropdown-actions"]'); + await page.click(tableWrapper.findRowSelectionArea(1).toSelector()); + await page.keys(['Tab']); + }, + }, + { + description: 'first column sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1' }, + }, + { + description: 'first column sticky state and selection', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1', selectionType: 'single' }, + }, + { + description: 'first column sticky state and striped rows', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '1', stripedRows: 'true' }, + }, + { + description: 'first two columns sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsFirst: '2' }, + }, + { + description: 'last column sticky state', + path: 'table/sticky-columns', + screenshotType: 'screenshotArea', + configuration: { width: 800 }, + queryParams: { stickyColumnsLast: '1' }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tabs.ts b/test/definitions/visual/tabs.ts new file mode 100644 index 0000000000..90b9c61f0f --- /dev/null +++ b/test/definitions/visual/tabs.ts @@ -0,0 +1,80 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tabs', + componentName: 'tabs', + tests: [ + { + description: 'Permutations', + path: 'tabs/permutations', + screenshotType: 'permutations', + }, + { + description: 'Responsive permutations', + path: 'tabs/responsive-permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'focuses next tab header after clicking on tab header without an href', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#first-tabs li:nth-child(3) button'); + await page.keys('ArrowRight'); + }, + }, + { + description: 'layout at ${width}px', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + }, + { + description: 'focus active tab - default variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this'); + await page.focusNextElement(); + }, + }, + { + description: 'focus active tab - container variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this-2'); + await page.focusNextElement(); + }, + }, + { + description: 'focus content - default variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'focus content - container variant', + path: 'tabs/integration-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#click-this-2'); + await page.focusNextElement(); + await page.focusNextElement(); + }, + }, + { + description: 'Style Permutations', + path: 'tabs/style-permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tag-editor.ts b/test/definitions/visual/tag-editor.ts new file mode 100644 index 0000000000..041fdbef6a --- /dev/null +++ b/test/definitions/visual/tag-editor.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TagEditor', + componentName: 'tag-editor', + tests: [ + { + description: 'Permutations', + path: 'tag-editor/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/text-content.ts b/test/definitions/visual/text-content.ts new file mode 100644 index 0000000000..29b72be343 --- /dev/null +++ b/test/definitions/visual/text-content.ts @@ -0,0 +1,33 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TextContainer', + componentName: 'text-content', + tests: [ + { + description: 'color property', + path: 'text-content/permutations', + screenshotType: 'screenshotArea', + }, + { + description: 'with nested Link components', + path: 'text-content/link-nesting', + screenshotType: 'screenshotArea', + }, + { + description: 'with nested Box components', + path: 'text-content/box-nesting', + screenshotType: 'screenshotArea', + }, + { + description: 'with icons in content', + path: 'text-content/iconography', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/text-filter.ts b/test/definitions/visual/text-filter.ts new file mode 100644 index 0000000000..f291e70574 --- /dev/null +++ b/test/definitions/visual/text-filter.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TextFilter', + componentName: 'text-filter', + tests: [ + { + description: 'permutations', + path: 'text-filter/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/textarea.ts b/test/definitions/visual/textarea.ts new file mode 100644 index 0000000000..2047d7559d --- /dev/null +++ b/test/definitions/visual/textarea.ts @@ -0,0 +1,77 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const testStates = [ + { toggle: 'toggle-invalid', label: 'invalid', isInteractive: true }, + { toggle: 'toggle-disabled', label: 'disabled', isInteractive: false }, + { toggle: 'toggle-readonly', label: 'readonly', isInteractive: true }, + { toggle: 'toggle-warning', label: 'warning', isInteractive: true }, +]; + +function pseudoSelectorTests(withStyling: boolean): TestSuite { + return { + description: withStyling ? 'Pseudo Selectors with Custom Styling' : 'Pseudo Selectors', + tests: testStates.flatMap(state => [ + { + description: `${state.label} - focus`, + path: 'textarea/pseudo-selectors', + screenshotType: 'screenshotArea' as const, + setup: async (page: any, wrapper: any) => { + const textareaSelector = wrapper + .findTextarea('[data-testid="test-textarea"]') + .findNativeTextarea() + .toSelector(); + if (withStyling) { + await page.click('#toggle-styling'); + } + await page.click(`#${state.toggle}`); + if (state.isInteractive) { + await page.click(textareaSelector); + } + }, + }, + { + description: `${state.label} - focus + hover`, + path: 'textarea/pseudo-selectors', + screenshotType: 'screenshotArea' as const, + setup: async (page: any, wrapper: any) => { + const textareaSelector = wrapper + .findTextarea('[data-testid="test-textarea"]') + .findNativeTextarea() + .toSelector(); + if (withStyling) { + await page.click('#toggle-styling'); + } + await page.click(`#${state.toggle}`); + if (state.isInteractive) { + await page.click(textareaSelector); + } + await page.hoverElement(textareaSelector); + }, + }, + ]), + }; +} + +const suite: TestSuite = { + description: 'Textarea', + componentName: 'textarea', + tests: [ + { + description: 'Permutations', + path: 'textarea/permutations', + screenshotType: 'permutations', + }, + { + description: 'Style Permutations', + path: 'textarea/style-permutations', + screenshotType: 'permutations', + }, + pseudoSelectorTests(false), + pseudoSelectorTests(true), + ], +}; + +export default suite; diff --git a/test/definitions/visual/tiles.ts b/test/definitions/visual/tiles.ts new file mode 100644 index 0000000000..51c290fc80 --- /dev/null +++ b/test/definitions/visual/tiles.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tiles', + componentName: 'tiles', + tests: [ + { + description: 'tiles at "${breakpoint}"', + path: 'tiles/simple', + screenshotType: 'screenshotArea', + }, + { + description: 'permutations at "${breakpoint}"', + path: 'tiles/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/time-input.ts b/test/definitions/visual/time-input.ts new file mode 100644 index 0000000000..b2e6fc2dcc --- /dev/null +++ b/test/definitions/visual/time-input.ts @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Time Input', + componentName: 'time-input', + tests: [ + { + description: 'Permutations', + path: 'time-input/permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/toggle-button.ts b/test/definitions/visual/toggle-button.ts new file mode 100644 index 0000000000..e91340dfdb --- /dev/null +++ b/test/definitions/visual/toggle-button.ts @@ -0,0 +1,26 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toggle button', + componentName: 'toggle-button', + tests: [ + { + description: 'permutations', + path: 'toggle-button/permutations', + screenshotType: 'permutations', + }, + { + description: 'hovering over normal variant', + path: 'toggle-button/permutations', + screenshotType: 'permutations', + setup: async page => { + await page.hoverElement('[aria-label="Favorite"][aria-pressed="false"]'); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/toggle.ts b/test/definitions/visual/toggle.ts new file mode 100644 index 0000000000..b99818470d --- /dev/null +++ b/test/definitions/visual/toggle.ts @@ -0,0 +1,37 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Toggle', + componentName: 'toggle', + tests: [ + { + description: 'Permutations', + path: 'toggle/permutations', + screenshotType: 'permutations', + }, + { + description: 'Toggle is focused', + path: 'toggle/focus-test', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: 'Toggle has label with a correct width', + path: 'toggle/labels-highlight', + screenshotType: 'screenshotArea', + }, + { + description: 'Style custom page', + path: 'toggle/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/token-group.ts b/test/definitions/visual/token-group.ts new file mode 100644 index 0000000000..99ac6b49e6 --- /dev/null +++ b/test/definitions/visual/token-group.ts @@ -0,0 +1,23 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'TokenGroup', + componentName: 'token-group', + tests: [ + { + description: 'Permutations', + path: 'token-group/permutations', + screenshotType: 'permutations', + }, + { + description: 'Simple', + path: 'token-group/index', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/top-navigation.ts b/test/definitions/visual/top-navigation.ts new file mode 100644 index 0000000000..d58b5afa4a --- /dev/null +++ b/test/definitions/visual/top-navigation.ts @@ -0,0 +1,54 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Top navigation', + componentName: 'top-navigation', + tests: [ + { + description: 'Responsiveness', + path: 'top-navigation/screenshot', + screenshotType: 'screenshotArea', + configuration: { width: 1300 }, + }, + { + description: 'Dropdown menu utility', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 1300, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findUtility(4).toSelector()); + }, + }, + { + description: 'Utility permutations', + path: 'top-navigation/utility.permutations', + screenshotType: 'permutations', + }, + { + description: 'Overflow menu - outer', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 500, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); + await page.keys(['Tab']); + }, + }, + { + description: 'Overflow menu - dropdown', + path: 'top-navigation/scenario-full-page', + screenshotType: 'viewport', + configuration: { width: 500, height: 800 }, + setup: async (page, wrapper) => { + await page.click(wrapper.findTopNavigation().findOverflowMenuButton().toSelector()); + await page.click(wrapper.findTopNavigation().findOverflowMenu().findUtility(3).toSelector()); + await page.keys(['Tab']); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/tree-view.ts b/test/definitions/visual/tree-view.ts new file mode 100644 index 0000000000..baab73ad51 --- /dev/null +++ b/test/definitions/visual/tree-view.ts @@ -0,0 +1,28 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Tree View', + componentName: 'tree-view', + tests: [ + { + description: 'basic', + path: 'tree-view/basic', + screenshotType: 'screenshotArea', + }, + { + description: 'with different toggle icon', + path: 'tree-view/basic', + screenshotType: 'screenshotArea', + setup: async (page, wrapper) => { + const select = wrapper.findSelect(); + await page.click(select.findTrigger().toSelector()); + await page.click(select.findDropdown().findOptionByValue('custom').toSelector()); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/wizard.ts b/test/definitions/visual/wizard.ts new file mode 100644 index 0000000000..ac8a3fe795 --- /dev/null +++ b/test/definitions/visual/wizard.ts @@ -0,0 +1,34 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'Wizard', + componentName: 'wizard', + tests: [ + { + description: 'first step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + }, + { + description: 'second step', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('#next'); + }, + }, + { + description: 'steps menu expanded in mobile view', + path: 'wizard/wizard-screenshot', + screenshotType: 'screenshotArea', + setup: async page => { + await page.click('[role="button"][aria-expanded]'); + }, + }, + ], +}; + +export default suite; diff --git a/tsconfig.test-definitions.json b/tsconfig.test-definitions.json index 30e82b4047..3f5961a78b 100644 --- a/tsconfig.test-definitions.json +++ b/tsconfig.test-definitions.json @@ -16,5 +16,5 @@ "skipLibCheck": true }, "include": ["test/definitions", "test/types.ts"], - "exclude": [] + "exclude": ["test/definitions/utils.ts"] } diff --git a/tsconfig.visual.json b/tsconfig.visual.json new file mode 100644 index 0000000000..be61d962ef --- /dev/null +++ b/tsconfig.visual.json @@ -0,0 +1,4 @@ +{ + "extends": "./tsconfig.integ.json", + "include": ["test/definitions/utils.ts", "test/visual/**/*.test.ts", "types"] +}