From 7f1642076f8d02f814ccd150d80c3d0b0c8934cd Mon Sep 17 00:00:00 2001 From: Dave Lockhart Date: Thu, 21 May 2026 16:40:49 -0400 Subject: [PATCH 1/3] GAUD-10062: expose waitForElem --- README.md | 18 ++++++++++++++++-- src/browser/fixture.js | 2 +- src/browser/index.js | 2 +- test/browser/fixture.test.js | 31 ++++++++++++++++++++++++++++++- 4 files changed, 48 insertions(+), 5 deletions(-) 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..2fa0ed09 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -36,7 +36,7 @@ function getComposedChildren(node) { * @param {*} elem * @param {boolean} awaitLoadingComplete */ -async function waitForElem(elem, awaitLoadingComplete = true) { +export async function waitForElem(elem, awaitLoadingComplete = true) { if (!elem) return; 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..17ede69c 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,17 @@ 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)); + await waitPromise; + expect(elem.shadowRoot.querySelector(slowElem).finished).to.be.true; + }); + }); describe('waitForFonts', () => { From f61e7926317a382cbe6ac95db31fe7b59d7d9584 Mon Sep 17 00:00:00 2001 From: Dave Lockhart Date: Thu, 21 May 2026 16:44:13 -0400 Subject: [PATCH 2/3] flip order --- src/browser/fixture.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/browser/fixture.js b/src/browser/fixture.js index 2fa0ed09..503ac469 100644 --- a/src/browser/fixture.js +++ b/src/browser/fixture.js @@ -42,14 +42,14 @@ export async function waitForElem(elem, awaitLoadingComplete = true) { 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(); } From dc16a70e96bcc155bfb8527852f4d896a28b547b Mon Sep 17 00:00:00 2001 From: Dave Lockhart Date: Fri, 22 May 2026 09:39:33 -0400 Subject: [PATCH 3/3] add extra assertion --- test/browser/fixture.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/browser/fixture.test.js b/test/browser/fixture.test.js index 17ede69c..83d46aca 100644 --- a/test/browser/fixture.test.js +++ b/test/browser/fixture.test.js @@ -357,6 +357,7 @@ describe('fixture', () => { 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; });