diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml index 03fef630db..429e8d61bb 100644 --- a/.github/workflows/visual-regression.yml +++ b/.github/workflows/visual-regression.yml @@ -33,7 +33,7 @@ jobs: strategy: fail-fast: false matrix: - shard: [1, 2] + shard: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] steps: - uses: actions/checkout@v4 @@ -111,7 +111,7 @@ jobs: - name: Run visual regression tests run: | echo "NEW_HOST=${NEW_HOST}" - NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/2" + NODE_OPTIONS=--experimental-vm-modules node_modules/.bin/jest -c jest.visual.config.js --shard="${SHARD}/10" env: TZ: UTC SHARD: ${{ matrix.shard }} diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 51305af5b7..a71cac4244 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -29,6 +29,19 @@ import breadcrumbGroupSuite from './visual/breadcrumb-group'; import buttonSuite from './visual/button'; import buttonDropdownSuite from './visual/button-dropdown'; import buttonGroupSuite from './visual/button-group'; +import cardsSuite from './visual/cards'; +import checkboxSuite from './visual/checkbox'; +import collectionPreferencesSuite from './visual/collection-preferences'; +import columnLayoutSuite from './visual/column-layout'; +import containerSuite from './visual/container'; +import containerStickySuite from './visual/container-sticky'; +import contentLayoutSuite from './visual/content-layout'; +import contentLayoutPermutationsSuite from './visual/content-layout-permutations'; +import copyToClipboardSuite from './visual/copy-to-clipboard'; +import dateInputSuite from './visual/date-input'; +import datePickerSuite from './visual/date-picker'; +import dateRangePickerSuite from './visual/date-range-picker'; +import expandableSectionSuite from './visual/expandable-section'; import tableSuite from './visual/table'; // Per-component exports (grouped by component) @@ -57,6 +70,17 @@ export const breadcrumbGroup: TestSuite[] = [breadcrumbGroupSuite]; export const button: TestSuite[] = [buttonSuite]; export const buttonDropdown: TestSuite[] = [buttonDropdownSuite]; export const buttonGroup: TestSuite[] = [buttonGroupSuite]; +export const cards: TestSuite[] = [cardsSuite]; +export const checkbox: TestSuite[] = [checkboxSuite]; +export const collectionPreferences: TestSuite[] = [collectionPreferencesSuite]; +export const columnLayout: TestSuite[] = [columnLayoutSuite]; +export const container: TestSuite[] = [containerSuite, containerStickySuite]; +export const contentLayout: TestSuite[] = [contentLayoutSuite, contentLayoutPermutationsSuite]; +export const copyToClipboard: TestSuite[] = [copyToClipboardSuite]; +export const dateInput: TestSuite[] = [dateInputSuite]; +export const datePicker: TestSuite[] = [datePickerSuite]; +export const dateRangePicker: TestSuite[] = [dateRangePickerSuite]; +export const expandableSection: TestSuite[] = [expandableSectionSuite]; export const table: TestSuite[] = [tableSuite]; export const allSuites: TestSuite[] = [ @@ -73,5 +97,16 @@ export const allSuites: TestSuite[] = [ ...button, ...buttonDropdown, ...buttonGroup, + ...cards, + ...checkbox, + ...collectionPreferences, + ...columnLayout, + ...container, + ...contentLayout, + ...copyToClipboard, + ...dateInput, + ...datePicker, + ...dateRangePicker, + ...expandableSection, ...table, ]; diff --git a/test/definitions/page-object.ts b/test/definitions/page-object.ts index f06c428ec9..62a0fd174a 100644 --- a/test/definitions/page-object.ts +++ b/test/definitions/page-object.ts @@ -22,4 +22,17 @@ export default class VisualTestPageObject extends RawScreenshotPageObject { }); await this.waitForJsTimers(500); } + + async doubleClick(selector: string): Promise { + const element = this.browser.$(selector); + await element.doubleClick(); + } + + /** + * Scrolls the element matching the given selector into view. + */ + async scrollIntoView(selector: string): Promise { + const element = this.browser.$(selector); + await element.scrollIntoView(); + } } diff --git a/test/definitions/visual/app-layout-z-index.ts b/test/definitions/visual/app-layout-z-index.ts index 5f225a8918..9cc202afb2 100644 --- a/test/definitions/visual/app-layout-z-index.ts +++ b/test/definitions/visual/app-layout-z-index.ts @@ -9,11 +9,11 @@ const suite: TestSuite = { { description: 'Z-index', tests: [ - ...[600, 1280].flatMap(width => [ + ...[600, 1280].flatMap(width => [ { description: `button dropdown (${width}px)`, path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, + screenshotType: 'viewport', configuration: { width }, setup: async ({ page }) => { await page.click('button=Button dropdown'); @@ -24,7 +24,7 @@ const suite: TestSuite = { { description: `select (${width}px)`, path: 'app-layout/with-absolute-components', - screenshotType: 'viewport' as const, + screenshotType: 'viewport', configuration: { width, height: 800 }, setup: async ({ page }) => { await page.click('[data-testid="select-demo"] button'); @@ -34,7 +34,7 @@ const suite: TestSuite = { { description: `split-panel and full-page table (${width}px)`, path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, + screenshotType: 'viewport', configuration: { width }, }, ]), @@ -63,7 +63,7 @@ const suite: TestSuite = { { description: 'split-panel and full-page with open navigation (600px)', path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, + screenshotType: 'viewport', configuration: { width: 600 }, setup: async ({ page }) => { await page.click('button[aria-label="Open navigation"]'); @@ -72,7 +72,7 @@ const suite: TestSuite = { { description: 'split-panel and full-page with open tools (600px)', path: 'app-layout/with-full-page-table-and-split-panel', - screenshotType: 'viewport' as const, + screenshotType: 'viewport', configuration: { width: 600 }, setup: async ({ page }) => { await page.click('button[aria-label="Open tools"]'); diff --git a/test/definitions/visual/cards.ts b/test/definitions/visual/cards.ts new file mode 100644 index 0000000000..ac3f806fdb --- /dev/null +++ b/test/definitions/visual/cards.ts @@ -0,0 +1,16 @@ +// 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: [2200, 1920, 1400, 1200, 992, 768].map(width => ({ + description: `permutations at ${width}`, + path: 'cards/permutations', + screenshotType: 'permutations', + configuration: { width }, + })), +}; + +export default suite; diff --git a/test/definitions/visual/checkbox.ts b/test/definitions/visual/checkbox.ts new file mode 100644 index 0000000000..d7c0f05363 --- /dev/null +++ b/test/definitions/visual/checkbox.ts @@ -0,0 +1,36 @@ +// 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/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts new file mode 100644 index 0000000000..e0ce182718 --- /dev/null +++ b/test/definitions/visual/collection-preferences.ts @@ -0,0 +1,71 @@ +// 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: [ + ...[ + [600, 1100], + [1280, 700], + ].map(([width, height]) => ({ + description: `width ${width}px, height ${height}px`, + tests: [ + { + description: `complete at ${width}x${height}`, + path: 'collection-preferences/simple', + screenshotType: 'viewport', + configuration: { width, height }, + setup: async ({ page }) => { + await page.click('.cp-1 button'); + }, + }, + { + description: `visible content only at ${width}x${height}`, + 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', + 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..a32a49d388 --- /dev/null +++ b/test/definitions/visual/column-layout.ts @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestDefinition, TestSuite } from '../types'; + +const TEST_WIDTHS: [string, number][] = [ + ['default', 400], + ['xxs', 500], + ['xs', 800], + ['m', 1200], +]; + +const suite: TestSuite = { + description: 'ColumnLayout', + componentName: 'column-layout', + tests: TEST_WIDTHS.flatMap(([breakpoint, width]) => [ + { + description: `column-layout at "${breakpoint}"`, + path: 'column-layout/simple', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: `permutations at "${breakpoint}"`, + path: 'column-layout/permutations', + screenshotType: 'permutations', + configuration: { width }, + }, + ]), +}; + +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..d16f05262b --- /dev/null +++ b/test/definitions/visual/container-sticky.ts @@ -0,0 +1,43 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestDefinition, TestSuite } from '../types'; + +function stickyTest( + width: number, + description: string, + params?: Record, + withScroll = true +): TestDefinition { + return { + description, + path: 'container/sticky-permutations', + screenshotType: 'viewport', + configuration: { width }, + ...(params && { queryParams: params }), + ...(withScroll && { + setup: async ({ page }) => { + await page.windowScrollTo({ top: 200 }); + }, + }), + }; +} + +const suite: TestSuite = { + description: 'Container sticky permutations', + componentName: 'container', + tests: [1400, 600].flatMap(width => [ + stickyTest(width, `simple - at ${width}px`), + stickyTest(width, `with notifications - at ${width}px`, { hasNotifications: 'true' }), + stickyTest(width, `with breadcrumbs - at ${width}px`, { hasBreadcrumbs: 'true' }), + stickyTest(width, `with an alert - at ${width}px`, { hasNotifications: 'true', hasAlert: 'true' }), + stickyTest( + width, + `with an alert - at ${width}px without scroll`, + { hasNotifications: 'true', hasAlert: 'true' }, + false + ), + stickyTest(width, `with high-contrast header - at ${width}px`, { hasNotifications: 'true', highContrast: 'true' }), + ]), +}; + +export default suite; diff --git a/test/definitions/visual/container.ts b/test/definitions/visual/container.ts new file mode 100644 index 0000000000..6a46f2e331 --- /dev/null +++ b/test/definitions/visual/container.ts @@ -0,0 +1,58 @@ +// 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: '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' }, + }, + ...['side', 'top'].flatMap(position => + [465, 688, 1120].map(width => ({ + description: `media ${position} permutations at ${width}`, + path: `container/media-${position}-permutations`, + screenshotType: 'permutations', + configuration: { width }, + })) + ), + { + 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..41722859cf --- /dev/null +++ b/test/definitions/visual/content-layout-permutations.ts @@ -0,0 +1,256 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestDefinition, TestSuite } from '../types'; + +function contentLayoutPermutation(params: Record, width = 1400): TestDefinition { + return { + description: `${JSON.stringify(params)} at ${width}`, + path: 'content-layout/permutations', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + queryParams: params, + }; +} + +const suite: TestSuite = { + description: 'ContentLayout permutations', + componentName: 'content-layout', + tests: [ + // default and high-contrast headerVariants with all background styles + ...(['default', 'high-contrast'] as const).flatMap(headerVariant => + (['none', 'gradient', 'image'] as const).flatMap(headerBackgroundStyle => + [1400, 600].flatMap(width => [ + contentLayoutPermutation( + { + headerVariant, + headerBackgroundStyle, + hasContainer: 'true', + defaultPadding: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }, + width + ), + contentLayoutPermutation( + { + headerVariant, + headerBackgroundStyle, + hasContainer: 'true', + defaultPadding: 'true', + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }, + width + ), + ]) + ) + ), + // maxContentWidth variants + ...['default', 'high-contrast'].flatMap(headerVariant => + ['none', 'gradient', 'image'].map(headerBackgroundStyle => + contentLayoutPermutation({ + headerVariant, + headerBackgroundStyle, + hasContainer: 'true', + defaultPadding: 'true', + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + maxContentWidth: '1000', + }) + ) + ), + // divider headerVariant + ...[1400, 600].flatMap(width => [ + contentLayoutPermutation( + { + headerVariant: 'divider', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }, + width + ), + contentLayoutPermutation( + { + headerVariant: 'divider', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + maxContentWidth: '1000', + }, + width + ), + ]), + // with app layout + contentLayoutPermutation({ + headerVariant: 'divider', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }), + contentLayoutPermutation({ + headerVariant: 'high-contrast', + headerBackgroundStyle: 'gradient', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + maxContentWidth: '800', + }), + // with open navigation + contentLayoutPermutation({ + headerVariant: 'high-contrast', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }), + contentLayoutPermutation({ + headerVariant: 'divider', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'true', + hasNotifications: 'true', + disableOverlap: 'false', + hasAppLayout: 'true', + hasAppLayoutWithOpenNavigation: 'true', + hasSecondaryHeader: 'false', + removeHeader: 'false', + maxContentWidth: '700', + }), + // with secondary header + ...[1400, 600].map(width => + contentLayoutPermutation( + { + headerVariant: 'high-contrast', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + }, + width + ) + ), + contentLayoutPermutation({ + headerVariant: 'high-contrast', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'true', + removeHeader: 'false', + maxContentWidth: '900', + }), + // without header + ...[1400, 600].flatMap(width => + [true, false].flatMap(defaultPadding => + ['default', 'high-contrast', 'divider'].map(headerVariant => + contentLayoutPermutation( + { + headerVariant, + defaultPadding: `${defaultPadding}`, + removeHeader: 'true', + hasContainer: `${defaultPadding}`, + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + }, + width + ) + ) + ) + ), + // without default padding + ...[1400, 600].map(width => + contentLayoutPermutation( + { + headerVariant: 'default', + defaultPadding: 'false', + hasContainer: 'false', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'false', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }, + width + ) + ), + // with disabled overlap + ...[1400, 600].map(width => + contentLayoutPermutation( + { + headerVariant: 'high-contrast', + defaultPadding: 'true', + hasContainer: 'true', + hasBreadcrumbs: 'false', + hasNotifications: 'false', + disableOverlap: 'true', + hasAppLayout: 'false', + hasAppLayoutWithOpenNavigation: 'false', + hasSecondaryHeader: 'false', + removeHeader: 'false', + }, + width + ) + ), + ], +}; + +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..91486938a3 --- /dev/null +++ b/test/definitions/visual/content-layout.ts @@ -0,0 +1,58 @@ +// 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: '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'); + }, + }, + ...[1400, 600].flatMap(width => + [false, true].flatMap(hasBreadcrumbs => + [false, true].flatMap(hasNotifications => + [true, false].map(hasOverlap => ({ + description: `without header at ${width} ${hasBreadcrumbs ? 'with' : 'without'} breadcrumbs, ${hasNotifications ? 'with' : 'without'} notifications, ${hasOverlap ? 'with' : 'without'} overlap`, + path: 'content-layout/without-header', + screenshotType: 'screenshotArea', + configuration: { width }, + queryParams: { + hasBreadcrumbs: `${hasBreadcrumbs}`, + hasNotifications: `${hasNotifications}`, + disableOverlap: `${!hasOverlap}`, + }, + })) + ) + ) + ), + ], +}; + +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..6014ac4a8c --- /dev/null +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -0,0 +1,27 @@ +// 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.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..628e120d9f --- /dev/null +++ b/test/definitions/visual/date-input.ts @@ -0,0 +1,22 @@ +// 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..368cac7d0e --- /dev/null +++ b/test/definitions/visual/date-picker.ts @@ -0,0 +1,65 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { ElementWrapper } from '@cloudscape-design/test-utils-core/selectors'; + +import VisualTestPageObject from '../page-object'; +import { TestSuite } from '../types'; + +async function focusFirstMonth(page: VisualTestPageObject, wrapper: ElementWrapper) { + await page.click(wrapper.findDatePicker().findOpenCalendarButton().toSelector()); + await page.keys(['Tab', 'Tab', 'Tab']); +} + +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', + tests: [ + { + description: 'focus ring on selected month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async ({ page, wrapper }) => { + await focusFirstMonth(page, wrapper); + }, + }, + { + description: 'focus ring on current month', + path: 'date-picker/month-picker', + screenshotType: 'screenshotArea', + setup: async ({ page, wrapper }) => { + await focusFirstMonth(page, wrapper); + 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 focusFirstMonth(page, wrapper); + 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..a6180c8016 --- /dev/null +++ b/test/definitions/visual/date-range-picker.ts @@ -0,0 +1,102 @@ +// 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: 'Date Range Picker', + componentName: 'date-range-picker', + tests: [ + ...[450, 1200].flatMap(width => [ + { + description: `Absolute range at ${width}px`, + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea' as const, + configuration: { width, 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"]'); + if (width >= 1000) { + await page.click('[data-date="2018-02-24"]'); + } else { + await page.click('[data-date="2018-01-27"]'); + } + }, + }, + { + description: `Relative range at ${width}px`, + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea' as const, + configuration: { width, height: 950 }, + setup: async ({ page }) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + await page.focusNextElement(); + if (width >= 1000) { + await page.keys(['ArrowLeft']); + await page.keys(['Enter']); + } else { + await page.keys(['Space']); + await page.keys(['ArrowUp']); + await page.keys(['Enter']); + } + await page.focusNextElement(); + await page.keys(['ArrowDown', 'ArrowDown', 'ArrowDown', 'ArrowDown']); + }, + }, + ]), + { + 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: '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 headerSelector = wrapper.findDateRangePicker().findDropdown().findHeader().find('h2 span').toSelector(); + await page.doubleClick(headerSelector); + }, + }, + { + description: 'does not select text when double-clicking next button', + path: 'date-range-picker/with-value', + screenshotType: 'screenshotArea', + setup: async ({ page, wrapper, browser }) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const nextButtonSelector = wrapper.findDateRangePicker().findDropdown().findNextMonthButton().toSelector(); + await browser!.execute((sel: string) => { + const el = document.querySelector(sel); + if (el) { + el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true })); + } + }, nextButtonSelector); + }, + }, + ], +}; + +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..3d1dcadab2 --- /dev/null +++ b/test/definitions/visual/expandable-section.ts @@ -0,0 +1,59 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestDefinition, TestSuite } from '../types'; + +const allVariants = ['container', 'default', 'footer', 'navigation']; +const variantsWithDescription = ['container', 'default', 'footer']; +const variantsWithInteractiveElements = ['container']; + +const headerText = 'Header text'; +const headerDescription = 'Header description'; + +function focusTest(options: Record): TestDefinition { + return { + description: `focus - ${JSON.stringify(options)}`, + path: 'expandable-section/focus', + screenshotType: 'screenshotArea' as const, + queryParams: options, + setup: async ({ page }) => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }; +} + +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', + }, + // Focus tests - with only heading + ...allVariants.map(variant => focusTest({ headerText, variant })), + // Focus tests - with heading and description + ...variantsWithDescription.map(variant => focusTest({ headerText, headerDescription, variant })), + // Focus tests - with interactive elements + ...variantsWithInteractiveElements.map(variant => + focusTest({ headerText, hasHeaderInfo: 'true', hasHeaderActions: 'true', variant }) + ), + // Focus tests - with interactive elements and description + ...variantsWithInteractiveElements.map(variant => + focusTest({ headerText, headerDescription, hasHeaderInfo: 'true', hasHeaderActions: 'true', variant }) + ), + { + description: 'stacked variant', + path: 'expandable-section/stacked-variant.permutations', + screenshotType: 'permutations', + }, + ], +}; + +export default suite; diff --git a/test/visual/cards.test.ts b/test/visual/cards.test.ts new file mode 100644 index 0000000000..ecb282b266 --- /dev/null +++ b/test/visual/cards.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/cards'; + +runTestSuites([suite]); diff --git a/test/visual/checkbox.test.ts b/test/visual/checkbox.test.ts new file mode 100644 index 0000000000..f569e2e7ab --- /dev/null +++ b/test/visual/checkbox.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/checkbox'; + +runTestSuites([suite]); diff --git a/test/visual/collection-preferences.test.ts b/test/visual/collection-preferences.test.ts new file mode 100644 index 0000000000..ac54029622 --- /dev/null +++ b/test/visual/collection-preferences.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/collection-preferences'; + +runTestSuites([suite]); diff --git a/test/visual/column-layout.test.ts b/test/visual/column-layout.test.ts new file mode 100644 index 0000000000..55749a8e8b --- /dev/null +++ b/test/visual/column-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/column-layout'; + +runTestSuites([suite]); diff --git a/test/visual/container-sticky.test.ts b/test/visual/container-sticky.test.ts new file mode 100644 index 0000000000..ce014d9805 --- /dev/null +++ b/test/visual/container-sticky.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/container-sticky'; + +runTestSuites([suite]); diff --git a/test/visual/container.test.ts b/test/visual/container.test.ts new file mode 100644 index 0000000000..d738bd8429 --- /dev/null +++ b/test/visual/container.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/container'; + +runTestSuites([suite]); diff --git a/test/visual/content-layout-permutations.test.ts b/test/visual/content-layout-permutations.test.ts new file mode 100644 index 0000000000..20de7543dc --- /dev/null +++ b/test/visual/content-layout-permutations.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/content-layout-permutations'; + +runTestSuites([suite]); diff --git a/test/visual/content-layout.test.ts b/test/visual/content-layout.test.ts new file mode 100644 index 0000000000..cc62ebf2a4 --- /dev/null +++ b/test/visual/content-layout.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/content-layout'; + +runTestSuites([suite]); diff --git a/test/visual/copy-to-clipboard.test.ts b/test/visual/copy-to-clipboard.test.ts new file mode 100644 index 0000000000..8f6f3afa5a --- /dev/null +++ b/test/visual/copy-to-clipboard.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/copy-to-clipboard'; + +runTestSuites([suite]); diff --git a/test/visual/date-input.test.ts b/test/visual/date-input.test.ts new file mode 100644 index 0000000000..d711364611 --- /dev/null +++ b/test/visual/date-input.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-input'; + +runTestSuites([suite]); diff --git a/test/visual/date-picker.test.ts b/test/visual/date-picker.test.ts new file mode 100644 index 0000000000..fd7087585b --- /dev/null +++ b/test/visual/date-picker.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-picker'; + +runTestSuites([suite]); diff --git a/test/visual/date-range-picker.test.ts b/test/visual/date-range-picker.test.ts new file mode 100644 index 0000000000..df5ed2d355 --- /dev/null +++ b/test/visual/date-range-picker.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/date-range-picker'; + +runTestSuites([suite]); diff --git a/test/visual/expandable-section.test.ts b/test/visual/expandable-section.test.ts new file mode 100644 index 0000000000..b052d5cf5f --- /dev/null +++ b/test/visual/expandable-section.test.ts @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { runTestSuites } from '../definitions/utils'; +import suite from '../definitions/visual/expandable-section'; + +runTestSuites([suite]);