chrome-devtools-frontend
Version:
Chrome DevTools UI
121 lines (104 loc) • 4.14 kB
text/typescript
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {html, render} from '../../lit/lit.js';
import * as RenderCoordinator from '../render_coordinator/render_coordinator.js';
import * as ComponentHelpers from './helpers.js';
const TestElement = class extends HTMLElement {
renderCount = 0;
renderAsyncCount = 0;
readonly renderBound = this.#render.bind(this);
readonly renderAsyncBound = this.#renderAsync.bind(this);
#render() {
if (!ComponentHelpers.ScheduledRender.isScheduledRender(this)) {
throw new Error('Render is not scheduled');
}
this.renderCount++;
}
#renderAsync() {
if (!ComponentHelpers.ScheduledRender.isScheduledRender(this)) {
throw new Error('Render is not scheduled');
}
return new Promise<void>(resolve => {
setTimeout(() => {
this.renderAsyncCount++;
resolve();
}, 40);
});
}
};
customElements.define('x-devtools-test-element', TestElement);
describe('ComponentHelpers', () => {
describe('Directives', () => {
describe('nodeRenderedCallback', () => {
it('runs when any node is rendered', () => {
const targetDiv = document.createElement('div');
const callback = sinon.spy();
function fakeComponentRender(this: HTMLDivElement) {
render(
// clang-format off
html`
<span on-render=${ComponentHelpers.Directives.nodeRenderedCallback(callback)}>
hello world
</span>`,
// clang-format on
targetDiv, {host: this});
}
fakeComponentRender.call(targetDiv);
assert.isNotEmpty(targetDiv.innerHTML);
assert.strictEqual(callback.callCount, 1);
});
it('runs again when Lit re-renders', () => {
const targetDiv = document.createElement('div');
const callback = sinon.spy();
function fakeComponentRender(this: HTMLDivElement, output: string) {
render(
// clang-format off
html`
<span on-render=${ComponentHelpers.Directives.nodeRenderedCallback(callback)}>
${output}
</span>`,
// clang-format on
targetDiv, {host: this});
}
fakeComponentRender.call(targetDiv, 'render one');
assert.strictEqual(callback.callCount, 1);
fakeComponentRender.call(targetDiv, 'render two');
assert.strictEqual(callback.callCount, 2);
});
});
});
describe('scheduleRender', () => {
it('throws if renders are unscheduled', () => {
const element = new TestElement();
assert.throws(() => {
element.renderBound();
}, 'Render is not scheduled');
});
it('only renders once if second render call is made before the first has been handled', async () => {
const element = new TestElement();
void ComponentHelpers.ScheduledRender.scheduleRender(element, element.renderBound);
void ComponentHelpers.ScheduledRender.scheduleRender(element, element.renderBound);
await RenderCoordinator.done();
assert.strictEqual(element.renderCount, 1);
});
it('handles async callbacks', async () => {
const element = new TestElement();
void ComponentHelpers.ScheduledRender.scheduleRender(element, async () => {
void ComponentHelpers.ScheduledRender.scheduleRender(element, element.renderAsyncBound);
await element.renderAsyncBound();
});
await RenderCoordinator.done();
assert.strictEqual(element.renderAsyncCount, 2);
});
it('re-renders if second render call is made during the first', async () => {
const element = new TestElement();
void ComponentHelpers.ScheduledRender.scheduleRender(element, () => {
void ComponentHelpers.ScheduledRender.scheduleRender(element, element.renderBound);
element.renderBound();
});
await RenderCoordinator.done();
assert.strictEqual(element.renderCount, 2);
});
});
});