diff --git a/README.md b/README.md index 39c8d888..74752e1a 100644 --- a/README.md +++ b/README.md @@ -139,9 +139,9 @@ it('should work when printing', async() => { }); ``` -### Accessibility Testing with aXe +### Accessibility Testing with axe -Elements can be processed by the [aXe accessibility validator](https://github.com/dequelabs/axe-core), which will automatically fail the test if any violations are detected. +Elements can be processed by the [axe accessibility validator](https://github.com/dequelabs/axe-core), which will automatically fail the test if any violations are detected. ```javascript it('should be accessible', async() => { @@ -302,6 +302,20 @@ it('should wait for updates', async() => { }); ``` +If that change causes a different set of nested components to be rendered, and those nested components take some time to finish *their* rendering, it's possible that awaiting `updateComplete` isn't enough. + +In this case, `waitForElem` can be used to recursively wait in a similar way to the original `fixture` call: + +```javascript +import { fixture, waitForElem } from '@brightspace-ui/testing'; + +it('should wait for recursive re-rendering', async() => { + const elem = await fixture(html``); + elem.errorState = true; + await waitForElem(elem); +}); +``` + #### Waiting for `setTimeout` or `requestAnimationFrame` To wait a fixed amount of time (analogous to `setTimeout`), use `aTimeout`. To wait until the moment before browser repaints the screen (analogous to `requestAnimationFrame`), use `nextFrame`. diff --git a/src/browser/fixture.js b/src/browser/fixture.js index a86c9138..503ac469 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -36,20 +36,20 @@ function getComposedChildren(node) { * @param {*} elem * @param {boolean} awaitLoadingComplete */ -async function waitForElem(elem, awaitLoadingComplete = true) { +export async function waitForElem(elem, awaitLoadingComplete = true) { if (!elem) return; const doWait = async() => { - const update = elem.updateComplete; - if (typeof update === 'object' && Promise.resolve(update) === update) { - await update; + if (awaitLoadingComplete && typeof elem.getLoadingComplete === 'function') { + await elem.getLoadingComplete(); await nextFrame(); } - if (awaitLoadingComplete && typeof elem.getLoadingComplete === 'function') { - await elem.getLoadingComplete(); + const update = elem.updateComplete; + if (typeof update === 'object' && Promise.resolve(update) === update) { + await update; await nextFrame(); } diff --git a/src/browser/index.js b/src/browser/index.js index 54c4d6f8..cc27b0ac 100644 --- a/src/browser/index.js +++ b/src/browser/index.js @@ -3,5 +3,5 @@ import './axe.js'; export { assert, aTimeout, defineCE, expect, html, nextFrame, oneDefaultPreventedEvent, oneEvent, waitUntil } from '@open-wc/testing'; export { clickAt, clickElem, clickElemAt, dragDropElems, focusElem, hoverAt, hoverElem, hoverElemAt, sendKeys, sendKeysElem, setViewport } from './commands.js'; -export { fixture } from './fixture.js'; +export { fixture, waitForElem } from './fixture.js'; export { runConstructor } from './constructor.js'; diff --git a/test/browser/fixture.test.js b/test/browser/fixture.test.js index 02a50638..83d46aca 100644 --- a/test/browser/fixture.test.js +++ b/test/browser/fixture.test.js @@ -1,5 +1,5 @@ -import { defineCE, expect, fixture, html, waitUntil } from '../../src/browser/index.js'; +import { defineCE, expect, fixture, html, waitForElem, waitUntil } from '../../src/browser/index.js'; import { LitElement, nothing } from 'lit'; import { restore, stub } from 'sinon'; import { focusElem } from '../../src/browser/commands.js'; @@ -64,6 +64,24 @@ const removedElem = defineCE( } ); +const fastOrSlowElem = defineCE( + class extends LitElement { + static properties = { + slow: { type: Boolean } + }; + constructor() { + super(); + this.slow = false; + } + render() { + if (this.slow) { + return unsafeHTML(`<${slowElem} id="slow">slow`); + } + return html`

fast

`; + } + } +); + describe('fixture', () => { afterEach(() => restore()); @@ -332,6 +350,18 @@ describe('fixture', () => { expect(elem.querySelector(removedElem)).to.be.null; }); + it('should wait for elements that are updated to be slow', async() => { + const elem = await fixture(`<${fastOrSlowElem}>`); + expect(elem.shadowRoot.querySelector('p').textContent).to.equal('fast'); + elem.slow = true; + const waitPromise = waitForElem(elem); + await waitUntil(() => resolves.has('slow')); + timeouts.push(setTimeout(() => resolves.get('slow')(), 50)); + expect(elem.shadowRoot.querySelector(slowElem).finished).to.be.false; + await waitPromise; + expect(elem.shadowRoot.querySelector(slowElem).finished).to.be.true; + }); + }); describe('waitForFonts', () => {