dimsum-e2e-tests 3.80.0-next.3 → 3.80.0-next.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/DSStorybook/baseline.js +0 -186
  3. package/ds-accordion/DSAccordion.visual.spec.js +0 -14
  4. package/ds-accordion/DSAccordionCO.js +0 -2
  5. package/ds-apppicker/aria-disabled/DSAppPicker.aria-disabled.visual.spec.js +14 -1
  6. package/ds-apppicker/basic/DSAppPicker.basic.visual.spec.js +12 -7
  7. package/ds-banner/DSBanner.text-wrapping.visual.spec.js +3 -6
  8. package/ds-banner/DSBanner.visual.spec.js +7 -8
  9. package/ds-banner/DSBannerCO.js +4 -5
  10. package/ds-banner/slots/DSBanner.slots.func.spec.js +6 -0
  11. package/ds-banner/slots/DSBanner.slots.visual.spec.js +1 -2
  12. package/ds-chip/DSChip.axe-core.func.spec.js +0 -17
  13. package/ds-chip/DSChip.visual.spec.js +0 -22
  14. package/ds-chip/DSChipCO.js +0 -6
  15. package/ds-fast-list/DSFastListCO.js +37 -0
  16. package/ds-fast-list/basic/DSFastList.basic.axe-core.func.spec.js +28 -0
  17. package/ds-fast-list/basic/DSFastList.basic.visual.spec.js +23 -0
  18. package/ds-fast-list/scroll-to-index/DSFastList.scroll-to-index.func.spec.js +45 -0
  19. package/ds-panel-header/DSPanelHeader.func.spec.js +24 -0
  20. package/ds-panel-header/DSPanelHeaderCO.js +17 -0
  21. package/ds-pills-v2/DSPillsV2CO.js +53 -1
  22. package/ds-pills-v2/pitfalls/DSPills.inputPill.func.spec.js +26 -0
  23. package/ds-pills-v2/pitfalls/DSPills.inputWithFormItemBlockURL.axe-core.func.spec.js +38 -0
  24. package/ds-pills-v2/pitfalls/DSPills.inputWithFormItemBlockURL.func.spec.js +72 -0
  25. package/ds-progress-indicator/DSProgressIndicator.axe-core.func.spec.js +8 -5
  26. package/ds-progress-indicator/DSProgressIndicator.func.spec.js +18 -4
  27. package/ds-progress-indicator/DSProgressIndicator.visual.spec.js +14 -6
  28. package/ds-progress-indicator/DSProgressIndicatorCO.js +80 -9
  29. package/ds-progress-indicator/aria-contract/DSProgressIndicator.aria-contract.func.spec.js +104 -0
  30. package/ds-progress-indicator/slots/DSProgressIndicator.slots.axe-core.func.spec.js +31 -0
  31. package/ds-progress-indicator/slots/DSProgressIndicator.slots.func.spec.js +87 -0
  32. package/ds-progress-indicator/slots/DSProgressIndicator.slots.visual.spec.js +27 -0
  33. package/ds-slider-v2/DSSliderV2.axe-core.func.spec.js +21 -0
  34. package/ds-slider-v2/DSSliderV2.visual.spec.js +19 -1
  35. package/ds-slider-v2/DSSliderV2CO.js +6 -1
  36. package/ds-slider-v2/has-error/DSSliderV2.has-error.visual.spec.js +44 -0
  37. package/ds-slider-v2/interaction-states/DSSliderV2.interaction-states.visual.spec.js +53 -0
  38. package/ds-virtuallist/DSVirtualList.visual.spec.js +4 -5
  39. package/ds-virtuallist/DSVirtualListCO.js +28 -9
  40. package/ds-virtuallist/basic/DSVirtualList.basic.axe-core.func.spec.js +28 -0
  41. package/ds-virtuallist/scroll-to-index/DSVirtualList.scroll-to-index.func.spec.js +45 -0
  42. package/package.json +149 -149
  43. package/paths.js +3 -49
  44. package/ds-mobile/Accordion/DSAccordion.visual.spec.js +0 -33
  45. package/ds-mobile/Accordion/DSAccordionCO.js +0 -37
  46. package/ds-mobile/CategoryBox/CategoryBox.visual.spec.js +0 -18
  47. package/ds-mobile/CategoryBox/CategoryBoxCO.js +0 -17
  48. package/ds-mobile/CollectionBox/CollectionBox.visual.spec.js +0 -16
  49. package/ds-mobile/CollectionBox/CollectionBoxCO.js +0 -15
  50. package/ds-mobile/GlobalHeader/DSGlobalHeader.visual.spec.js +0 -55
  51. package/ds-mobile/GlobalHeader/DSGlobalHeaderCO.js +0 -36
  52. package/ds-mobile/GroupBox/GroupBox.visual.spec.js +0 -15
  53. package/ds-mobile/GroupBox/GroupBoxCO.js +0 -15
  54. package/ds-mobile/InfiniteLoader/InfiniteLoader.func.spec.js +0 -90
  55. package/ds-mobile/InfiniteLoader/InfiniteLoaderCO.js +0 -29
  56. package/ds-mobile/MobileActionToolbar/MobileActionToolbar.visual.spec.js +0 -39
  57. package/ds-mobile/MobileActionToolbar/MobileActionToolbarCO.js +0 -19
  58. package/ds-mobile/MobileBanner/MobileBanner.visual.spec.js +0 -23
  59. package/ds-mobile/MobileBanner/MobileBannerCO.js +0 -23
  60. package/ds-mobile/MobileCard/Card.func.spec.js +0 -38
  61. package/ds-mobile/MobileCard/Card.visual.spec.js +0 -103
  62. package/ds-mobile/MobileCard/MobileCardCO.js +0 -42
  63. package/ds-mobile/MobileContextMenu/MobileContextMenu.func.spec.js +0 -88
  64. package/ds-mobile/MobileContextMenu/MobileContextMenu.visual.spec.js +0 -31
  65. package/ds-mobile/MobileContextMenu/MobileContextMenuCO.js +0 -72
  66. package/ds-mobile/MobileDatePicker/MobileDatePicker.func.spec.js +0 -15
  67. package/ds-mobile/MobileDatePicker/MobileDatePicker.visual.spec.js +0 -15
  68. package/ds-mobile/MobileDatePicker/MobileDatePickerCO.js +0 -35
  69. package/ds-mobile/MobileDropdownMenu/MobileDropdownMenu.func.spec.js +0 -38
  70. package/ds-mobile/MobileDropdownMenu/MobileDropdownMenu.visual.spec.js +0 -16
  71. package/ds-mobile/MobileDropdownMenu/MobileDropdownMenuCO.js +0 -37
  72. package/ds-mobile/MobileEmptyState/MobileEmptyState.visual.spec.js +0 -15
  73. package/ds-mobile/MobileEmptyState/MobileEmptyStateCO.js +0 -14
  74. package/ds-mobile/MobileFilterbar/MobileFilterbar.visual.spec.js +0 -39
  75. package/ds-mobile/MobileFilterbar/MobileFilterbarCO.js +0 -19
  76. package/ds-mobile/MobileFooter/MobileFooter.func.spec.js +0 -26
  77. package/ds-mobile/MobileFooter/MobileFooter.visual.spec.js +0 -51
  78. package/ds-mobile/MobileFooter/MobileFooterCO.js +0 -27
  79. package/ds-mobile/MobileListItem/MobileListItem.visual.spec.js +0 -56
  80. package/ds-mobile/MobileListItem/MobileListItemCO.js +0 -19
  81. package/ds-mobile/MobileLoadingPage/MobileLoadingPage.visual.spec.js +0 -37
  82. package/ds-mobile/MobileLoadingPage/MobileLoadingPageCO.js +0 -23
  83. package/ds-mobile/MobilePageHeader/MobilePageHeader.func.spec.js +0 -22
  84. package/ds-mobile/MobilePageHeader/MobilePageHeader.visual.spec.js +0 -135
  85. package/ds-mobile/MobilePageHeader/MobilePageHeaderCO.js +0 -67
  86. package/ds-mobile/MobileSelectList/MobileSelectList.visual.spec.js +0 -56
  87. package/ds-mobile/MobileSelectList/MobileSelectListCO.js +0 -21
  88. package/ds-mobile/MobileSeparator/MobileSeparator.visual.spec.js +0 -28
  89. package/ds-mobile/MobileSeparator/MobileSeparatorCO.js +0 -17
  90. package/ds-mobile/MobileTimePicker/MobileTimePickerCO.js +0 -41
  91. package/ds-mobile/Modal/MobileModal.visual.spec.js +0 -56
  92. package/ds-mobile/Modal/MobileModalCO.js +0 -28
  93. package/ds-mobile/PageFilter/PageFilter.visual.spec.js +0 -46
  94. package/ds-mobile/PageFilter/PageFilterCO.js +0 -19
  95. package/ds-mobile/PageForm/PageForm.visual.spec.js +0 -14
  96. package/ds-mobile/PageForm/PageFormCO.js +0 -11
  97. package/ds-mobile/PageList/PageList.visual.spec.js +0 -31
  98. package/ds-mobile/PageList/PageListCO.js +0 -19
  99. package/ds-mobile/PageSummary/PageSummary.visual.spec.js +0 -20
  100. package/ds-mobile/PageSummary/PageSummaryCO.js +0 -11
  101. package/ds-mobile/SwipeToRefresh/SwipeToRefresh.func.spec.js +0 -26
  102. package/ds-mobile/SwipeToRefresh/SwipeToRefreshCO.js +0 -20
  103. package/ds-mobile/Tabs/MobileTabs.visual.spec.js +0 -91
  104. package/ds-mobile/Tabs/TabsCO.js +0 -37
  105. package/ds-mobile/Tooltip/MobileTooltip.func.spec.js +0 -17
  106. package/ds-mobile/Tooltip/MobileTooltip.visual.spec.js +0 -16
  107. package/ds-mobile/Tooltip/TooltipCO.js +0 -23
  108. package/ds-mobile/helpers.js +0 -7
@@ -0,0 +1,104 @@
1
+ import DSProgressIndicatorCO from '../DSProgressIndicatorCO';
2
+
3
+ // Coverage for the accessibility remediation shipped in PUI-18856 (PR #8353):
4
+ // PUI-18671 the progressbar used to carry a hardcoded aria-label="progress bar". It is now named
5
+ // by the visible title through aria-labelledby, and that string survives only as the
6
+ // fallback for an untitled indicator.
7
+ // PUI-18673 aria-valuetext now mirrors whatever the visible counter renders, default or custom.
8
+ // PUI-18675 each segment accepts a `label` that becomes its own accessible name. Only the Dimsum
9
+ // half of that defect is assertable here; the visible non-colour alternative is the
10
+ // consumer's, see stories/docs/principles/a11y/tradesoff.
11
+ // PUI-18672 an invalid `max` no longer produces width: NaN% and an inverted ARIA range. `max` was
12
+ // deliberately NOT made required (KNOWN_INTENTIONAL_DEVIATIONS.md, deviation 6), so what
13
+ // is asserted below is the shipped fail-safe: an indeterminate progressbar.
14
+ //
15
+ // Every assertion is a DOM attribute, which is identical across engines, so this runs Chrome only.
16
+ // The `|| 'Chrome'` branch is mandatory: BrowserStack reports the capitalised name and a
17
+ // lowercase-only guard would register zero tests while still exiting 0.
18
+ if (
19
+ (!browser.capabilities['ice:options'].isPhone &&
20
+ !browser.capabilities['ice:options'].isTablet &&
21
+ browser.capabilities.browserName === 'chrome') ||
22
+ browser.capabilities.browserName === 'Chrome'
23
+ ) {
24
+ describe('PUI-19209 - DSProgressIndicator:: progressbar ARIA contract - Func', () => {
25
+ before('loading page', async () => {
26
+ const errorOnGo = await DSProgressIndicatorCO.ariaContractURL.go();
27
+ if (errorOnGo) throw errorOnGo;
28
+ });
29
+
30
+ it('01: should name the progressbar from the visible title and fall back when untitled', async () => {
31
+ const titled = await DSProgressIndicatorCO.getProgress('titled-case');
32
+ await titled.waitForDisplayed();
33
+ await expect(titled).not.toHaveAttribute('aria-label');
34
+
35
+ // The id is generated with React.useId(), so it is resolved at runtime rather than hardcoded.
36
+ const labelledBy = await titled.getAttribute('aria-labelledby');
37
+ const title = await DSProgressIndicatorCO.getTitle('titled-case');
38
+ await expect(await title.getAttribute('id')).toEqual(labelledBy);
39
+ await expect(await DSProgressIndicatorCO.getByIdAttribute(labelledBy)).toHaveText('Uploading documents');
40
+
41
+ const titleless = await DSProgressIndicatorCO.getProgress('titleless-case');
42
+ await expect(titleless).toHaveAttribute('aria-label', 'progress bar');
43
+ await expect(titleless).not.toHaveAttribute('aria-labelledby');
44
+ });
45
+
46
+ it('02: should mirror the visible counter in aria-valuetext for default and custom renderers', async () => {
47
+ const defaultCounter = await DSProgressIndicatorCO.getCounter('titled-case');
48
+ await expect(defaultCounter).toHaveText('50 of 100');
49
+ const defaultBar = await DSProgressIndicatorCO.getProgress('titled-case');
50
+ await expect(defaultBar).toHaveAttribute('aria-valuetext', '50 of 100');
51
+
52
+ const customCounter = await DSProgressIndicatorCO.getCounter('custom-counter-case');
53
+ await expect(customCounter).toHaveText('3 files remaining');
54
+ const customBar = await DSProgressIndicatorCO.getProgress('custom-counter-case');
55
+ await expect(customBar).toHaveAttribute('aria-valuetext', '3 files remaining');
56
+ });
57
+
58
+ it('03: should give each labelled segment its own accessible name', async () => {
59
+ const labelled = await DSProgressIndicatorCO.getProgressBars('labelled-segments-case');
60
+ await expect(labelled).toBeElementsArrayOfSize(2);
61
+
62
+ // A segment label wins over the indicator title, so aria-labelledby is dropped entirely.
63
+ await expect(labelled[0]).toHaveAttribute('aria-label', 'Approved documents');
64
+ await expect(labelled[0]).not.toHaveAttribute('aria-labelledby');
65
+ await expect(labelled[1]).toHaveAttribute('aria-label', 'Rejected documents');
66
+ await expect(labelled[1]).not.toHaveAttribute('aria-labelledby');
67
+
68
+ // Each segment reports its own range rather than the aggregate shown by the counter.
69
+ await expect(labelled[0]).toHaveAttribute('aria-valuetext', '50 of 100');
70
+ await expect(labelled[1]).toHaveAttribute('aria-valuetext', '100 of 200');
71
+
72
+ // An unlabelled segment still falls back to the indicator title.
73
+ const mixed = await DSProgressIndicatorCO.getProgressBars('mixed-segments-case');
74
+ await expect(mixed).toBeElementsArrayOfSize(2);
75
+ await expect(mixed[0]).toHaveAttribute('aria-label', 'Completed');
76
+ await expect(mixed[1]).not.toHaveAttribute('aria-label');
77
+ const fallbackId = await mixed[1].getAttribute('aria-labelledby');
78
+ await expect(await DSProgressIndicatorCO.getByIdAttribute(fallbackId)).toHaveText('Batch upload');
79
+ });
80
+
81
+ it('04: should render indeterminate for an invalid max and clamp an overflowing value', async () => {
82
+ const expectIndeterminate = async (caseId) => {
83
+ const bar = await DSProgressIndicatorCO.getProgress(caseId);
84
+ await expect(bar).toHaveAttribute('role', 'progressbar');
85
+ await expect(bar).not.toHaveAttribute('aria-valuenow');
86
+ await expect(bar).not.toHaveAttribute('aria-valuemin');
87
+ await expect(bar).not.toHaveAttribute('aria-valuemax');
88
+ await expect(bar).not.toHaveAttribute('aria-valuetext');
89
+ // The pre-fix behaviour was width: NaN%; the guard now yields a valid zero-length fill.
90
+ await expect(await bar.getAttribute('style')).toContain('width: 0%');
91
+ // The indicator is still named, so the invalid range degrades without losing the label.
92
+ await expect(bar).toHaveAttribute('aria-labelledby');
93
+ };
94
+
95
+ await expectIndeterminate('omitted-max-case');
96
+ await expectIndeterminate('zero-max-case');
97
+
98
+ const overflow = await DSProgressIndicatorCO.getProgress('overflow-case');
99
+ await expect(overflow).toHaveAttribute('aria-valuenow', '100');
100
+ await expect(overflow).toHaveAttribute('aria-valuemax', '100');
101
+ await expect(await overflow.getAttribute('style')).toContain('width: 100%');
102
+ });
103
+ });
104
+ }
@@ -0,0 +1,31 @@
1
+ import DSProgressIndicatorCO from '../DSProgressIndicatorCO';
2
+ import { axeCoreCheck } from '../../helpers';
3
+
4
+ // SlotsTest is the only ProgressIndicator story that overrides color/backgroundColor, so it is the
5
+ // one place where the test code itself can introduce a contrast regression. This run is what makes
6
+ // the WCAG 2 AA pairs in the story load-bearing rather than advisory. It also guards the
7
+ // consumer-supplied slot aria-label against colliding with the component's own naming.
8
+ //
9
+ // Chrome only: axe-core results do not vary by engine. The `|| 'Chrome'` branch is mandatory,
10
+ // BrowserStack reports the capitalised name and a lowercase-only guard registers zero tests.
11
+ if (
12
+ (!browser.capabilities['ice:options'].isPhone &&
13
+ !browser.capabilities['ice:options'].isTablet &&
14
+ browser.capabilities.browserName === 'chrome') ||
15
+ browser.capabilities.browserName === 'Chrome'
16
+ ) {
17
+ describe('PUI-19212 - DSProgressIndicator:: slot theming - Axe-Core', () => {
18
+ before('loading page', async () => {
19
+ const errorOnGo = await DSProgressIndicatorCO.slotsURL.go();
20
+ if (errorOnGo) throw errorOnGo;
21
+ });
22
+
23
+ it('01: should have no accessibility violations with custom slot theming', async () => {
24
+ const root = await DSProgressIndicatorCO.getSlot('dsProgressindicatorRoot', 'segmented-slots-case');
25
+ await root.waitForDisplayed();
26
+
27
+ const result = await axeCoreCheck();
28
+ expect(result.length).toBe(0);
29
+ });
30
+ });
31
+ }
@@ -0,0 +1,87 @@
1
+ import DSProgressIndicatorCO from '../DSProgressIndicatorCO';
2
+
3
+ // Slot contract coverage for the compliance migration in PUI-18856 / PUI-18941. The package dropped
4
+ // ds-classnames/aggregatedClasses for the native slot system, so each visual element must now be
5
+ // addressable through data-dimsum-slot and must accept consumer slot props.
6
+ //
7
+ // The consumer's `data-testid` REPLACES the component default, which is why this spec addresses the
8
+ // elements through data-dimsum-slot and then asserts the overridden testid, rather than the other
9
+ // way round.
10
+ //
11
+ // Slot attribute pass-through is engine-independent, so this runs Chrome only. The `|| 'Chrome'`
12
+ // branch is mandatory: BrowserStack reports the capitalised name and a lowercase-only guard would
13
+ // register zero tests while still exiting 0.
14
+ if (
15
+ (!browser.capabilities['ice:options'].isPhone &&
16
+ !browser.capabilities['ice:options'].isTablet &&
17
+ browser.capabilities.browserName === 'chrome') ||
18
+ browser.capabilities.browserName === 'Chrome'
19
+ ) {
20
+ describe('PUI-19210 - DSProgressIndicator:: slot contract and slot prop forwarding - Func', () => {
21
+ before('loading page', async () => {
22
+ const errorOnGo = await DSProgressIndicatorCO.slotsURL.go();
23
+ if (errorOnGo) throw errorOnGo;
24
+ });
25
+
26
+ it('01: should expose the five slots on both the single and the segmented indicator', async () => {
27
+ const expectFiveSlots = async (caseId) => {
28
+ const [root, title, counter, progressBar, progress] = await Promise.all([
29
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorRoot', caseId),
30
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorTitle', caseId),
31
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorCounter', caseId),
32
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorProgressBar', caseId),
33
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorProgress', caseId),
34
+ ]);
35
+
36
+ await root.waitForDisplayed();
37
+ await expect(root).toHaveAttribute('data-testid', 'custom-root-data');
38
+ await expect(title).toHaveAttribute('data-testid', 'custom-title-data');
39
+ await expect(counter).toHaveAttribute('data-testid', 'custom-counter-data');
40
+ await expect(progressBar).toHaveAttribute('data-testid', 'custom-progress-bar-data');
41
+ await expect(progress).toHaveAttribute('data-testid', 'custom-progress-data');
42
+ };
43
+
44
+ await expectFiveSlots('single-slots-case');
45
+ await expectFiveSlots('segmented-slots-case');
46
+ });
47
+
48
+ it('02: should forward consumer slot props to every slot and to each segment', async () => {
49
+ const [title, counter, progress] = await Promise.all([
50
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorTitle', 'single-slots-case'),
51
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorCounter', 'single-slots-case'),
52
+ DSProgressIndicatorCO.getSlot('dsProgressindicatorProgress', 'single-slots-case'),
53
+ ]);
54
+
55
+ await expect(title).toHaveAttribute('lang', 'en');
56
+ await expect(counter).toHaveAttribute('title', 'custom counter title');
57
+
58
+ // The progress slot carries role="progressbar", so a slot-level aria-label is legal there and
59
+ // is forwarded. It does not become the accessible name though: the component also emits its
60
+ // own aria-labelledby pointing at the title, and aria-labelledby outranks aria-label. Chrome's
61
+ // accessibility tree names these bars after their title, not "custom progress aria".
62
+ //
63
+ // That is the intended outcome, not a gap. Keeping the name tied to the visible title is the
64
+ // WCAG 2.5.3 Label in Name principle, and localisation is handled by passing a translated
65
+ // `title` rather than by overriding the label. The slot-level aria-label only takes effect on
66
+ // a titleless indicator, where the component would otherwise fall back to "progress bar".
67
+ // Asserting both attributes here pins the forwarding contract without implying the override
68
+ // wins the name.
69
+ await expect(progress).toHaveAttribute('role', 'progressbar');
70
+ await expect(progress).toHaveAttribute('aria-label', 'custom progress aria');
71
+ await expect(progress).toHaveAttribute('aria-labelledby');
72
+
73
+ // Slot props must reach every segment, not just the first one. The segments are addressed by
74
+ // index rather than with .map(): WebdriverIO's element array overrides .map() to return a
75
+ // Promise, so Promise.all(segments.map(...)) throws "object is not iterable".
76
+ const segments = await DSProgressIndicatorCO.getSlots('dsProgressindicatorProgress', 'segmented-slots-case');
77
+ await expect(segments).toBeElementsArrayOfSize(3);
78
+
79
+ await expect(segments[0]).toHaveAttribute('data-testid', 'custom-progress-data');
80
+ await expect(segments[0]).toHaveAttribute('aria-label', 'custom progress aria');
81
+ await expect(segments[1]).toHaveAttribute('data-testid', 'custom-progress-data');
82
+ await expect(segments[1]).toHaveAttribute('aria-label', 'custom progress aria');
83
+ await expect(segments[2]).toHaveAttribute('data-testid', 'custom-progress-data');
84
+ await expect(segments[2]).toHaveAttribute('aria-label', 'custom progress aria');
85
+ });
86
+ });
87
+ }
@@ -0,0 +1,27 @@
1
+ import DSProgressIndicatorCO from '../DSProgressIndicatorCO';
2
+
3
+ // Visual half of the PUI-18856 slot migration: proves the styleOverrides payload actually reaches
4
+ // each slot after the move off ds-classnames. The functional spec covers the attribute contract, so
5
+ // this only needs the rendered appearance.
6
+ //
7
+ // This snapshot also guards the cascade fix for the `progress-bar` slot: its styleOverride used to lose to
8
+ // the retained SCSS rule `.em-ds-determinate-progress-indicator .em-ds-progress-bar` (two classes beat the
9
+ // single styled-components class) and the track stayed grey. The package now re-emits every slot override
10
+ // that a legacy descendant rule could beat under `&&&`, so all five slots take their overrides here. jsdom
11
+ // does not honour specificity, so this real-browser snapshot is the only place the cascade itself is proven.
12
+ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
13
+ describe('PUI-19211 - DSProgressIndicator:: slot theming - Visual', () => {
14
+ before('loading page', async () => {
15
+ const errorOnGo = await DSProgressIndicatorCO.slotsURL.go();
16
+ if (errorOnGo) throw errorOnGo;
17
+ });
18
+
19
+ it('01: should display the single and segmented indicators with slot theming applied', async () => {
20
+ const root = await DSProgressIndicatorCO.getSlot('dsProgressindicatorRoot', 'segmented-slots-case');
21
+ await root.waitForDisplayed();
22
+
23
+ const snapshot = await browser.percyCheckScreenshot();
24
+ await expect(snapshot).toEqual(0);
25
+ });
26
+ });
27
+ }
@@ -118,4 +118,25 @@ if (
118
118
  expect(result.length).toBe(0);
119
119
  });
120
120
  });
121
+
122
+ // Tests for the hasError state introduced in PUI-18434 (spec v1.5.0 Error row).
123
+ describe('PUI-19224 - DSSliderV2:: hasError state - Axe-Core', () => {
124
+ before('loading page', async () => {
125
+ const errorOnGo = await DSSliderV2CO.hasErrorURL.go();
126
+ if (errorOnGo) throw errorOnGo;
127
+ });
128
+ it('01: should display idle error sliders and pass axe-core scan', async () => {
129
+ const slider = await DSSliderV2CO.getSliderRootByIndex();
130
+ await slider.waitForDisplayed();
131
+ const result = await axeCoreCheck();
132
+ expect(result.length).toBe(0);
133
+ });
134
+ it('02: should display focused error slider handle and pass axe-core scan', async () => {
135
+ await browser.keys(Key.Tab);
136
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
137
+ await expect(thumb).toBeFocused();
138
+ const result = await axeCoreCheck();
139
+ expect(result.length).toBe(0);
140
+ });
141
+ });
121
142
  }
@@ -1,6 +1,6 @@
1
1
  import { Key } from 'webdriverio';
2
2
  import DSSliderV2CO from './DSSliderV2CO';
3
- import { setTextSize } from '../helpers';
3
+ import { mouseOver, setTextSize } from '../helpers';
4
4
 
5
5
  if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
6
6
  describe('PUI-12332 - SliderV2 - Disabled - Visual Test', () => {
@@ -15,6 +15,14 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
15
15
  const snapshot = await browser.eyesCheckSnapshot(DSSliderV2CO.snapshotPath('slider-v2-disabled'));
16
16
  await expect(snapshot).toEqual(0);
17
17
  });
18
+ it('02: should display disabled single slider handle hovered', async () => {
19
+ // Spec v1.5.0 "Disabled + Hover": only the cursor changes (not-allowed). The snapshot guards that
20
+ // no hover tint or background change reappears on the handle or the track (PUI-18434).
21
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
22
+ await mouseOver(thumb);
23
+ const snapshot = await browser.percyCheckScreenshot();
24
+ await expect(snapshot).toEqual(0);
25
+ });
18
26
  });
19
27
  describe('PUI-14389 - SliderV2 - applyAriaDisabled - Visual Test', () => {
20
28
  before('loading page', async () => {
@@ -36,6 +44,16 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
36
44
  const snapshot = await browser.eyesCheckSnapshot(DSSliderV2CO.snapshotPath('slider-v2-aria-disabled-kb-nav'));
37
45
  await expect(snapshot).toEqual(0);
38
46
  });
47
+ it('03: should display aria-disabled single slider handle hovered, not focused', async () => {
48
+ // Spec v1.5.0 "Aria-Disabled + Hover": only the cursor changes. Before PUI-18434 the handle background
49
+ // turned N-050 on hover; the snapshot guards against that regression. 01/02 left a handle focused and
50
+ // the story has three focusable handles, so the focus is released explicitly first.
51
+ await browser.execute(() => document.activeElement && document.activeElement.blur());
52
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
53
+ await mouseOver(thumb);
54
+ const snapshot = await browser.percyCheckScreenshot();
55
+ await expect(snapshot).toEqual(0);
56
+ });
39
57
  });
40
58
 
41
59
  describe('PUI-12333 - SliderV2 - Custom tick marks values - Visual Test', () => {
@@ -33,6 +33,10 @@ export default class DSSliderCO extends PageObject {
33
33
 
34
34
  static applyAriaDisabledURL = new Urlbuilder(PATH_E2E_SLIDER_V2, 'apply-aria-disabled-test');
35
35
 
36
+ static hasErrorURL = new Urlbuilder(PATH_E2E_SLIDER_V2, 'has-error-test');
37
+
38
+ static interactionStatesURL = new Urlbuilder(PATH_E2E_SLIDER_V2, 'interaction-states-test');
39
+
36
40
  // Snapshots
37
41
  static snapshotPath(example = 'basic') {
38
42
  return PageObject.getSnapshotPathBuilder('Slider', example);
@@ -129,7 +133,8 @@ export default class DSSliderCO extends PageObject {
129
133
  }
130
134
 
131
135
  static async getSliderTickMarkValueSlot(index = 0) {
132
- return $$('[data-dimsum-slot="dsSliderv2TickMarkValue"]')[index];
136
+ // Nested slot since 3.80.0-next: the slot name lives on data-dimsum-parent-slot (see PUI-18434)
137
+ return $$('[data-dimsum-parent-slot="dsSliderv2TickMarkValue"]')[index];
133
138
  }
134
139
 
135
140
  static async getSliderRangeWrapperSlot(index = 0) {
@@ -0,0 +1,44 @@
1
+ import DSSliderV2CO from '../DSSliderV2CO';
2
+ import { mouseOver, tabStops } from '../../helpers';
3
+
4
+ // Where the pointer rests when a snapshot must NOT include a hover state.
5
+ const PARKED_X = 500;
6
+ const PARKED_Y = 400;
7
+
8
+ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
9
+ // Tests for the hasError state introduced in PUI-18434 (spec v1.5.0 Error row): active track E-900,
10
+ // inactive track E-200, handle border 2px solid E-900, handle background N-000.
11
+ describe('PUI-19223 - DSSliderV2:: hasError state - Visual', () => {
12
+ before('loading page', async () => {
13
+ const errorOnGo = await DSSliderV2CO.hasErrorURL.go();
14
+ if (errorOnGo) throw errorOnGo;
15
+ });
16
+
17
+ it('01: should display idle error single and range sliders, mouse parked', async () => {
18
+ const slider = await DSSliderV2CO.getSliderRootByIndex(0);
19
+ await slider.waitForDisplayed();
20
+ await mouseOver(null, PARKED_X, PARKED_Y);
21
+ const snapshot = await browser.percyCheckScreenshot();
22
+ await expect(snapshot).toEqual(0);
23
+ });
24
+
25
+ it('02: should display error single slider handle hovered', async () => {
26
+ // Spec v1.5.0 defines no Error + Hover row. Current behavior: the error tokens stay as they are.
27
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
28
+ await mouseOver(thumb);
29
+ const snapshot = await browser.percyCheckScreenshot();
30
+ await expect(snapshot).toEqual(0);
31
+ });
32
+
33
+ it('03: should display error single slider handle focused, not hovered', async () => {
34
+ // Spec v1.5.0 defines no Error + Focus row. Current behavior: the handle border switches to the
35
+ // focus token (B-800) while both track segments keep the error tokens.
36
+ await mouseOver(null, PARKED_X, PARKED_Y);
37
+ await tabStops(1);
38
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
39
+ await expect(thumb).toBeFocused();
40
+ const snapshot = await browser.percyCheckScreenshot();
41
+ await expect(snapshot).toEqual(0);
42
+ });
43
+ });
44
+ }
@@ -0,0 +1,53 @@
1
+ import DSSliderV2CO from '../DSSliderV2CO';
2
+ import { mouseOver, tabStops } from '../../helpers';
3
+
4
+ // Where the pointer rests when a snapshot must NOT include a hover state.
5
+ const PARKED_X = 500;
6
+ const PARKED_Y = 400;
7
+
8
+ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
9
+ // Tests for the spec v1.5.0 alignment introduced in PUI-18434: hover and focus tint the handle border
10
+ // AND the active track segment together (idle B-600 -> hover B-700 -> focus B-800), focus renders no
11
+ // outline. The story starts mid-range so the active segment is long enough for the tint to be visible.
12
+ describe('PUI-19222 - DSSliderV2:: handle and track hover and focus states - Visual', () => {
13
+ before('loading page', async () => {
14
+ const errorOnGo = await DSSliderV2CO.interactionStatesURL.go();
15
+ if (errorOnGo) throw errorOnGo;
16
+ });
17
+
18
+ it('01: should display idle single and range sliders, mouse parked', async () => {
19
+ const slider = await DSSliderV2CO.getSliderRootByIndex(0);
20
+ await slider.waitForDisplayed();
21
+ await mouseOver(null, PARKED_X, PARKED_Y);
22
+ const snapshot = await browser.percyCheckScreenshot();
23
+ await expect(snapshot).toEqual(0);
24
+ });
25
+
26
+ it('02: should display single slider handle hovered', async () => {
27
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
28
+ await mouseOver(thumb);
29
+ const snapshot = await browser.percyCheckScreenshot();
30
+ await expect(snapshot).toEqual(0);
31
+ });
32
+
33
+ it('03: should display range slider min handle hovered', async () => {
34
+ // The handle and the track are shared between single and range mode, so the hover tint must also
35
+ // apply to the range slider (PUI-18434 scope note).
36
+ const thumb = await DSSliderV2CO.getSliderThumb(1);
37
+ await mouseOver(thumb);
38
+ const snapshot = await browser.percyCheckScreenshot();
39
+ await expect(snapshot).toEqual(0);
40
+ });
41
+
42
+ // Focus is reached through Tab on purpose. PUI-19226: in range mode a mouse click on a handle does not
43
+ // focus it, so the focus tokens can only be captured via keyboard until that defect is fixed.
44
+ it('04: should display single slider handle focused, not hovered', async () => {
45
+ await mouseOver(null, PARKED_X, PARKED_Y);
46
+ await tabStops(1);
47
+ const thumb = await DSSliderV2CO.getSliderThumb(0);
48
+ await expect(thumb).toBeFocused();
49
+ const snapshot = await browser.percyCheckScreenshot();
50
+ await expect(snapshot).toEqual(0);
51
+ });
52
+ });
53
+ }
@@ -10,13 +10,13 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
10
10
  const addChildrenBtn = await DSVirtualListCO.getAdd1Btn();
11
11
  await addChildrenBtn.click();
12
12
  await addChildrenBtn.click();
13
- const snapshot = await browser.checkSnapshot(DSVirtualListCO.snapshotPath('vlist-2children'));
13
+ const snapshot = await browser.percyCheckScreenshot();
14
14
  await expect(snapshot).toEqual(0);
15
15
  });
16
16
  it('should add content to a children and display properly', async () => {
17
17
  const addContentBtn = await DSVirtualListCO.getChangeTimeBtn();
18
18
  await addContentBtn.click();
19
- const snapshot = await browser.checkSnapshot(DSVirtualListCO.snapshotPath('vlist-1content'));
19
+ const snapshot = await browser.percyCheckScreenshot();
20
20
  await expect(snapshot).toEqual(0);
21
21
  });
22
22
  it('should add a children and content to that children and display properly', async () => {
@@ -24,7 +24,7 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
24
24
  await addChildrenBtn.click();
25
25
  const addContentBtn = await DSVirtualListCO.getChangeTimeBtn();
26
26
  await addContentBtn.click();
27
- const snapshot = await browser.checkSnapshot(DSVirtualListCO.snapshotPath('vlist-2content'));
27
+ const snapshot = await browser.percyCheckScreenshot();
28
28
  await expect(snapshot).toEqual(0);
29
29
  });
30
30
  });
@@ -34,10 +34,9 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
34
34
  if (errorOnGo) throw errorOnGo;
35
35
  });
36
36
  it('01: should display a virtual list with customization correctly', async () => {
37
- await browser.eyesOpen();
38
37
  const virtualListContainer = await DSVirtualListCO.getVirtualListContainer();
39
38
  await virtualListContainer.waitForDisplayed();
40
- const snapshot = await browser.eyesCheckSnapshot(DSVirtualListCO.snapshotPath('virtual-list-customization'));
39
+ const snapshot = await browser.percyCheckScreenshot();
41
40
  await expect(snapshot).toEqual(0);
42
41
  });
43
42
  });
@@ -1,12 +1,12 @@
1
- import { PATH_VIRTUALLIST, PATH_E2E_VIRTUAL_LIST, PATH_E2E_CUSTOMIZATION_SUPPORT } from '../paths';
1
+ import { PATH_E2E_VIRTUAL_LIST, PATH_E2E_CUSTOMIZATION_SUPPORT } from '../paths';
2
2
  import { PageObject, Urlbuilder, getElementByIndex } from '../helpers';
3
3
 
4
4
  export default class DSVirtualListCO extends PageObject {
5
- static basicURL = new Urlbuilder(PATH_VIRTUALLIST, 'basic');
5
+ static testChanges = new Urlbuilder(PATH_E2E_VIRTUAL_LIST, 'test-element-changes');
6
6
 
7
- static dynamicChanges = new Urlbuilder(PATH_VIRTUALLIST, 'dynamic-element-changes');
7
+ static basicTest = new Urlbuilder(PATH_E2E_VIRTUAL_LIST, 'basic-test');
8
8
 
9
- static testChanges = new Urlbuilder(PATH_E2E_VIRTUAL_LIST, 'test-element-changes');
9
+ static scrollToIndexTest = new Urlbuilder(PATH_E2E_VIRTUAL_LIST, 'scroll-to-index-test');
10
10
 
11
11
  static customization = new Urlbuilder(PATH_E2E_CUSTOMIZATION_SUPPORT, 'theming-virtual-list');
12
12
 
@@ -18,12 +18,31 @@ export default class DSVirtualListCO extends PageObject {
18
18
  return $('[data-testid="virtual-list-container"]');
19
19
  }
20
20
 
21
- static getAdd1Btn = async () => getElementByIndex(this.getButtons, 0);
21
+ static async getListWrapper() {
22
+ return $('[data-testid="ds-virtuallist-list-wrapper"]');
23
+ }
22
24
 
23
- static getChangeTimeBtn = async () => getElementByIndex(this.getButtons, 1);
25
+ static async getItems() {
26
+ return $$('[data-testid="ds-virtuallist-item-wrapper"]');
27
+ }
28
+
29
+ // Only items inside the virtualization window exist in the DOM, so this resolves
30
+ // to nothing for indexes outside it — that absence is the assertion we rely on.
31
+ static async getItemByIndex(index) {
32
+ return $(`[data-testid="ds-virtuallist-item-wrapper"][data-index="${index}"]`);
33
+ }
34
+
35
+ // The scroll-to-index story renders a single input and a single button, so the
36
+ // component-owned data-testids are unambiguous there.
37
+ static async getJumpInput() {
38
+ return $('[data-testid="ds-inputtext-input"]');
39
+ }
24
40
 
25
- // Snapshots
26
- static snapshotPath(example = 'basic') {
27
- return PageObject.getSnapshotPathBuilder('VirtualList', example, 'ds-virtuallist');
41
+ static async getJumpButton() {
42
+ return $('[data-testid="ds-button"]');
28
43
  }
44
+
45
+ static getAdd1Btn = async () => getElementByIndex(this.getButtons, 0);
46
+
47
+ static getChangeTimeBtn = async () => getElementByIndex(this.getButtons, 1);
29
48
  }
@@ -0,0 +1,28 @@
1
+ import DSVirtualListCO from '../DSVirtualListCO';
2
+ import { axeCoreCheck } from '../../helpers';
3
+
4
+ // Coverage for the ds-virtual-list compliance remediation in PUI-19125.
5
+ // DOM-attribute/a11y assertions are identical across engines, so this runs Chrome only.
6
+ // The `|| browserName === 'Chrome'` fallback is mandatory: BrowserStack reports the
7
+ // capitalized name and a lowercase-only guard would register 0 tests (false green).
8
+ if (
9
+ (!browser.capabilities['ice:options'].isPhone &&
10
+ !browser.capabilities['ice:options'].isTablet &&
11
+ browser.capabilities.browserName === 'chrome') ||
12
+ browser.capabilities.browserName === 'Chrome'
13
+ ) {
14
+ describe('PUI-19196 - DSVirtualList:: virtualized list accessibility - Axe-Core', () => {
15
+ before(async () => {
16
+ const errorOnGo = await DSVirtualListCO.basicTest.go();
17
+ if (errorOnGo) throw errorOnGo;
18
+ });
19
+
20
+ it('01: should have no accessibility violations on the rendered list', async () => {
21
+ const container = await DSVirtualListCO.getVirtualListContainer();
22
+ await container.waitForDisplayed();
23
+
24
+ const result = await axeCoreCheck();
25
+ expect(result.length).toBe(0);
26
+ });
27
+ });
28
+ }
@@ -0,0 +1,45 @@
1
+ import DSVirtualListCO from '../DSVirtualListCO';
2
+
3
+ // Coverage for the ds-virtual-list compliance remediation in PUI-19125.
4
+ // The in-package Playwright CT already covers this in Chromium; this spec exists to run the same
5
+ // contract across the full browser matrix.
6
+ // The window size is not stable (items are measured after mount, so the overscan window grows),
7
+ // which is why the assertions are "bounded window + index recycled", never an exact item count.
8
+ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
9
+ describe('PUI-19197 - DSVirtualList:: scroll to index via actionRef - Func', () => {
10
+ before(async () => {
11
+ const errorOnGo = await DSVirtualListCO.scrollToIndexTest.go();
12
+ if (errorOnGo) throw errorOnGo;
13
+ });
14
+
15
+ it('01: should render only a window of the 200 children, leaving far items out of the DOM', async () => {
16
+ const firstItem = await DSVirtualListCO.getItemByIndex(0);
17
+ await expect(firstItem).toBeExisting();
18
+
19
+ const farItem = await DSVirtualListCO.getItemByIndex(150);
20
+ await expect(farItem).not.toBeExisting();
21
+
22
+ const items = await DSVirtualListCO.getItems();
23
+ await expect(items.length).toBeLessThan(60);
24
+ });
25
+
26
+ it('02: should mount the requested item and recycle the first one after jumping', async () => {
27
+ const jumpInput = await DSVirtualListCO.getJumpInput();
28
+ await jumpInput.setValue('150');
29
+ const jumpButton = await DSVirtualListCO.getJumpButton();
30
+ await jumpButton.click();
31
+
32
+ const farItem = await DSVirtualListCO.getItemByIndex(150);
33
+ await farItem.waitForExist();
34
+ await expect(farItem).toBeDisplayed();
35
+ await expect(farItem).toHaveText('Test 150');
36
+
37
+ // the window moved instead of growing: index 0 is no longer in the DOM
38
+ const firstItem = await DSVirtualListCO.getItemByIndex(0);
39
+ await expect(firstItem).not.toBeExisting();
40
+
41
+ const items = await DSVirtualListCO.getItems();
42
+ await expect(items.length).toBeLessThan(60);
43
+ });
44
+ });
45
+ }