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${slowElem}>`);
+ }
+ 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}>${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', () => {