mirror of
https://github.com/material-components/material-web.git
synced 2024-10-26 21:56:56 +03:00
c390291687
PiperOrigin-RevId: 576601342
139 lines
3.6 KiB
TypeScript
139 lines
3.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2021 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
// import 'jasmine'; (google3-only)
|
|
|
|
import {ReactiveElement, TemplateResult, render as litRender} from 'lit';
|
|
|
|
import {installSkipWebAnimations} from './skip-animations.js';
|
|
|
|
/**
|
|
* Test environment setup for screenshot tests.
|
|
*/
|
|
export class Environment {
|
|
/**
|
|
* An array of root containers for rendering screenshot test elements.
|
|
*/
|
|
private readonly roots: HTMLElement[] = [];
|
|
|
|
constructor() {
|
|
// Replace RAF with setTimeout, since setTimeout is overridden to be
|
|
// synchronous in Jasmine clock installation.
|
|
window.requestAnimationFrame = (callback: FrameRequestCallback) => {
|
|
return setTimeout(callback, 1);
|
|
};
|
|
window.cancelAnimationFrame = (id: number) => {
|
|
clearTimeout(id);
|
|
};
|
|
|
|
beforeAll(() => {
|
|
jasmine.clock().install();
|
|
});
|
|
|
|
afterAll(() => {
|
|
jasmine.clock().uninstall();
|
|
for (const root of this.roots) {
|
|
document.body.appendChild(root);
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* This marks the environment to run without web animations. This is useful
|
|
* when the tested code calls `.animate`.
|
|
*/
|
|
withoutWebAnimations() {
|
|
installSkipWebAnimations();
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Waits for stability on the page to prevent flaky-ness tests. Use this if
|
|
* waiting for an API that uses `requestAnimationFrame()` or when waiting for
|
|
* a Lit element to render.
|
|
*/
|
|
async waitForStability() {
|
|
// Move forward any `requestAnimationFrame()`s since they are replaced with
|
|
// setTimeout(callback, 1) for jasmine clock support.
|
|
jasmine.clock().tick(1);
|
|
|
|
const currentRoot = this.getCurrentRoot();
|
|
if (currentRoot) {
|
|
await this.waitForLitRender(currentRoot);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Waits for all Lit `ReactiveElement` children of the given parent node to
|
|
* finish rendering.
|
|
*
|
|
* @param root a parent node to wait for rendering on.
|
|
*/
|
|
private async waitForLitRender(root: ParentNode) {
|
|
for (const element of root.querySelectorAll('*')) {
|
|
if (this.isReactiveElement(element)) {
|
|
await element.updateComplete;
|
|
await this.waitForLitRender(element.renderRoot);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tests if an element is a Lit `ReactiveElement`.
|
|
*
|
|
* @param element the element to test.
|
|
* @return true if the element is a `ReactiveElement`.
|
|
*/
|
|
private isReactiveElement(element: Element): element is ReactiveElement {
|
|
return Boolean((element as ReactiveElement).updateComplete);
|
|
}
|
|
|
|
/**
|
|
* Render a Lit template in the environment's root container.
|
|
*
|
|
* @param template a Lit `TemplateResult` to render.
|
|
* @return The root container the template was rendered to.
|
|
*/
|
|
render(template: TemplateResult) {
|
|
const root = this.createNewRoot();
|
|
litRender(template, root);
|
|
return root;
|
|
}
|
|
|
|
/**
|
|
* Creates a new root container for screenshot rendering and adds it to the
|
|
* body.
|
|
*
|
|
* Previous root containers will be hidden and displayed at the end of
|
|
* testing for easier debugging.
|
|
*
|
|
* @return A new root container.
|
|
*/
|
|
private createNewRoot() {
|
|
const currentRoot = this.getCurrentRoot();
|
|
if (currentRoot) {
|
|
currentRoot.remove();
|
|
}
|
|
|
|
const root = document.createElement('div');
|
|
root.id = 'root';
|
|
root.style.display = 'inline-flex';
|
|
document.body.appendChild(root);
|
|
this.roots.push(root);
|
|
return root;
|
|
}
|
|
|
|
/**
|
|
* Get the current root container.
|
|
*
|
|
* @return The current root container or undefined is nothing as been rendered
|
|
* yet.
|
|
*/
|
|
protected getCurrentRoot(): HTMLElement | undefined {
|
|
return this.roots[this.roots.length - 1];
|
|
}
|
|
}
|