From 28c9dbab1fd4b6b1d9ebe93f25f42b80d1db8a55 Mon Sep 17 00:00:00 2001 From: Cory LaViska Date: Thu, 3 Jun 2021 08:42:51 -0400 Subject: [PATCH] add initial tests --- docs/resources/changelog.md | 3 + src/components/alert/alert.test.ts | 8 +- src/components/details/details.test.ts | 4 +- src/components/dialog/dialog.test.ts | 132 ++++++++++++++++++++ src/components/drawer/drawer.test.ts | 132 ++++++++++++++++++++ src/components/dropdown/dropdown.test.ts | 147 +++++++++++++++++++++++ src/components/tooltip/tooltip.test.ts | 117 ++++++++++++++++++ 7 files changed, 537 insertions(+), 6 deletions(-) create mode 100644 src/components/dialog/dialog.test.ts create mode 100644 src/components/drawer/drawer.test.ts create mode 100644 src/components/dropdown/dropdown.test.ts create mode 100644 src/components/tooltip/tooltip.test.ts diff --git a/docs/resources/changelog.md b/docs/resources/changelog.md index 812ebe89..202971f4 100644 --- a/docs/resources/changelog.md +++ b/docs/resources/changelog.md @@ -10,6 +10,7 @@ _During the beta period, these restrictions may be relaxed in the event of a mis - Added `?` to optional arguments in methods tables - Added the `scrollPosition()` method to `sl-textarea` to get/set scroll position +- Added intial tests for `sl-dialog`, `sl-drawer`, `sl-dropdown`, and `sl-tooltip` - Fixed a bug in `sl-tab-group` where scrollable tab icons were not displaying correctly - Fixed a bug in `sl-dialog` and `sl-drawer` where preventing clicks on the overlay no longer worked as described [#452](https://github.com/shoelace-style/shoelace/issues/452) - Fixed a bug in `sl-dialog` and `sl-drawer` where setting initial focus no longer worked as described [#453](https://github.com/shoelace-style/shoelace/issues/453) @@ -27,6 +28,8 @@ Technical reasons aside, canceling these events seldom led to a good user experi - 🚨 BREAKING: `sl-show` and `sl-hide` events are no longer cancelable - Added Iconoir example to the icon docs +- Added Web Test Runner +- Added intial tests for `sl-alert` and `sl-details` - Changed the `cancelable` default to `false` for the internal `@event` decorator - Fixed a bug where toggling `open` stopped working in `sl-alert`, `sl-dialog`, `sl-drawer`, `sl-dropdown`, and `sl-tooltip` - Fixed a bug in `sl-range` where setting a value outside the default `min` or `max` would clamp the value [#448](https://github.com/shoelace-style/shoelace/issues/448) diff --git a/src/components/alert/alert.test.ts b/src/components/alert/alert.test.ts index a6e87f1b..409ddc2f 100644 --- a/src/components/alert/alert.test.ts +++ b/src/components/alert/alert.test.ts @@ -13,10 +13,10 @@ describe('', () => { }); it('should not be visible without the open attribute', async () => { - const el = await fixture(html` I am an alert `); + const el = await fixture(html` I am an alert `); const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; - expect(base.hidden).to.be.false; + expect(base.hidden).to.be.true; }); it('should emit sl-show and sl-after-show when calling show()', async () => { @@ -55,7 +55,7 @@ describe('', () => { expect(base.hidden).to.be.true; }); - it('should emit sl-show and sl-after-show when adding the open attribute', async () => { + it('should emit sl-show and sl-after-show when setting open = true', async () => { const el = (await fixture(html` I am an alert `)) as SlAlert; const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; const showHandler = sinon.spy(); @@ -73,7 +73,7 @@ describe('', () => { expect(base.hidden).to.be.false; }); - it('should emit sl-hide and sl-after-hide when removing the open attribute', async () => { + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { const el = (await fixture(html` I am an alert `)) as SlAlert; const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; const hideHandler = sinon.spy(); diff --git a/src/components/details/details.test.ts b/src/components/details/details.test.ts index 7f277329..125cc3f6 100644 --- a/src/components/details/details.test.ts +++ b/src/components/details/details.test.ts @@ -79,7 +79,7 @@ describe('', () => { expect(body.hidden).to.be.true; }); - it('should emit sl-show and sl-after-show when adding the open attribute', async () => { + it('should emit sl-show and sl-after-show when setting open = true', async () => { const el = (await fixture(html` Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore @@ -103,7 +103,7 @@ describe('', () => { expect(body.hidden).to.be.false; }); - it('should emit sl-hide and sl-after-hide when removing the open attribute', async () => { + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { const el = (await fixture(html` Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore diff --git a/src/components/dialog/dialog.test.ts b/src/components/dialog/dialog.test.ts new file mode 100644 index 00000000..b1a2906d --- /dev/null +++ b/src/components/dialog/dialog.test.ts @@ -0,0 +1,132 @@ +import { expect, fixture, html, waitUntil } from '@open-wc/testing'; +import sinon from 'sinon'; + +import '../../../dist/shoelace.js'; +import type SlDialog from './dialog'; + +describe('', () => { + it('should be visible with the open attribute', async () => { + const el = await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.false; + }); + + it('should not be visible without the open attribute', async () => { + const el = await fixture(html` Lorem ipsum dolor sit amet, consectetur adipiscing elit. `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when calling show()', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDialog; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.show(); + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when calling hide()', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDialog; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.hide(); + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when setting open = true', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDialog; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.open = true; + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDialog; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.open = false; + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); + + it('should not close when sl-overlay-dismiss is prevented', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDialog; + const overlay = el.shadowRoot?.querySelector('[part="overlay"]') as HTMLElement; + + el.addEventListener('sl-overlay-dismiss', event => event.preventDefault()); + overlay.click(); + + expect(el.open).to.be.true; + }); + + it('should allow initial focus to be set', async () => { + const el = (await fixture(html` `)) as SlDialog; + const input = el.querySelector('input'); + const initialFocusHandler = sinon.spy(event => { + event.preventDefault(); + input.focus(); + }); + + el.addEventListener('sl-initial-focus', initialFocusHandler); + el.show(); + + await waitUntil(() => initialFocusHandler.calledOnce); + + expect(initialFocusHandler).to.have.been.calledOnce; + expect(document.activeElement).to.equal(input); + }); +}); diff --git a/src/components/drawer/drawer.test.ts b/src/components/drawer/drawer.test.ts new file mode 100644 index 00000000..d9b30d31 --- /dev/null +++ b/src/components/drawer/drawer.test.ts @@ -0,0 +1,132 @@ +import { expect, fixture, html, waitUntil } from '@open-wc/testing'; +import sinon from 'sinon'; + +import '../../../dist/shoelace.js'; +import type SlDrawer from './drawer'; + +describe('', () => { + it('should be visible with the open attribute', async () => { + const el = await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.false; + }); + + it('should not be visible without the open attribute', async () => { + const el = await fixture(html` Lorem ipsum dolor sit amet, consectetur adipiscing elit. `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when calling show()', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDrawer; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.show(); + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when calling hide()', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDrawer; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.hide(); + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when setting open = true', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDrawer; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.open = true; + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDrawer; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.open = false; + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); + + it('should not close when sl-overlay-dismiss is prevented', async () => { + const el = (await fixture(html` + Lorem ipsum dolor sit amet, consectetur adipiscing elit. + `)) as SlDrawer; + const overlay = el.shadowRoot?.querySelector('[part="overlay"]') as HTMLElement; + + el.addEventListener('sl-overlay-dismiss', event => event.preventDefault()); + overlay.click(); + + expect(el.open).to.be.true; + }); + + it('should allow initial focus to be set', async () => { + const el = (await fixture(html` `)) as SlDrawer; + const input = el.querySelector('input'); + const initialFocusHandler = sinon.spy(event => { + event.preventDefault(); + input.focus(); + }); + + el.addEventListener('sl-initial-focus', initialFocusHandler); + el.show(); + + await waitUntil(() => initialFocusHandler.calledOnce); + + expect(initialFocusHandler).to.have.been.calledOnce; + expect(document.activeElement).to.equal(input); + }); +}); diff --git a/src/components/dropdown/dropdown.test.ts b/src/components/dropdown/dropdown.test.ts new file mode 100644 index 00000000..032cabae --- /dev/null +++ b/src/components/dropdown/dropdown.test.ts @@ -0,0 +1,147 @@ +import { expect, fixture, html, waitUntil } from '@open-wc/testing'; +import sinon from 'sinon'; + +import '../../../dist/shoelace.js'; +import type SlDropdown from './dropdown'; + +describe('', () => { + it('should be visible with the open attribute', async () => { + const el = await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `); + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + + expect(panel.hidden).to.be.false; + }); + + it('should not be visible without the open attribute', async () => { + const el = await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `); + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + + expect(panel.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when calling show()', async () => { + const el = (await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `)) as SlDropdown; + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.show(); + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(panel.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when calling hide()', async () => { + const el = (await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `)) as SlDropdown; + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.hide(); + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(panel.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when setting open = true', async () => { + const el = (await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `)) as SlDropdown; + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.open = true; + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(panel.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { + const el = (await fixture(html` + + Toggle + + Item 1 + Item 2 + Item 3 + + + `)) as SlDropdown; + const panel = el.shadowRoot?.querySelector('[part="panel"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.open = false; + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(panel.hidden).to.be.true; + }); +}); diff --git a/src/components/tooltip/tooltip.test.ts b/src/components/tooltip/tooltip.test.ts new file mode 100644 index 00000000..d9d9dfcb --- /dev/null +++ b/src/components/tooltip/tooltip.test.ts @@ -0,0 +1,117 @@ +import { expect, fixture, html, waitUntil } from '@open-wc/testing'; +import sinon from 'sinon'; + +import '../../../dist/shoelace.js'; +import type SlTooltip from './tooltip'; + +describe('', () => { + it('should be visible with the open attribute', async () => { + const el = await fixture(html` + + Hover Me + + `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.false; + }); + + it('should not be visible without the open attribute', async () => { + const el = await fixture(html` + + Hover Me + + `); + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when calling show()', async () => { + const el = (await fixture(html` + + Hover Me + + `)) as SlTooltip; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.show(); + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when calling hide()', async () => { + const el = (await fixture(html` + + Hover Me + + `)) as SlTooltip; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.hide(); + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); + + it('should emit sl-show and sl-after-show when setting open = true', async () => { + const el = (await fixture(html` + + Hover Me + + `)) as SlTooltip; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const showHandler = sinon.spy(); + const afterShowHandler = sinon.spy(); + + el.addEventListener('sl-show', showHandler); + el.addEventListener('sl-after-show', afterShowHandler); + el.open = true; + + await waitUntil(() => showHandler.calledOnce); + await waitUntil(() => afterShowHandler.calledOnce); + + expect(showHandler).to.have.been.calledOnce; + expect(afterShowHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.false; + }); + + it('should emit sl-hide and sl-after-hide when setting open = false', async () => { + const el = (await fixture(html` + + Hover Me + + `)) as SlTooltip; + const base = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + const hideHandler = sinon.spy(); + const afterHideHandler = sinon.spy(); + + el.addEventListener('sl-hide', hideHandler); + el.addEventListener('sl-after-hide', afterHideHandler); + el.open = false; + + await waitUntil(() => hideHandler.calledOnce); + await waitUntil(() => afterHideHandler.calledOnce); + + expect(hideHandler).to.have.been.calledOnce; + expect(afterHideHandler).to.have.been.calledOnce; + expect(base.hidden).to.be.true; + }); +});