dimsum-e2e-tests 3.70.0-next.61 → 3.70.0-next.63

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 (27) hide show
  1. package/CHANGELOG.md +776 -766
  2. package/ds-apppicker/DSAppPickerCO.js +7 -1
  3. package/ds-apppicker/basic/DSAppPicker.basic.keyboard-nav.func.spec.js +18 -7
  4. package/ds-apppicker/closeAfterSelection/DSAppPicker.closeAfterSelection.func.spec.js +1 -1
  5. package/ds-apppicker/reflow/DSAppPicker.reflow.visual.spec.js +5 -6
  6. package/ds-banner/DSBanner.text-resize.visual.spec.js +5 -2
  7. package/ds-banner/DSBannerCO.js +4 -0
  8. package/ds-card-v3/reflow/DSCardV3.reflow.visual.spec.js +2 -0
  9. package/ds-controlled-form/ds-controlled-checkbox/DSControlledCheckboxCO.js +40 -1
  10. package/ds-controlled-form/ds-controlled-checkbox/slots/DSControlledCheckbox.slots.func.spec.js +78 -0
  11. package/ds-controlled-form/ds-controlled-radio/DSControlledRadioCO.js +40 -1
  12. package/ds-controlled-form/ds-controlled-radio/slots/DSControlledRadio.slots.func.spec.js +78 -0
  13. package/ds-dataviz/DSDataViz.axis.visual.spec.js +1 -2
  14. package/ds-dataviz/DSDataViz.ticks.visual.spec.js +1 -2
  15. package/ds-form-combobox-multi/DSComboboxMultiCO.js +16 -0
  16. package/ds-form-combobox-multi/aria-disabled/DSComboboxMulti.aria-disabled.visual.spec.js +4 -2
  17. package/ds-form-combobox-multi/theme-font/DSComboboxMulti.theme-font.func.spec.js +38 -0
  18. package/ds-form-combobox-single/DSComboboxSingleCO.js +12 -0
  19. package/ds-form-combobox-single/theme-font/DSComboboxSingle.theme-font.func.spec.js +34 -0
  20. package/ds-grid/DSGridCO.js +20 -0
  21. package/ds-grid/layout-mode/DSGrid.layout-mode.func.spec.js +51 -0
  22. package/ds-grid/layout-mode/DSGrid.layout-mode.visual.spec.js +22 -0
  23. package/ds-hooks/DSHook.UseFontSizeMedia.visual.spec.js +5 -2
  24. package/ds-hooks/DSHookCO.js +55 -1
  25. package/ds-query-builder/DSQueryBuilder.visual.spec.js +6 -6
  26. package/package.json +149 -149
  27. package/paths.js +2 -0
@@ -126,8 +126,14 @@ export default class DSAppPickerCO extends PageObject {
126
126
  return $$('[data-testid="ds-chip-label"]')[index];
127
127
  }
128
128
 
129
+ // Selection is exposed as `aria-current="true"`, not `aria-selected`. The chips are `role="button"`,
130
+ // which does not support `aria-selected` — PUI-17213/PUI-17860 (#8260) swapped it for `aria-current`
131
+ // plus the "X of Y" position announcement, signed off with the A11y SME.
132
+ // The attribute is omitted (not `false`) when unselected, so `getAttribute` resolves to null: compare
133
+ // the value instead of calling a string method on it, so an unselected chip returns false rather than
134
+ // throwing.
129
135
  static async isSelected(element) {
130
- return (await element.getAttribute('aria-selected')).includes('true');
136
+ return (await element.getAttribute('aria-current')) === 'true';
131
137
  }
132
138
 
133
139
  // Slots
@@ -1,11 +1,7 @@
1
1
  import { Key } from 'webdriverio';
2
2
  import DSAppPickerCO from '../DSAppPickerCO';
3
3
 
4
- if (
5
- !browser.capabilities['ice:options'].isPhone &&
6
- !browser.capabilities['ice:options'].isTablet &&
7
- browser.capabilities.browserName !== 'Safari'
8
- ) {
4
+ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
9
5
  describe('PUI-8030 - AppPicker:: Basic - keyboard nav func', () => {
10
6
  before('loading page', async () => {
11
7
  const errorOnGo = await DSAppPickerCO.basicURL.go();
@@ -34,12 +30,27 @@ if (
34
30
  const appPickerChip = await DSAppPickerCO.getChipByIndex(1);
35
31
  await expect(appPickerChip).toBeFocused();
36
32
  });
37
- it('05: should close the menu - Escape', async () => {
33
+ // PUI-17950 made Escape the dismissal mechanism for a chip's expanded label, satisfying the
34
+ // "Dismissible" clause of WCAG 2.1 AA 1.4.13. Escape on an open panel is therefore two-stage
35
+ // whenever the focused chip's label truncates: the first press dismisses the expansion, only
36
+ // the second reaches the dialog. Chip 1 here truncates, so both presses are needed. The
37
+ // dismissal is conditional, not blanket — on a chip whose label fits, Escape still closes the
38
+ // panel in a single press, so this is not a rule that can be applied by rote to every spec.
39
+ it('05: should dismiss the expanded chip label and keep the menu open - Escape', async () => {
40
+ const appPickerChip = await DSAppPickerCO.getChipByIndex(1);
41
+ expect(await appPickerChip.getAttribute('data-expanded')).toEqual('true');
42
+ await browser.keys(Key.Escape);
43
+ const appPicker = await DSAppPickerCO.getAppPickerRoot();
44
+ await expect(appPicker).toBeDisplayedInViewport();
45
+ await expect(appPickerChip).toBeFocused();
46
+ expect(await appPickerChip.getAttribute('data-expanded')).not.toEqual('true');
47
+ });
48
+ it('06: should close the menu - Escape', async () => {
38
49
  const openAppPickerBtn = await DSAppPickerCO.getOpenAppPicker();
39
50
  await browser.keys(Key.Escape);
40
51
  await expect(openAppPickerBtn).toBeFocused();
41
52
  });
42
- it('06: should reopen the menu and focus the first enabled chip - Space', async () => {
53
+ it('07: should reopen the menu and focus the first enabled chip - Space', async () => {
43
54
  await browser.keys(Key.Space);
44
55
  const appPicker = await DSAppPickerCO.getAppPickerRoot();
45
56
  const appPickerChip = await DSAppPickerCO.getChipByIndex(1);
@@ -20,7 +20,7 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
20
20
  await expect(appPicker).not.toBeDisplayedInViewport();
21
21
  await expect(triggerBtn).toBeFocused();
22
22
  });
23
- it('03: should open appPicker and have the third chip still focused', async () => {
23
+ it('03: should reopen appPicker with the selected app still marked as current', async () => {
24
24
  const openAppPickerBtn = await DSAppPickerCO.getOpenAppPicker();
25
25
  await openAppPickerBtn.click();
26
26
  const reportChip = await DSAppPickerCO.getChipByIndex(6);
@@ -1,18 +1,19 @@
1
1
  /* eslint-disable wdio/no-pause */
2
2
  import DSAppPickerCO from '../DSAppPickerCO';
3
+ import { setTextSize } from '../../helpers';
3
4
 
4
5
  if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
5
6
  describe('PUI-17867 - AppPicker:: Reflow test -visual', () => {
6
7
  before('loading page', async () => {
7
8
  const errorOnGo = await DSAppPickerCO.reflowURL.go();
8
9
  if (errorOnGo) throw errorOnGo;
9
- await browser.maximizeWindow();
10
+ await setTextSize('26');
10
11
  });
11
12
 
12
13
  it('01: should show appPicker open', async () => {
13
14
  const openAppPickerBtn = await DSAppPickerCO.getOpenAppPicker();
14
15
  await openAppPickerBtn.click();
15
- const snapshot = await browser.percyCheckScreenshot(DSAppPickerCO.snapshotPath('reflow-appPicker-open'));
16
+ const snapshot = await browser.percyCheckScreenshot();
16
17
  await expect(snapshot).toEqual(0);
17
18
  });
18
19
 
@@ -20,16 +21,14 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
20
21
  const secondPipelineChip = await DSAppPickerCO.getChipByLabelAndIndex('Pipeline', 1);
21
22
  await secondPipelineChip.scrollIntoView();
22
23
  await browser.pause(300);
23
- const snapshot = await browser.percyCheckScreenshot(DSAppPickerCO.snapshotPath('reflow-appPicker-scrolled'));
24
+ const snapshot = await browser.percyCheckScreenshot();
24
25
  await expect(snapshot).toEqual(0);
25
26
  });
26
27
 
27
28
  it('03: should select the 2nd Pipeline chip', async () => {
28
29
  const secondPipelineChip = await DSAppPickerCO.getChipByLabelAndIndex('Pipeline', 1);
29
30
  await secondPipelineChip.click();
30
- const snapshot = await browser.percyCheckScreenshot(
31
- DSAppPickerCO.snapshotPath('reflow-appPicker-pipeline-selected'),
32
- );
31
+ const snapshot = await browser.percyCheckScreenshot();
33
32
  await expect(snapshot).toEqual(0);
34
33
  });
35
34
  });
@@ -1,14 +1,17 @@
1
1
  import DSBannerCO from './DSBannerCO';
2
+ import { setTextSize } from '../helpers';
2
3
 
3
4
  if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
4
5
  describe('PUI-17535 - DSBanner - Text Resize In Different Layouts - Visual Test', () => {
5
6
  before('loading page', async () => {
6
7
  const errorOnGo = await DSBannerCO.textResizeInDifferentLayoutsTest.go();
7
8
  if (errorOnGo) throw errorOnGo;
9
+ await setTextSize('26');
8
10
  });
9
11
  it('01: should display text resize in different layouts properly', async () => {
10
- await browser.eyesOpen();
11
- const snapshot = await browser.eyesCheckSnapshot(DSBannerCO.snapshotPath('text-resize-in-different-layouts'));
12
+ const banner = await DSBannerCO.getBannerByIndex();
13
+ await banner.waitForDisplayed();
14
+ const snapshot = await browser.percyCheckScreenshot();
12
15
  await expect(snapshot).toEqual(0);
13
16
  });
14
17
  });
@@ -62,6 +62,10 @@ export default class DSBannerCO extends PageObject {
62
62
  return $('[data-testid="ds-banner"');
63
63
  }
64
64
 
65
+ static async getBannerByIndex(index = 0) {
66
+ return $$('[data-testid="ds-banner"]')[index];
67
+ }
68
+
65
69
  static async getBannerContainer() {
66
70
  return $('[data-testid="ds-banner-container"]');
67
71
  }
@@ -1,10 +1,12 @@
1
1
  import DSCardV3CO from '../DSCardV3CO';
2
+ import { setTextSize } from '../../helpers';
2
3
 
3
4
  if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
4
5
  describe('PUI-13981 - CardV3: Reflow -Visual Test', () => {
5
6
  before('loading page', async () => {
6
7
  const errorOnGo = await DSCardV3CO.reflowURL.go();
7
8
  if (errorOnGo) throw errorOnGo;
9
+ await setTextSize('26');
8
10
  });
9
11
  it('01: should display cards after a 200% of text resize', async () => {
10
12
  const expandContentButton = await DSCardV3CO.getExpandContentButton();
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable import/no-relative-packages */
2
- import { PATH_E2E_CHECKBOX } from '../../paths';
2
+ import { PATH_E2E_CHECKBOX, PATH_E2E_CHECKBOX_SLOTS } from '../../paths';
3
3
  import { PageObject, Urlbuilder, getElementByIndex } from '../../helpers';
4
4
 
5
5
  export default class DSControlledCheckboxCO extends PageObject {
@@ -25,6 +25,8 @@ export default class DSControlledCheckboxCO extends PageObject {
25
25
 
26
26
  static themeFontURL = new Urlbuilder(PATH_E2E_CHECKBOX, 'theme-font-family-test');
27
27
 
28
+ static slotsTest = new Urlbuilder(PATH_E2E_CHECKBOX_SLOTS, 'slots-test');
29
+
28
30
  static async getCheckbox() {
29
31
  return $('[data-testid="ds-checkbox"]');
30
32
  }
@@ -80,4 +82,41 @@ export default class DSControlledCheckboxCO extends PageObject {
80
82
  static async getFallbackCheckboxLabel() {
81
83
  return $('[data-testid="theme-font-fallback"] [data-testid="ds-checkbox-label-typography"]');
82
84
  }
85
+
86
+ // Slots rendered through styled(DSTypography) expose the owner slot on
87
+ // `data-dimsum-parent-slot` (the element's own `data-dimsum-slot` is the typography root),
88
+ // while plain-tag slots expose it on `data-dimsum-slot`.
89
+ static async getSlotsRoot() {
90
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsCheckboxRoot"]');
91
+ }
92
+
93
+ static async getSlotsInputWrapper() {
94
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsCheckboxInputWrapper"]');
95
+ }
96
+
97
+ static async getSlotsInput() {
98
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsCheckboxInput"]');
99
+ }
100
+
101
+ static async getSlotsLabel() {
102
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsCheckboxLabel"]');
103
+ }
104
+
105
+ static async getSlotsLabelTypography() {
106
+ return $('[data-testid="slots-wrapped"] [data-dimsum-parent-slot="dsCheckboxLabelTypography"]');
107
+ }
108
+
109
+ static async getSlotsTruncatedLabel() {
110
+ return $('[data-testid="slots-truncated"] [data-dimsum-parent-slot="dsCheckboxTruncatedLabel"]');
111
+ }
112
+
113
+ // The truncation tooltip renders through a portal, outside the story wrapper, so both tooltip
114
+ // slots are queried document-wide instead of scoped to the instance.
115
+ static async getSlotsTooltipContainer() {
116
+ return $('[data-dimsum-slot="dsCheckboxTooltipContainer"]');
117
+ }
118
+
119
+ static async getSlotsTooltipText() {
120
+ return $('[data-dimsum-parent-slot="dsCheckboxTooltipText"]');
121
+ }
83
122
  }
@@ -0,0 +1,78 @@
1
+ import DSControlledCheckboxCO from '../DSControlledCheckboxCO';
2
+
3
+ // Slot surface added when the DSControlledCheckbox label moved to DSTypography under PUI-18774:
4
+ // label-typography, truncated-label, tooltip-container and tooltip-text. The tooltip pair only
5
+ // exists while the label is truncated AND hovered, which is why this lives in E2E rather than in
6
+ // the package's jest slot tests.
7
+ if (
8
+ (!browser.capabilities['ice:options'].isPhone &&
9
+ !browser.capabilities['ice:options'].isTablet &&
10
+ browser.capabilities.browserName === 'chrome') ||
11
+ browser.capabilities.browserName === 'Chrome'
12
+ ) {
13
+ describe('PUI-18976 - DSControlledCheckbox:: label typography and tooltip slots - Func', () => {
14
+ before('loading page', async () => {
15
+ const errorOnGo = await DSControlledCheckboxCO.slotsTest.go();
16
+ if (errorOnGo) throw errorOnGo;
17
+ });
18
+
19
+ it('01: should have custom aria-* for each rendered label slot', async () => {
20
+ const root = await DSControlledCheckboxCO.getSlotsRoot();
21
+ const inputWrapper = await DSControlledCheckboxCO.getSlotsInputWrapper();
22
+ const input = await DSControlledCheckboxCO.getSlotsInput();
23
+ const label = await DSControlledCheckboxCO.getSlotsLabel();
24
+ const labelTypography = await DSControlledCheckboxCO.getSlotsLabelTypography();
25
+ const truncatedLabel = await DSControlledCheckboxCO.getSlotsTruncatedLabel();
26
+ await expect(root).toHaveAttribute('aria-label', 'root aria');
27
+ await expect(inputWrapper).toHaveAttribute('aria-label', 'inputwrapper aria');
28
+ await expect(input).toHaveAttribute('aria-label', 'input aria');
29
+ await expect(label).toHaveAttribute('aria-label', 'label aria');
30
+ await expect(labelTypography).toHaveAttribute('aria-label', 'labeltypography aria');
31
+ await expect(truncatedLabel).toHaveAttribute('aria-label', 'truncatedlabel aria');
32
+ });
33
+
34
+ it('02: should have custom data-* for each rendered label slot', async () => {
35
+ const root = await DSControlledCheckboxCO.getSlotsRoot();
36
+ const inputWrapper = await DSControlledCheckboxCO.getSlotsInputWrapper();
37
+ const input = await DSControlledCheckboxCO.getSlotsInput();
38
+ const label = await DSControlledCheckboxCO.getSlotsLabel();
39
+ const labelTypography = await DSControlledCheckboxCO.getSlotsLabelTypography();
40
+ const truncatedLabel = await DSControlledCheckboxCO.getSlotsTruncatedLabel();
41
+ await expect(root).toHaveAttribute('data-testid', 'root data');
42
+ await expect(inputWrapper).toHaveAttribute('data-testid', 'inputwrapper data');
43
+ await expect(input).toHaveAttribute('data-testid', 'input data');
44
+ await expect(label).toHaveAttribute('data-testid', 'label data');
45
+ await expect(labelTypography).toHaveAttribute('data-testid', 'labeltypography data');
46
+ await expect(truncatedLabel).toHaveAttribute('data-testid', 'truncatedlabel data');
47
+ });
48
+
49
+ it('03: should have custom lang for each rendered label slot', async () => {
50
+ const root = await DSControlledCheckboxCO.getSlotsRoot();
51
+ const inputWrapper = await DSControlledCheckboxCO.getSlotsInputWrapper();
52
+ const input = await DSControlledCheckboxCO.getSlotsInput();
53
+ const label = await DSControlledCheckboxCO.getSlotsLabel();
54
+ const labelTypography = await DSControlledCheckboxCO.getSlotsLabelTypography();
55
+ const truncatedLabel = await DSControlledCheckboxCO.getSlotsTruncatedLabel();
56
+ await expect(root).toHaveAttribute('lang', 'ja');
57
+ await expect(inputWrapper).toHaveAttribute('lang', 'it');
58
+ await expect(input).toHaveAttribute('lang', 'de');
59
+ await expect(label).toHaveAttribute('lang', 'fr');
60
+ await expect(labelTypography).toHaveAttribute('lang', 'es');
61
+ await expect(truncatedLabel).toHaveAttribute('lang', 'pt');
62
+ });
63
+
64
+ it('04: should have custom aria-*, data-* and lang for the tooltip slots on hover', async () => {
65
+ const truncatedLabel = await DSControlledCheckboxCO.getSlotsTruncatedLabel();
66
+ await truncatedLabel.moveTo();
67
+ const tooltipContainer = await DSControlledCheckboxCO.getSlotsTooltipContainer();
68
+ await tooltipContainer.waitForDisplayed();
69
+ const tooltipText = await DSControlledCheckboxCO.getSlotsTooltipText();
70
+ await expect(tooltipContainer).toHaveAttribute('aria-label', 'tooltipcontainer aria');
71
+ await expect(tooltipContainer).toHaveAttribute('data-testid', 'tooltipcontainer data');
72
+ await expect(tooltipContainer).toHaveAttribute('lang', 'zh');
73
+ await expect(tooltipText).toHaveAttribute('aria-label', 'tooltiptext aria');
74
+ await expect(tooltipText).toHaveAttribute('data-testid', 'tooltiptext data');
75
+ await expect(tooltipText).toHaveAttribute('lang', 'ru');
76
+ });
77
+ });
78
+ }
@@ -1,4 +1,4 @@
1
- import { PATH_E2E_RADIO } from '../../paths';
1
+ import { PATH_E2E_RADIO, PATH_E2E_RADIO_SLOTS } from '../../paths';
2
2
  import { PageObject, Urlbuilder, getElementByIndex } from '../../helpers';
3
3
 
4
4
  export default class DSControlledRadioCO extends PageObject {
@@ -18,6 +18,8 @@ export default class DSControlledRadioCO extends PageObject {
18
18
 
19
19
  static themeFontURL = new Urlbuilder(PATH_E2E_RADIO, 'theme-font-family-test');
20
20
 
21
+ static slotsTest = new Urlbuilder(PATH_E2E_RADIO_SLOTS, 'slots-test');
22
+
21
23
  static async getRadio() {
22
24
  return $('[data-testid="ds-radio"]');
23
25
  }
@@ -53,4 +55,41 @@ export default class DSControlledRadioCO extends PageObject {
53
55
  static async getFallbackRadioLabel() {
54
56
  return $('[data-testid="theme-font-fallback"] [data-testid="ds-radio-label-typography"]');
55
57
  }
58
+
59
+ // Slots rendered through styled(DSTypography) expose the owner slot on
60
+ // `data-dimsum-parent-slot` (the element's own `data-dimsum-slot` is the typography root),
61
+ // while plain-tag slots expose it on `data-dimsum-slot`.
62
+ static async getSlotsRoot() {
63
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsRadioRoot"]');
64
+ }
65
+
66
+ static async getSlotsInputWrapper() {
67
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsRadioInputWrapper"]');
68
+ }
69
+
70
+ static async getSlotsInput() {
71
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsRadioInput"]');
72
+ }
73
+
74
+ static async getSlotsLabel() {
75
+ return $('[data-testid="slots-wrapped"] [data-dimsum-slot="dsRadioLabel"]');
76
+ }
77
+
78
+ static async getSlotsLabelTypography() {
79
+ return $('[data-testid="slots-wrapped"] [data-dimsum-parent-slot="dsRadioLabelTypography"]');
80
+ }
81
+
82
+ static async getSlotsTruncatedLabel() {
83
+ return $('[data-testid="slots-truncated"] [data-dimsum-parent-slot="dsRadioTruncatedLabel"]');
84
+ }
85
+
86
+ // The truncation tooltip renders through a portal, outside the story wrapper, so both tooltip
87
+ // slots are queried document-wide instead of scoped to the instance.
88
+ static async getSlotsTooltipContainer() {
89
+ return $('[data-dimsum-slot="dsRadioTooltipContainer"]');
90
+ }
91
+
92
+ static async getSlotsTooltipText() {
93
+ return $('[data-dimsum-parent-slot="dsRadioTooltipText"]');
94
+ }
56
95
  }
@@ -0,0 +1,78 @@
1
+ import DSControlledRadioCO from '../DSControlledRadioCO';
2
+
3
+ // Slot surface added when the DSControlledRadio label moved to DSTypography under PUI-18774:
4
+ // label-typography, truncated-label, tooltip-container and tooltip-text. The tooltip pair only
5
+ // exists while the label is truncated AND hovered, which is why this lives in E2E rather than in
6
+ // the package's jest slot tests.
7
+ if (
8
+ (!browser.capabilities['ice:options'].isPhone &&
9
+ !browser.capabilities['ice:options'].isTablet &&
10
+ browser.capabilities.browserName === 'chrome') ||
11
+ browser.capabilities.browserName === 'Chrome'
12
+ ) {
13
+ describe('PUI-18975 - DSControlledRadio:: label typography and tooltip slots - Func', () => {
14
+ before('loading page', async () => {
15
+ const errorOnGo = await DSControlledRadioCO.slotsTest.go();
16
+ if (errorOnGo) throw errorOnGo;
17
+ });
18
+
19
+ it('01: should have custom aria-* for each rendered label slot', async () => {
20
+ const root = await DSControlledRadioCO.getSlotsRoot();
21
+ const inputWrapper = await DSControlledRadioCO.getSlotsInputWrapper();
22
+ const input = await DSControlledRadioCO.getSlotsInput();
23
+ const label = await DSControlledRadioCO.getSlotsLabel();
24
+ const labelTypography = await DSControlledRadioCO.getSlotsLabelTypography();
25
+ const truncatedLabel = await DSControlledRadioCO.getSlotsTruncatedLabel();
26
+ await expect(root).toHaveAttribute('aria-label', 'root aria');
27
+ await expect(inputWrapper).toHaveAttribute('aria-label', 'inputwrapper aria');
28
+ await expect(input).toHaveAttribute('aria-label', 'input aria');
29
+ await expect(label).toHaveAttribute('aria-label', 'label aria');
30
+ await expect(labelTypography).toHaveAttribute('aria-label', 'labeltypography aria');
31
+ await expect(truncatedLabel).toHaveAttribute('aria-label', 'truncatedlabel aria');
32
+ });
33
+
34
+ it('02: should have custom data-* for each rendered label slot', async () => {
35
+ const root = await DSControlledRadioCO.getSlotsRoot();
36
+ const inputWrapper = await DSControlledRadioCO.getSlotsInputWrapper();
37
+ const input = await DSControlledRadioCO.getSlotsInput();
38
+ const label = await DSControlledRadioCO.getSlotsLabel();
39
+ const labelTypography = await DSControlledRadioCO.getSlotsLabelTypography();
40
+ const truncatedLabel = await DSControlledRadioCO.getSlotsTruncatedLabel();
41
+ await expect(root).toHaveAttribute('data-testid', 'root data');
42
+ await expect(inputWrapper).toHaveAttribute('data-testid', 'inputwrapper data');
43
+ await expect(input).toHaveAttribute('data-testid', 'input data');
44
+ await expect(label).toHaveAttribute('data-testid', 'label data');
45
+ await expect(labelTypography).toHaveAttribute('data-testid', 'labeltypography data');
46
+ await expect(truncatedLabel).toHaveAttribute('data-testid', 'truncatedlabel data');
47
+ });
48
+
49
+ it('03: should have custom lang for each rendered label slot', async () => {
50
+ const root = await DSControlledRadioCO.getSlotsRoot();
51
+ const inputWrapper = await DSControlledRadioCO.getSlotsInputWrapper();
52
+ const input = await DSControlledRadioCO.getSlotsInput();
53
+ const label = await DSControlledRadioCO.getSlotsLabel();
54
+ const labelTypography = await DSControlledRadioCO.getSlotsLabelTypography();
55
+ const truncatedLabel = await DSControlledRadioCO.getSlotsTruncatedLabel();
56
+ await expect(root).toHaveAttribute('lang', 'ja');
57
+ await expect(inputWrapper).toHaveAttribute('lang', 'it');
58
+ await expect(input).toHaveAttribute('lang', 'de');
59
+ await expect(label).toHaveAttribute('lang', 'fr');
60
+ await expect(labelTypography).toHaveAttribute('lang', 'es');
61
+ await expect(truncatedLabel).toHaveAttribute('lang', 'pt');
62
+ });
63
+
64
+ it('04: should have custom aria-*, data-* and lang for the tooltip slots on hover', async () => {
65
+ const truncatedLabel = await DSControlledRadioCO.getSlotsTruncatedLabel();
66
+ await truncatedLabel.moveTo();
67
+ const tooltipContainer = await DSControlledRadioCO.getSlotsTooltipContainer();
68
+ await tooltipContainer.waitForDisplayed();
69
+ const tooltipText = await DSControlledRadioCO.getSlotsTooltipText();
70
+ await expect(tooltipContainer).toHaveAttribute('aria-label', 'tooltipcontainer aria');
71
+ await expect(tooltipContainer).toHaveAttribute('data-testid', 'tooltipcontainer data');
72
+ await expect(tooltipContainer).toHaveAttribute('lang', 'zh');
73
+ await expect(tooltipText).toHaveAttribute('aria-label', 'tooltiptext aria');
74
+ await expect(tooltipText).toHaveAttribute('data-testid', 'tooltiptext data');
75
+ await expect(tooltipText).toHaveAttribute('lang', 'ru');
76
+ });
77
+ });
78
+ }
@@ -65,8 +65,7 @@ if (!browser.capabilities['ice:options'].isPhone) {
65
65
 
66
66
  // fails on firefox due to fake-positive of infinte loop
67
67
  if (browser.capabilities.browserName !== 'Firefox') {
68
- // Skipped until PUI-16334 gets fixed
69
- describe.skip('PUI-9401 - DSDataViz, TimeLinear axis -Visual', () => {
68
+ describe('PUI-9401 - DSDataViz, TimeLinear axis -Visual', () => {
70
69
  before('loading page', async () => {
71
70
  const errorOnGo = await DSDataVizCO.timeLinearAxis.go();
72
71
  if (errorOnGo) throw errorOnGo;
@@ -62,8 +62,7 @@ if (!browser.capabilities['ice:options'].isPhone) {
62
62
  });
63
63
  });
64
64
 
65
- // Skipped until PUI-16334 gets fixed
66
- describe.skip('PUI-10321 - DSDataViz, Force ticks -Visual', () => {
65
+ describe('PUI-10321 - DSDataViz, Force ticks -Visual', () => {
67
66
  before('loading page', async () => {
68
67
  const errorOnGo = await DSDataVizCO.forceTicks.go();
69
68
  if (errorOnGo) throw errorOnGo;
@@ -39,6 +39,8 @@ export default class DSComboboxMultiCO extends PageObject {
39
39
 
40
40
  static withTooltip = new Urlbuilder(PATH_E2E_COMBOBOX_MULTI, 'with-tooltip-test');
41
41
 
42
+ static themeFontURL = new Urlbuilder(PATH_E2E_COMBOBOX_MULTI, 'theme-font-family-test');
43
+
42
44
  static controlledMenuStateWithSectionsSlotsTest = new Urlbuilder(
43
45
  PATH_E2E_COMBOBOX_MULTI_SLOTS,
44
46
  'controlled-menu-state-with-section-slots-test',
@@ -294,4 +296,18 @@ export default class DSComboboxMultiCO extends PageObject {
294
296
  static snapshotPath(example = 'basic') {
295
297
  return PageObject.getSnapshotPathBuilder('DSComboboxMulti', example, 'ds-combobox-multi');
296
298
  }
299
+
300
+ // The selection wrapper and the selected pills' labels both render through
301
+ // styled(DSTypography), so the owner slot lands on `data-dimsum-parent-slot`.
302
+ static async getThemedSelection() {
303
+ return $('[data-testid="theme-font-sentinel"] [data-dimsum-parent-slot="dsComboboxSelection"]');
304
+ }
305
+
306
+ static async getFallbackSelection() {
307
+ return $('[data-testid="theme-font-fallback"] [data-dimsum-parent-slot="dsComboboxSelection"]');
308
+ }
309
+
310
+ static async getThemedPillLabel() {
311
+ return $('[data-testid="theme-font-sentinel"] [data-dimsum-parent-slot="dsPillLabel"]');
312
+ }
297
313
  }
@@ -11,8 +11,10 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
11
11
  });
12
12
  it('01: The first aria-disabled combobox should be focused', async () => {
13
13
  await browser.eyesOpen();
14
- const firstCombobox = await DSComboboxMultiCO.getComboboxByIndex(0);
15
- await firstCombobox.click();
14
+ // the container centre lands on the aria-hidden overlay input, which never focuses the
15
+ // combobox; the selected-values area is the only pointer target that reliably focuses it
16
+ const firstComboboxValues = await DSComboboxMultiCO.getSelectedValuesByIndex(0);
17
+ await firstComboboxValues.click();
16
18
  const snapshot = await browser.eyesCheckSnapshot(
17
19
  DSComboboxMultiCO.snapshotPath('combobox-multi-aria-disabled-click'),
18
20
  );
@@ -0,0 +1,38 @@
1
+ import DSComboboxMultiCO from '../DSComboboxMultiCO';
2
+
3
+ // Tests the theme-driven font-family mechanism covered by the combobox theming spike PUI-18214
4
+ // (the selection wrapper and the selected pills' labels render through DSTypography, which reads
5
+ // theme.fonts.default, falling back to proxima-nova). Theme-agnostic: uses a sentinel font,
6
+ // never the dsConsumer POC theme values, so it survives any future theme change.
7
+ // Out of scope on purpose: the native <input> that holds typed text mirrors b2 as raw CSS and
8
+ // declares no font-family, so it does NOT follow theme.fonts.default (measured under the sentinel
9
+ // theme it still computes proxima-nova, i.e. the inherited page font, while the selection text
10
+ // switches). Asserting it here would lock in that gap; it is reported separately instead.
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-18974 - ComboboxMulti:: theme font-family wiring - Func', () => {
18
+ before('loading page', async () => {
19
+ const errorOnGo = await DSComboboxMultiCO.themeFontURL.go();
20
+ if (errorOnGo) throw errorOnGo;
21
+ });
22
+
23
+ it('01: should read selection and pill font-family from theme with proxima-nova fallback', async () => {
24
+ const themed = await DSComboboxMultiCO.getThemedSelection();
25
+ await themed.waitForDisplayed();
26
+ const themedFont = (await themed.getCSSProperty('font-family')).value.toLowerCase();
27
+ await expect(themedFont).toContain('sentinelfont');
28
+
29
+ const themedPill = await DSComboboxMultiCO.getThemedPillLabel();
30
+ const themedPillFont = (await themedPill.getCSSProperty('font-family')).value.toLowerCase();
31
+ await expect(themedPillFont).toContain('sentinelfont');
32
+
33
+ const fallback = await DSComboboxMultiCO.getFallbackSelection();
34
+ const fallbackFont = (await fallback.getCSSProperty('font-family')).value.toLowerCase();
35
+ await expect(fallbackFont).toContain('proxima-nova');
36
+ });
37
+ });
38
+ }
@@ -45,6 +45,8 @@ export default class DSComboboxSingleCO extends PageObject {
45
45
 
46
46
  static withTooltip = new Urlbuilder(PATH_E2E_COMBOBOX_SINGLE, 'with-tooltip-test');
47
47
 
48
+ static themeFontURL = new Urlbuilder(PATH_E2E_COMBOBOX_SINGLE, 'theme-font-family-test');
49
+
48
50
  // COMBOBOX - INPUT
49
51
  static async getCombobox() {
50
52
  return $('[data-testid="combobox-container"]');
@@ -275,4 +277,14 @@ export default class DSComboboxSingleCO extends PageObject {
275
277
  static snapshotPath(example = 'basic') {
276
278
  return PageObject.getSnapshotPathBuilder('DSComboboxV3', example, 'ds-combobox');
277
279
  }
280
+
281
+ // The selection text renders through styled(DSTypography), so the owner slot lands on
282
+ // `data-dimsum-parent-slot` (the element's own `data-dimsum-slot` is the typography root).
283
+ static async getThemedSelection() {
284
+ return $('[data-testid="theme-font-sentinel"] [data-dimsum-parent-slot="dsComboboxSelection"]');
285
+ }
286
+
287
+ static async getFallbackSelection() {
288
+ return $('[data-testid="theme-font-fallback"] [data-dimsum-parent-slot="dsComboboxSelection"]');
289
+ }
278
290
  }
@@ -0,0 +1,34 @@
1
+ import DSComboboxSingleCO from '../DSComboboxSingleCO';
2
+
3
+ // Tests the theme-driven font-family mechanism covered by the combobox theming spike PUI-18214
4
+ // (the selection text renders through DSTypography, which reads theme.fonts.default, falling
5
+ // back to proxima-nova). Theme-agnostic: uses a sentinel font, never the dsConsumer POC theme
6
+ // values, so it survives any future theme change.
7
+ // Out of scope on purpose: the native <input> that holds typed text mirrors b2 as raw CSS and
8
+ // declares no font-family, so it does NOT follow theme.fonts.default (measured under the sentinel
9
+ // theme it still computes proxima-nova, i.e. the inherited page font, while the selection text
10
+ // switches). Asserting it here would lock in that gap; it is reported separately instead.
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-18973 - ComboboxSingle:: theme font-family wiring - Func', () => {
18
+ before('loading page', async () => {
19
+ const errorOnGo = await DSComboboxSingleCO.themeFontURL.go();
20
+ if (errorOnGo) throw errorOnGo;
21
+ });
22
+
23
+ it('01: should read selection font-family from theme with proxima-nova fallback', async () => {
24
+ const themed = await DSComboboxSingleCO.getThemedSelection();
25
+ await themed.waitForDisplayed();
26
+ const themedFont = (await themed.getCSSProperty('font-family')).value.toLowerCase();
27
+ await expect(themedFont).toContain('sentinelfont');
28
+
29
+ const fallback = await DSComboboxSingleCO.getFallbackSelection();
30
+ const fallbackFont = (await fallback.getCSSProperty('font-family')).value.toLowerCase();
31
+ await expect(fallbackFont).toContain('proxima-nova');
32
+ });
33
+ });
34
+ }
@@ -43,6 +43,8 @@ export default class DSGridCO extends PageObject {
43
43
 
44
44
  static breakpointsV2 = new Urlbuilder(PATH_E2E_GRID, 'with-breakpoints-v-2-test');
45
45
 
46
+ static layoutMode = new Urlbuilder(PATH_E2E_GRID, 'with-layout-mode-test');
47
+
46
48
  static getUrl(component = 'basic') {
47
49
  return PageObject.getUrl(PATH_E2E_GRID, component);
48
50
  }
@@ -70,6 +72,24 @@ export default class DSGridCO extends PageObject {
70
72
  return $(`//div[text()="Scrollable Body"]`);
71
73
  }
72
74
 
75
+ static async getGridByTestId(testId) {
76
+ return $(`[data-testid="${testId}"]`);
77
+ }
78
+
79
+ // Computed grid-template-columns, e.g. "640px 640px 640px". Returned as a string so callers can
80
+ // compare two grids to each other without depending on the resolved px values.
81
+ static async getResolvedColumns(testId) {
82
+ const grid = await this.getGridByTestId(testId);
83
+ await grid.waitForDisplayed();
84
+ return String((await grid.getCSSProperty('grid-template-columns')).value);
85
+ }
86
+
87
+ // Number of resolved column tracks. This is the only cross-browser-stable signal of which
88
+ // breakpoint definition won, and it never asserts px values.
89
+ static async getResolvedColumnCount(testId) {
90
+ return (await this.getResolvedColumns(testId)).trim().split(/\s+/).filter(Boolean).length;
91
+ }
92
+
73
93
  // Snapshots
74
94
  static snapshotPath(example = 'basic') {
75
95
  return PageObject.getSnapshotPathBuilder('Grid', example);