From 87a2f9ce14389590c9f7c42a1a2203a2863fdb3e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 12:33:25 +0200 Subject: [PATCH 01/16] chore: Add visual tests for more components (C-I) --- test/definitions/index.ts | 57 ++++ test/definitions/visual/cards.ts | 16 ++ test/definitions/visual/checkbox.ts | 36 +++ test/definitions/visual/code-editor.ts | 67 +++++ .../visual/collection-preferences.ts | 65 +++++ test/definitions/visual/column-layout.ts | 31 +++ test/definitions/visual/container-sticky.ts | 68 +++++ test/definitions/visual/container.ts | 58 ++++ .../visual/content-layout-permutations.ts | 256 ++++++++++++++++++ test/definitions/visual/content-layout.ts | 59 ++++ test/definitions/visual/copy-to-clipboard.ts | 29 ++ test/definitions/visual/date-input.ts | 22 ++ test/definitions/visual/date-picker.ts | 54 ++++ test/definitions/visual/date-range-picker.ts | 107 ++++++++ test/definitions/visual/expandable-section.ts | 59 ++++ test/definitions/visual/flashbar-stacked.ts | 43 +++ test/definitions/visual/flashbar.ts | 56 ++++ test/definitions/visual/form-field.ts | 24 ++ test/definitions/visual/form.ts | 22 ++ test/definitions/visual/header.ts | 18 ++ test/definitions/visual/input.ts | 22 ++ test/definitions/visual/item-card.ts | 32 +++ test/visual/cards.test.ts | 6 + test/visual/checkbox.test.ts | 6 + test/visual/code-editor.test.ts | 6 + test/visual/collection-preferences.test.ts | 6 + test/visual/column-layout.test.ts | 6 + test/visual/container-sticky.test.ts | 6 + test/visual/container.test.ts | 6 + .../content-layout-permutations.test.ts | 6 + test/visual/content-layout.test.ts | 6 + test/visual/copy-to-clipboard.test.ts | 6 + test/visual/date-input.test.ts | 6 + test/visual/date-picker.test.ts | 6 + test/visual/date-range-picker.test.ts | 6 + test/visual/expandable-section.test.ts | 6 + test/visual/flashbar-stacked.test.ts | 6 + test/visual/flashbar.test.ts | 6 + test/visual/form-field.test.ts | 6 + test/visual/form.test.ts | 6 + test/visual/header.test.ts | 6 + test/visual/input.test.ts | 6 + test/visual/item-card.test.ts | 6 + 43 files changed, 1327 insertions(+) create mode 100644 test/definitions/visual/cards.ts create mode 100644 test/definitions/visual/checkbox.ts create mode 100644 test/definitions/visual/code-editor.ts create mode 100644 test/definitions/visual/collection-preferences.ts create mode 100644 test/definitions/visual/column-layout.ts create mode 100644 test/definitions/visual/container-sticky.ts create mode 100644 test/definitions/visual/container.ts create mode 100644 test/definitions/visual/content-layout-permutations.ts create mode 100644 test/definitions/visual/content-layout.ts create mode 100644 test/definitions/visual/copy-to-clipboard.ts create mode 100644 test/definitions/visual/date-input.ts create mode 100644 test/definitions/visual/date-picker.ts create mode 100644 test/definitions/visual/date-range-picker.ts create mode 100644 test/definitions/visual/expandable-section.ts create mode 100644 test/definitions/visual/flashbar-stacked.ts create mode 100644 test/definitions/visual/flashbar.ts create mode 100644 test/definitions/visual/form-field.ts create mode 100644 test/definitions/visual/form.ts create mode 100644 test/definitions/visual/header.ts create mode 100644 test/definitions/visual/input.ts create mode 100644 test/definitions/visual/item-card.ts create mode 100644 test/visual/cards.test.ts create mode 100644 test/visual/checkbox.test.ts create mode 100644 test/visual/code-editor.test.ts create mode 100644 test/visual/collection-preferences.test.ts create mode 100644 test/visual/column-layout.test.ts create mode 100644 test/visual/container-sticky.test.ts create mode 100644 test/visual/container.test.ts create mode 100644 test/visual/content-layout-permutations.test.ts create mode 100644 test/visual/content-layout.test.ts create mode 100644 test/visual/copy-to-clipboard.test.ts create mode 100644 test/visual/date-input.test.ts create mode 100644 test/visual/date-picker.test.ts create mode 100644 test/visual/date-range-picker.test.ts create mode 100644 test/visual/expandable-section.test.ts create mode 100644 test/visual/flashbar-stacked.test.ts create mode 100644 test/visual/flashbar.test.ts create mode 100644 test/visual/form-field.test.ts create mode 100644 test/visual/form.test.ts create mode 100644 test/visual/header.test.ts create mode 100644 test/visual/input.test.ts create mode 100644 test/visual/item-card.test.ts diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 51305af5b7..28a3cf7bfd 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -29,6 +29,27 @@ 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 codeEditorSuite from './visual/code-editor'; +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 flashbarSuite from './visual/flashbar'; +import flashbarStackedSuite from './visual/flashbar-stacked'; +import formSuite from './visual/form'; +import formFieldSuite from './visual/form-field'; +import headerSuite from './visual/header'; +import inputSuite from './visual/input'; +import itemCardSuite from './visual/item-card'; import tableSuite from './visual/table'; // Per-component exports (grouped by component) @@ -57,6 +78,24 @@ 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 codeEditor: TestSuite[] = [codeEditorSuite]; +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 flashbar: TestSuite[] = [flashbarSuite, flashbarStackedSuite]; +export const form: TestSuite[] = [formSuite]; +export const formField: TestSuite[] = [formFieldSuite]; +export const header: TestSuite[] = [headerSuite]; +export const input: TestSuite[] = [inputSuite]; +export const itemCard: TestSuite[] = [itemCardSuite]; export const table: TestSuite[] = [tableSuite]; export const allSuites: TestSuite[] = [ @@ -73,5 +112,23 @@ export const allSuites: TestSuite[] = [ ...button, ...buttonDropdown, ...buttonGroup, + ...cards, + ...checkbox, + ...codeEditor, + ...collectionPreferences, + ...columnLayout, + ...container, + ...contentLayout, + ...copyToClipboard, + ...dateInput, + ...datePicker, + ...dateRangePicker, + ...expandableSection, + ...flashbar, + ...form, + ...formField, + ...header, + ...input, + ...itemCard, ...table, ]; diff --git a/test/definitions/visual/cards.ts b/test/definitions/visual/cards.ts new file mode 100644 index 0000000000..5779061a6c --- /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' as const, + 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/code-editor.ts b/test/definitions/visual/code-editor.ts new file mode 100644 index 0000000000..a1550118e2 --- /dev/null +++ b/test/definitions/visual/code-editor.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 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..4748b24143 --- /dev/null +++ b/test/definitions/visual/collection-preferences.ts @@ -0,0 +1,65 @@ +// 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: 'CollectionPreferences', + componentName: 'collection-preferences', + tests: [ + ...( + [ + [600, 1100], + [1280, 700], + ] as [number, number][] + ).flatMap(([width, height]) => [ + { + description: `complete at ${width}x${height}`, + path: 'collection-preferences/simple', + screenshotType: 'viewport' as const, + 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' as const, + 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: '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..478ee9d0c2 --- /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' as const, + configuration: { width }, + }, + { + description: `permutations at "${breakpoint}"`, + path: 'column-layout/permutations', + screenshotType: 'permutations' as const, + 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..60b2a6f217 --- /dev/null +++ b/test/definitions/visual/container-sticky.ts @@ -0,0 +1,68 @@ +// 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 sticky permutations', + componentName: 'container', + tests: [1400, 600].flatMap(width => [ + { + description: `simple - at ${width}px`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + setup: async ({ page }) => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `with notifications - at ${width}px`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { hasNotifications: 'true' }, + setup: async ({ page }) => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `with breadcrumbs - at ${width}px`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { hasBreadcrumbs: 'true' }, + setup: async ({ page }) => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `with an alert - at ${width}px`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + setup: async ({ page }) => { + await page.windowScrollTo({ top: 200 }); + }, + }, + { + description: `with an alert - at ${width}px without scroll`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + queryParams: { hasNotifications: 'true', hasAlert: 'true' }, + }, + { + description: `with high-contrast header - at ${width}px`, + path: 'container/sticky-permutations', + screenshotType: 'viewport' as const, + configuration: { width }, + 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..5038b51381 --- /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'] as const).flatMap(position => + [465, 688, 1120].map(width => ({ + description: `media ${position} permutations at ${width}`, + path: `container/media-${position}-permutations`, + screenshotType: 'permutations' as const, + 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..c665254d47 --- /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'] as const).flatMap(headerVariant => + (['none', 'gradient', 'image'] as const).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'] as const).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..7f3a608d65 --- /dev/null +++ b/test/definitions/visual/content-layout.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 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'); + }, + }, + // without header (from content-layout.test.ts) + ...[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' as const, + 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..d8db12599e --- /dev/null +++ b/test/definitions/visual/copy-to-clipboard.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: '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, browser }) => { + await page.click('[aria-label="Copy dummy text"]'); + await browser!.execute((sel: string) => { + document.querySelector(sel)?.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..c2cc20645b --- /dev/null +++ b/test/definitions/visual/date-picker.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: '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: '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..c18c4a1750 --- /dev/null +++ b/test/definitions/visual/date-range-picker.ts @@ -0,0 +1,107 @@ +// 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, browser }) => { + await page.click('#focusable-before'); + await page.focusNextElement(); + await page.keys(['Enter']); + const headerSelector = wrapper.findDateRangePicker().findDropdown().findHeader().find('h2 span').toSelector(); + await browser!.execute((sel: string) => { + const el = document.querySelector(sel); + if (el) { + el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true })); + } + }, 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/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts new file mode 100644 index 0000000000..2e14e597fa --- /dev/null +++ b/test/definitions/visual/flashbar-stacked.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'; + +const widths = [380, 450, 600, 1200]; + +const suite: TestSuite = { + description: 'Flashbar stacked notifications', + componentName: 'flashbar', + tests: widths.flatMap(width => [ + { + description: `${width}px, collapsed`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + { + description: `${width}px, collapsed, notifications bar button focused`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + setup: async ({ page }) => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: `${width}px, expanded`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + pixelDiffTolerance: 5, + setup: async ({ page }) => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await page.waitForJsTimers(500); + }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts new file mode 100644 index 0000000000..af200176ff --- /dev/null +++ b/test/definitions/visual/flashbar.ts @@ -0,0 +1,56 @@ +// 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: 'Flashbar', + componentName: 'flashbar', + tests: [ + ...[600, 1280].flatMap(width => [ + { + description: `permutations at ${width}`, + path: 'flashbar/permutations', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + { + description: `runtime-action at ${width}`, + path: 'flashbar/runtime-action', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + ]), + { + 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..7e45e111eb --- /dev/null +++ b/test/definitions/visual/form-field.ts @@ -0,0 +1,24 @@ +// 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: 'FormField', + componentName: 'form-field', + tests: [576, 768, 992, 1200].flatMap(width => [ + { + description: `Permutations at ${width}px`, + path: 'form-field/permutations', + screenshotType: 'permutations' as const, + configuration: { width }, + }, + { + description: `Scenarios at ${width}px`, + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea' as const, + configuration: { width }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts new file mode 100644 index 0000000000..b5d155bbb6 --- /dev/null +++ b/test/definitions/visual/form.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: '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/header.ts b/test/definitions/visual/header.ts new file mode 100644 index 0000000000..d63eb6e16f --- /dev/null +++ b/test/definitions/visual/header.ts @@ -0,0 +1,18 @@ +// 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: 'Header', + componentName: 'header', + tests: [1, 2, 3].flatMap(level => + [1500, 850, 400].map(width => ({ + description: `level-${level} at ${width}px`, + path: `header/level-${level}`, + screenshotType: 'screenshotArea' as const, + configuration: { width }, + })) + ), +}; + +export default suite; diff --git a/test/definitions/visual/input.ts b/test/definitions/visual/input.ts new file mode 100644 index 0000000000..924414f2a2 --- /dev/null +++ b/test/definitions/visual/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: '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..3c6712c57b --- /dev/null +++ b/test/definitions/visual/item-card.ts @@ -0,0 +1,32 @@ +// 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: 'variant permutations', + path: 'item-card/variant-permutations', + screenshotType: 'permutations', + }, + { + description: 'padding permutations', + path: 'item-card/padding-permutations', + screenshotType: 'permutations', + }, + { + description: 'style custom', + path: 'item-card/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +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/code-editor.test.ts b/test/visual/code-editor.test.ts new file mode 100644 index 0000000000..735638ea5d --- /dev/null +++ b/test/visual/code-editor.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/code-editor'; + +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]); diff --git a/test/visual/flashbar-stacked.test.ts b/test/visual/flashbar-stacked.test.ts new file mode 100644 index 0000000000..5e37f288af --- /dev/null +++ b/test/visual/flashbar-stacked.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/flashbar-stacked'; + +runTestSuites([suite]); diff --git a/test/visual/flashbar.test.ts b/test/visual/flashbar.test.ts new file mode 100644 index 0000000000..d02aaae84c --- /dev/null +++ b/test/visual/flashbar.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/flashbar'; + +runTestSuites([suite]); diff --git a/test/visual/form-field.test.ts b/test/visual/form-field.test.ts new file mode 100644 index 0000000000..8406e9351b --- /dev/null +++ b/test/visual/form-field.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/form-field'; + +runTestSuites([suite]); diff --git a/test/visual/form.test.ts b/test/visual/form.test.ts new file mode 100644 index 0000000000..89a44f2ec8 --- /dev/null +++ b/test/visual/form.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/form'; + +runTestSuites([suite]); diff --git a/test/visual/header.test.ts b/test/visual/header.test.ts new file mode 100644 index 0000000000..c08e211517 --- /dev/null +++ b/test/visual/header.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/header'; + +runTestSuites([suite]); diff --git a/test/visual/input.test.ts b/test/visual/input.test.ts new file mode 100644 index 0000000000..361de80dd5 --- /dev/null +++ b/test/visual/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/input'; + +runTestSuites([suite]); diff --git a/test/visual/item-card.test.ts b/test/visual/item-card.test.ts new file mode 100644 index 0000000000..1c06f6493b --- /dev/null +++ b/test/visual/item-card.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/item-card'; + +runTestSuites([suite]); From 8f8f89c2c6d18b4c4ca8c8f4a39a20e4ab2492f0 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 17:44:26 +0200 Subject: [PATCH 02/16] Fix types --- test/definitions/visual/container-sticky.ts | 87 ++++++++------------- 1 file changed, 31 insertions(+), 56 deletions(-) diff --git a/test/definitions/visual/container-sticky.ts b/test/definitions/visual/container-sticky.ts index 60b2a6f217..d16f05262b 100644 --- a/test/definitions/visual/container-sticky.ts +++ b/test/definitions/visual/container-sticky.ts @@ -2,66 +2,41 @@ // 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 => [ - { - description: `simple - at ${width}px`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `with notifications - at ${width}px`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { hasNotifications: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `with breadcrumbs - at ${width}px`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { hasBreadcrumbs: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `with an alert - at ${width}px`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, - { - description: `with an alert - at ${width}px without scroll`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { hasNotifications: 'true', hasAlert: 'true' }, - }, - { - description: `with high-contrast header - at ${width}px`, - path: 'container/sticky-permutations', - screenshotType: 'viewport' as const, - configuration: { width }, - queryParams: { hasNotifications: 'true', highContrast: 'true' }, - setup: async ({ page }) => { - await page.windowScrollTo({ top: 200 }); - }, - }, + 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' }), ]), }; From 38d471790e86898f134a5dd4c4151ba738d7afee Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 18:59:39 +0200 Subject: [PATCH 03/16] Fix wait for Ace --- test/definitions/visual/code-editor.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index a1550118e2..b188ec4625 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -1,8 +1,21 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 +import VisualTestPageObject from '../page-object'; import { TestSuite } from '../types'; -const ACE_SELECTOR = '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright'; +async function waitForAceTheme(page: VisualTestPageObject) { + await page.waitForAssertion(async () => { + const found = await (page as any).browser.execute(() => { + const el: HTMLElement | null = document.querySelector( + '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' + ); + return el !== null && el.offsetHeight > 0; + }); + if (!found) { + throw new Error('Ace editor with theme class not found or not visible'); + } + }); +} const suite: TestSuite = { description: 'Code editor', @@ -13,7 +26,7 @@ const suite: TestSuite = { path: 'code-editor/simple', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await waitForAceTheme(page); }, }, { @@ -31,7 +44,7 @@ const suite: TestSuite = { path: 'code-editor/themes', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await waitForAceTheme(page); }, }, { @@ -39,7 +52,7 @@ const suite: TestSuite = { path: 'code-editor/permutations', screenshotType: 'permutations', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR + ' .ace_error'); + await waitForAceTheme(page); await page.waitForVisible('.ace_gutter-cell.ace_gutter-active-line.ace_error'); }, }, @@ -48,7 +61,7 @@ const suite: TestSuite = { path: 'code-editor/simple', screenshotType: 'screenshotArea', setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await waitForAceTheme(page); await page.click('#mode-toggle'); }, }, @@ -58,7 +71,7 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', configuration: { width: 360 }, setup: async ({ page }) => { - await page.waitForVisible(ACE_SELECTOR); + await waitForAceTheme(page); }, }, ], From 448c24d88c27725eec86647382ca729fd9269fe0 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 19:06:19 +0200 Subject: [PATCH 04/16] Remove some tests --- test/definitions/visual/flashbar-stacked.ts | 43 ---------------- test/definitions/visual/flashbar.ts | 56 --------------------- test/definitions/visual/form-field.ts | 24 --------- test/definitions/visual/form.ts | 22 -------- test/definitions/visual/header.ts | 18 ------- test/definitions/visual/input.ts | 22 -------- test/definitions/visual/item-card.ts | 32 ------------ test/visual/flashbar-stacked.test.ts | 6 --- test/visual/flashbar.test.ts | 6 --- test/visual/form-field.test.ts | 6 --- test/visual/form.test.ts | 6 --- test/visual/header.test.ts | 6 --- test/visual/input.test.ts | 6 --- test/visual/item-card.test.ts | 6 --- 14 files changed, 259 deletions(-) delete mode 100644 test/definitions/visual/flashbar-stacked.ts delete mode 100644 test/definitions/visual/flashbar.ts delete mode 100644 test/definitions/visual/form-field.ts delete mode 100644 test/definitions/visual/form.ts delete mode 100644 test/definitions/visual/header.ts delete mode 100644 test/definitions/visual/input.ts delete mode 100644 test/definitions/visual/item-card.ts delete mode 100644 test/visual/flashbar-stacked.test.ts delete mode 100644 test/visual/flashbar.test.ts delete mode 100644 test/visual/form-field.test.ts delete mode 100644 test/visual/form.test.ts delete mode 100644 test/visual/header.test.ts delete mode 100644 test/visual/input.test.ts delete mode 100644 test/visual/item-card.test.ts diff --git a/test/definitions/visual/flashbar-stacked.ts b/test/definitions/visual/flashbar-stacked.ts deleted file mode 100644 index 2e14e597fa..0000000000 --- a/test/definitions/visual/flashbar-stacked.ts +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import { TestDefinition, TestSuite } from '../types'; - -const widths = [380, 450, 600, 1200]; - -const suite: TestSuite = { - description: 'Flashbar stacked notifications', - componentName: 'flashbar', - tests: widths.flatMap(width => [ - { - description: `${width}px, collapsed`, - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - { - description: `${width}px, collapsed, notifications bar button focused`, - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - }, - }, - { - description: `${width}px, expanded`, - path: 'flashbar/collapsible.visual-tests', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - pixelDiffTolerance: 5, - setup: async ({ page }) => { - await page.click('#focus-target'); - await page.focusNextElement(); - await page.keys(['Space']); - await page.waitForJsTimers(500); - }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts deleted file mode 100644 index af200176ff..0000000000 --- a/test/definitions/visual/flashbar.ts +++ /dev/null @@ -1,56 +0,0 @@ -// 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: 'Flashbar', - componentName: 'flashbar', - tests: [ - ...[600, 1280].flatMap(width => [ - { - description: `permutations at ${width}`, - path: 'flashbar/permutations', - screenshotType: 'permutations' as const, - configuration: { width }, - }, - { - description: `runtime-action at ${width}`, - path: 'flashbar/runtime-action', - screenshotType: 'permutations' as const, - configuration: { width }, - }, - ]), - { - 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 deleted file mode 100644 index 7e45e111eb..0000000000 --- a/test/definitions/visual/form-field.ts +++ /dev/null @@ -1,24 +0,0 @@ -// 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: 'FormField', - componentName: 'form-field', - tests: [576, 768, 992, 1200].flatMap(width => [ - { - description: `Permutations at ${width}px`, - path: 'form-field/permutations', - screenshotType: 'permutations' as const, - configuration: { width }, - }, - { - description: `Scenarios at ${width}px`, - path: 'form-field/form-field-columns', - screenshotType: 'screenshotArea' as const, - configuration: { width }, - }, - ]), -}; - -export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts deleted file mode 100644 index b5d155bbb6..0000000000 --- a/test/definitions/visual/form.ts +++ /dev/null @@ -1,22 +0,0 @@ -// 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/header.ts b/test/definitions/visual/header.ts deleted file mode 100644 index d63eb6e16f..0000000000 --- a/test/definitions/visual/header.ts +++ /dev/null @@ -1,18 +0,0 @@ -// 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: 'Header', - componentName: 'header', - tests: [1, 2, 3].flatMap(level => - [1500, 850, 400].map(width => ({ - description: `level-${level} at ${width}px`, - path: `header/level-${level}`, - screenshotType: 'screenshotArea' as const, - configuration: { width }, - })) - ), -}; - -export default suite; diff --git a/test/definitions/visual/input.ts b/test/definitions/visual/input.ts deleted file mode 100644 index 924414f2a2..0000000000 --- a/test/definitions/visual/input.ts +++ /dev/null @@ -1,22 +0,0 @@ -// 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 deleted file mode 100644 index 3c6712c57b..0000000000 --- a/test/definitions/visual/item-card.ts +++ /dev/null @@ -1,32 +0,0 @@ -// 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: 'variant permutations', - path: 'item-card/variant-permutations', - screenshotType: 'permutations', - }, - { - description: 'padding permutations', - path: 'item-card/padding-permutations', - screenshotType: 'permutations', - }, - { - description: 'style custom', - path: 'item-card/style-custom', - screenshotType: 'screenshotArea', - }, - ], -}; - -export default suite; diff --git a/test/visual/flashbar-stacked.test.ts b/test/visual/flashbar-stacked.test.ts deleted file mode 100644 index 5e37f288af..0000000000 --- a/test/visual/flashbar-stacked.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/flashbar-stacked'; - -runTestSuites([suite]); diff --git a/test/visual/flashbar.test.ts b/test/visual/flashbar.test.ts deleted file mode 100644 index d02aaae84c..0000000000 --- a/test/visual/flashbar.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/flashbar'; - -runTestSuites([suite]); diff --git a/test/visual/form-field.test.ts b/test/visual/form-field.test.ts deleted file mode 100644 index 8406e9351b..0000000000 --- a/test/visual/form-field.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/form-field'; - -runTestSuites([suite]); diff --git a/test/visual/form.test.ts b/test/visual/form.test.ts deleted file mode 100644 index 89a44f2ec8..0000000000 --- a/test/visual/form.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/form'; - -runTestSuites([suite]); diff --git a/test/visual/header.test.ts b/test/visual/header.test.ts deleted file mode 100644 index c08e211517..0000000000 --- a/test/visual/header.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/header'; - -runTestSuites([suite]); diff --git a/test/visual/input.test.ts b/test/visual/input.test.ts deleted file mode 100644 index 361de80dd5..0000000000 --- a/test/visual/input.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/input'; - -runTestSuites([suite]); diff --git a/test/visual/item-card.test.ts b/test/visual/item-card.test.ts deleted file mode 100644 index 1c06f6493b..0000000000 --- a/test/visual/item-card.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/item-card'; - -runTestSuites([suite]); From e62ba3182df2dffd9ee58673ac6efafc3941b288 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 19:06:35 +0200 Subject: [PATCH 05/16] Increase shards --- .github/workflows/visual-regression.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 }} From d1f1a74028c9ac26757373e7e8cabff2561d3ba5 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 24 Jul 2026 19:18:31 +0200 Subject: [PATCH 06/16] Missing changes --- test/definitions/index.ts | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 28a3cf7bfd..940ddafb0e 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -43,13 +43,6 @@ 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 flashbarSuite from './visual/flashbar'; -import flashbarStackedSuite from './visual/flashbar-stacked'; -import formSuite from './visual/form'; -import formFieldSuite from './visual/form-field'; -import headerSuite from './visual/header'; -import inputSuite from './visual/input'; -import itemCardSuite from './visual/item-card'; import tableSuite from './visual/table'; // Per-component exports (grouped by component) @@ -90,12 +83,6 @@ export const dateInput: TestSuite[] = [dateInputSuite]; export const datePicker: TestSuite[] = [datePickerSuite]; export const dateRangePicker: TestSuite[] = [dateRangePickerSuite]; export const expandableSection: TestSuite[] = [expandableSectionSuite]; -export const flashbar: TestSuite[] = [flashbarSuite, flashbarStackedSuite]; -export const form: TestSuite[] = [formSuite]; -export const formField: TestSuite[] = [formFieldSuite]; -export const header: TestSuite[] = [headerSuite]; -export const input: TestSuite[] = [inputSuite]; -export const itemCard: TestSuite[] = [itemCardSuite]; export const table: TestSuite[] = [tableSuite]; export const allSuites: TestSuite[] = [ @@ -124,11 +111,5 @@ export const allSuites: TestSuite[] = [ ...datePicker, ...dateRangePicker, ...expandableSection, - ...flashbar, - ...form, - ...formField, - ...header, - ...input, - ...itemCard, ...table, ]; From 9ed7b1df329890f0b20d4585cfd0357692fa84ea Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 27 Jul 2026 08:45:54 +0200 Subject: [PATCH 07/16] Fix waiting for Ace --- test/definitions/visual/code-editor.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index b188ec4625..f21c8c4a4a 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -4,8 +4,21 @@ import VisualTestPageObject from '../page-object'; import { TestSuite } from '../types'; async function waitForAceTheme(page: VisualTestPageObject) { + // Force a full page reload so that the CSP meta tag is re-evaluated + // with the code-editor hash (which adds worker-src: blob:). + // Without this, hash-only navigation from a prior test leaves the old CSP + // in place and ace's web worker is blocked. + const browser = (page as any).browser; + // Navigate away and back to guarantee a fresh document load. + // Unlike location.reload(), this is synchronous from WebdriverIO's + // perspective — the subsequent browser.url() won't execute until + // about:blank has fully loaded, ensuring no stale DOM races. + const currentUrl = await browser.getUrl(); + await browser.url('about:blank'); + await browser.url(currentUrl); + await page.waitForVisible('.screenshot-area'); await page.waitForAssertion(async () => { - const found = await (page as any).browser.execute(() => { + const found = await browser.execute(() => { const el: HTMLElement | null = document.querySelector( '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' ); From 369da294cccc09689f1d52ba451335b252a9c06e Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 27 Jul 2026 09:13:29 +0200 Subject: [PATCH 08/16] Wait for longer --- test/definitions/visual/code-editor.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index f21c8c4a4a..31ec1d3c27 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -17,17 +17,19 @@ async function waitForAceTheme(page: VisualTestPageObject) { await browser.url('about:blank'); await browser.url(currentUrl); await page.waitForVisible('.screenshot-area'); - await page.waitForAssertion(async () => { - const found = await browser.execute(() => { - const el: HTMLElement | null = document.querySelector( - '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' - ); - return el !== null && el.offsetHeight > 0; - }); - if (!found) { - throw new Error('Ace editor with theme class not found or not visible'); - } - }); + // Ace loads asynchronously and needs time to initialize + apply theme. + // waitForAssertion only retries 5 times (~500ms) which isn't enough in CI. + // Use waitUntil with a generous timeout instead. + await browser.waitUntil( + () => + browser.execute(() => { + const el: HTMLElement | null = document.querySelector( + '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' + ); + return el !== null && el.offsetHeight > 0; + }), + { timeout: 30000, timeoutMsg: 'Ace editor with theme class not found or not visible within 30s' } + ); } const suite: TestSuite = { From 7a214dc047d0fe895e512f3316f33f62f02ef1b9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 27 Jul 2026 10:12:54 +0200 Subject: [PATCH 09/16] Do not reuse browser --- test/definitions/utils.ts | 116 ++++++++++++------------- test/definitions/visual/code-editor.ts | 17 +--- 2 files changed, 59 insertions(+), 74 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index a70701ff1e..1e22d52b03 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -60,81 +60,79 @@ async function preparePage( } 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); + registerSuites(suites); } -function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { +function registerSuites(suites: Array) { for (const item of suites) { if (isTestDefinition(item)) { - registerTest(item, getBrowser); + registerTest(item); } else { describe(item.description, () => { - registerSuites(item.tests, getBrowser); + registerSuites(item.tests); }); } } } -function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { +function registerTest(testDef: TestDefinition) { test(testDef.description, async () => { - const browser = getBrowser(); - const page = new VisualTestPageObject(browser); - - const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); - const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - - const tolerance = testDef.pixelDiffTolerance ?? 0; + const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); + const creator = getBrowserCreator('ChromeHeadlessIntegration', 'local', { + seleniumUrl: 'http://localhost:9515', + }); + const windowSize = { + width: testDef.configuration?.width ?? defaultWindowSize.width, + height: testDef.configuration?.height ?? defaultWindowSize.height, + }; + const browser = await creator.getBrowser(windowSize); + try { + const page = new VisualTestPageObject(browser); + + const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); + const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + + const tolerance = testDef.pixelDiffTolerance ?? 0; + + if (testDef.screenshotType === 'permutations') { + await preparePage(browser, page, newUrl, testDef, testDef.configuration); + const newPermutations = await page.capturePermutations(); + + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + const oldPermutations = await page.capturePermutations(); + + expect(newPermutations.length).toBe(oldPermutations.length); + + // Compare each permutation individually, wrapping each in an Allure step. + let failures = 0; + for (let i = 0; i < newPermutations.length; i++) { + const id = newPermutations[i].id || ''; + const index = `#${(i + 1).toString().padStart(3, '0')}`; + await step(`Permutation ${index}`, async () => { + const permResult = await compareScreenshots(newPermutations[i], oldPermutations[i]); + await attachDiffImages(permResult, index); + if (permResult.diffPixels > tolerance) { + failures++; + throw new Error(`Permutation ${index} differs by ${permResult.diffPixels} pixels\n${id}`); + } + }); + } + expect(failures).toBe(0); + return; + } - if (testDef.screenshotType === 'permutations') { + // Non-permutation: single screenshot comparison await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPermutations = await page.capturePermutations(); + const newRaw = await captureSingleScreenshot(page, testDef); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPermutations = await page.capturePermutations(); - - expect(newPermutations.length).toBe(oldPermutations.length); - - // Compare each permutation individually, wrapping each in an Allure step. - let failures = 0; - for (let i = 0; i < newPermutations.length; i++) { - const id = newPermutations[i].id || ''; - const index = `#${(i + 1).toString().padStart(3, '0')}`; - await step(`Permutation ${index}`, async () => { - const permResult = await compareScreenshots(newPermutations[i], oldPermutations[i]); - await attachDiffImages(permResult, index); - if (permResult.diffPixels > tolerance) { - failures++; - throw new Error(`Permutation ${index} differs by ${permResult.diffPixels} pixels\n${id}`); - } - }); - } - expect(failures).toBe(0); - return; - } - - // Non-permutation: single screenshot comparison - await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newRaw = await captureSingleScreenshot(page, testDef); + const oldRaw = await captureSingleScreenshot(page, testDef); - await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldRaw = await captureSingleScreenshot(page, testDef); - - const result = await compareScreenshots(newRaw, oldRaw); - await attachDiffImages(result, testDef.description); - expect(result.diffPixels).toBeLessThanOrEqual(tolerance); + const result = await compareScreenshots(newRaw, oldRaw); + await attachDiffImages(result, testDef.description); + expect(result.diffPixels).toBeLessThanOrEqual(tolerance); + } finally { + await browser.deleteSession(); + } }); } diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index 31ec1d3c27..66569322c1 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -4,22 +4,9 @@ import VisualTestPageObject from '../page-object'; import { TestSuite } from '../types'; async function waitForAceTheme(page: VisualTestPageObject) { - // Force a full page reload so that the CSP meta tag is re-evaluated - // with the code-editor hash (which adds worker-src: blob:). - // Without this, hash-only navigation from a prior test leaves the old CSP - // in place and ace's web worker is blocked. + // Each test gets a fresh browser session, so CSP is always correct. + // Ace loads asynchronously — wait for it to initialize and apply the theme. const browser = (page as any).browser; - // Navigate away and back to guarantee a fresh document load. - // Unlike location.reload(), this is synchronous from WebdriverIO's - // perspective — the subsequent browser.url() won't execute until - // about:blank has fully loaded, ensuring no stale DOM races. - const currentUrl = await browser.getUrl(); - await browser.url('about:blank'); - await browser.url(currentUrl); - await page.waitForVisible('.screenshot-area'); - // Ace loads asynchronously and needs time to initialize + apply theme. - // waitForAssertion only retries 5 times (~500ms) which isn't enough in CI. - // Use waitUntil with a generous timeout instead. await browser.waitUntil( () => browser.execute(() => { From f0b1892311a638690c3b130aa9ded977fced33b9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Mon, 27 Jul 2026 10:39:50 +0200 Subject: [PATCH 10/16] Debug --- test/definitions/visual/code-editor.ts | 37 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts index 66569322c1..9f6fb46785 100644 --- a/test/definitions/visual/code-editor.ts +++ b/test/definitions/visual/code-editor.ts @@ -7,16 +7,33 @@ async function waitForAceTheme(page: VisualTestPageObject) { // Each test gets a fresh browser session, so CSP is always correct. // Ace loads asynchronously — wait for it to initialize and apply the theme. const browser = (page as any).browser; - await browser.waitUntil( - () => - browser.execute(() => { - const el: HTMLElement | null = document.querySelector( - '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' - ); - return el !== null && el.offsetHeight > 0; - }), - { timeout: 30000, timeoutMsg: 'Ace editor with theme class not found or not visible within 30s' } - ); + try { + await browser.waitUntil( + () => + browser.execute(() => { + const el: HTMLElement | null = document.querySelector( + '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' + ); + return el !== null && el.offsetHeight > 0; + }), + { timeout: 30000, timeoutMsg: 'Ace editor with theme class not found or not visible within 30s' } + ); + } catch (e) { + // Capture a debug screenshot and page state on failure + const screenshot = await browser.takeScreenshot(); + const debugInfo = await browser.execute(() => { + const ace = document.querySelector('.ace_editor'); + const classes = ace ? ace.className : 'NO .ace_editor ELEMENT FOUND'; + const cspMeta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); + const csp = cspMeta ? cspMeta.getAttribute('content') : 'NO CSP META TAG'; + const url = location.href; + return { classes, csp, url, bodyHTML: document.body.innerHTML.slice(0, 2000) }; + }); + const { attachment } = await import('allure-js-commons'); + await attachment('debug-screenshot-on-failure', Buffer.from(screenshot, 'base64'), 'image/png'); + await attachment('debug-page-state', JSON.stringify(debugInfo, null, 2), 'application/json'); + throw e; + } } const suite: TestSuite = { From 8da4ec2843f988104c8d7b63187fa11d7a05fea9 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 30 Jul 2026 18:01:20 +0200 Subject: [PATCH 11/16] Revert changes in utils --- test/definitions/utils.ts | 116 +++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/test/definitions/utils.ts b/test/definitions/utils.ts index 1e22d52b03..a70701ff1e 100644 --- a/test/definitions/utils.ts +++ b/test/definitions/utils.ts @@ -60,79 +60,81 @@ async function preparePage( } export function runTestSuites(suites: Array) { - registerSuites(suites); + 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) { +function registerSuites(suites: Array, getBrowser: () => WebdriverIO.Browser) { for (const item of suites) { if (isTestDefinition(item)) { - registerTest(item); + registerTest(item, getBrowser); } else { describe(item.description, () => { - registerSuites(item.tests); + registerSuites(item.tests, getBrowser); }); } } } -function registerTest(testDef: TestDefinition) { +function registerTest(testDef: TestDefinition, getBrowser: () => WebdriverIO.Browser) { test(testDef.description, async () => { - const { default: getBrowserCreator } = await import('@cloudscape-design/browser-test-tools/browser'); - const creator = getBrowserCreator('ChromeHeadlessIntegration', 'local', { - seleniumUrl: 'http://localhost:9515', - }); - const windowSize = { - width: testDef.configuration?.width ?? defaultWindowSize.width, - height: testDef.configuration?.height ?? defaultWindowSize.height, - }; - const browser = await creator.getBrowser(windowSize); - try { - const page = new VisualTestPageObject(browser); - - const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); - const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); - - const tolerance = testDef.pixelDiffTolerance ?? 0; - - if (testDef.screenshotType === 'permutations') { - await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newPermutations = await page.capturePermutations(); - - await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldPermutations = await page.capturePermutations(); - - expect(newPermutations.length).toBe(oldPermutations.length); - - // Compare each permutation individually, wrapping each in an Allure step. - let failures = 0; - for (let i = 0; i < newPermutations.length; i++) { - const id = newPermutations[i].id || ''; - const index = `#${(i + 1).toString().padStart(3, '0')}`; - await step(`Permutation ${index}`, async () => { - const permResult = await compareScreenshots(newPermutations[i], oldPermutations[i]); - await attachDiffImages(permResult, index); - if (permResult.diffPixels > tolerance) { - failures++; - throw new Error(`Permutation ${index} differs by ${permResult.diffPixels} pixels\n${id}`); - } - }); - } - expect(failures).toBe(0); - return; - } + const browser = getBrowser(); + const page = new VisualTestPageObject(browser); + + const newUrl = buildUrl(newHost, testDef.path, testDef.queryParams); + const oldUrl = buildUrl(oldHost, testDef.path, testDef.queryParams); + + const tolerance = testDef.pixelDiffTolerance ?? 0; - // Non-permutation: single screenshot comparison + if (testDef.screenshotType === 'permutations') { await preparePage(browser, page, newUrl, testDef, testDef.configuration); - const newRaw = await captureSingleScreenshot(page, testDef); + const newPermutations = await page.capturePermutations(); await preparePage(browser, page, oldUrl, testDef, testDef.configuration); - const oldRaw = await captureSingleScreenshot(page, testDef); - - const result = await compareScreenshots(newRaw, oldRaw); - await attachDiffImages(result, testDef.description); - expect(result.diffPixels).toBeLessThanOrEqual(tolerance); - } finally { - await browser.deleteSession(); + const oldPermutations = await page.capturePermutations(); + + expect(newPermutations.length).toBe(oldPermutations.length); + + // Compare each permutation individually, wrapping each in an Allure step. + let failures = 0; + for (let i = 0; i < newPermutations.length; i++) { + const id = newPermutations[i].id || ''; + const index = `#${(i + 1).toString().padStart(3, '0')}`; + await step(`Permutation ${index}`, async () => { + const permResult = await compareScreenshots(newPermutations[i], oldPermutations[i]); + await attachDiffImages(permResult, index); + if (permResult.diffPixels > tolerance) { + failures++; + throw new Error(`Permutation ${index} differs by ${permResult.diffPixels} pixels\n${id}`); + } + }); + } + expect(failures).toBe(0); + return; } + + // Non-permutation: single screenshot comparison + await preparePage(browser, page, newUrl, testDef, testDef.configuration); + const newRaw = await captureSingleScreenshot(page, testDef); + + await preparePage(browser, page, oldUrl, testDef, testDef.configuration); + const oldRaw = await captureSingleScreenshot(page, testDef); + + const result = await compareScreenshots(newRaw, oldRaw); + await attachDiffImages(result, testDef.description); + expect(result.diffPixels).toBeLessThanOrEqual(tolerance); }); } From bed45f1541f1e2e93e6b19364871d204d1652d13 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 30 Jul 2026 18:01:38 +0200 Subject: [PATCH 12/16] Exclude code editor --- test/definitions/index.ts | 3 - test/definitions/visual/code-editor.ts | 99 -------------------------- test/visual/code-editor.test.ts | 6 -- 3 files changed, 108 deletions(-) delete mode 100644 test/definitions/visual/code-editor.ts delete mode 100644 test/visual/code-editor.test.ts diff --git a/test/definitions/index.ts b/test/definitions/index.ts index 940ddafb0e..a71cac4244 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -31,7 +31,6 @@ import buttonDropdownSuite from './visual/button-dropdown'; import buttonGroupSuite from './visual/button-group'; import cardsSuite from './visual/cards'; import checkboxSuite from './visual/checkbox'; -import codeEditorSuite from './visual/code-editor'; import collectionPreferencesSuite from './visual/collection-preferences'; import columnLayoutSuite from './visual/column-layout'; import containerSuite from './visual/container'; @@ -73,7 +72,6 @@ export const buttonDropdown: TestSuite[] = [buttonDropdownSuite]; export const buttonGroup: TestSuite[] = [buttonGroupSuite]; export const cards: TestSuite[] = [cardsSuite]; export const checkbox: TestSuite[] = [checkboxSuite]; -export const codeEditor: TestSuite[] = [codeEditorSuite]; export const collectionPreferences: TestSuite[] = [collectionPreferencesSuite]; export const columnLayout: TestSuite[] = [columnLayoutSuite]; export const container: TestSuite[] = [containerSuite, containerStickySuite]; @@ -101,7 +99,6 @@ export const allSuites: TestSuite[] = [ ...buttonGroup, ...cards, ...checkbox, - ...codeEditor, ...collectionPreferences, ...columnLayout, ...container, diff --git a/test/definitions/visual/code-editor.ts b/test/definitions/visual/code-editor.ts deleted file mode 100644 index 9f6fb46785..0000000000 --- a/test/definitions/visual/code-editor.ts +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 -import VisualTestPageObject from '../page-object'; -import { TestSuite } from '../types'; - -async function waitForAceTheme(page: VisualTestPageObject) { - // Each test gets a fresh browser session, so CSP is always correct. - // Ace loads asynchronously — wait for it to initialize and apply the theme. - const browser = (page as any).browser; - try { - await browser.waitUntil( - () => - browser.execute(() => { - const el: HTMLElement | null = document.querySelector( - '.ace_editor.ace-dawn, .ace_editor.ace-tomorrow-night-bright' - ); - return el !== null && el.offsetHeight > 0; - }), - { timeout: 30000, timeoutMsg: 'Ace editor with theme class not found or not visible within 30s' } - ); - } catch (e) { - // Capture a debug screenshot and page state on failure - const screenshot = await browser.takeScreenshot(); - const debugInfo = await browser.execute(() => { - const ace = document.querySelector('.ace_editor'); - const classes = ace ? ace.className : 'NO .ace_editor ELEMENT FOUND'; - const cspMeta = document.querySelector('meta[http-equiv="Content-Security-Policy"]'); - const csp = cspMeta ? cspMeta.getAttribute('content') : 'NO CSP META TAG'; - const url = location.href; - return { classes, csp, url, bodyHTML: document.body.innerHTML.slice(0, 2000) }; - }); - const { attachment } = await import('allure-js-commons'); - await attachment('debug-screenshot-on-failure', Buffer.from(screenshot, 'base64'), 'image/png'); - await attachment('debug-page-state', JSON.stringify(debugInfo, null, 2), 'application/json'); - throw e; - } -} - -const suite: TestSuite = { - description: 'Code editor', - componentName: 'code-editor', - tests: [ - { - description: 'simple', - path: 'code-editor/simple', - screenshotType: 'screenshotArea', - setup: async ({ page }) => { - await waitForAceTheme(page); - }, - }, - { - 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 waitForAceTheme(page); - }, - }, - { - description: 'permutations', - path: 'code-editor/permutations', - screenshotType: 'permutations', - setup: async ({ page }) => { - await waitForAceTheme(page); - 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 waitForAceTheme(page); - await page.click('#mode-toggle'); - }, - }, - { - description: 'compare simple on small screen', - path: 'code-editor/simple', - screenshotType: 'screenshotArea', - configuration: { width: 360 }, - setup: async ({ page }) => { - await waitForAceTheme(page); - }, - }, - ], -}; - -export default suite; diff --git a/test/visual/code-editor.test.ts b/test/visual/code-editor.test.ts deleted file mode 100644 index 735638ea5d..0000000000 --- a/test/visual/code-editor.test.ts +++ /dev/null @@ -1,6 +0,0 @@ -// 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/code-editor'; - -runTestSuites([suite]); From e464851b91cb65b133b190ab8da8f48fc65e0cdb Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Thu, 30 Jul 2026 18:49:36 +0200 Subject: [PATCH 13/16] Refactor --- test/definitions/page-object.ts | 8 ++ test/definitions/visual/app-layout-z-index.ts | 12 +-- test/definitions/visual/cards.ts | 2 +- .../visual/collection-preferences.ts | 90 ++++++++++--------- test/definitions/visual/column-layout.ts | 4 +- test/definitions/visual/container.ts | 4 +- .../visual/content-layout-permutations.ts | 6 +- test/definitions/visual/content-layout.ts | 3 +- test/definitions/visual/copy-to-clipboard.ts | 6 +- test/definitions/visual/date-picker.ts | 65 ++++++++------ 10 files changed, 111 insertions(+), 89 deletions(-) diff --git a/test/definitions/page-object.ts b/test/definitions/page-object.ts index f06c428ec9..ca8a148856 100644 --- a/test/definitions/page-object.ts +++ b/test/definitions/page-object.ts @@ -22,4 +22,12 @@ export default class VisualTestPageObject extends RawScreenshotPageObject { }); await this.waitForJsTimers(500); } + + /** + * 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 index 5779061a6c..ac3f806fdb 100644 --- a/test/definitions/visual/cards.ts +++ b/test/definitions/visual/cards.ts @@ -8,7 +8,7 @@ const suite: TestSuite = { tests: [2200, 1920, 1400, 1200, 992, 768].map(width => ({ description: `permutations at ${width}`, path: 'cards/permutations', - screenshotType: 'permutations' as const, + screenshotType: 'permutations', configuration: { width }, })), }; diff --git a/test/definitions/visual/collection-preferences.ts b/test/definitions/visual/collection-preferences.ts index 4748b24143..e0ce182718 100644 --- a/test/definitions/visual/collection-preferences.ts +++ b/test/definitions/visual/collection-preferences.ts @@ -1,35 +1,36 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import { TestDefinition, TestSuite } from '../types'; +import { TestSuite } from '../types'; const suite: TestSuite = { description: 'CollectionPreferences', componentName: 'collection-preferences', tests: [ - ...( - [ - [600, 1100], - [1280, 700], - ] as [number, number][] - ).flatMap(([width, height]) => [ - { - description: `complete at ${width}x${height}`, - path: 'collection-preferences/simple', - screenshotType: 'viewport' as const, - configuration: { width, height }, - setup: async ({ page }) => { - await page.click('.cp-1 button'); + ...[ + [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' as const, - setup: async ({ page }) => { - await page.click('.cp-4 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', @@ -39,25 +40,30 @@ const suite: TestSuite = { }, }, { - 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']); - }, + 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']); + }, + }, + ], }, ], }; diff --git a/test/definitions/visual/column-layout.ts b/test/definitions/visual/column-layout.ts index 478ee9d0c2..a32a49d388 100644 --- a/test/definitions/visual/column-layout.ts +++ b/test/definitions/visual/column-layout.ts @@ -16,13 +16,13 @@ const suite: TestSuite = { { description: `column-layout at "${breakpoint}"`, path: 'column-layout/simple', - screenshotType: 'screenshotArea' as const, + screenshotType: 'screenshotArea', configuration: { width }, }, { description: `permutations at "${breakpoint}"`, path: 'column-layout/permutations', - screenshotType: 'permutations' as const, + screenshotType: 'permutations', configuration: { width }, }, ]), diff --git a/test/definitions/visual/container.ts b/test/definitions/visual/container.ts index 5038b51381..6a46f2e331 100644 --- a/test/definitions/visual/container.ts +++ b/test/definitions/visual/container.ts @@ -34,11 +34,11 @@ const suite: TestSuite = { screenshotType: 'screenshotArea', queryParams: { position: 'top', height: '150px', content: '4-3' }, }, - ...(['side', 'top'] as const).flatMap(position => + ...['side', 'top'].flatMap(position => [465, 688, 1120].map(width => ({ description: `media ${position} permutations at ${width}`, path: `container/media-${position}-permutations`, - screenshotType: 'permutations' as const, + screenshotType: 'permutations', configuration: { width }, })) ), diff --git a/test/definitions/visual/content-layout-permutations.ts b/test/definitions/visual/content-layout-permutations.ts index c665254d47..41722859cf 100644 --- a/test/definitions/visual/content-layout-permutations.ts +++ b/test/definitions/visual/content-layout-permutations.ts @@ -56,8 +56,8 @@ const suite: TestSuite = { ) ), // maxContentWidth variants - ...(['default', 'high-contrast'] as const).flatMap(headerVariant => - (['none', 'gradient', 'image'] as const).map(headerBackgroundStyle => + ...['default', 'high-contrast'].flatMap(headerVariant => + ['none', 'gradient', 'image'].map(headerBackgroundStyle => contentLayoutPermutation({ headerVariant, headerBackgroundStyle, @@ -195,7 +195,7 @@ const suite: TestSuite = { // without header ...[1400, 600].flatMap(width => [true, false].flatMap(defaultPadding => - (['default', 'high-contrast', 'divider'] as const).map(headerVariant => + ['default', 'high-contrast', 'divider'].map(headerVariant => contentLayoutPermutation( { headerVariant, diff --git a/test/definitions/visual/content-layout.ts b/test/definitions/visual/content-layout.ts index 7f3a608d65..91486938a3 100644 --- a/test/definitions/visual/content-layout.ts +++ b/test/definitions/visual/content-layout.ts @@ -35,14 +35,13 @@ const suite: TestSuite = { await page.click('[data-testid="toggle-content-layout"] input'); }, }, - // without header (from content-layout.test.ts) ...[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' as const, + screenshotType: 'screenshotArea', configuration: { width }, queryParams: { hasBreadcrumbs: `${hasBreadcrumbs}`, diff --git a/test/definitions/visual/copy-to-clipboard.ts b/test/definitions/visual/copy-to-clipboard.ts index d8db12599e..6014ac4a8c 100644 --- a/test/definitions/visual/copy-to-clipboard.ts +++ b/test/definitions/visual/copy-to-clipboard.ts @@ -16,11 +16,9 @@ const suite: TestSuite = { path: 'copy-to-clipboard/scenario-split-panel', screenshotType: 'screenshotArea', configuration: { width: 1280, height: 900 }, - setup: async ({ page, browser }) => { + setup: async ({ page }) => { await page.click('[aria-label="Copy dummy text"]'); - await browser!.execute((sel: string) => { - document.querySelector(sel)?.scrollIntoView(); - }, '[data-testid="scroll-me"]'); + await page.scrollIntoView('[data-testid="scroll-me"]'); }, }, ], diff --git a/test/definitions/visual/date-picker.ts b/test/definitions/visual/date-picker.ts index c2cc20645b..368cac7d0e 100644 --- a/test/definitions/visual/date-picker.ts +++ b/test/definitions/visual/date-picker.ts @@ -1,7 +1,16 @@ // 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', @@ -20,33 +29,35 @@ const suite: TestSuite = { screenshotType: 'permutations', }, { - 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']); - }, + 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']); + }, + }, + ], }, ], }; From 2d96313319ad90f96e3a4c5c239b4596cbf646de Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 31 Jul 2026 07:08:37 +0200 Subject: [PATCH 14/16] Add doubleClick method --- test/definitions/page-object.ts | 5 +++++ test/definitions/visual/date-range-picker.ts | 9 ++------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/definitions/page-object.ts b/test/definitions/page-object.ts index ca8a148856..62a0fd174a 100644 --- a/test/definitions/page-object.ts +++ b/test/definitions/page-object.ts @@ -23,6 +23,11 @@ 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. */ diff --git a/test/definitions/visual/date-range-picker.ts b/test/definitions/visual/date-range-picker.ts index c18c4a1750..a6180c8016 100644 --- a/test/definitions/visual/date-range-picker.ts +++ b/test/definitions/visual/date-range-picker.ts @@ -71,17 +71,12 @@ const suite: TestSuite = { description: 'selects text when double-clicking calendar header', path: 'date-range-picker/with-value', screenshotType: 'screenshotArea', - setup: async ({ page, wrapper, browser }) => { + 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 browser!.execute((sel: string) => { - const el = document.querySelector(sel); - if (el) { - el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true })); - } - }, headerSelector); + await page.doubleClick(headerSelector); }, }, { From 00954389da8a0447d95cdced49615b487b602dc1 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 31 Jul 2026 08:28:18 +0200 Subject: [PATCH 15/16] chore: Fix area chart test in Safari --- test/definitions/visual/area-chart.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/definitions/visual/area-chart.ts b/test/definitions/visual/area-chart.ts index 812e362a1f..75ec2f55f7 100644 --- a/test/definitions/visual/area-chart.ts +++ b/test/definitions/visual/area-chart.ts @@ -89,7 +89,8 @@ const suite: TestSuite = { await page.keys(['Tab']); await page.keys(['Tab']); await page.keys(['ArrowRight']); - await page.keys(['Shift', 'Tab']); + // 'Null' releases Shift so the next ArrowRight isn't interpreted as Shift+ArrowRight in Safari. + await page.keys(['Shift', 'Tab', 'Null']); await page.keys(['ArrowRight']); await page.waitForVisible(TEST_CHART_TOOLTIP_HEADER); }, From 992d201fa0b8e0f31cf47d35e38638eb00de14f6 Mon Sep 17 00:00:00 2001 From: Joan Perals Tresserra Date: Fri, 31 Jul 2026 16:48:03 +0200 Subject: [PATCH 16/16] chore: Add visual regression tests for more components (D-R) --- test/definitions/index.ts | 55 +++++++++++++++++++ test/definitions/visual/divider.ts | 17 ++++++ test/definitions/visual/error-boundary.ts | 17 ++++++ test/definitions/visual/file-dropzone.ts | 17 ++++++ test/definitions/visual/file-input.ts | 17 ++++++ test/definitions/visual/file-token-group.ts | 17 ++++++ test/definitions/visual/file-upload.ts | 17 ++++++ test/definitions/visual/flashbar-stacked.ts | 40 ++++++++++++++ test/definitions/visual/flashbar.ts | 59 +++++++++++++++++++++ test/definitions/visual/form-field.ts | 27 ++++++++++ test/definitions/visual/form.ts | 22 ++++++++ test/definitions/visual/grid.ts | 23 ++++++++ test/definitions/visual/header.ts | 18 +++++++ test/definitions/visual/help-panel.ts | 36 +++++++++++++ test/definitions/visual/icon.ts | 38 +++++++++++++ test/definitions/visual/input.ts | 22 ++++++++ test/definitions/visual/link.ts | 37 +++++++++++++ test/definitions/visual/pagination.ts | 17 ++++++ test/definitions/visual/progress-bar.ts | 30 +++++++++++ test/definitions/visual/radio-group.ts | 45 ++++++++++++++++ 20 files changed, 571 insertions(+) create mode 100644 test/definitions/visual/divider.ts create mode 100644 test/definitions/visual/error-boundary.ts create mode 100644 test/definitions/visual/file-dropzone.ts create mode 100644 test/definitions/visual/file-input.ts create mode 100644 test/definitions/visual/file-token-group.ts create mode 100644 test/definitions/visual/file-upload.ts create mode 100644 test/definitions/visual/flashbar-stacked.ts create mode 100644 test/definitions/visual/flashbar.ts create mode 100644 test/definitions/visual/form-field.ts create mode 100644 test/definitions/visual/form.ts create mode 100644 test/definitions/visual/grid.ts create mode 100644 test/definitions/visual/header.ts create mode 100644 test/definitions/visual/help-panel.ts create mode 100644 test/definitions/visual/icon.ts create mode 100644 test/definitions/visual/input.ts create mode 100644 test/definitions/visual/link.ts create mode 100644 test/definitions/visual/pagination.ts create mode 100644 test/definitions/visual/progress-bar.ts create mode 100644 test/definitions/visual/radio-group.ts diff --git a/test/definitions/index.ts b/test/definitions/index.ts index a71cac4244..3e92b723f8 100644 --- a/test/definitions/index.ts +++ b/test/definitions/index.ts @@ -41,7 +41,26 @@ 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 dividerSuite from './visual/divider'; +import errorBoundarySuite from './visual/error-boundary'; import expandableSectionSuite from './visual/expandable-section'; +import fileDropzoneSuite from './visual/file-dropzone'; +import fileInputSuite from './visual/file-input'; +import fileTokenGroupSuite from './visual/file-token-group'; +import fileUploadSuite from './visual/file-upload'; +import flashbarSuite from './visual/flashbar'; +import flashbarStackedSuite from './visual/flashbar-stacked'; +import formSuite from './visual/form'; +import formFieldSuite from './visual/form-field'; +import gridSuite from './visual/grid'; +import headerSuite from './visual/header'; +import helpPanelSuite from './visual/help-panel'; +import iconSuite from './visual/icon'; +import inputSuite from './visual/input'; +import linkSuite from './visual/link'; +import paginationSuite from './visual/pagination'; +import progressBarSuite from './visual/progress-bar'; +import radioGroupSuite from './visual/radio-group'; import tableSuite from './visual/table'; // Per-component exports (grouped by component) @@ -81,6 +100,24 @@ export const dateInput: TestSuite[] = [dateInputSuite]; export const datePicker: TestSuite[] = [datePickerSuite]; export const dateRangePicker: TestSuite[] = [dateRangePickerSuite]; export const expandableSection: TestSuite[] = [expandableSectionSuite]; +export const divider: TestSuite[] = [dividerSuite]; +export const errorBoundary: TestSuite[] = [errorBoundarySuite]; +export const fileDropzone: TestSuite[] = [fileDropzoneSuite]; +export const fileInput: TestSuite[] = [fileInputSuite]; +export const fileTokenGroup: TestSuite[] = [fileTokenGroupSuite]; +export const fileUpload: TestSuite[] = [fileUploadSuite]; +export const flashbar: TestSuite[] = [flashbarSuite, flashbarStackedSuite]; +export const form: TestSuite[] = [formSuite]; +export const formField: TestSuite[] = [formFieldSuite]; +export const grid: TestSuite[] = [gridSuite]; +export const header: TestSuite[] = [headerSuite]; +export const helpPanel: TestSuite[] = [helpPanelSuite]; +export const icon: TestSuite[] = [iconSuite]; +export const input: TestSuite[] = [inputSuite]; +export const link: TestSuite[] = [linkSuite]; +export const pagination: TestSuite[] = [paginationSuite]; +export const progressBar: TestSuite[] = [progressBarSuite]; +export const radioGroup: TestSuite[] = [radioGroupSuite]; export const table: TestSuite[] = [tableSuite]; export const allSuites: TestSuite[] = [ @@ -108,5 +145,23 @@ export const allSuites: TestSuite[] = [ ...datePicker, ...dateRangePicker, ...expandableSection, + ...divider, + ...errorBoundary, + ...fileDropzone, + ...fileInput, + ...fileTokenGroup, + ...fileUpload, + ...flashbar, + ...form, + ...formField, + ...grid, + ...header, + ...helpPanel, + ...icon, + ...input, + ...link, + ...pagination, + ...progressBar, + ...radioGroup, ...table, ]; diff --git a/test/definitions/visual/divider.ts b/test/definitions/visual/divider.ts new file mode 100644 index 0000000000..232f6f239f --- /dev/null +++ b/test/definitions/visual/divider.ts @@ -0,0 +1,17 @@ +// 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/error-boundary.ts b/test/definitions/visual/error-boundary.ts new file mode 100644 index 0000000000..b9b276ac7b --- /dev/null +++ b/test/definitions/visual/error-boundary.ts @@ -0,0 +1,17 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import { TestSuite } from '../types'; + +const suite: TestSuite = { + description: 'ErrorBoundary', + componentName: 'error-boundary', + tests: [ + { + description: 'fallback states', + path: 'error-boundary/error-boundary-fallbacks', + screenshotType: 'screenshotArea', + }, + ], +}; + +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..4618c4f644 --- /dev/null +++ b/test/definitions/visual/file-dropzone.ts @@ -0,0 +1,17 @@ +// 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..d65cfac909 --- /dev/null +++ b/test/definitions/visual/file-input.ts @@ -0,0 +1,17 @@ +// 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..e36e874ab2 --- /dev/null +++ b/test/definitions/visual/file-token-group.ts @@ -0,0 +1,17 @@ +// 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..0e6eb205bc --- /dev/null +++ b/test/definitions/visual/file-upload.ts @@ -0,0 +1,17 @@ +// 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..2fbac11248 --- /dev/null +++ b/test/definitions/visual/flashbar-stacked.ts @@ -0,0 +1,40 @@ +// 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: 'Flashbar stacked notifications', + componentName: 'flashbar', + tests: [380, 450, 600, 1200].flatMap(width => [ + { + description: `${width}px, collapsed`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + { + description: `${width}px, collapsed, notifications bar button focused`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async ({ page }) => { + await page.click('#focus-target'); + await page.focusNextElement(); + }, + }, + { + description: `${width}px, expanded`, + path: 'flashbar/collapsible.visual-tests', + screenshotType: 'screenshotArea', + configuration: { width }, + setup: async ({ page }) => { + await page.click('#focus-target'); + await page.focusNextElement(); + await page.keys(['Space']); + await page.pause(500); + }, + }, + ]), +}; + +export default suite; diff --git a/test/definitions/visual/flashbar.ts b/test/definitions/visual/flashbar.ts new file mode 100644 index 0000000000..0102369ba0 --- /dev/null +++ b/test/definitions/visual/flashbar.ts @@ -0,0 +1,59 @@ +// 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: [ + ...[600, 1280].map(width => ({ + description: `width ${width}px`, + tests: [ + { + description: 'permutations', + path: 'flashbar/permutations', + screenshotType: 'permutations', + configuration: { width }, + }, + { + description: 'runtime-action', + path: 'flashbar/runtime-action', + screenshotType: 'permutations', + configuration: { width }, + }, + ], + })), + { + 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..d2d4ef2d3b --- /dev/null +++ b/test/definitions/visual/form-field.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: 'FormField', + componentName: 'form-field', + tests: [576, 768, 992, 1200].map(width => ({ + description: `Layout at ${width}px`, + tests: [ + { + description: 'Permutations', + path: 'form-field/permutations', + screenshotType: 'permutations', + configuration: { width }, + }, + { + description: '2.1 Scenarios', + path: 'form-field/form-field-columns', + screenshotType: 'screenshotArea', + configuration: { width }, + }, + ], + })), +}; + +export default suite; diff --git a/test/definitions/visual/form.ts b/test/definitions/visual/form.ts new file mode 100644 index 0000000000..b5d155bbb6 --- /dev/null +++ b/test/definitions/visual/form.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: '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..bde016fb77 --- /dev/null +++ b/test/definitions/visual/grid.ts @@ -0,0 +1,23 @@ +// 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], + ['xs', 800], + ['m', 1200], + ['l', 1400], +]; + +const suite: TestSuite = { + description: 'Grid', + componentName: 'grid', + tests: TEST_WIDTHS.map(([breakpoint, width]) => ({ + description: `grid at "${breakpoint}"`, + path: 'grid', + screenshotType: 'screenshotArea', + configuration: { width }, + })), +}; + +export default suite; diff --git a/test/definitions/visual/header.ts b/test/definitions/visual/header.ts new file mode 100644 index 0000000000..33c2f553e8 --- /dev/null +++ b/test/definitions/visual/header.ts @@ -0,0 +1,18 @@ +// 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: 'Header', + componentName: 'header', + tests: [1, 2, 3].flatMap(level => + [1500, 850, 400].map(width => ({ + description: `level-${level} at ${width}px`, + path: `header/level-${level}`, + screenshotType: 'screenshotArea', + configuration: { width }, + })) + ), +}; + +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..bd5e93c72a --- /dev/null +++ b/test/definitions/visual/help-panel.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: 'HelpPanel', + componentName: 'help-panel', + tests: [ + { + description: 'permutations', + path: 'help-panel/permutations', + screenshotType: 'screenshotArea', + setup: async ({ page }) => { + await page.focusInputs(); + }, + }, + { + description: 'with AppLayout', + path: 'help-panel/with-app-layout', + screenshotType: 'screenshotArea', + setup: async ({ page }) => { + await page.focusInputs(); + }, + }, + { + description: 'loading state - with AppLayout', + path: 'help-panel/loading-with-app-layout', + screenshotType: 'screenshotArea', + setup: async ({ page }) => { + await page.focusInputs(); + }, + }, + ], +}; + +export default suite; diff --git a/test/definitions/visual/icon.ts b/test/definitions/visual/icon.ts new file mode 100644 index 0000000000..82126f6fda --- /dev/null +++ b/test/definitions/visual/icon.ts @@ -0,0 +1,38 @@ +// 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: 'Icon', + componentName: 'icon', + tests: [ + { + description: 'Alignment with text', + path: 'icon/text-align', + screenshotType: 'screenshotArea', + configuration: { width: 300 }, + }, + ...['normal', 'disabled', 'error', 'inverted', 'subtle', 'success', 'warning'].map(variant => ({ + description: `Icons in ${variant} variant`, + path: `icon/variant-${variant}`, + 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..5331976345 --- /dev/null +++ b/test/definitions/visual/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: '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/link.ts b/test/definitions/visual/link.ts new file mode 100644 index 0000000000..e58a23266c --- /dev/null +++ b/test/definitions/visual/link.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: '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/pagination.ts b/test/definitions/visual/pagination.ts new file mode 100644 index 0000000000..1a34de233d --- /dev/null +++ b/test/definitions/visual/pagination.ts @@ -0,0 +1,17 @@ +// 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/progress-bar.ts b/test/definitions/visual/progress-bar.ts new file mode 100644 index 0000000000..f64a72a1c3 --- /dev/null +++ b/test/definitions/visual/progress-bar.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: '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/radio-group.ts b/test/definitions/visual/radio-group.ts new file mode 100644 index 0000000000..420165c14e --- /dev/null +++ b/test/definitions/visual/radio-group.ts @@ -0,0 +1,45 @@ +// 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: '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', + tests: [600, 1280].map(width => ({ + description: `permutations at ${width}`, + path: 'radio-group/horizontal.permutations', + screenshotType: 'permutations', + configuration: { width }, + })), + }, + { + description: 'Style custom page', + path: 'radio-group/style-custom', + screenshotType: 'screenshotArea', + }, + ], +}; + +export default suite;