dimsum-e2e-tests 3.70.0-next.45 → 3.70.0-next.47
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.
- package/CHANGELOG.md +12 -0
- package/ds-autocomplete/slots/DSAutocomplete.slots.func.spec.js +6 -1
- package/ds-card/DSCard.cardv2_v1.func.spec.js +54 -0
- package/ds-card/DSCard.cardv2_v1.visual.spec.js +29 -0
- package/ds-card/DSCardCO.js +4 -0
- package/ds-card-v3/DSCardV3CO.js +78 -0
- package/ds-card-v3/accordion/DSCardV3.accordion.axe-core.func.spec.js +28 -0
- package/ds-card-v3/accordion/DSCardV3.accordion.visual.spec.js +4 -6
- package/ds-card-v3/disabled/DSCardV3.disabled.visual.spec.js +1 -2
- package/ds-card-v3/drag-n-drop/DSCardV3.DnD.axe-core.func.spec.js +61 -0
- package/ds-card-v3/drag-n-drop/DSCardV3.DnD.func.spec.js +129 -0
- package/ds-card-v3/drag-n-drop/DSCardV3.DnD.reorder.func.spec.js +73 -0
- package/ds-card-v3/drag-n-drop/DSCardV3.DnD.visual.spec.js +5 -12
- package/ds-card-v3/global-attributes/DSCardV3.global-attributes.axe-core.func.spec.js +33 -0
- package/ds-card-v3/global-attributes/DSCardV3.global-attributes.visual.spec.js +20 -0
- package/ds-card-v3/multi-select/DSCardV3.multiselect.keyboard.func.spec.js +65 -0
- package/ds-card-v3/multi-select/DSCardV3.multiselect.visual.spec.js +17 -0
- package/ds-card-v3/multiple-action/DSCardV3.multiple-action.func.spec.js +53 -0
- package/ds-card-v3/multiple-action/DSCardV3.multiple-action.visual.spec.js +18 -0
- package/ds-card-v3/multiple-control/DSCardV3.multi-control.func.spec.js +25 -0
- package/ds-card-v3/no-action/DSCardV3.no-action.axe-core.func.spec.js +22 -0
- package/ds-card-v3/no-action/DSCardV3.no-action.func.spec.js +23 -0
- package/ds-card-v3/no-action/DSCardV3.no-action.visual.spec.js +15 -0
- package/ds-card-v3/notification-badges/DSCardV3.notification-badges.func.spec.js +19 -0
- package/ds-card-v3/reflow/DSCardV3.reflow.axe-core.func.spec.js +28 -0
- package/ds-card-v3/reflow/DSCardV3.reflow.func.spec.js +36 -0
- package/ds-card-v3/reflow/DSCardV3.reflow.visual.spec.js +1 -2
- package/ds-card-v3/single-select/DSCardV3.single-select.axe-core.func.spec.js +37 -0
- package/ds-card-v3/single-select/DSCardV3.single-select.visual.spec.js +1 -2
- package/ds-card-v3/slots/DSCardV3.slots.visual.spec.js +1 -2
- package/ds-card-v3/variant-accented/DSCardV3.variant-accented.func.spec.js +28 -0
- package/ds-card-v3/variant-accented/DSCardV3.variant-accented.visual.spec.js +35 -0
- package/ds-card-v3/variant-accented-disabled/DSCardV3.variant-accented-disabled.func.spec.js +53 -0
- package/ds-card-v3/variant-disabled/DSCardV3.variant-disabled.axe-core.func.spec.js +22 -0
- package/ds-chat/DSChatCO.js +3 -3
- package/ds-chat/components/ComposerCO.js +1 -1
- package/ds-chat/components/HeaderCO.js +1 -1
- package/ds-controlled-form/react-hook-form/ReactHook.func.spec.js +1 -3
- package/ds-data-table-async/DSDataTableCO.js +6 -0
- package/ds-data-table-async/components/FilterBarCO.js +7 -0
- package/ds-data-table-async/components/HeaderCO.js +15 -1
- package/ds-data-table-async/filtersV2/DSDataTable.filtersV2.axe-core.func.spec.js +45 -29
- package/ds-data-table-async/filtersV2/pill-label/DSDataTable.pillLabel.axe-core.func.spec.js +23 -0
- package/ds-data-table-async/filtersV2/pill-label/DSDataTable.pillLabel.func.spec.js +50 -0
- package/ds-data-table-async/filtersV2/pill-label/DSDataTable.pillLabel.visual.spec.js +21 -0
- package/ds-data-table-async/resizable/DSDataTable.resizerA11y.axe-core.func.spec.js +46 -0
- package/ds-data-table-async/resizable/DSDataTable.resizerA11y.func.spec.js +108 -0
- package/ds-form-combobox-single/aria-busy-loading/DSComboboxSingle.aria-busy-loading.axe-core.func.spec.js +1 -1
- package/ds-form-date-time-picker/DSControlledDateTimePickerCO.js +7 -0
- package/ds-form-date-time-picker/date-time-full/DateTimeFull.axe-core.func.spec.js +8 -1
- package/ds-form-date-time-picker/date-time-full/DateTimePicker.DateTimeFull.visual.spec.js +9 -0
- package/ds-hooks/DSHookCO.js +1 -22
- package/ds-menu-button/slots/DSMenuButton.slots.axe-core.func.spec.js +28 -0
- package/ds-menu-button/slots/DSMenuButton.slots.func.spec.js +19 -0
- package/ds-shuttle-v2/explicit-selection-ui/DSShuttleV2.explicit-selection-ui.visual.spec.js +3 -0
- package/package.json +150 -150
- package/paths.js +0 -2
- package/ds-hooks/DSHook.func.spec.js +0 -60
|
@@ -135,6 +135,8 @@ export default class DataTableCO extends PageObject {
|
|
|
135
135
|
|
|
136
136
|
static allFiltersV2 = new Urlbuilder(PATH_E2E_DATATABLE_FILTERS_V2, 'all-filters-v-2');
|
|
137
137
|
|
|
138
|
+
static pillLabelCustomization = new Urlbuilder(PATH_E2E_DATATABLE_FILTERS_V2, 'pill-label-customization-test');
|
|
139
|
+
|
|
138
140
|
// ADVANCED
|
|
139
141
|
|
|
140
142
|
static groupByCol = new Urlbuilder(PATH_E2E_DATATABLE_ADVANCED, 'group-by-column');
|
|
@@ -198,6 +200,10 @@ export default class DataTableCO extends PageObject {
|
|
|
198
200
|
'grouping-with-hidden-and-resizable-columns',
|
|
199
201
|
);
|
|
200
202
|
|
|
203
|
+
static resizerSeparatorMode = new Urlbuilder(PATH_E2E_DATATABLE_COLUMN, 'resizer-separator-mode-test');
|
|
204
|
+
|
|
205
|
+
static resizerSliderMode = new Urlbuilder(PATH_E2E_DATATABLE_COLUMN, 'resizer-slider-mode-test');
|
|
206
|
+
|
|
201
207
|
// Text Wrapping
|
|
202
208
|
|
|
203
209
|
static textWrapTruncColumn = new Urlbuilder(PATH_E2E_DATATABLE_TEXT_WRAPPING, 'text-wrap-truncate-column');
|
|
@@ -70,6 +70,13 @@ export default class FilterBarCO extends PageObject {
|
|
|
70
70
|
|
|
71
71
|
static getPillCloseButtonByIndex = async (index) => getElementByIndex(this.getAllPillCloseButtons, index);
|
|
72
72
|
|
|
73
|
+
// Label pill wrapper exposes role="group" + aria-label = the pill label text (pillLabel || Header).
|
|
74
|
+
static getLabelPillByName = async (name) => $(`[role="group"][aria-label="${name}"]`);
|
|
75
|
+
|
|
76
|
+
// SVG icon inside a label pill (found by its accessible name) — asserts a custom pill renderer drew
|
|
77
|
+
// its own icon. The label pill wrapper is the role="group" element; its descendant <svg> is the icon.
|
|
78
|
+
static getPillIconByName = async (name) => $(`[role="group"][aria-label="${name}"] svg`);
|
|
79
|
+
|
|
73
80
|
static async getFilterBarDropDownOption(label) {
|
|
74
81
|
return $(`[role=option][label="${label}"]`);
|
|
75
82
|
}
|
|
@@ -51,7 +51,11 @@ export default class HeaderCO extends PageObject {
|
|
|
51
51
|
|
|
52
52
|
static getHeadersWrapper = async () => $('[data-testid="ds-datatable-head-wrapper"]');
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
// PUI-17784: resize handle changed from <input type="range" aria-label="Resize column"> to a
|
|
55
|
+
// <button role="application" aria-labelledby> with dynamic "Resize {column} column" naming; the old
|
|
56
|
+
// selector never matched data-testid="column" (unverified in d2) and stopped matching aria-label after
|
|
57
|
+
// that fix.
|
|
58
|
+
static getResizeHandler = async (index) => $$('[data-testid="ds-datatable-resizer"]')[index];
|
|
55
59
|
|
|
56
60
|
static getFilterBtnByIndex = async (index) => $$('[data-testid="data-table-filter-menu-button"] button')[index];
|
|
57
61
|
|
|
@@ -60,6 +64,16 @@ export default class HeaderCO extends PageObject {
|
|
|
60
64
|
return headerCell.$('[data-testid="data-table-sort-button"]');
|
|
61
65
|
};
|
|
62
66
|
|
|
67
|
+
// PUI-17786: a11y accessors for the resize handle. Targets the same [data-testid="ds-datatable-resizer"]
|
|
68
|
+
// as the getResizeHandler selector fix (separate PR); kept distinct so these a11y specs run standalone.
|
|
69
|
+
static getResizer = async (index) => $$('[data-testid="ds-datatable-resizer"]')[index];
|
|
70
|
+
|
|
71
|
+
static getResizerAccessibleName = async (resizer) => {
|
|
72
|
+
const labelledById = await resizer.getAttribute('aria-labelledby');
|
|
73
|
+
if (!labelledById) return null;
|
|
74
|
+
return (await $(`#${labelledById}`)).getProperty('textContent');
|
|
75
|
+
};
|
|
76
|
+
|
|
63
77
|
// Snapshots
|
|
64
78
|
static snapshotPath(example = 'basic') {
|
|
65
79
|
return PageObject.getSnapshotPathBuilder('DataTable', example, 'ds-data-table');
|
|
@@ -1,37 +1,53 @@
|
|
|
1
1
|
import DSDataTableCO from '../DSDataTableCO';
|
|
2
|
-
import { FiltersCO } from '../components';
|
|
2
|
+
import { FilterBarCO, FiltersCO } from '../components';
|
|
3
3
|
import { axeCoreCheck } from '../../helpers';
|
|
4
4
|
|
|
5
|
-
if (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
if (
|
|
6
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
7
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
8
|
+
browser.capabilities.browserName === 'chrome') ||
|
|
9
|
+
browser.capabilities.browserName === 'Chrome'
|
|
10
|
+
) {
|
|
11
|
+
describe('PUI-18108 - DataTable:: Filters V2 - All Filter Types - Axe-Core', () => {
|
|
12
|
+
before(async () => {
|
|
13
|
+
const errorOnGo = await DSDataTableCO.allFiltersV2.go();
|
|
14
|
+
if (errorOnGo) throw errorOnGo;
|
|
15
|
+
await DSDataTableCO.waitForDataTable();
|
|
16
|
+
});
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
it('01: should have no accessibility violations in default state', async () => {
|
|
19
|
+
const result = await axeCoreCheck();
|
|
20
|
+
expect(result.length).toBe(0);
|
|
21
|
+
});
|
|
18
22
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
it('02: should have no accessibility violations with filter popper open', async () => {
|
|
24
|
+
await FiltersCO.openFilterPopperByColumnName('id');
|
|
25
|
+
const footer = await FiltersCO.getFilterFooter();
|
|
26
|
+
await footer.waitForDisplayed();
|
|
27
|
+
// `scrollable-region-focusable` is a documented axe-core false positive for the filter
|
|
28
|
+
// popper option list: it reuses the DS combobox listbox (TanStack Virtual + container
|
|
29
|
+
// tabindex=-1 Firefox scroll workaround), leaving a residual scrollHeight. The options are
|
|
30
|
+
// fully keyboard-operable through the combobox widget model, so WCAG 2.1.1 is satisfied.
|
|
31
|
+
// Same disposition as the ds-combobox axe specs; dev team confirmed no real a11y issue.
|
|
32
|
+
const result = await axeCoreCheck({ extraDisabledRules: ['scrollable-region-focusable'] });
|
|
33
|
+
expect(result.length).toBe(0);
|
|
34
|
+
});
|
|
26
35
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
36
|
+
it('03: should have no accessibility violations with applied filter and pills', async () => {
|
|
37
|
+
// Self-contained: re-open the single-select ('id') popper (openFilterPopperByColumnName
|
|
38
|
+
// clicks the header cell first, resetting any leftover popper state), pick an option and
|
|
39
|
+
// apply so a pill renders, then scan.
|
|
40
|
+
await FiltersCO.openFilterPopperByColumnName('id');
|
|
41
|
+
const footer = await FiltersCO.getFilterFooter();
|
|
42
|
+
await footer.waitForDisplayed();
|
|
43
|
+
const menuItem = await FiltersCO.getFilterSingleSelectMenuItem(0);
|
|
44
|
+
await menuItem.click();
|
|
45
|
+
const applyBtn = await FiltersCO.getFilterApplyButton();
|
|
46
|
+
await applyBtn.click();
|
|
47
|
+
const pills = await FilterBarCO.getAllPills();
|
|
48
|
+
await expect(pills.length).toBeGreaterThan(0);
|
|
49
|
+
const result = await axeCoreCheck({ extraDisabledRules: ['scrollable-region-focusable'] });
|
|
50
|
+
expect(result.length).toBe(0);
|
|
35
51
|
});
|
|
36
|
-
}
|
|
52
|
+
});
|
|
37
53
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import DSDataTableCO from '../../DSDataTableCO';
|
|
2
|
+
import { axeCoreCheck } from '../../../helpers';
|
|
3
|
+
|
|
4
|
+
// Tests for column.pillLabel added in PUI-8318 (custom pill labels must not introduce a11y violations)
|
|
5
|
+
if (
|
|
6
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
7
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
8
|
+
browser.capabilities.browserName === 'chrome') ||
|
|
9
|
+
browser.capabilities.browserName === 'Chrome'
|
|
10
|
+
) {
|
|
11
|
+
describe('PUI-18727 - DSDataTable:: custom filter pill label - Axe-Core', () => {
|
|
12
|
+
before(async () => {
|
|
13
|
+
const errorOnGo = await DSDataTableCO.pillLabelCustomization.go();
|
|
14
|
+
if (errorOnGo) throw errorOnGo;
|
|
15
|
+
await DSDataTableCO.waitForDataTable();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('01: should have no accessibility violations with custom and default pill labels', async () => {
|
|
19
|
+
const result = await axeCoreCheck();
|
|
20
|
+
expect(result.length).toBe(0);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import DSDataTableCO from '../../DSDataTableCO';
|
|
2
|
+
import { FilterBarCO } from '../../components';
|
|
3
|
+
|
|
4
|
+
// Tests for column.pillLabel added in PUI-8318 (overrides Header as filter pill label + remove aria-label)
|
|
5
|
+
if (
|
|
6
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
7
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
8
|
+
browser.capabilities.browserName === 'chrome') ||
|
|
9
|
+
browser.capabilities.browserName === 'Chrome'
|
|
10
|
+
) {
|
|
11
|
+
describe('PUI-18726 - DSDataTable:: custom filter pill label - Func', () => {
|
|
12
|
+
before(async () => {
|
|
13
|
+
const errorOnGo = await DSDataTableCO.pillLabelCustomization.go();
|
|
14
|
+
if (errorOnGo) throw errorOnGo;
|
|
15
|
+
await DSDataTableCO.waitForDataTable();
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('01: should use pillLabel over Header on the OOTB pill label and remove aria-labels', async () => {
|
|
19
|
+
const roleLabel = await FilterBarCO.getLabelPillByName('Role');
|
|
20
|
+
await expect(roleLabel).toBeDisplayed();
|
|
21
|
+
|
|
22
|
+
const removeReact = await FilterBarCO.getPillCloseButtonByName('Remove React dev from Role');
|
|
23
|
+
await expect(removeReact).toBeExisting();
|
|
24
|
+
|
|
25
|
+
const removeFullstack = await FilterBarCO.getPillCloseButtonByName('Remove Fullstack dev from Role');
|
|
26
|
+
await expect(removeFullstack).toBeExisting();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('02: should fall back to Header when the column has no pillLabel', async () => {
|
|
30
|
+
const nameLabel = await FilterBarCO.getLabelPillByName('Name');
|
|
31
|
+
await expect(nameLabel).toBeDisplayed();
|
|
32
|
+
|
|
33
|
+
const removeAlice = await FilterBarCO.getPillCloseButtonByName('Remove Alice from Name');
|
|
34
|
+
await expect(removeAlice).toBeExisting();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('03: should render a fully custom pill (columnHeader + icon) via customPillRenderers', async () => {
|
|
38
|
+
const compensationLabel = await FilterBarCO.getLabelPillByName('Compensation');
|
|
39
|
+
await expect(compensationLabel).toBeDisplayed();
|
|
40
|
+
|
|
41
|
+
// Custom renderer draws its own icon — proves customPillRenderers replaces the OOTB pill content.
|
|
42
|
+
const customIcon = await FilterBarCO.getPillIconByName('Compensation');
|
|
43
|
+
await expect(customIcon).toBeExisting();
|
|
44
|
+
|
|
45
|
+
const removeAriaLabel = 'Remove 10000.00 - 90000.00 from Compensation';
|
|
46
|
+
const removeCompensation = await FilterBarCO.getPillCloseButtonByName(removeAriaLabel);
|
|
47
|
+
await expect(removeCompensation).toBeExisting();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint-disable wdio/no-pause */
|
|
2
|
+
import DSDataTableCO from '../../DSDataTableCO';
|
|
3
|
+
|
|
4
|
+
// Visual coverage for PUI-8318 filter pill customizations: the composable filter bar renders the
|
|
5
|
+
// OOTB pills (Role via pillLabel, Name via Header fallback) alongside a fully custom pill
|
|
6
|
+
// (Compensation) with a custom icon and a custom color, produced through customPillRenderers.
|
|
7
|
+
if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:options'].isTablet) {
|
|
8
|
+
describe('PUI-18747 - DSDataTable:: custom filter pill rendering - Visual', () => {
|
|
9
|
+
before(async () => {
|
|
10
|
+
const errorOnGo = await DSDataTableCO.pillLabelCustomization.go();
|
|
11
|
+
if (errorOnGo) throw errorOnGo;
|
|
12
|
+
await DSDataTableCO.waitForDataTable();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('01: should render OOTB and custom pills (icon + color) in the composable filter bar', async () => {
|
|
16
|
+
await browser.pause(300);
|
|
17
|
+
const snapshot = await browser.percyCheckScreenshot(DSDataTableCO.snapshotPath('pill-label-custom-pills'));
|
|
18
|
+
await expect(snapshot).toEqual(0);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Key } from 'webdriverio';
|
|
2
|
+
import DSDataTableCO from '../DSDataTableCO';
|
|
3
|
+
import { HeaderCO } from '../components';
|
|
4
|
+
import { axeCoreCheck } from '../../helpers';
|
|
5
|
+
|
|
6
|
+
// Axe-core for the resizable-column a11y modes (PUI-17786 / PUI-17784). The resize handle is
|
|
7
|
+
// aria-hidden until its header is "entered" (child-navigation mode), so each test enters the header
|
|
8
|
+
// first to expose the resizer to the accessibility tree before scanning.
|
|
9
|
+
if (
|
|
10
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
11
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
12
|
+
browser.capabilities.browserName === 'chrome') ||
|
|
13
|
+
browser.capabilities.browserName === 'Chrome'
|
|
14
|
+
) {
|
|
15
|
+
describe('PUI-18750 - DSDataTable:: resizer separator role a11y - Axe-Core', () => {
|
|
16
|
+
before(async () => {
|
|
17
|
+
const errorOnGo = await DSDataTableCO.resizerSeparatorMode.go();
|
|
18
|
+
if (errorOnGo) throw errorOnGo;
|
|
19
|
+
await DSDataTableCO.waitForDataTable();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('01: should have no accessibility violations with the separator resizer exposed', async () => {
|
|
23
|
+
const header = await HeaderCO.getHeaderCellByIndex(1);
|
|
24
|
+
await header.click();
|
|
25
|
+
await browser.keys(Key.Return);
|
|
26
|
+
const result = await axeCoreCheck();
|
|
27
|
+
expect(result.length).toBe(0);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('PUI-18751 - DSDataTable:: resizer slider role a11y - Axe-Core', () => {
|
|
32
|
+
before(async () => {
|
|
33
|
+
const errorOnGo = await DSDataTableCO.resizerSliderMode.go();
|
|
34
|
+
if (errorOnGo) throw errorOnGo;
|
|
35
|
+
await DSDataTableCO.waitForDataTable();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('01: should have no accessibility violations with the slider resizer exposed', async () => {
|
|
39
|
+
const header = await HeaderCO.getHeaderCellByIndex(1);
|
|
40
|
+
await header.click();
|
|
41
|
+
await browser.keys(Key.Return);
|
|
42
|
+
const result = await axeCoreCheck();
|
|
43
|
+
expect(result.length).toBe(0);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { Key } from 'webdriverio';
|
|
2
|
+
import DSDataTableCO from '../DSDataTableCO';
|
|
3
|
+
import { HeaderCO } from '../components';
|
|
4
|
+
|
|
5
|
+
// Tests for the resizable-column a11y contract from PUI-17786 / PUI-17784.
|
|
6
|
+
// The "50" bug (PUI-15786): the resizer was an <input type="range"> whose default value (50) was
|
|
7
|
+
// announced next to the column name. The fix exposes the handle as role="separator" (default) or
|
|
8
|
+
// role="slider" with a bounded aria-value* set and an accessible name "Resize {Header} column".
|
|
9
|
+
if (
|
|
10
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
11
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
12
|
+
browser.capabilities.browserName === 'chrome') ||
|
|
13
|
+
browser.capabilities.browserName === 'Chrome'
|
|
14
|
+
) {
|
|
15
|
+
describe('PUI-18748 - DSDataTable:: resizer separator role a11y - Func', () => {
|
|
16
|
+
before(async () => {
|
|
17
|
+
const errorOnGo = await DSDataTableCO.resizerSeparatorMode.go();
|
|
18
|
+
if (errorOnGo) throw errorOnGo;
|
|
19
|
+
await DSDataTableCO.waitForDataTable();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('01: should expose separator role, bounded value and accessible name (no range/"50")', async () => {
|
|
23
|
+
const resizer = await HeaderCO.getResizer(1);
|
|
24
|
+
await expect(resizer).toBeExisting();
|
|
25
|
+
await expect(resizer).toHaveAttribute('role', 'separator');
|
|
26
|
+
await expect(resizer).toHaveAttribute('aria-orientation', 'vertical');
|
|
27
|
+
await expect(resizer).toHaveAttribute('aria-valuemin', '70');
|
|
28
|
+
await expect(resizer).toHaveAttribute('aria-valuemax', '600');
|
|
29
|
+
|
|
30
|
+
// Validate the value by SHAPE, not a fixed number: the width is not deterministic across
|
|
31
|
+
// layouts. The point is that it is a real, bounded width (the fix) and NOT the stray "50"
|
|
32
|
+
// the old <input type="range"> announced.
|
|
33
|
+
const valueNow = await resizer.getAttribute('aria-valuenow'); // current width as string, e.g. "150"
|
|
34
|
+
expect(/^\d+$/.test(valueNow)).toBe(true); // digits only: a real value, not empty/null/"150px"
|
|
35
|
+
expect(Number(valueNow)).toBeGreaterThanOrEqual(70); // >= aria-valuemin (70): the "50" guard
|
|
36
|
+
const valueText = await resizer.getAttribute('aria-valuetext'); // SR text, e.g. "150 pixels"
|
|
37
|
+
expect(/^\d+ pixels$/.test(valueText)).toBe(true); // must read "<width> pixels"
|
|
38
|
+
|
|
39
|
+
// Accessible name comes from aria-labelledby, not aria-label (regression guard).
|
|
40
|
+
expect(await resizer.getAttribute('aria-label')).toBe(null);
|
|
41
|
+
const name = await HeaderCO.getResizerAccessibleName(resizer);
|
|
42
|
+
expect(name).toContain('Resize Name column');
|
|
43
|
+
|
|
44
|
+
// "50" regression: the handle must not be a native range input.
|
|
45
|
+
expect((await resizer.getTagName()).toLowerCase()).not.toBe('input');
|
|
46
|
+
expect(await resizer.getAttribute('type')).not.toBe('range');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('02: should update aria-valuenow when resizing with the keyboard', async () => {
|
|
50
|
+
const nameHeader = await HeaderCO.getHeaderCellByIndex(1);
|
|
51
|
+
await nameHeader.click();
|
|
52
|
+
// Enter on the header focuses its resize handle directly (no drag/filter/sort child to precede it).
|
|
53
|
+
await browser.keys(Key.Return);
|
|
54
|
+
const resizer = await HeaderCO.getResizer(1);
|
|
55
|
+
await expect(resizer).toBeFocused();
|
|
56
|
+
|
|
57
|
+
const before = Number(await resizer.getAttribute('aria-valuenow'));
|
|
58
|
+
await browser.keys(Key.ArrowRight);
|
|
59
|
+
await browser.waitUntil(async () => Number(await resizer.getAttribute('aria-valuenow')) > before, {
|
|
60
|
+
timeout: 5000,
|
|
61
|
+
timeoutMsg: 'aria-valuenow did not increase after ArrowRight',
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('PUI-18749 - DSDataTable:: resizer slider role a11y - Func', () => {
|
|
67
|
+
before(async () => {
|
|
68
|
+
const errorOnGo = await DSDataTableCO.resizerSliderMode.go();
|
|
69
|
+
if (errorOnGo) throw errorOnGo;
|
|
70
|
+
await DSDataTableCO.waitForDataTable();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('01: should expose slider role, horizontal orientation and bounded value', async () => {
|
|
74
|
+
const resizer = await HeaderCO.getResizer(1);
|
|
75
|
+
await expect(resizer).toBeExisting();
|
|
76
|
+
await expect(resizer).toHaveAttribute('role', 'slider');
|
|
77
|
+
await expect(resizer).toHaveAttribute('aria-orientation', 'horizontal');
|
|
78
|
+
await expect(resizer).toHaveAttribute('aria-valuemin', '70');
|
|
79
|
+
await expect(resizer).toHaveAttribute('aria-valuemax', '600');
|
|
80
|
+
|
|
81
|
+
// Validate the value by SHAPE, not a fixed number (see separator spec above).
|
|
82
|
+
const valueNow = await resizer.getAttribute('aria-valuenow'); // current width as string, e.g. "150"
|
|
83
|
+
expect(/^\d+$/.test(valueNow)).toBe(true); // digits only: a real value, not empty/null/"150px"
|
|
84
|
+
expect(Number(valueNow)).toBeGreaterThanOrEqual(70); // >= aria-valuemin (70): the "50" guard
|
|
85
|
+
|
|
86
|
+
// Accessible name comes from aria-labelledby, not aria-label (regression guard).
|
|
87
|
+
expect(await resizer.getAttribute('aria-label')).toBe(null);
|
|
88
|
+
const name = await HeaderCO.getResizerAccessibleName(resizer);
|
|
89
|
+
expect(name).toContain('Resize Name column');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('02: should update aria-valuenow when resizing with the keyboard', async () => {
|
|
93
|
+
const nameHeader = await HeaderCO.getHeaderCellByIndex(1);
|
|
94
|
+
await nameHeader.click();
|
|
95
|
+
// Enter on the header focuses its resize handle directly (no drag/filter/sort child to precede it).
|
|
96
|
+
await browser.keys(Key.Return);
|
|
97
|
+
const resizer = await HeaderCO.getResizer(1);
|
|
98
|
+
await expect(resizer).toBeFocused();
|
|
99
|
+
|
|
100
|
+
const before = Number(await resizer.getAttribute('aria-valuenow'));
|
|
101
|
+
await browser.keys(Key.ArrowRight);
|
|
102
|
+
await browser.waitUntil(async () => Number(await resizer.getAttribute('aria-valuenow')) > before, {
|
|
103
|
+
timeout: 5000,
|
|
104
|
+
timeoutMsg: 'aria-valuenow did not increase after ArrowRight',
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|
|
@@ -24,7 +24,7 @@ if (
|
|
|
24
24
|
const listbox = await DSComboboxSingleCO.getComboListbox();
|
|
25
25
|
await listbox.waitForExist();
|
|
26
26
|
|
|
27
|
-
const result = await axeCoreCheck();
|
|
27
|
+
const result = await axeCoreCheck({ extraDisabledRules: ['scrollable-region-focusable'] });
|
|
28
28
|
expect(result.length).toBe(0);
|
|
29
29
|
});
|
|
30
30
|
});
|
|
@@ -281,6 +281,13 @@ export default class DSDateTimePickerCO extends PageObject {
|
|
|
281
281
|
static getCalendarSelectedFocusedDay = async () =>
|
|
282
282
|
$(`[data-testid="ds-controlled-date-time-picker-calendar-selected-focused-day"]`);
|
|
283
283
|
|
|
284
|
+
// Non-current-month day cells only carry the `notCurrentMonth` wrapper class (they share the
|
|
285
|
+
// generic calendar-day data-testid), so this class is the only distinguishing selector. The grid
|
|
286
|
+
// renders leading/trailing spillover cells that are hidden, so index 0 is a hidden filler — use
|
|
287
|
+
// index 1 for the first visible non-current-month day. PUI-14872.
|
|
288
|
+
static getCalendarNotCurrentMonthDayByIndex = async (index = 0) =>
|
|
289
|
+
$$(`.notCurrentMonth button[data-testid="ds-controlled-date-time-picker-calendar-day"]`)[index];
|
|
290
|
+
|
|
284
291
|
// TIMEWHEEL
|
|
285
292
|
static getTimeWheelHeaderLabel = async () =>
|
|
286
293
|
$(`[data-testid="ds-controlled-date-time-picker-timewheel-header-label"]`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/* eslint-disable prettier/prettier */
|
|
2
2
|
/* eslint-disable max-len */
|
|
3
3
|
/* eslint-disable max-lines */
|
|
4
|
-
import { axeCoreCheck } from '../../helpers';
|
|
4
|
+
import { axeCoreCheck, mouseOver } from '../../helpers';
|
|
5
5
|
import DateTimePickerCO from '../DSControlledDateTimePickerCO';
|
|
6
6
|
|
|
7
7
|
if (
|
|
@@ -21,5 +21,12 @@ if (
|
|
|
21
21
|
const result = await axeCoreCheck();
|
|
22
22
|
expect(result.length).toBe(0);
|
|
23
23
|
});
|
|
24
|
+
it('02: should pass axe-core color-contrast while hovering a non-current-month day', async () => {
|
|
25
|
+
const notCurrentMonthDay = await DateTimePickerCO.getCalendarNotCurrentMonthDayByIndex(1);
|
|
26
|
+
await notCurrentMonthDay.waitForDisplayed();
|
|
27
|
+
await mouseOver(notCurrentMonthDay);
|
|
28
|
+
const result = await axeCoreCheck();
|
|
29
|
+
expect(result.length).toBe(0);
|
|
30
|
+
});
|
|
24
31
|
});
|
|
25
32
|
}
|
|
@@ -45,5 +45,14 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
|
|
|
45
45
|
);
|
|
46
46
|
await expect(snapshot).toEqual(0);
|
|
47
47
|
});
|
|
48
|
+
it('05: should keep readable contrast when hovering a non-current-month day', async () => {
|
|
49
|
+
const notCurrentMonthDay = await DateTimePickerCO.getCalendarNotCurrentMonthDayByIndex(1);
|
|
50
|
+
await notCurrentMonthDay.waitForDisplayed();
|
|
51
|
+
await mouseOver(notCurrentMonthDay);
|
|
52
|
+
const snapshot = await browser.checkSnapshot(
|
|
53
|
+
DateTimePickerCO.snapshotPath('date-time-full-not-current-month-day-hover'),
|
|
54
|
+
);
|
|
55
|
+
await expect(snapshot).toEqual(0);
|
|
56
|
+
});
|
|
48
57
|
});
|
|
49
58
|
}
|
package/ds-hooks/DSHookCO.js
CHANGED
|
@@ -1,20 +1,9 @@
|
|
|
1
1
|
/* eslint-disable import/no-relative-packages */
|
|
2
|
-
import {
|
|
3
|
-
PATH_HOOKS,
|
|
4
|
-
PATH_HOOKS_BLUR_OUT,
|
|
5
|
-
PATH_HOOKS_FIRST_FOCUS_IN,
|
|
6
|
-
PATH_E2E_HOOKS,
|
|
7
|
-
PATH_E2E_USE_FONT_SIZE_DETECTOR,
|
|
8
|
-
PATH_E2E_USE_FONT_SIZE_MEDIA,
|
|
9
|
-
} from '../paths';
|
|
2
|
+
import { PATH_HOOKS, PATH_E2E_HOOKS, PATH_E2E_USE_FONT_SIZE_DETECTOR, PATH_E2E_USE_FONT_SIZE_MEDIA } from '../paths';
|
|
10
3
|
import { PageObject, Urlbuilder, getElementByIndex } from '../helpers';
|
|
11
4
|
|
|
12
5
|
export default class DSHookCO extends PageObject {
|
|
13
6
|
// Desktop
|
|
14
|
-
static blurOut = new Urlbuilder(PATH_HOOKS_BLUR_OUT, 'basic');
|
|
15
|
-
|
|
16
|
-
static firstFocusIn = new Urlbuilder(PATH_HOOKS_FIRST_FOCUS_IN, 'basic');
|
|
17
|
-
|
|
18
7
|
static specificFocus = new Urlbuilder(PATH_HOOKS, 'use-on-specific-focus');
|
|
19
8
|
|
|
20
9
|
static useDsTree = new Urlbuilder(PATH_E2E_HOOKS, 'use-ds-tree-test');
|
|
@@ -33,16 +22,6 @@ export default class DSHookCO extends PageObject {
|
|
|
33
22
|
|
|
34
23
|
static getBtnByIndex = async (index) => getElementByIndex(this.getBtns, index);
|
|
35
24
|
|
|
36
|
-
static async getH3s() {
|
|
37
|
-
return $$('div h3');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// mostly used
|
|
41
|
-
static getInfo = async () => getElementByIndex(this.getH3s, 3);
|
|
42
|
-
|
|
43
|
-
// used for first focus in story
|
|
44
|
-
static getInfo2 = async () => getElementByIndex(this.getH3s, 4);
|
|
45
|
-
|
|
46
25
|
// Snapshots
|
|
47
26
|
static snapshotPath(example = 'basic') {
|
|
48
27
|
return PageObject.getSnapshotPathBuilder('Hook', example);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { axeCoreCheck } from '../../helpers';
|
|
2
|
+
import DSMenuButtonCO from '../DSMenuButtonCO';
|
|
3
|
+
|
|
4
|
+
if (
|
|
5
|
+
(!browser.capabilities['ice:options'].isPhone &&
|
|
6
|
+
!browser.capabilities['ice:options'].isTablet &&
|
|
7
|
+
browser.capabilities.browserName === 'Chrome') ||
|
|
8
|
+
browser.capabilities.browserName === 'chrome'
|
|
9
|
+
) {
|
|
10
|
+
describe('PUI-17845 - DSMenuButton:: Slots - AxeCore', () => {
|
|
11
|
+
before('loading page', async () => {
|
|
12
|
+
const errorOnGo = await DSMenuButtonCO.slotsTest.go();
|
|
13
|
+
if (errorOnGo) throw errorOnGo;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('01: should render slots story with custom theme and pass axe-core scan with menu open', async () => {
|
|
17
|
+
const triggerBtn = await DSMenuButtonCO.getSlotsOpinionatedButtonRoot();
|
|
18
|
+
await triggerBtn.waitForDisplayed();
|
|
19
|
+
await triggerBtn.click();
|
|
20
|
+
|
|
21
|
+
const flyoutRoot = await DSMenuButtonCO.getSlotsFlyoutMenuRoot();
|
|
22
|
+
await flyoutRoot.waitForDisplayed();
|
|
23
|
+
|
|
24
|
+
const result = await axeCoreCheck();
|
|
25
|
+
expect(result.length).toBe(0);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -36,5 +36,24 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
|
|
|
36
36
|
await expect(floatingWrapper).toHaveAttribute('data-testid', 'im dsFlyoutmenuFloatingWrapper data');
|
|
37
37
|
await expect(menuItem).toHaveAttribute('data-testid', 'im dsMenuitemMenuItemWrapper data');
|
|
38
38
|
});
|
|
39
|
+
|
|
40
|
+
it('03: should apply per-item attributes from the item-slot function to each menu item', async () => {
|
|
41
|
+
const firstItem = await DSMenuButtonCO.getSlotsMenuItemWrappers(0);
|
|
42
|
+
const secondItem = await DSMenuButtonCO.getSlotsMenuItemWrappers(1);
|
|
43
|
+
const thirdItem = await DSMenuButtonCO.getSlotsMenuItemWrappers(2);
|
|
44
|
+
|
|
45
|
+
await expect(firstItem).toHaveAttribute(
|
|
46
|
+
'data-menu-item-wrapper',
|
|
47
|
+
'menu item wrapper for item with label Action 1',
|
|
48
|
+
);
|
|
49
|
+
await expect(secondItem).toHaveAttribute(
|
|
50
|
+
'data-menu-item-wrapper',
|
|
51
|
+
'menu item wrapper for item with label Action 2',
|
|
52
|
+
);
|
|
53
|
+
await expect(thirdItem).toHaveAttribute(
|
|
54
|
+
'data-menu-item-wrapper',
|
|
55
|
+
'menu item wrapper for item with label Action 3',
|
|
56
|
+
);
|
|
57
|
+
});
|
|
39
58
|
});
|
|
40
59
|
}
|
package/ds-shuttle-v2/explicit-selection-ui/DSShuttleV2.explicit-selection-ui.visual.spec.js
CHANGED
|
@@ -11,12 +11,15 @@ if (!browser.capabilities['ice:options'].isPhone && !browser.capabilities['ice:o
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it('01: should display unselected checkboxes and disabled checkboxes on prevented items', async () => {
|
|
14
|
+
const firstSelectable = await DSShuttleV2CO.getItemWrapper(0); // "Source 0.0" — selectable
|
|
15
|
+
await firstSelectable.waitForDisplayed();
|
|
14
16
|
const snapshot = await browser.percyCheckScreenshot();
|
|
15
17
|
await expect(snapshot).toEqual(0);
|
|
16
18
|
});
|
|
17
19
|
|
|
18
20
|
it('02: should display checked checkboxes and the BAB after selecting selectable items', async () => {
|
|
19
21
|
const firstSelectable = await DSShuttleV2CO.getItemWrapper(0); // "Source 0.0" — selectable
|
|
22
|
+
await firstSelectable.waitForDisplayed();
|
|
20
23
|
await firstSelectable.click();
|
|
21
24
|
const secondSelectable = await DSShuttleV2CO.getItemWrapper(2); // "Source 0.2" — selectable
|
|
22
25
|
await secondSelectable.click();
|