From 91e6a0c5ba2c2c932de7aeccf9710fe4c069ff37 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Sat, 18 Mar 2023 22:28:04 -0400 Subject: [PATCH 1/7] chore: add Examples & Options Playwright E2E tests --- demo/src/examples/example10.html | 2 +- demo/src/examples/example11.html | 6 ++--- demo/src/examples/example13.html | 2 +- demo/src/options/options01.html | 10 +++++++-- demo/src/options/options02.html | 6 ++--- demo/src/options/options07.html | 4 ++-- playwright/e2e/example10.spec.ts | 35 +++++++++++++++++++++++++++++ playwright/e2e/example11.spec.ts | 20 +++++++++++++++++ playwright/e2e/example13.spec.ts | 17 ++++++++++++++ playwright/e2e/options01.spec.ts | 21 ++++++++++++++++++ playwright/e2e/options02.spec.ts | 21 ++++++++++++++++++ playwright/e2e/options03.spec.ts | 14 ++++++++++++ playwright/e2e/options04.spec.ts | 18 +++++++++++++++ playwright/e2e/options05.spec.ts | 19 ++++++++++++++++ playwright/e2e/options06.spec.ts | 18 +++++++++++++++ playwright/e2e/options07.spec.ts | 38 ++++++++++++++++++++++++++++++++ playwright/e2e/options08.spec.ts | 19 ++++++++++++++++ playwright/e2e/options09.spec.ts | 18 +++++++++++++++ playwright/e2e/options10.spec.ts | 18 +++++++++++++++ playwright/e2e/options11.spec.ts | 18 +++++++++++++++ playwright/e2e/options12.spec.ts | 22 ++++++++++++++++++ playwright/e2e/options13.spec.ts | 23 +++++++++++++++++++ playwright/e2e/options14.spec.ts | 13 +++++++++++ playwright/e2e/options15.spec.ts | 19 ++++++++++++++++ playwright/e2e/options16.spec.ts | 17 ++++++++++++++ 25 files changed, 406 insertions(+), 12 deletions(-) create mode 100644 playwright/e2e/example10.spec.ts create mode 100644 playwright/e2e/example11.spec.ts create mode 100644 playwright/e2e/example13.spec.ts create mode 100644 playwright/e2e/options01.spec.ts create mode 100644 playwright/e2e/options02.spec.ts create mode 100644 playwright/e2e/options03.spec.ts create mode 100644 playwright/e2e/options04.spec.ts create mode 100644 playwright/e2e/options05.spec.ts create mode 100644 playwright/e2e/options06.spec.ts create mode 100644 playwright/e2e/options07.spec.ts create mode 100644 playwright/e2e/options08.spec.ts create mode 100644 playwright/e2e/options09.spec.ts create mode 100644 playwright/e2e/options10.spec.ts create mode 100644 playwright/e2e/options11.spec.ts create mode 100644 playwright/e2e/options12.spec.ts create mode 100644 playwright/e2e/options13.spec.ts create mode 100644 playwright/e2e/options14.spec.ts create mode 100644 playwright/e2e/options15.spec.ts create mode 100644 playwright/e2e/options16.spec.ts diff --git a/demo/src/examples/example10.html b/demo/src/examples/example10.html index 99bc51d2..0ff36d57 100644 --- a/demo/src/examples/example10.html +++ b/demo/src/examples/example10.html @@ -29,7 +29,7 @@

-
diff --git a/demo/src/examples/example11.html b/demo/src/examples/example11.html index cd78dc68..a5b6bece 100644 --- a/demo/src/examples/example11.html +++ b/demo/src/examples/example11.html @@ -26,7 +26,7 @@

- @@ -47,7 +47,7 @@

- @@ -68,7 +68,7 @@

- diff --git a/demo/src/examples/example13.html b/demo/src/examples/example13.html index 9f8f88c6..1fccbf6f 100644 --- a/demo/src/examples/example13.html +++ b/demo/src/examples/example13.html @@ -38,7 +38,7 @@

- +

diff --git a/demo/src/options/options01.html b/demo/src/options/options01.html index 499a93e7..8347d58a 100644 --- a/demo/src/options/options01.html +++ b/demo/src/options/options01.html @@ -28,7 +28,13 @@

- @@ -49,7 +55,7 @@

- diff --git a/demo/src/options/options02.html b/demo/src/options/options02.html index 733d04d4..04030d26 100644 --- a/demo/src/options/options02.html +++ b/demo/src/options/options02.html @@ -26,7 +26,7 @@

- @@ -39,7 +39,7 @@

- @@ -63,7 +63,7 @@

- diff --git a/demo/src/options/options07.html b/demo/src/options/options07.html index b6f7155b..35743038 100644 --- a/demo/src/options/options07.html +++ b/demo/src/options/options07.html @@ -34,7 +34,7 @@

- @@ -55,7 +55,7 @@

- diff --git a/playwright/e2e/example10.spec.ts b/playwright/e2e/example10.spec.ts new file mode 100644 index 00000000..3a19750a --- /dev/null +++ b/playwright/e2e/example10.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Example 10 - Large Select Dataset with Virtual Scroll', () => { + test('select should use virtual scroll', async ({ page }) => { + await page.goto('#/example10'); + await page.locator('[data-test="select10"].ms-parent').click(); + + const ulElm = await page.locator('.ms-drop ul'); + const liElms = await page.locator('.ms-drop ul li'); + await expect(liElms.nth(0)).toContainText('0'); + await liElms.nth(0).click(); + await expect(liElms.nth(1)).toContainText('1'); + await liElms.nth(1).click(); + await page.getByRole('button', { name: '0, 1' }).click(); + + // scroll to the middle and click 5001 + await page.locator('[data-test="select10"].ms-parent').click(); + await ulElm.evaluate((e) => (e.scrollTop = e.scrollHeight / 2)); + await page.locator('label').filter({ hasText: '5001' }).click(); + await page.getByRole('button', { name: '0, 1, 5001' }); + + // scroll to the end and select last 2 labels + await ulElm.evaluate((e) => (e.scrollTop = e.scrollHeight)); + await page.locator('label').filter({ hasText: '9998' }).click(); + await page.locator('label').filter({ hasText: '9999' }).click(); + await page.getByRole('button', { name: '5 of 10000 selected' }); + + // filter with text 999 and expect 9998 & 9999 to show up + await page.getByPlaceholder('🔎︎').click(); + await page.getByPlaceholder('🔎︎').fill('999'); + await page.locator('label').filter({ hasText: '9998' }).click(); + await page.locator('label').filter({ hasText: '9999' }).click(); + await page.getByRole('button', { name: '0, 1, 5001' }).click(); + }); +}); diff --git a/playwright/e2e/example11.spec.ts b/playwright/e2e/example11.spec.ts new file mode 100644 index 00000000..d0132848 --- /dev/null +++ b/playwright/e2e/example11.spec.ts @@ -0,0 +1,20 @@ +import { test, expect, Page } from '@playwright/test'; + +async function assertDropHeight(page: Page, selector: string, expectation: number, range = 3) { + const dropElm1 = await page.locator(selector); + const elmHeight = (await dropElm1?.boundingBox())!.height; + expect(elmHeight).toBeGreaterThanOrEqual(expectation - range); + expect(elmHeight).toBeLessThan(expectation + range); +} + +test.describe('Example 11 - Bootstrap Theme', () => { + test('select element have different element height & text font size', async ({ page }) => { + await page.goto('#/example11'); + await page.getByRole('button', { name: 'form-control-sm' }).click(); + await page.getByRole('listitem').filter({ hasText: 'January' }).locator('span').click(); + + assertDropHeight(page, '[data-test=select1] .ms-choice', 29); + assertDropHeight(page, '[data-test=select2] .ms-choice', 36); + assertDropHeight(page, '[data-test=select3] .ms-choice', 46); + }); +}); diff --git a/playwright/e2e/example13.spec.ts b/playwright/e2e/example13.spec.ts new file mode 100644 index 00000000..be160f5a --- /dev/null +++ b/playwright/e2e/example13.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Example 13 - Destroy Select', () => { + test('creating & destroying select multiple times', async ({ page }) => { + await page.goto('#/example13'); + await page.getByRole('button', { name: 'Dynamically Create' }).click(); + await page.getByRole('button', { name: '1, 3, 4' }).click(); + await page.getByRole('button', { name: 'Destroy' }).click(); + expect(page.locator['[data-test="select1"]']).toBeUndefined(); + + await page.locator('div').filter({ hasText: 'Use Select Option Label & Render HTML' }); + await page.getByRole('button', { name: 'Dynamically Create' }).click(); + await page.getByRole('button', { name: '1, 3, 4' }).click(); + await page.locator('label').filter({ hasText: 'January' }).click(); + await page.getByRole('button', { name: '3, 4' }).click(); + }); +}); diff --git a/playwright/e2e/options01.spec.ts b/playwright/e2e/options01.spec.ts new file mode 100644 index 00000000..7457b059 --- /dev/null +++ b/playwright/e2e/options01.spec.ts @@ -0,0 +1,21 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 01 - Placeholder', () => { + test('first and second select have placeholder', async ({ page }) => { + await page.goto('#/options01'); + const selectElm1 = await page.locator('[data-test="select1"]'); + const placeholderElm1 = await page.locator('[data-test="select1"] .ms-placeholder'); + expect(placeholderElm1).toHaveText('Here is the placeholder via attribute'); + placeholderElm1.click(); + await selectElm1.locator('span').filter({ hasText: 'February' }).click(); + await selectElm1.locator('span').filter({ hasText: 'June' }).click(); + await page.getByRole('button', { name: 'February, June' }).click(); + + const selectElm2 = await page.locator('[data-test="select2"]'); + const placeholderElm2 = await page.locator('[data-test="select2"] .ms-placeholder'); + expect(placeholderElm2).toHaveText('Here is the placeholder via javascript'); + placeholderElm2.click(); + await page.getByText('Group 2').click(); + await page.getByRole('button', { name: '[Group 2: Option 4, Option 5, Option 6]' }).click(); + }); +}); diff --git a/playwright/e2e/options02.spec.ts b/playwright/e2e/options02.spec.ts new file mode 100644 index 00000000..22d2f640 --- /dev/null +++ b/playwright/e2e/options02.spec.ts @@ -0,0 +1,21 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 02 - Single Radio', () => { + test('have radio button and expect drop to close after each selection', async ({ page }) => { + await page.goto('#/options02'); + await page.locator('div[data-test="select1"] .ms-choice span', { hasText: 'First' }).click(); + await page.getByRole('listitem').filter({ hasText: 'Third' }).locator('span').click(); + const drop1elm = await page.locator('div[data-test=select1] .ms-drop'); + expect(drop1elm).toBeHidden(); + + await page.locator('div[data-test="select2"] .ms-choice span', { hasText: 'Option 1' }).click(); + await page.getByRole('listitem').filter({ hasText: 'Option 4' }).locator('span').click(); + const drop2elm = await page.locator('div[data-test=select2] .ms-drop'); + expect(drop2elm).toBeHidden(); + + await page.locator('div[data-test="select3"].ms-parent').click(); + await page.locator('div[data-test="select3"] .ms-drop label span', { hasText: 'Fourth' }).click(); + const drop3elm = await page.locator('div[data-test=select3] .ms-drop'); + expect(drop3elm).toBeHidden(); + }); +}); diff --git a/playwright/e2e/options03.spec.ts b/playwright/e2e/options03.spec.ts new file mode 100644 index 00000000..a8d6cef5 --- /dev/null +++ b/playwright/e2e/options03.spec.ts @@ -0,0 +1,14 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 03 - Hide Select All', () => { + test('multiple select does not show Select All', async ({ page }) => { + await page.goto('#/options03'); + await page.getByRole('button').click(); + const selectAllElm = await page.locator('.ms-select-all'); + expect(selectAllElm).toHaveCount(0); + + await page.getByLabel('Third').check(); + await page.getByLabel('Second').check(); + await page.getByRole('button', { name: 'Second, Third' }).click(); + }); +}); diff --git a/playwright/e2e/options04.spec.ts b/playwright/e2e/options04.spec.ts new file mode 100644 index 00000000..e0fa3035 --- /dev/null +++ b/playwright/e2e/options04.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 04 - Hide OptGroup', () => { + test('no optgroup input should be found', async ({ page }) => { + await page.goto('#/options04'); + await page.getByRole('button').click(); + await page.getByLabel('2', { exact: true }).check(); + await page.getByLabel('1', { exact: true }).check(); + await page.getByLabel('3', { exact: true }).check(); + await page.getByLabel('4', { exact: true }).check(); + await page.getByLabel('5', { exact: true }).check(); + await page.getByLabel('5', { exact: true }).uncheck(); + await page.getByLabel('5', { exact: true }).check(); + await page.getByRole('button', { name: '5 of 15 selected' }).click(); + const optGroupCheckboxes = await page.locator('.optgroup input[type="checkbox"]'); + expect(optGroupCheckboxes).toHaveCount(0); + }); +}); diff --git a/playwright/e2e/options05.spec.ts b/playwright/e2e/options05.spec.ts new file mode 100644 index 00000000..c5a34ce8 --- /dev/null +++ b/playwright/e2e/options05.spec.ts @@ -0,0 +1,19 @@ +import { expect, test } from '@playwright/test'; +import type { Page } from '@playwright/test'; + +test.describe('Options 05 - Custom Drop Width', () => { + test('drop width is ~580px', async ({ page }) => { + await page.goto('#/options05'); + + await page.locator('.ms-parent').click(); + const dropElm = await page.locator('.ms-drop'); + const dropWidth = (await dropElm.boundingBox())!.width; + expect(dropWidth).toBeGreaterThanOrEqual(570); + expect(dropWidth).toBeLessThan(590); + + await page.getByLabel('6', { exact: true }).check(); + await page.getByLabel('8', { exact: true }).check(); + await page.getByLabel('30').check(); + await page.getByRole('button', { name: '6, 8, 30' }).click(); + }); +}); diff --git a/playwright/e2e/options06.spec.ts b/playwright/e2e/options06.spec.ts new file mode 100644 index 00000000..89f9d093 --- /dev/null +++ b/playwright/e2e/options06.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 06 - Custom Drop Width', () => { + test('drop height is ~130px', async ({ page }) => { + await page.goto('#/options06'); + await page.locator('.ms-parent').click(); + const dropElm = await page.locator('.ms-drop ul'); + const dropHeight = (await dropElm.boundingBox())!.height; + expect(dropHeight).toBeGreaterThanOrEqual(130); + expect(dropHeight).toBeLessThan(150); + + await page.locator('span').filter({ hasText: 'March' }).click(); + await page.getByRole('button', { name: 'March' }).click(); + await page.getByRole('button', { name: 'March' }).click(); + await page.getByText('[Select all]').click(); + await page.getByRole('button', { name: 'All selected' }).click(); + }); +}); diff --git a/playwright/e2e/options07.spec.ts b/playwright/e2e/options07.spec.ts new file mode 100644 index 00000000..836ca0f8 --- /dev/null +++ b/playwright/e2e/options07.spec.ts @@ -0,0 +1,38 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 07 - Max Height Unit', () => { + test('drop should display 5 labels at first and expect a fixed height when changing height unit input', async ({ page }) => { + await page.goto('#/options07'); + + await page.locator('div[data-test="select1"].ms-parent').click(); + let dropElm1 = await page.locator('div[data-test="select1"] .ms-drop ul'); + let dropHeight1 = (await dropElm1.boundingBox())!.height; + expect(dropHeight1).toBeGreaterThanOrEqual(160); + expect(dropHeight1).toBeLessThan(180); + + await page.locator('#number').fill('6'); + page.keyboard.press('Enter'); + + dropElm1 = await page.locator('div[data-test="select1"] .ms-drop ul'); + dropHeight1 = (await dropElm1.boundingBox())!.height; + expect(dropHeight1).toBeGreaterThanOrEqual(200); + expect(dropHeight1).toBeLessThan(210); + await page.locator('div[data-test="select1"].ms-parent').click(); + + // select 2 + await page.locator('div[data-test="select2"].ms-parent').click(); + let dropElm2 = await page.locator('div[data-test="select2"] .ms-drop ul'); + let dropHeight2 = (await dropElm2.boundingBox())!.height; + expect(dropHeight2).toBeGreaterThanOrEqual(185); + expect(dropHeight2).toBeLessThan(200); + + await page.locator('#number').fill('5'); + page.keyboard.press('Enter'); + + dropElm2 = await page.locator('div[data-test="select2"] .ms-drop ul'); + dropHeight2 = (await dropElm2.boundingBox())!.height; + expect(dropHeight2).toBeGreaterThanOrEqual(155); + expect(dropHeight2).toBeLessThan(175); + await page.locator('div[data-test="select2"].ms-parent').click(); + }); +}); diff --git a/playwright/e2e/options08.spec.ts b/playwright/e2e/options08.spec.ts new file mode 100644 index 00000000..c06d034e --- /dev/null +++ b/playwright/e2e/options08.spec.ts @@ -0,0 +1,19 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 08 - Drop Up showing on top of select', () => { + test('drop shows up (upward)', async ({ page }) => { + await page.goto('#/options08'); + await page.getByRole('button').click(); + const dropParentElm = await page.locator('.ms-parent'); + const parentY = (await dropParentElm.boundingBox())!.y; + expect(parentY).toBeGreaterThan(350); + + const dropElm = await page.locator('.ms-drop'); + const dropY = (await dropElm.boundingBox())!.y; + expect(dropY).toBeLessThanOrEqual(100); + + await page.locator('span').filter({ hasText: 'April' }).click(); + await page.locator('span').filter({ hasText: 'May' }).click(); + await page.getByRole('button', { name: 'April, May' }).click(); + }); +}); diff --git a/playwright/e2e/options09.spec.ts b/playwright/e2e/options09.spec.ts new file mode 100644 index 00000000..134b5ae9 --- /dev/null +++ b/playwright/e2e/options09.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 09 - Display Number Values', () => { + test('select February & March and expect drop parent to show "2, 3" value numbers', async ({ page }) => { + await page.goto('#/options09'); + await page.getByRole('button').click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + + const parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('2, 3'); + + await page.locator('span').filter({ hasText: 'July' }).click(); + await page.locator('span').filter({ hasText: 'August' }).click(); + await page.getByRole('button', { name: '4 of 12 selected' }).click(); + expect(parentSpan).toHaveText('4 of 12 selected'); + }); +}); diff --git a/playwright/e2e/options10.spec.ts b/playwright/e2e/options10.spec.ts new file mode 100644 index 00000000..55aa151c --- /dev/null +++ b/playwright/e2e/options10.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 10 - Display Selected Titles', () => { + test('select February & March and expect drop parent to show "February, March" selected text', async ({ page }) => { + await page.goto('#/options10'); + await page.getByRole('button').click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + + const parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February, March'); + + await page.locator('span').filter({ hasText: 'July' }).click(); + await page.locator('span').filter({ hasText: 'August' }).click(); + await page.getByRole('button', { name: '4 of 12 selected' }).click(); + expect(parentSpan).toHaveText('4 of 12 selected'); + }); +}); diff --git a/playwright/e2e/options11.spec.ts b/playwright/e2e/options11.spec.ts new file mode 100644 index 00000000..7b400566 --- /dev/null +++ b/playwright/e2e/options11.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 11 - Display Delimiter', () => { + test('select February & March and expect drop parent to show pipe separator as in "February | March"', async ({ page }) => { + await page.goto('#/options11'); + await page.getByRole('button').click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + + const parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February | March'); + + await page.locator('span').filter({ hasText: 'July' }).click(); + await page.locator('span').filter({ hasText: 'August' }).click(); + await page.getByRole('button', { name: '4 of 12 selected' }).click(); + expect(parentSpan).toHaveText('4 of 12 selected'); + }); +}); diff --git a/playwright/e2e/options12.spec.ts b/playwright/e2e/options12.spec.ts new file mode 100644 index 00000000..ab875501 --- /dev/null +++ b/playwright/e2e/options12.spec.ts @@ -0,0 +1,22 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 12 - Minimum Count Selected', () => { + test('shows a maximum of 8 selected options but 9 selection will show 9 of 19 selected', async ({ page }) => { + await page.goto('#/options12'); + await page.getByRole('button').click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + await page.locator('span').filter({ hasText: 'April' }).click(); + await page.locator('span').filter({ hasText: 'May' }).click(); + await page.locator('span').filter({ hasText: 'June' }).click(); + await page.locator('span').filter({ hasText: 'July' }).click(); + await page.locator('span').filter({ hasText: 'August' }).click(); + await page.locator('span').filter({ hasText: 'September' }).click(); + let parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February, March, April, May, June, July, August, September'); + + await page.locator('span').filter({ hasText: 'January' }).click(); + parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('9 of 12 selected'); + }); +}); diff --git a/playwright/e2e/options13.spec.ts b/playwright/e2e/options13.spec.ts new file mode 100644 index 00000000..e81871ba --- /dev/null +++ b/playwright/e2e/options13.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 13 - The Ellipsis', () => { + test('ellipsis is shown after selecting 6+ options', async ({ page }) => { + await page.goto('#/options13'); + await page.getByRole('button').click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + await page.locator('span').filter({ hasText: 'April' }).click(); + await page.locator('span').filter({ hasText: 'May' }).click(); + await page.locator('span').filter({ hasText: 'June' }).click(); + let parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February, March, April, May, June'); + + await page.locator('span').filter({ hasText: 'July' }).click(); + parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February, March, April, May, June...'); + + await page.locator('span').filter({ hasText: 'August' }).click(); + parentSpan = await page.locator('.ms-parent .ms-choice span'); + expect(parentSpan).toHaveText('February, March, April, May, June...'); + }); +}); diff --git a/playwright/e2e/options14.spec.ts b/playwright/e2e/options14.spec.ts new file mode 100644 index 00000000..8e5cc091 --- /dev/null +++ b/playwright/e2e/options14.spec.ts @@ -0,0 +1,13 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 14 - Select is opened', () => { + test('select is already opened when routing to the page', async ({ page }) => { + await page.goto('#/options14'); + const drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeVisible(); + + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('label').filter({ hasText: 'March' }).click(); + await page.getByRole('button', { name: 'February, March' }).click(); + }); +}); diff --git a/playwright/e2e/options15.spec.ts b/playwright/e2e/options15.spec.ts new file mode 100644 index 00000000..e4472d1e --- /dev/null +++ b/playwright/e2e/options15.spec.ts @@ -0,0 +1,19 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 15 - Select keep open', () => { + test('select stays open even when clicking outside the drop but will close when clicking on drop', async ({ page }) => { + await page.goto('#/options15'); + await page.locator('div .ms-parent').click(); + + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('label').filter({ hasText: 'March' }).click(); + + await page.locator('body').click(); + let drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeVisible(); + + await page.locator('div.ms-parent').click(); + drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeHidden(); + }); +}); diff --git a/playwright/e2e/options16.spec.ts b/playwright/e2e/options16.spec.ts new file mode 100644 index 00000000..b2955a85 --- /dev/null +++ b/playwright/e2e/options16.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 16 - Open on Hover', () => { + test('drop shows only after hovering the select element', async ({ page }) => { + await page.goto('#/options16'); + let drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeHidden(); + + await page.locator('div .ms-parent').hover(); + drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeVisible(); + + await page.locator('h2').hover(); + drop1elm = await page.locator('div .ms-drop'); + expect(drop1elm).toBeHidden(); + }); +}); From e9d0afedd89e3fd5e24616d368b1d3eb32577181 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Sat, 18 Mar 2023 23:07:42 -0400 Subject: [PATCH 2/7] chore: improve playwright test to avoid flacky failures --- playwright/e2e/options07.spec.ts | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/playwright/e2e/options07.spec.ts b/playwright/e2e/options07.spec.ts index 836ca0f8..d250d9e4 100644 --- a/playwright/e2e/options07.spec.ts +++ b/playwright/e2e/options07.spec.ts @@ -4,33 +4,45 @@ test.describe('Options 07 - Max Height Unit', () => { test('drop should display 5 labels at first and expect a fixed height when changing height unit input', async ({ page }) => { await page.goto('#/options07'); - await page.locator('div[data-test="select1"].ms-parent').click(); + const dropParent1 = await page.locator('div[data-test="select1"].ms-parent'); + await dropParent1.click(); let dropElm1 = await page.locator('div[data-test="select1"] .ms-drop ul'); - let dropHeight1 = (await dropElm1.boundingBox())!.height; + expect(dropElm1).toBeVisible(); + let dropElmBox1 = await dropElm1.boundingBox(); + let dropHeight1 = dropElmBox1!.height; expect(dropHeight1).toBeGreaterThanOrEqual(160); expect(dropHeight1).toBeLessThan(180); await page.locator('#number').fill('6'); page.keyboard.press('Enter'); + await page.waitForTimeout(2); dropElm1 = await page.locator('div[data-test="select1"] .ms-drop ul'); - dropHeight1 = (await dropElm1.boundingBox())!.height; + expect(dropElm1).toBeVisible(); + dropElmBox1 = await dropElm1.boundingBox(); + dropHeight1 = dropElmBox1!.height; expect(dropHeight1).toBeGreaterThanOrEqual(200); expect(dropHeight1).toBeLessThan(210); await page.locator('div[data-test="select1"].ms-parent').click(); // select 2 - await page.locator('div[data-test="select2"].ms-parent').click(); + const dropParent2 = await page.locator('div[data-test="select2"].ms-parent'); + await dropParent2.click(); let dropElm2 = await page.locator('div[data-test="select2"] .ms-drop ul'); - let dropHeight2 = (await dropElm2.boundingBox())!.height; + expect(dropElm2).toBeVisible(); + let dropElmBox2 = await dropElm2.boundingBox(); + let dropHeight2 = dropElmBox2!.height; expect(dropHeight2).toBeGreaterThanOrEqual(185); expect(dropHeight2).toBeLessThan(200); await page.locator('#number').fill('5'); page.keyboard.press('Enter'); + await page.waitForTimeout(2); dropElm2 = await page.locator('div[data-test="select2"] .ms-drop ul'); - dropHeight2 = (await dropElm2.boundingBox())!.height; + expect(dropElm2).toBeVisible(); + dropElmBox2 = await dropElm2.boundingBox(); + dropHeight2 = dropElmBox2!.height; expect(dropHeight2).toBeGreaterThanOrEqual(155); expect(dropHeight2).toBeLessThan(175); await page.locator('div[data-test="select2"].ms-parent').click(); From c438f683b2af8ef2bde2a1ec76c340dd6ae83ca8 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Mon, 20 Mar 2023 22:21:22 -0400 Subject: [PATCH 3/7] chore: add more Options E2E tests --- demo/src/options/options17.html | 8 +++---- demo/src/options/options18.html | 8 +++---- demo/src/options/options19.html | 2 +- demo/src/options/options21.html | 4 ++-- demo/src/options/options22.html | 8 +++---- demo/src/options/options24.html | 8 +++---- lib/src/MultipleSelectInstance.ts | 21 +++++++++--------- playwright/e2e/example04.spec.ts | 16 ++++++------- playwright/e2e/options17.spec.ts | 32 ++++++++++++++++++++++++++ playwright/e2e/options18.spec.ts | 35 +++++++++++++++++++++++++++++ playwright/e2e/options19.spec.ts | 26 ++++++++++++++++++++++ playwright/e2e/options20.spec.ts | 20 +++++++++++++++++ playwright/e2e/options21.spec.ts | 20 +++++++++++++++++ playwright/e2e/options22.spec.ts | 27 ++++++++++++++++++++++ playwright/e2e/options23.spec.ts | 30 +++++++++++++++++++++++++ playwright/e2e/options24.spec.ts | 37 +++++++++++++++++++++++++++++++ 16 files changed, 265 insertions(+), 37 deletions(-) create mode 100644 playwright/e2e/options17.spec.ts create mode 100644 playwright/e2e/options18.spec.ts create mode 100644 playwright/e2e/options19.spec.ts create mode 100644 playwright/e2e/options20.spec.ts create mode 100644 playwright/e2e/options21.spec.ts create mode 100644 playwright/e2e/options22.spec.ts create mode 100644 playwright/e2e/options23.spec.ts create mode 100644 playwright/e2e/options24.spec.ts diff --git a/demo/src/options/options17.html b/demo/src/options/options17.html index a1810451..a904803f 100644 --- a/demo/src/options/options17.html +++ b/demo/src/options/options17.html @@ -29,7 +29,7 @@

- @@ -52,7 +52,7 @@

- @@ -75,7 +75,7 @@

- @@ -98,7 +98,7 @@

- diff --git a/demo/src/options/options18.html b/demo/src/options/options18.html index 6314428b..5629978d 100644 --- a/demo/src/options/options18.html +++ b/demo/src/options/options18.html @@ -26,7 +26,7 @@

- @@ -66,7 +66,7 @@

- @@ -121,7 +121,7 @@

- @@ -161,7 +161,7 @@

- diff --git a/demo/src/options/options19.html b/demo/src/options/options19.html index f751e199..d999c5f5 100644 --- a/demo/src/options/options19.html +++ b/demo/src/options/options19.html @@ -28,7 +28,7 @@

+ + + + + + + + + + { test('every select drop parent should have different width', async ({ page }) => { await page.goto('#/example04'); - assertDropWidth(page, 'div[data-test=select1]', 92); + assertDropWidth(page, '[data-test=select1].ms-parent', 92); await page.getByRole('button', { name: 'First', exact: true }).click(); - await page.locator('div[data-test=select1] .ms-drop li label').filter({ hasText: 'Fourth' }).click(); + await page.locator('[data-test=select1].ms-drop li label').filter({ hasText: 'Fourth' }).click(); await expect(page.locator('div[data-test=select1] .ms-choice span')).toHaveText('Fourth'); - assertDropWidth(page, 'div[data-test=select2]', 108); + assertDropWidth(page, 'div[data-test=select2].ms-parent', 108); await page.getByRole('button', { name: 'Options 1' }).click(); - await page.locator('div[data-test=select2] .ms-drop li label').filter({ hasText: 'Options 3' }).click(); + await page.locator('[data-test=select2].ms-drop li label').filter({ hasText: 'Options 3' }).click(); await expect(page.locator('div[data-test=select2] .ms-choice span')).toHaveText('Options 3'); - assertDropWidth(page, 'div[data-test=select3]', 315); + assertDropWidth(page, 'div[data-test=select3].ms-parent', 315); await page.getByRole('button', { name: 'This is the first option and value is 1' }).first().click(); await page.getByRole('listitem').filter({ hasText: 'This is the fourth option and value is 4' }).locator('label').click(); await expect(page.locator('div[data-test=select3] .ms-choice span')).toHaveText('This is the fourth option and value is 4'); - assertDropWidth(page, 'div[data-test=select4]', 200); + assertDropWidth(page, 'div[data-test=select4].ms-parent', 200); await page.getByRole('button', { name: 'This is the first option and value is 1' }).click(); await page.getByRole('listitem').filter({ hasText: 'This is the fourth option and value is 4' }).locator('label').click(); await expect(page.locator('div[data-test=select4] .ms-choice span')).toHaveText('This is the fourth option and value is 4'); - assertDropWidth(page, 'div[data-test=select5]', 120); + assertDropWidth(page, 'div[data-test=select5].ms-parent', 120); await page.locator('div:nth-child(5) > .col-sm-9 > .ms-parent > .ms-choice').click(); await page.getByText('Group 2', { exact: true }).click(); await page.getByRole('button', { name: '[Group 2: Option 4, Option 5, Option 6]' }).click(); await expect(page.locator('div[data-test=select5] .ms-choice span')).toHaveText('[Group 2: Option 4, Option 5, Option 6]'); - assertDropWidth(page, 'div[data-test=select6]', 170); + assertDropWidth(page, 'div[data-test=select6].ms-parent', 170); await page.locator('div:nth-child(6) > .col-sm-9 > .ms-parent > .ms-choice').click(); await page.getByText('This is group 2').click(); await page.getByRole('button', { name: '[This is group 2: This is option 4, This is option 5, This is option 6]' }).click(); diff --git a/playwright/e2e/options17.spec.ts b/playwright/e2e/options17.spec.ts new file mode 100644 index 00000000..5cb9af75 --- /dev/null +++ b/playwright/e2e/options17.spec.ts @@ -0,0 +1,32 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 17 - Drop Container', () => { + test('first select should not be visible without a container but all other selects should be visible', async ({ page }) => { + await page.goto('#/options17'); + await page.locator('.select1.ms-parent').click(); + let drop1elm = await page.locator('.select1.ms-parent .ms-drop'); + expect(drop1elm).not.toBeVisible(); + + await page.locator('.select2.ms-parent').click(); + await page.getByRole('listitem').filter({ hasText: 'February' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'August' }).locator('span').click(); + let drop2elm = await page.locator('[data-test=select2].ms-drop'); + expect(drop2elm).toBeVisible(); + await page.getByRole('button', { name: 'February, August' }).click(); + + await page.locator('.select3.ms-parent').click(); + await page.getByRole('listitem').filter({ hasText: 'January' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'March' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'April' }).locator('span').click(); + let drop3elm = await page.locator('[data-test=select3].ms-drop'); + expect(drop3elm).toBeVisible(); + await page.getByRole('button', { name: 'January, March, April' }).click(); + + await page.locator('.select4.ms-parent').click(); + await page.getByRole('listitem').filter({ hasText: 'April' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'May' }).locator('span').click(); + let drop4elm = await page.locator('[data-test=select4].ms-drop'); + expect(drop4elm).toBeVisible(); + await page.getByRole('button', { name: 'April, May' }).click(); + }); +}); diff --git a/playwright/e2e/options18.spec.ts b/playwright/e2e/options18.spec.ts new file mode 100644 index 00000000..515c1f3b --- /dev/null +++ b/playwright/e2e/options18.spec.ts @@ -0,0 +1,35 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 18 - Drop Filtering', () => { + test('filtering on all type of select', async ({ page }) => { + await page.goto('#/options18'); + await page.locator('[data-test=select1].ms-parent').click(); + await page.locator('[data-test=select1] .ms-search input').fill('gh'); + await page.keyboard.press('Enter'); + await expect(page.locator('[data-test=select1].ms-drop li.hide-radio')).toHaveCount(2); + await page.getByRole('listitem').filter({ hasText: 'fgh' }).locator('label').click(); + + await page.locator('[data-test=select2].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }).fill('33'); + await page.keyboard.press('Enter'); + await expect(page.locator('[data-test=select2].ms-drop li.group')).toHaveCount(1); + await expect(page.locator('[data-test=select2].ms-drop li.option-level-1')).toHaveCount(1); + await page.getByRole('listitem').filter({ hasText: 'Group 11' }).locator('label').click(); + await page.getByRole('listitem').filter({ hasText: '333' }).locator('span').click(); + + await page.locator('[data-test=select3].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }).fill('ef'); + await page.keyboard.press('Enter'); + await page.locator('span').filter({ hasText: 'def' }).click(); + await page.locator('span').filter({ hasText: 'efg' }).click(); + const selectAll3 = await page.locator('[data-test=select3] .ms-select-all input[type=checkbox]'); + expect(selectAll3).toBeChecked(); + await page.locator('[data-test=select3].ms-parent').click(); + + await page.locator('[data-test=select4].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }).fill('10'); + await page.keyboard.press('Enter'); + await page.getByText('Group 20').click(); + await page.getByRole('button', { name: '[Group 20: 210]' }).click(); + }); +}); diff --git a/playwright/e2e/options19.spec.ts b/playwright/e2e/options19.spec.ts new file mode 100644 index 00000000..cda25a9d --- /dev/null +++ b/playwright/e2e/options19.spec.ts @@ -0,0 +1,26 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 19 - Filter Only Optgroup', () => { + test('filtering by optgroup', async ({ page }) => { + await page.goto('#/options19'); + const disabledLabel = await page.locator('.ms-drop li label.disabled'); + expect(disabledLabel).toHaveCount(1); + expect(disabledLabel).toHaveText('000'); + + await page.locator('.ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }).fill('B'); + await page.keyboard.press('Enter'); + await page.getByLabel('Group B').check(); + + const labelElms = await page.locator('.ms-drop input[data-name="selectItem"]'); + expect(labelElms).toHaveCount(5); + await page.getByRole('button', { name: '5 of 18 selected' }).click(); + + // reopen & add Group A + await page.locator('.ms-parent').click(); + await page.getByText('Group A').click(); + await page.getByRole('button', { name: '9 of 18 selected' }).dblclick(); + await page.getByText('[Select all]').click(); + await page.getByRole('button', { name: '17 of 18 selected' }).click(); + }); +}); diff --git a/playwright/e2e/options20.spec.ts b/playwright/e2e/options20.spec.ts new file mode 100644 index 00000000..0cb93713 --- /dev/null +++ b/playwright/e2e/options20.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 20 - Filter Placeholder', () => { + test('select drop filter has a placeholder text', async ({ page }) => { + await page.goto('#/options20'); + await page.getByRole('button').click(); + const placeholderLocator = await page.getByPlaceholder('The filter placeholder'); + expect(placeholderLocator).toHaveCount(1); + placeholderLocator.focus(); + await page.keyboard.type('de'); + await page.getByLabel('def').check(); + await page.getByLabel('cde').check(); + const selectAllLoc = await page.locator('.ms-select-all input[type=checkbox]'); + expect(selectAllLoc).toBeChecked(); + expect(await page.locator('.ms-drop input[data-name="selectItem"]')).toHaveCount(2); + expect(await page.locator('ul li.selected input[data-name="selectItem"]')).toHaveCount(2); + expect(await page.locator('.ms-choice span')).toHaveText('cde, def'); + expect(await page.getByRole('button', { name: 'cde, def' })).toHaveCount(1); + }); +}); diff --git a/playwright/e2e/options21.spec.ts b/playwright/e2e/options21.spec.ts new file mode 100644 index 00000000..d0d08f56 --- /dev/null +++ b/playwright/e2e/options21.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 21 - Filter Accept On Enter', () => { + test('select drop filter has a placeholder text', async ({ page }) => { + await page.goto('#/options21'); + await page.locator('[data-test=select1].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('ef'); + expect(await page.locator('.ms-drop li.hide-radio')).toHaveCount(2); + await page.keyboard.press('Enter'); + expect(await page.locator('[data-test=select1] .ms-choice span')).toHaveText('def'); + + await page.locator('[data-test=select2].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('g'); + expect(await page.locator('.ms-drop li.hide-radio')).toHaveCount(2); + await page.keyboard.press('Enter'); + expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText('efg, fgh, ghi'); + }); +}); diff --git a/playwright/e2e/options22.spec.ts b/playwright/e2e/options22.spec.ts new file mode 100644 index 00000000..ef6cce08 --- /dev/null +++ b/playwright/e2e/options22.spec.ts @@ -0,0 +1,27 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 22 - Filter By Data Length', () => { + test('filter shows only when data collection has over 10 items', async ({ page }) => { + await page.goto('#/options22'); + await page.locator('[data-test="select1"].ms-parent').click(); + expect(await page.getByRole('textbox', { name: '🔎︎' })).toHaveCount(0); + await page.getByRole('listitem').filter({ hasText: 'abc' }).locator('label').click(); + + await page.locator('[data-test="select2"].ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('g'); + await page.getByText('[Select all]').first().click(); + await page.getByRole('button', { name: 'efg, fgh, ghi' }).click(); + + await page.locator('[data-test="select3"].ms-parent').click(); + await page.getByRole('checkbox', { name: 'Group 2' }).check(); + await page.getByRole('button', { name: '[Group 2: Option 4, Option 5, Option 6]' }).click(); + + await page.locator('[data-test="select4"].ms-parent').click(); + await page.getByRole('checkbox', { name: 'Group 3' }).check(); + await page.getByRole('button', { name: '5 of 11 selected' }).click(); + await page.getByRole('button', { name: '5 of 11 selected' }).click(); + await page.getByRole('listitem').filter({ hasText: 'Group 1' }).locator('label').click(); + await page.getByRole('button', { name: '8 of 11 selected' }).click(); + }); +}); diff --git a/playwright/e2e/options23.spec.ts b/playwright/e2e/options23.spec.ts new file mode 100644 index 00000000..2b4d3b96 --- /dev/null +++ b/playwright/e2e/options23.spec.ts @@ -0,0 +1,30 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 23 - Custom Filter', () => { + test('filter starts with text', async ({ page }) => { + await page.goto('#/options23'); + await page.locator('.ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('g'); + await page.getByLabel('ghi').check(); + await page.getByRole('button', { name: 'ghi' }).click(); + let selectAllLoc = await page.locator('.ms-select-all input[type=checkbox]'); + expect(selectAllLoc).toBeChecked(); + expect(await page.locator('.ms-choice span')).toHaveText('ghi'); + + await page.getByLabel('Case Sensitive').check(); + await page.locator('.ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('g'); + selectAllLoc = await page.locator('.ms-select-all input[type=checkbox]'); + expect(selectAllLoc).toBeChecked(); + expect(await page.locator('.ms-choice span')).toHaveText('ghi'); + await page.locator('.ms-parent').click(); + + // typing "G" shouldn't return anything but "No matches..." + await page.locator('.ms-parent').click(); + await page.getByRole('textbox', { name: '🔎︎' }); + await page.keyboard.type('G'); + await page.getByText('No matches found').click(); + }); +}); diff --git a/playwright/e2e/options24.spec.ts b/playwright/e2e/options24.spec.ts new file mode 100644 index 00000000..1377a5ce --- /dev/null +++ b/playwright/e2e/options24.spec.ts @@ -0,0 +1,37 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 24 - Show Clear button', () => { + test('clear button expect input to be cleared', async ({ page }) => { + await page.goto(']#/options24'); + await page.locator('[data-test=select1].ms-parent').click(); + expect(await page.locator('[data-test=select1] .ms-choice span')).toHaveText('January'); + await page.locator('[data-test=select1] .ms-choice .icon-close').click(); + expect(await page.locator('[data-test=select1] .ms-choice span')).toHaveText(''); + await page.locator('[data-test=select1].ms-parent').click(); + + await page.locator('[data-test=select2].ms-parent').click(); + expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText('Option 1'); + await page.locator('[data-test=select2] .ms-choice .icon-close').click(); + expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText(''); + await page.locator('[data-test=select2].ms-parent').click(); + + await page.locator('[data-test=select3].ms-parent').click(); + await page.getByRole('checkbox', { name: 'February' }).check(); + await page.getByRole('checkbox', { name: 'March' }).check(); + await page.getByRole('checkbox', { name: 'April' }).check(); + expect(await page.locator('[data-test=select3] .ms-choice span')).toHaveText('February, March, April'); + await page.getByRole('checkbox', { name: 'May' }).check(); + expect(await page.locator('[data-test=select3] .ms-choice span')).toHaveText('4 of 12 selected'); + await page.locator('[data-test=select3].ms-parent').click(); + await page.locator('[data-test=select3] .ms-choice .icon-close').click(); + expect(await page.locator('[data-test=select3] .ms-choice span')).toHaveText(''); + + await page.locator('[data-test=select4].ms-parent').click(); + await page.getByLabel('Group 2').check(); + expect(await page.locator('[data-test=select4] .ms-choice span')).toHaveText('[Group 2: Option 4, Option 5, Option 6]'); + await page.getByLabel('Group 3').check(); + expect(await page.locator('[data-test=select4] .ms-choice span')).toHaveText('6 of 9 selected'); + await page.locator('[data-test=select4] .ms-choice .icon-close').click(); + expect(await page.locator('[data-test=select4] .ms-choice span')).toHaveText(''); + }); +}); From 1e278e1a89e8222b81efab4939725a7a1ebb5aa3 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 21 Mar 2023 01:38:07 -0400 Subject: [PATCH 4/7] chore: add full Options demo E2E test suite --- demo/src/options/options25.html | 8 ++--- demo/src/options/options26.html | 4 +-- demo/src/options/options27.html | 5 ++- demo/src/options/options27.ts | 1 + demo/src/options/options28.html | 5 ++- demo/src/options/options28.ts | 1 + demo/src/options/options29.html | 4 +-- demo/src/options/options30.html | 8 ++--- demo/src/options/options31.html | 4 +-- lib/src/MultipleSelectInstance.ts | 3 +- .../multipleSelectOption.interface.ts | 3 ++ playwright/e2e/options25.spec.ts | 25 +++++++++++++ playwright/e2e/options26.spec.ts | 36 +++++++++++++++++++ playwright/e2e/options27.spec.ts | 23 ++++++++++++ playwright/e2e/options28.spec.ts | 19 ++++++++++ playwright/e2e/options29.spec.ts | 14 ++++++++ playwright/e2e/options30.spec.ts | 32 +++++++++++++++++ playwright/e2e/options31.spec.ts | 24 +++++++++++++ playwright/e2e/options32.spec.ts | 18 ++++++++++ playwright/playwright.config.ts | 2 +- 20 files changed, 220 insertions(+), 19 deletions(-) create mode 100644 playwright/e2e/options25.spec.ts create mode 100644 playwright/e2e/options26.spec.ts create mode 100644 playwright/e2e/options27.spec.ts create mode 100644 playwright/e2e/options28.spec.ts create mode 100644 playwright/e2e/options29.spec.ts create mode 100644 playwright/e2e/options30.spec.ts create mode 100644 playwright/e2e/options31.spec.ts create mode 100644 playwright/e2e/options32.spec.ts diff --git a/demo/src/options/options25.html b/demo/src/options/options25.html index e00b9700..0f325dcd 100644 --- a/demo/src/options/options25.html +++ b/demo/src/options/options25.html @@ -29,7 +29,7 @@

- @@ -50,7 +50,7 @@

- @@ -74,7 +74,7 @@

- @@ -95,7 +95,7 @@

- diff --git a/demo/src/options/options26.html b/demo/src/options/options26.html index 0435768c..d60a28e4 100644 --- a/demo/src/options/options26.html +++ b/demo/src/options/options26.html @@ -26,7 +26,7 @@

- @@ -47,7 +47,7 @@

- diff --git a/demo/src/options/options27.html b/demo/src/options/options27.html index cdddde38..e1890576 100644 --- a/demo/src/options/options27.html +++ b/demo/src/options/options27.html @@ -17,7 +17,10 @@

-
Use textTemplate to customize the text template.
+
+ Use textTemplate to customize the text template and renderOptionLabelAsHtml to render selected + labels as HTML. +

diff --git a/demo/src/options/options27.ts b/demo/src/options/options27.ts index a4e921f3..fc2f1e44 100644 --- a/demo/src/options/options27.ts +++ b/demo/src/options/options27.ts @@ -5,6 +5,7 @@ export default class Example { mount() { this.ms1 = multipleSelect('select', { + renderOptionLabelAsHtml: true, // without this flag, html code will be showing as plain text textTemplate: (el) => { return '' + el.innerHTML; }, diff --git a/demo/src/options/options28.html b/demo/src/options/options28.html index 526087c8..647e03dd 100644 --- a/demo/src/options/options28.html +++ b/demo/src/options/options28.html @@ -17,7 +17,10 @@

-
Use labelTemplate to custom the optgroup label template.
+
+ Use labelTemplate to custom the optgroup label template and renderOptionLabelAsHtml to render + selected labels as HTML. +

diff --git a/demo/src/options/options28.ts b/demo/src/options/options28.ts index 584ca776..7c7f731c 100644 --- a/demo/src/options/options28.ts +++ b/demo/src/options/options28.ts @@ -5,6 +5,7 @@ export default class Example { mount() { this.ms1 = multipleSelect('select', { + renderOptionLabelAsHtml: true, // without this flag, html code will be showing as plain text labelTemplate: (el) => { return '' + el.getAttribute('label'); }, diff --git a/demo/src/options/options29.html b/demo/src/options/options29.html index 53df1b73..5c2b51b5 100644 --- a/demo/src/options/options29.html +++ b/demo/src/options/options29.html @@ -28,7 +28,7 @@

- @@ -49,7 +49,7 @@

- diff --git a/demo/src/options/options30.html b/demo/src/options/options30.html index 19b06c0a..6b2a99a5 100644 --- a/demo/src/options/options30.html +++ b/demo/src/options/options30.html @@ -30,7 +30,7 @@

- @@ -51,7 +51,7 @@

- @@ -72,7 +72,7 @@

- @@ -93,7 +93,7 @@

- diff --git a/demo/src/options/options31.html b/demo/src/options/options31.html index cf8643b8..467a5033 100644 --- a/demo/src/options/options31.html +++ b/demo/src/options/options31.html @@ -32,7 +32,7 @@

- @@ -53,7 +53,7 @@

- +

diff --git a/lib/src/MultipleSelectInstance.ts b/lib/src/MultipleSelectInstance.ts index c44696ae..f98d74a3 100644 --- a/lib/src/MultipleSelectInstance.ts +++ b/lib/src/MultipleSelectInstance.ts @@ -937,10 +937,9 @@ export class MultipleSelectInstance { } else { html = getSelectOptionHtml(); } - if (html !== null) { spanElm?.classList.remove('ms-placeholder'); - if (this.options.useSelectOptionLabelToHtml) { + if (this.options.renderOptionLabelAsHtml || this.options.useSelectOptionLabelToHtml) { spanElm.innerHTML = this.options.sanitizer ? this.options.sanitizer(html) : html; } else { spanElm.textContent = html; diff --git a/lib/src/interfaces/multipleSelectOption.interface.ts b/lib/src/interfaces/multipleSelectOption.interface.ts index bf6c4b63..4d8ad402 100644 --- a/lib/src/interfaces/multipleSelectOption.interface.ts +++ b/lib/src/interfaces/multipleSelectOption.interface.ts @@ -118,6 +118,9 @@ export interface MultipleSelectOption extends MultipleSelectLocale { /** Defines the position of select dropdown, can only be bottom or top. By default this option is set to bottom. */ position: 'bottom' | 'top'; + /** Defaults to False, should we render option labels as html? */ + renderOptionLabelAsHtml?: boolean; + /** Whether or not Multiple Select show select all checkbox. */ selectAll?: boolean; diff --git a/playwright/e2e/options25.spec.ts b/playwright/e2e/options25.spec.ts new file mode 100644 index 00000000..246a9153 --- /dev/null +++ b/playwright/e2e/options25.spec.ts @@ -0,0 +1,25 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 25 - Show OK Button', () => { + test('show OK button on multiple select only', async ({ page }) => { + await page.goto('#/options25'); + await page.locator('[data-test=select1].ms-parent').click(); + await page.getByRole('checkbox', { name: 'April' }).check(); + await page.getByRole('listitem').filter({ hasText: 'March' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'February' }).locator('span').click(); + await page.getByRole('button', { name: 'OK' }).click(); + + await page.locator('[data-test=select2].ms-parent').click(); + await page.getByLabel('Group 1').check(); + await page.getByLabel('Group 2').check(); + await page.getByRole('button', { name: 'OK' }).click(); + + await page.locator('[data-test=select3].ms-parent').click(); + await page.locator('[data-test=select3] .ms-select-all input[data-name="selectAll"]').click(); + await page.getByRole('button', { name: 'OK' }).click(); + + await page.locator('[data-test=select4].ms-parent').click(); + await page.locator('[data-test=select4] ul > li').nth(2).click(); + await page.locator('span').filter({ hasText: 'Third' }); + }); +}); diff --git a/playwright/e2e/options26.spec.ts b/playwright/e2e/options26.spec.ts new file mode 100644 index 00000000..83a3194c --- /dev/null +++ b/playwright/e2e/options26.spec.ts @@ -0,0 +1,36 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 26 - The Styler', () => { + test.beforeEach(async ({ page }) => { + await page.goto('#/options26'); + }); + + test('first select has January & June with custom styling', async ({ page }) => { + await page.locator('[data-test=select1].ms-parent').click(); + const optionLoc1 = await page.locator('[data-test=select1] .ms-drop ul li').nth(0); + optionLoc1.click(); + expect(optionLoc1).toHaveText('January'); + await expect(optionLoc1).toHaveCSS('color', 'rgb(255, 0, 0)'); + await expect(optionLoc1).toHaveCSS('background-color', 'rgb(255, 238, 0)'); + + const optionLoc5 = await page.locator('[data-test=select1] .ms-drop ul li').nth(5); + optionLoc5.click(); + expect(optionLoc5).toHaveText('June'); + await expect(optionLoc5).toHaveCSS('color', 'rgb(255, 255, 255)'); + await expect(optionLoc5).toHaveCSS('background-color', 'rgb(0, 0, 0)'); + const selectedText1 = page.locator('[data-test=select1] .ms-choice span', { hasText: 'January, June' }); + await selectedText1.waitFor(); + }); + + test('second select has January & June with custom styling', async ({ page }) => { + await page.locator('[data-test=select2].ms-parent').click(); + const optionLoc1 = await page.locator('[data-test=select2] .ms-drop ul li').nth(1); + optionLoc1.click(); + expect(optionLoc1).toHaveText('Option 1'); + await expect(optionLoc1).toHaveCSS('color', 'rgb(0, 0, 255)'); + await expect(optionLoc1).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); + await page.waitForTimeout(10); + + expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText('[Group 1: Option 1]'); + }); +}); diff --git a/playwright/e2e/options27.spec.ts b/playwright/e2e/options27.spec.ts new file mode 100644 index 00000000..5ef0148c --- /dev/null +++ b/playwright/e2e/options27.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 27 - Text Template', () => { + test('option labels & selected options shows as html', async ({ page }) => { + await page.goto('#/options27'); + await page.locator('.ms-parent').click(); + const optionLoc1 = await page.locator('.ms-drop ul li').nth(0); + optionLoc1.click(); + expect(await optionLoc1.locator('label span')).toHaveText('January'); + expect(await optionLoc1.locator('span').innerHTML()).toBe('January'); + + const optionLoc4 = await page.locator('.ms-drop ul li').nth(3); + optionLoc4.click(); + expect(await optionLoc4.locator('label span')).toHaveText('April'); + expect(await optionLoc4.locator('span').innerHTML()).toBe('April'); + + await page.waitForTimeout(75); + expect(await page.locator('.ms-choice span')).toHaveText('January, April'); + expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe( + 'January, April' + ); + }); +}); diff --git a/playwright/e2e/options28.spec.ts b/playwright/e2e/options28.spec.ts new file mode 100644 index 00000000..80f072cf --- /dev/null +++ b/playwright/e2e/options28.spec.ts @@ -0,0 +1,19 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 28 - Label Template', () => { + test('option labels & selected options shows as html', async ({ page }) => { + await page.goto('#/options28'); + await page.locator('.ms-parent').click(); + + // await page.locator('.ms-drop li input[data-name="selectItem"]').check(); + await page.locator('.ms-drop ul li label').nth(1).click(); + expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe('[Group 1: Option 1]'); + + const optgroup1 = await page.locator('.ms-drop ul label.optgroup').nth(0); + optgroup1.click(); + await page.waitForTimeout(75); + expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe( + '[Group 1: Option 1, Option 2, Option 3]' + ); + }); +}); diff --git a/playwright/e2e/options29.spec.ts b/playwright/e2e/options29.spec.ts new file mode 100644 index 00000000..c697e790 --- /dev/null +++ b/playwright/e2e/options29.spec.ts @@ -0,0 +1,14 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 29 - Auto-Adjust Drop Position', () => { + test('first select is showing dropdown & second select is showing dropup', async ({ page }) => { + await page.goto('#/options29'); + await page.locator('[data-test=select1].ms-parent').click(); + const dropLoc1 = await page.locator('[data-test=select1].ms-drop'); + await expect(dropLoc1).toHaveClass('ms-drop bottom'); + + await page.locator('[data-test=select2].ms-parent').click(); + const dropLoc2 = await page.locator('[data-test=select2].ms-drop'); + await expect(dropLoc2).toHaveClass('ms-drop top'); + }); +}); diff --git a/playwright/e2e/options30.spec.ts b/playwright/e2e/options30.spec.ts new file mode 100644 index 00000000..bb82e09f --- /dev/null +++ b/playwright/e2e/options30.spec.ts @@ -0,0 +1,32 @@ +import { test, expect, Page } from '@playwright/test'; + +async function assertDropSize(page: Page, selector: string, type: 'height' | 'width', expectation: number, range = 15) { + const dropElm1 = await page.locator(selector); + const elmSize = (await dropElm1?.boundingBox())![type]; + expect(elmSize).toBeGreaterThanOrEqual(expectation - range); + expect(elmSize).toBeLessThan(expectation + range); +} + +test.describe('Options 30 - Auto-Adjust Drop Height/Width', () => { + test('each select have different height/width by available space', async ({ page }) => { + await page.goto('#/options30'); + await page.locator('[data-test=select1].ms-parent').click(); + assertDropSize(page, '[data-test=select1] .ms-choice', 'width', 75); + assertDropSize(page, '[data-test=select1].ms-drop', 'width', 130); + assertDropSize(page, '[data-test=select1].ms-drop', 'height', 198); + + await page.locator('[data-test=select2].ms-parent').click(); + assertDropSize(page, '[data-test=select2] .ms-choice', 'width', 200); + assertDropSize(page, '[data-test=select2].ms-drop', 'width', 200); + assertDropSize(page, '[data-test=select2].ms-drop', 'height', 260); + + await page.locator('[data-test=select3].ms-parent').click(); + assertDropSize(page, '[data-test=select3] .ms-choice', 'width', 135); + assertDropSize(page, '[data-test=select3].ms-drop', 'width', 135); + assertDropSize(page, '[data-test=select3].ms-drop', 'height', 320); + + await page.locator('[data-test=select4].ms-parent').click(); + assertDropSize(page, '[data-test=select4] .ms-choice', 'width', 200); + assertDropSize(page, '[data-test=select4].ms-drop', 'width', 294); + }); +}); diff --git a/playwright/e2e/options31.spec.ts b/playwright/e2e/options31.spec.ts new file mode 100644 index 00000000..93b94a5b --- /dev/null +++ b/playwright/e2e/options31.spec.ts @@ -0,0 +1,24 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 31 - Use Select Option as Label', () => { + test('both select show selected value numbers & 2nd select can also render value number with html', async ({ page }) => { + await page.goto('#/options31'); + await page.locator('[data-test=select1].ms-parent').click(); + await page.locator('[data-test=select1].ms-drop ul li').nth(3).click(); + expect(await page.locator('[data-test=select1].ms-parent .ms-choice span')).toHaveText('3, 4, 6'); + await page.locator('[data-test=select1].ms-parent').click(); + + expect(await page.locator('[data-test=select2].ms-parent .ms-choice span').innerHTML()).toBe('1'); + await page.locator('[data-test=select2].ms-parent').click(); + expect(await page.locator('[data-test=select2].ms-drop ul li span').nth(0).innerHTML()).toBe( + ' January' + ); + const optionLoc4 = await page.locator('[data-test=select2].ms-drop ul li').nth(3); + optionLoc4.click(); + expect(await optionLoc4.locator('label span')).toHaveText('April'); + await page.waitForTimeout(75); + expect(await page.locator('[data-test=select2].ms-parent .ms-choice span').innerHTML()).toBe( + '1, 4' + ); + }); +}); diff --git a/playwright/e2e/options32.spec.ts b/playwright/e2e/options32.spec.ts new file mode 100644 index 00000000..1b8379e7 --- /dev/null +++ b/playwright/e2e/options32.spec.ts @@ -0,0 +1,18 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Options 32 - Sanitizer', () => { + test('select shows image not found and JS alert should be sanitized and not trigger', async ({ page }) => { + let alertTriggered = false; + page.on('dialog', async (alert) => { + alertTriggered = true; + alert.message(); + await alert.dismiss(); + }); + + await page.goto('#/options32'); + await page.locator('.ms-parent', { hasText: 'Placeholder with cross-site scripting code...' }).click(); + await page.locator('span').filter({ hasText: 'February' }).click(); + await page.locator('span').filter({ hasText: 'March' }).click(); + await expect(alertTriggered).toBeFalsy(); + }); +}); diff --git a/playwright/playwright.config.ts b/playwright/playwright.config.ts index 816280cf..811f0b7a 100644 --- a/playwright/playwright.config.ts +++ b/playwright/playwright.config.ts @@ -4,7 +4,7 @@ export default defineConfig({ fullyParallel: true, // reporter: process.env.CI ? 'dot' : 'list', reporter: [['html', { outputFolder: '../playwright-report' }]], - // retries: process.env.CI ? 1 : 0, + retries: process.env.CI ? 1 : 0, testDir: './e2e', timeout: 30 * 1000, expect: { From 7adad7d36a87f0c0ba7dab80a8366557fd3540cf Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 21 Mar 2023 01:55:04 -0400 Subject: [PATCH 5/7] chore: add full Examples & Options demo E2E test suite --- playwright/e2e/example12.spec.ts | 17 +++++++++++++++++ playwright/e2e/options27.spec.ts | 2 +- playwright/e2e/options28.spec.ts | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 playwright/e2e/example12.spec.ts diff --git a/playwright/e2e/example12.spec.ts b/playwright/e2e/example12.spec.ts new file mode 100644 index 00000000..0af932ee --- /dev/null +++ b/playwright/e2e/example12.spec.ts @@ -0,0 +1,17 @@ +import { test, expect } from '@playwright/test'; + +test.describe('Example 12 - Font Awesome', () => { + test('1st select show radio icons & 2nd selecto shows checkbox icons', async ({ page }) => { + await page.goto('#/example12'); + await page.locator('.ms-parent').nth(0).click(); + // expect(await page.screenshot()).toMatchSnapshot(); + await page.getByRole('button', { name: 'January' }).click(); + expect(await page.locator('.ms-drop').nth(0)).not.toBeVisible(); + + await page.locator('.ms-parent').nth(1).click(); + await page.getByRole('listitem').filter({ hasText: 'March' }).locator('span').click(); + await page.getByRole('listitem').filter({ hasText: 'April' }).locator('span').click(); + // expect(await page.screenshot()).toMatchSnapshot(); + await page.getByRole('button', { name: 'OK' }).click(); + }); +}); diff --git a/playwright/e2e/options27.spec.ts b/playwright/e2e/options27.spec.ts index 5ef0148c..d97c561a 100644 --- a/playwright/e2e/options27.spec.ts +++ b/playwright/e2e/options27.spec.ts @@ -14,7 +14,7 @@ test.describe('Options 27 - Text Template', () => { expect(await optionLoc4.locator('label span')).toHaveText('April'); expect(await optionLoc4.locator('span').innerHTML()).toBe('April'); - await page.waitForTimeout(75); + await page.waitForTimeout(90); expect(await page.locator('.ms-choice span')).toHaveText('January, April'); expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe( 'January, April' diff --git a/playwright/e2e/options28.spec.ts b/playwright/e2e/options28.spec.ts index 80f072cf..3094a6a4 100644 --- a/playwright/e2e/options28.spec.ts +++ b/playwright/e2e/options28.spec.ts @@ -11,7 +11,7 @@ test.describe('Options 28 - Label Template', () => { const optgroup1 = await page.locator('.ms-drop ul label.optgroup').nth(0); optgroup1.click(); - await page.waitForTimeout(75); + await page.waitForTimeout(90); expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe( '[Group 1: Option 1, Option 2, Option 3]' ); From 23d12ca69e4e756620909574aaad529d2052be06 Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 21 Mar 2023 09:48:49 -0400 Subject: [PATCH 6/7] chore: increase timeout to avoid flacky E2E test failure --- playwright/e2e/options26.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playwright/e2e/options26.spec.ts b/playwright/e2e/options26.spec.ts index 83a3194c..95536d93 100644 --- a/playwright/e2e/options26.spec.ts +++ b/playwright/e2e/options26.spec.ts @@ -29,7 +29,7 @@ test.describe('Options 26 - The Styler', () => { expect(optionLoc1).toHaveText('Option 1'); await expect(optionLoc1).toHaveCSS('color', 'rgb(0, 0, 255)'); await expect(optionLoc1).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); - await page.waitForTimeout(10); + await page.waitForTimeout(25); expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText('[Group 1: Option 1]'); }); From 8fa1426a631a881566e7b4fd91025e943e7b3b2c Mon Sep 17 00:00:00 2001 From: ghiscoding Date: Tue, 21 Mar 2023 09:53:27 -0400 Subject: [PATCH 7/7] chore: improve Playwright tests to avoid flackyness failure --- playwright/e2e/options26.spec.ts | 5 ++--- playwright/e2e/options28.spec.ts | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/playwright/e2e/options26.spec.ts b/playwright/e2e/options26.spec.ts index 95536d93..00449697 100644 --- a/playwright/e2e/options26.spec.ts +++ b/playwright/e2e/options26.spec.ts @@ -29,8 +29,7 @@ test.describe('Options 26 - The Styler', () => { expect(optionLoc1).toHaveText('Option 1'); await expect(optionLoc1).toHaveCSS('color', 'rgb(0, 0, 255)'); await expect(optionLoc1).toHaveCSS('background-color', 'rgba(0, 0, 0, 0)'); - await page.waitForTimeout(25); - - expect(await page.locator('[data-test=select2] .ms-choice span')).toHaveText('[Group 1: Option 1]'); + const dropLoc2 = await page.locator('[data-test=select2] .ms-choice span', { hasText: '[Group 1: Option 1]' }); + await dropLoc2.waitFor(); }); }); diff --git a/playwright/e2e/options28.spec.ts b/playwright/e2e/options28.spec.ts index 3094a6a4..b2d6c382 100644 --- a/playwright/e2e/options28.spec.ts +++ b/playwright/e2e/options28.spec.ts @@ -11,7 +11,7 @@ test.describe('Options 28 - Label Template', () => { const optgroup1 = await page.locator('.ms-drop ul label.optgroup').nth(0); optgroup1.click(); - await page.waitForTimeout(90); + await page.waitForTimeout(100); expect(await page.locator('.ms-parent .ms-choice span').innerHTML()).toBe( '[Group 1: Option 1, Option 2, Option 3]' );