From bc3e9c43da280cfdcf81509b1abfe0f4f661f0ae Mon Sep 17 00:00:00 2001 From: Christos Hrousis Date: Tue, 28 Sep 2021 22:27:19 +1000 Subject: [PATCH] test: components/breadcrumb-item (#541) * test: migrate skipped tests to the breadcrumb-item * test: when rendering a HTMLAnchorElement * test: add checks for classes. * test: uplift langauge and query selector to test label part * test: default button test * refactor: set default for rel on prop declaration. --- .../breadcrumb-item/breadcrumb-item.test.ts | 157 +++++++++++++++++- .../breadcrumb-item/breadcrumb-item.ts | 7 +- src/components/breadcrumb/breadcrumb.test.ts | 16 -- 3 files changed, 158 insertions(+), 22 deletions(-) diff --git a/src/components/breadcrumb-item/breadcrumb-item.test.ts b/src/components/breadcrumb-item/breadcrumb-item.test.ts index a8f38cb4da..fd4abb1007 100644 --- a/src/components/breadcrumb-item/breadcrumb-item.test.ts +++ b/src/components/breadcrumb-item/breadcrumb-item.test.ts @@ -1,13 +1,160 @@ -import { expect, fixture, html, waitUntil } from '@open-wc/testing'; -// import sinon from 'sinon'; +import { expect, fixture, html } from '@open-wc/testing'; import '../../../dist/shoelace.js'; import type SlBreadcrumbItem from './breadcrumb-item'; describe('', () => { - it('should render a component', async () => { - const el = await fixture(html` `); + let el: SlBreadcrumbItem; - expect(el).to.exist; + describe('when not provided a href attribute', async () => { + before(async () => { + el = await fixture(html` Home `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + it('should hide the seperator from screen readers', async () => { + const separator: HTMLSpanElement = el.shadowRoot.querySelector('[part="separator"]'); + expect(separator).attribute('aria-hidden', 'true'); + }); + + it('should render a HTMLButtonElement as the part "label", with a set type "button"', () => { + const button: HTMLButtonElement = el.shadowRoot.querySelector('[part="label"]'); + expect(button).to.exist; + expect(button).attribute('type', 'button'); + }); + }); + + describe('when provided a href attribute', async () => { + describe('and no target', () => { + before(async () => { + el = await fixture(html` + Home + `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + it('should render a HTMLAnchorElement as the part "label", with the supplied href value', () => { + const hyperlink: HTMLAnchorElement = el.shadowRoot.querySelector('[part="label"]'); + expect(hyperlink).attribute('href', 'https://jsonplaceholder.typicode.com/'); + }); + }); + + describe('and target, without rel', () => { + before(async () => { + el = await fixture(html` + Help + `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + describe('should render a HTMLAnchorElement as the part "label"', () => { + let hyperlink: HTMLAnchorElement; + + before(() => { + hyperlink = el.shadowRoot.querySelector('[part="label"]'); + }); + + it('should use the supplied href value, as the href attribute value', () => { + expect(hyperlink).attribute('href', 'https://jsonplaceholder.typicode.com/'); + }); + + it('should default rel attribute to "noreferrer noopener"', () => { + expect(hyperlink).attribute('rel', 'noreferrer noopener'); + }); + }); + }); + + describe('and target, with rel', () => { + before(async () => { + el = await fixture(html` + Help + `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + describe('should render a HTMLAnchorElement', () => { + let hyperlink: HTMLAnchorElement; + + before(() => { + hyperlink = el.shadowRoot.querySelector('a'); + }); + + it('should use the supplied href value, as the href attribute value', () => { + expect(hyperlink).attribute('href', 'https://jsonplaceholder.typicode.com/'); + }); + + it('should use the supplied rel value, as the rel attribute value', () => { + expect(hyperlink).attribute('rel', 'alternate'); + }); + }); + }); + }); + + describe('when provided an element in the slot "prefix" to support prefix icons', async () => { + before(async () => { + el = await fixture(html` + + / + Home + + `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + it('should accept as an assigned child in the shadow root', () => { + const slot = el.shadowRoot.querySelector('slot[name=prefix]'); + const childNodes = slot.assignedNodes({ flatten: true }); + + expect(childNodes.length).to.eq(1); + }); + + it('should append class "breadcrumb-item--has-prefix" to "base" part', () => { + const part = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + expect(part.classList.value).to.equal('breadcrumb-item breadcrumb-item--has-prefix'); + }); + }); + + describe('when provided an element in the slot "suffix" to support suffix icons', async () => { + before(async () => { + el = await fixture(html` + + / + Security + + `); + }); + + it('should render a component that passes accessibility test', async () => { + await expect(el).to.be.accessible(); + }); + + it('should accept as an assigned child in the shadow root', () => { + const slot = el.shadowRoot.querySelector('slot[name=suffix]'); + const childNodes = slot.assignedNodes({ flatten: true }); + + expect(childNodes.length).to.eq(1); + }); + + it('should append class "breadcrumb-item--has-suffix" to "base" part', () => { + const part = el.shadowRoot?.querySelector('[part="base"]') as HTMLElement; + expect(part.classList.value).to.equal('breadcrumb-item breadcrumb-item--has-suffix'); + }); }); }); diff --git a/src/components/breadcrumb-item/breadcrumb-item.ts b/src/components/breadcrumb-item/breadcrumb-item.ts index 3872db636a..e1c3cfdc4b 100644 --- a/src/components/breadcrumb-item/breadcrumb-item.ts +++ b/src/components/breadcrumb-item/breadcrumb-item.ts @@ -34,6 +34,11 @@ export default class SlBreadcrumbItem extends LitElement { /** Tells the browser where to open the link. Only used when `href` is set. */ @property() target: '_blank' | '_parent' | '_self' | '_top'; + /** Optionally allows the user to determine how the link should talk to the browser. + * ref: https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types + */ + @property() rel: string = 'noreferrer noopener'; + handleSlotChange() { this.hasPrefix = hasSlot(this, 'prefix'); this.hasSuffix = hasSlot(this, 'suffix'); @@ -62,7 +67,7 @@ export default class SlBreadcrumbItem extends LitElement { class="breadcrumb-item__label breadcrumb-item__label--link" href="${this.href}" target="${this.target}" - rel=${ifDefined(this.target ? 'noreferrer noopener' : undefined)} + rel=${ifDefined(this.target ? this.rel : undefined)} > diff --git a/src/components/breadcrumb/breadcrumb.test.ts b/src/components/breadcrumb/breadcrumb.test.ts index fdd5641e2b..6a42759508 100644 --- a/src/components/breadcrumb/breadcrumb.test.ts +++ b/src/components/breadcrumb/breadcrumb.test.ts @@ -80,14 +80,6 @@ describe('', () => { it('should render a component that passes accessibility test', async () => { await expect(el).to.be.accessible(); }); - - it.skip('should accept "prefix" as an assigned child in the shadow root', () => { - // TODO: I suspect this test doesn't work because the slot resides inside the breadcrumb-item not the breadcrumb list - const slot = el.shadowRoot.querySelector('slot[name=prefix]'); - const childNodes = slot.assignedNodes({ flatten: true }); - - expect(childNodes.length).to.eq(1); - }); }); describe('when provided a standard list of el-breadcrumb-item children and an element in the slot "suffix" to support suffix icons', async () => { @@ -108,13 +100,5 @@ describe('', () => { it('should render a component that passes accessibility test', async () => { await expect(el).to.be.accessible(); }); - - it.skip('should accept "suffix" as an assigned child in the shadow root', () => { - // TODO: I suspect this test doesn't work because the slot resides inside the breadcrumb-item not the breadcrumb list - const slot = el.shadowRoot.querySelector('slot[name=suffix]'); - const childNodes = slot.assignedNodes({ flatten: true }); - - expect(childNodes.length).to.eq(1); - }); }); });