diff --git a/.changeset/five-carrots-heal.md b/.changeset/five-carrots-heal.md new file mode 100644 index 00000000000..4e4c65303ac --- /dev/null +++ b/.changeset/five-carrots-heal.md @@ -0,0 +1,5 @@ +--- +'@siemens/ix': patch +--- + +Fix **ix-select** keyboard navigation and wrap behavior when new items are created diff --git a/packages/core/playwright-ct.config.ts b/packages/core/playwright-ct.config.ts index 7b6c6ccc925..d88d9766893 100644 --- a/packages/core/playwright-ct.config.ts +++ b/packages/core/playwright-ct.config.ts @@ -28,7 +28,7 @@ const config: PlaywrightTestConfig = { }, }, ], - retries: 2, + retries: 3, }; export default config; diff --git a/packages/core/src/components/field-label/tests/field-label.ct.ts b/packages/core/src/components/field-label/tests/field-label.ct.ts index 8cc0e00e0ab..c15513935eb 100644 --- a/packages/core/src/components/field-label/tests/field-label.ct.ts +++ b/packages/core/src/components/field-label/tests/field-label.ct.ts @@ -163,13 +163,13 @@ test('valid color with valid textarea field', async ({ mount, page }) => { test('invalid color with invalid textarea field', async ({ mount, page }) => { await mount(` - valid field + invalid field `); const fieldElement = page.locator('ix-textarea'); const labelElement = page.locator('ix-field-label'); - await expect(fieldElement).not.toHaveClass(/ix-invalid--required/); + await expect(fieldElement).toHaveClass(/ix-invalid--required/); await expect(labelElement.locator('ix-typography')).toHaveAttribute( 'style', diff --git a/packages/core/src/components/select/select.tsx b/packages/core/src/components/select/select.tsx index e567eb551d0..ee9c338488a 100644 --- a/packages/core/src/components/select/select.tsx +++ b/packages/core/src/components/select/select.tsx @@ -304,14 +304,6 @@ export class Select implements IxInputFieldComponent { @Watch('dropdownShow') watchDropdownShow(show: boolean) { if (show && this.dropdownElement) { - this.arrowFocusController = new ArrowFocusController( - this.visibleNonShadowItems, - this.dropdownElement, - this.focusControllerCallbackBind - ); - - this.arrowFocusController.wrap = !this.editable; - this.itemObserver.observe(this.dropdownElement, { childList: true, subtree: true, @@ -494,6 +486,21 @@ export class Select implements IxInputFieldComponent { this.updateFormInternalValue(this.value); } + componentDidRender(): void { + if (!this.dropdownShow || this.arrowFocusController) { + return; + } + + this.arrowFocusController = new ArrowFocusController( + this.visibleNonShadowItems, + this.dropdownElement, + this.focusControllerCallbackBind + ); + + this.arrowFocusController.wrap = + !this.isAddItemVisible() && !this.visibleShadowItems.length; + } + @Listen('ix-select-item:valueChange') @Listen('ix-select-item:labelChange') onLabelChange(event: IxSelectItemLabelChangeEvent) { diff --git a/packages/core/src/components/select/test/select-keyboard.ct.ts b/packages/core/src/components/select/test/select-keyboard.ct.ts new file mode 100644 index 00000000000..5b19b71e8a1 --- /dev/null +++ b/packages/core/src/components/select/test/select-keyboard.ct.ts @@ -0,0 +1,488 @@ +/* + * SPDX-FileCopyrightText: 2024 Siemens AG + * + * SPDX-License-Identifier: MIT + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { expect, Locator } from '@playwright/test'; +import { test } from '@utils/test'; + +function selectController(select: Locator) { + const input = select.locator('input'); + const dropdown = select.locator('ix-dropdown'); + const dropdownChevron = select.locator('ix-icon-button'); + + const dropdownVisible = async () => { + await (await dropdown.elementHandle()).waitForElementState('stable'); + await expect(dropdown).toBeVisible(); + }; + + return { + async clickDropdownChevron() { + await dropdownChevron.click(); + await dropdownVisible(); + }, + async fillInput(text: string) { + await input.fill(text); + }, + async focusInput() { + await input.click(); + await expect(input).toBeFocused(); + }, + async arrowDown(skipDropdownCheck = false) { + if (!skipDropdownCheck) { + await dropdownVisible(); + } + await select.page().keyboard.press('ArrowDown', { delay: 50 }); + }, + async arrowUp(skipDropdownCheck = false) { + if (!skipDropdownCheck) { + await dropdownVisible(); + } + await select.page().keyboard.press('ArrowUp', { delay: 50 }); + }, + async pressEnter() { + await select.page().keyboard.press('Enter'); + }, + async getDropdownItemsLocator(onlyVisible = false) { + let selector = 'ix-select-item'; + + if (onlyVisible) { + selector += ':not(.d-none)'; + } + + await dropdownVisible(); + return select.locator(selector).all(); + }, + async getFocusDropdownItemLocator() { + await dropdownVisible(); + + const focusDropdownItem = select.locator( + 'ix-select-item .dropdown-item:focus-visible' + ); + return focusDropdownItem; + }, + + async getAddItemDropdownItemLocator() { + await dropdownVisible(); + + const addItem = dropdown.locator('ix-dropdown-item.add-item'); + await (await addItem.elementHandle()).waitForElementState('stable'); + return addItem; + }, + + async getItemCheckedLocator() { + await dropdownVisible(); + const itemChecked = select.locator('ix-select-item .checkmark'); + + expect((await itemChecked.elementHandle()).waitForElementState('stable')); + + return itemChecked; + }, + }; +} + +test.describe('arrow key navigation', () => { + test.describe('ArrowDown', () => { + test('input -> slotted item', async ({ mount, page }) => { + await mount(` + + + + + `); + + const select = page.locator('ix-select'); + const selectCtrl = selectController(select); + + await selectCtrl.focusInput(); + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + const dropdownItems = await selectCtrl.getDropdownItemsLocator(); + const focusItem = await selectCtrl.getFocusDropdownItemLocator(); + + expect(dropdownItems).toHaveLength(2); + await expect(focusItem).toBeFocused(); + await expect(focusItem).toHaveText('Item 2'); + }); + + test('slot -> dynamic item', async ({ mount, page }) => { + await mount(` + + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + + await selectCtrl.focusInput(); + await selectCtrl.fillInput('New Item'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + const dropdownItems = await selectCtrl.getDropdownItemsLocator(); + const focusItem = await selectCtrl.getFocusDropdownItemLocator(); + + expect(dropdownItems).toHaveLength(3); + await expect(focusItem).toBeFocused(); + await expect(focusItem).toHaveText('New Item'); + }); + + test('input -> dynamic item', async ({ mount, page }) => { + await mount(` + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + + await selectCtrl.focusInput(); + await selectCtrl.fillInput('New Item'); + + await selectCtrl.arrowDown(); + + const visibleDropdownItems = + await selectCtrl.getDropdownItemsLocator(true); + expect(visibleDropdownItems).toHaveLength(0); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + await expect(addItem).toBeFocused(); + await expect(addItem).toHaveText('New Item'); + }); + + test('input -> add item', async ({ mount, page }) => { + await mount(` + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('New Item'); + await selectCtrl.pressEnter(); + await selectCtrl.clickDropdownChevron(); + await selectCtrl.arrowDown(); + + const items = await selectCtrl.getDropdownItemsLocator(); + expect(items).toHaveLength(1); + + const focusItem = await selectCtrl.getFocusDropdownItemLocator(); + await expect(focusItem).toBeFocused(); + await expect(focusItem).toHaveText('New Item'); + }); + + test('slot -> add item', async ({ mount, page }) => { + await mount(` + + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item'); + + expect(await selectCtrl.getDropdownItemsLocator()).toHaveLength(2); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect( + await selectCtrl.getAddItemDropdownItemLocator() + ).toBeFocused(); + }); + + test('dynamic item -> add item', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 2'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + await selectCtrl.getItemCheckedLocator(); + + await selectCtrl.fillInput(''); + await selectCtrl.fillInput('I'); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect( + await selectCtrl.getAddItemDropdownItemLocator() + ).toBeFocused(); + }); + + test('wrap - dynamic item -> slot', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 2'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + + expect(await selectCtrl.getDropdownItemsLocator()).toHaveLength(2); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + const itemsBeforeNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsBeforeNavigation.at(1)).toHaveText('Item 2'); + await expect(itemsBeforeNavigation.at(0)).not.toBeFocused(); + await expect(itemsBeforeNavigation.at(1)).toBeFocused(); + + await selectCtrl.arrowDown(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(1)).not.toBeFocused(); + }); + + test('wrap - add item -> slot', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect(addItem).toBeFocused(); + + await selectCtrl.arrowDown(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + }); + + test('wrap - add item -> dynamic item', async ({ mount, page }) => { + await mount(` + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 1'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + await selectCtrl.getItemCheckedLocator(); + + await selectCtrl.fillInput(''); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect(addItem).toBeFocused(); + + await selectCtrl.arrowDown(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + }); + }); + + test.describe('ArrowUp', () => { + test('dynamic item -> slot', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('I'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + await selectCtrl.getItemCheckedLocator(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + const itemsBeforeNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsBeforeNavigation.at(1)).toBeFocused(); + await expect(itemsBeforeNavigation.at(1)).toHaveText('I'); + + await selectCtrl.arrowUp(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + }); + + test('add item -> slot', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect(addItem).toBeFocused(); + + await selectCtrl.arrowUp(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + }); + + test('add item -> dynamic item', async ({ mount, page }) => { + await mount(` + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 1'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + await selectCtrl.getItemCheckedLocator(); + + await selectCtrl.fillInput(''); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + await selectCtrl.arrowDown(); + + await expect(addItem).toBeFocused(); + + await selectCtrl.arrowUp(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + }); + + test('wrap - slot -> dynamic item', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 2'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + + expect(await selectCtrl.getDropdownItemsLocator()).toHaveLength(2); + + await selectCtrl.arrowDown(); + + const itemsBeforeNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsBeforeNavigation.at(0)).toHaveText('Item 1'); + await expect(itemsBeforeNavigation.at(0)).toBeFocused(); + await expect(itemsBeforeNavigation.at(1)).not.toBeFocused(); + + await selectCtrl.arrowUp(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(1)).toHaveText('Item 2'); + await expect(itemsAfterNavigation.at(0)).not.toBeFocused(); + await expect(itemsAfterNavigation.at(1)).toBeFocused(); + }); + + test('wrap - slot -> add-item', async ({ mount, page }) => { + await mount(` + + + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + + const itemsBeforeNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsBeforeNavigation.at(0)).toHaveText('Item 1'); + await expect(itemsBeforeNavigation.at(0)).toBeFocused(); + + await selectCtrl.arrowUp(); + + await expect(addItem).toBeFocused(); + }); + + test('wrap - dynamic item -> add item', async ({ mount, page }) => { + await mount(` + + `); + + const selectCtrl = selectController(page.locator('ix-select')); + await selectCtrl.focusInput(); + await selectCtrl.fillInput('Item 1'); + await selectCtrl.pressEnter(); + + await selectCtrl.clickDropdownChevron(); + await selectCtrl.getItemCheckedLocator(); + + await selectCtrl.fillInput(''); + await selectCtrl.fillInput('I'); + + const addItem = await selectCtrl.getAddItemDropdownItemLocator(); + + await selectCtrl.arrowDown(); + + const itemsAfterNavigation = await selectCtrl.getDropdownItemsLocator(); + await expect(itemsAfterNavigation.at(0)).toBeFocused(); + await expect(itemsAfterNavigation.at(0)).toHaveText('Item 1'); + + await selectCtrl.arrowUp(); + + await expect(addItem).toBeFocused(); + }); + }); +}); diff --git a/packages/core/src/components/select/test/select.ct.ts b/packages/core/src/components/select/test/select.ct.ts index 75f55e4302f..99f20007442 100644 --- a/packages/core/src/components/select/test/select.ct.ts +++ b/packages/core/src/components/select/test/select.ct.ts @@ -6,7 +6,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { expect, Page } from '@playwright/test'; +import { expect } from '@playwright/test'; import { getFormValue, preventFormSubmission, test } from '@utils/test'; test('renders', async ({ mount, page }) => { @@ -375,333 +375,6 @@ test('pass object as value and check if it is selectable', async ({ ).toBeVisible(); }); -test.describe('arrow key navigation', () => { - const expectFocusMoved = async ( - direction: 'ArrowDown' | 'ArrowUp', - pattern: string, - page: Page - ) => { - const selector = `.dropdown-item:nth-of-type(${pattern}):focus-visible`; - await page.keyboard.press(direction); - await page.locator(selector); - await expect(page.locator(selector)).toBeFocused(); - }; - - test.describe('ArrowDown', () => { - test('input -> slotted item', async ({ mount, page }) => { - await mount(` - - Test - Test - - `); - - await page.locator('ix-select input').click(); - await expectFocusMoved('ArrowDown', '1', page); - }); - - test('input -> dynamic item', async ({ mount, page }) => { - await mount(` - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - }); - - test('input -> add item', async ({ mount, page }) => { - await mount(` - - `); - - await page.waitForSelector('ix-select'); - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.keyboard.down('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await expectFocusMoved('ArrowDown', '1', page); - }); - - test('slot -> dynamic item', async ({ mount, page }) => { - await mount(` - - Test - Test - - `); - - await page.waitForSelector('ix-select'); - const input = page.locator('ix-select input'); - await input.focus(); - await page.keyboard.down('I'); - await page.keyboard.down('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('ix-dropdown'); - - await expectFocusMoved('ArrowDown', '1', page); - await expectFocusMoved('ArrowDown', 'odd', page); - await page.keyboard.down('ArrowDown'); - - const itemThree = page.locator('ix-select-item').last(); - await expect(itemThree).toBeFocused(); - }); - - test('slot -> add item', async ({ mount, page }) => { - await mount(` - - Test - Test - - `); - - await page.waitForSelector('ix-select'); - const input = page.locator('ix-select input'); - await input.focus(); - await page.keyboard.down('I'); - await page.waitForSelector('.dropdown-item-icon'); - - await expectFocusMoved('ArrowDown', '1', page); - await expectFocusMoved('ArrowDown', 'odd', page); - await page.keyboard.press('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - }); - - test('dynamic item -> add item', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('Item 2'); - await page.keyboard.down('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await input.clear(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - await expectFocusMoved('ArrowDown', 'odd', page); - await page.keyboard.down('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - }); - - test('wrap - dynamic item -> slot', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('Item 2'); - await page.keyboard.press('Enter'); - await input.clear(); - - await page.keyboard.down('ArrowDown'); - await page.waitForSelector('ix-dropdown'); - await page.keyboard.down('ArrowDown'); - - const itemTwo = page.locator('ix-select-item').nth(1); - await expect(itemTwo).toBeFocused(); - - await expectFocusMoved('ArrowDown', '1', page); - }); - - test('wrap - add item -> slot', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await page.keyboard.down('ArrowDown'); - await page.keyboard.down('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - - await expectFocusMoved('ArrowDown', '1', page); - }); - - test('wrap - add item -> dynamic item', async ({ mount, page }) => { - await mount(` - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('Item 1'); - await page.keyboard.press('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await input.clear(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await page.keyboard.down('ArrowDown'); - await page.keyboard.down('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - - await expectFocusMoved('ArrowDown', '1', page); - }); - }); - - test.describe('ArrowUp', () => { - test('dynamic item -> slot', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.keyboard.down('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await expectFocusMoved('ArrowDown', '1', page); - await expectFocusMoved('ArrowDown', 'odd', page); - await expectFocusMoved('ArrowUp', '1', page); - }); - - test('add item -> slot', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - await page.keyboard.down('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - - await expectFocusMoved('ArrowUp', '1', page); - }); - - test('add item -> dynamic item', async ({ mount, page }) => { - await mount(` - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('Item 1'); - await page.keyboard.press('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await input.clear(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - await page.keyboard.down('ArrowDown'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - - await expectFocusMoved('ArrowUp', '1', page); - }); - - test('wrap - slot -> dynamic item', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('input'); - await input.focus(); - await input.fill('Item 2'); - await page.keyboard.press('Enter'); - await page.locator('ix-icon-button').click(); - await page.locator('input').clear(); - - await page.keyboard.down('ArrowDown'); - await page.keyboard.down('ArrowUp'); - - const itemTwo = page.locator('ix-select-item').last(); - await expect(itemTwo).toBeFocused(); - }); - - test('wrap - slot -> add-item', async ({ mount, page }) => { - await mount(` - - Test - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - await page.keyboard.down('ArrowUp'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - }); - - test('wrap - dynamic item -> add item', async ({ mount, page }) => { - await mount(` - - `); - - const input = page.locator('ix-select input'); - await input.focus(); - await input.fill('Item 1'); - await page.keyboard.press('Enter'); - await page.locator('ix-icon-button').click(); - await page.waitForSelector('.checkmark'); - - await input.clear(); - await input.fill('I'); - await page.waitForSelector('.add-item'); - - await expectFocusMoved('ArrowDown', '1', page); - await page.keyboard.down('ArrowUp'); - - const addItem = page.locator('.add-item'); - await expect(addItem).toBeFocused(); - }); - }); -}); - test('form-ready', async ({ mount, page }) => { await mount(`
diff --git a/packages/core/src/tests/utils/test/page.ts b/packages/core/src/tests/utils/test/page.ts index 6209025dea7..b41a4cec8be 100644 --- a/packages/core/src/tests/utils/test/page.ts +++ b/packages/core/src/tests/utils/test/page.ts @@ -7,6 +7,7 @@ * LICENSE file in the root directory of this source tree. */ import { + ElementHandle, Page, PageScreenshotOptions, test as testBase, @@ -21,6 +22,10 @@ async function extendPageFixture(page: Page, testInfo: TestInfo) { type: theme, }); page.goto = async (url: string, options) => { + if ((testInfo as any).componentTest) { + return originalGoto(url, options); + } + const response = await originalGoto( `http://127.0.0.1:8080/src/tests/${url}?theme=${theme}`, options @@ -38,15 +43,43 @@ async function extendPageFixture(page: Page, testInfo: TestInfo) { return page; } -export const regressionTest = testBase.extend({ +export const regressionTest = testBase.extend<{ + mount: ( + selector: string, + config?: { + headTags?: string[]; + } + ) => Promise>; + createElement: ( + selector: string, + appendTo?: ElementHandle + ) => Promise>; +}>({ page: async ({ page }, use, testInfo) => { page = await extendPageFixture(page, testInfo); await use(page); }, -}); + createElement: async ({ page }, use) => { + use((selector, appendTo) => + page.evaluateHandle( + async ({ selector, appendTo }) => { + const elm = document.createElement(selector); -export const test = testBase.extend({ + if (appendTo) { + appendTo.appendChild(elm); + } + + return elm; + }, + { + selector, + appendTo, + } + ) + ); + }, mount: async ({ page }, use, testInfo) => { + (testInfo as any).componentTest = true; const theme = testInfo.project.metadata?.theme ?? 'theme-classic-dark'; testInfo.annotations.push({ type: theme, @@ -100,3 +133,5 @@ export const test = testBase.extend({ ); }, }); + +export const test = regressionTest;