blob: 52defcb3642b2e81eef77bac08e3ae7bae25b57c [file] [log] [blame] [edit]
import { TEST_INVOKER_LOOKUP } from "./test-invoker.mjs";
export class TestRunner {
#frame;
#page;
#params;
#suite;
#test;
#callback;
#type;
constructor(frame, page, params, suite, test, callback, type) {
this.#suite = suite;
this.#test = test;
this.#params = params;
this.#callback = callback;
this.#page = page;
this.#frame = frame;
this.#type = type;
}
get page() {
return this.#page;
}
get test() {
return this.#test;
}
_runSyncStep(test, page) {
test.run(page);
}
async runTest() {
// Prepare all mark labels outside the measuring loop.
const suiteName = this.#suite.name;
const testName = this.#test.name;
const syncStartLabel = `${suiteName}.${testName}-start`;
const syncEndLabel = `${suiteName}.${testName}-sync-end`;
const asyncEndLabel = `${suiteName}.${testName}-async-end`;
let syncTime;
let asyncStartTime;
let asyncTime;
const runSync = async () => {
if (this.#params.warmupBeforeSync) {
performance.mark("warmup-start");
const startTime = performance.now();
// Infinite loop for the specified ms.
while (performance.now() - startTime < this.#params.warmupBeforeSync)
continue;
performance.mark("warmup-end");
}
performance.mark(syncStartLabel);
const syncStartTime = performance.now();
if (this.#type === "async")
await this._runSyncStep(this.test, this.page);
else
this._runSyncStep(this.test, this.page);
const mark = performance.mark(syncEndLabel);
const syncEndTime = mark.startTime;
syncTime = syncEndTime - syncStartTime;
asyncStartTime = syncEndTime;
};
const measureAsync = () => {
const bodyReference = this.#frame ? this.#frame.contentDocument.body : document.body;
const windowReference = this.#frame ? this.#frame.contentWindow : window;
// Some browsers don't immediately update the layout for paint.
// Force the layout here to ensure we're measuring the layout time.
const height = bodyReference.getBoundingClientRect().height;
windowReference._unusedHeightValue = height; // Prevent dead code elimination.
const asyncEndTime = performance.now();
performance.mark(asyncEndLabel);
asyncTime = asyncEndTime - asyncStartTime;
if (this.#params.warmupBeforeSync)
performance.measure("warmup", "warmup-start", "warmup-end");
performance.measure(`${suiteName}.${testName}-sync`, syncStartLabel, syncEndLabel);
performance.measure(`${suiteName}.${testName}-async`, syncEndLabel, asyncEndLabel);
};
const report = () => this.#callback(this.#test, syncTime, asyncTime);
const invokerType = this.#suite.type === "async" || this.#params.useAsyncSteps ? "async" : this.#params.measurementMethod;
const invokerClass = TEST_INVOKER_LOOKUP[invokerType];
const invoker = new invokerClass(runSync, measureAsync, report, this.#params);
return invoker.start();
}
}
export class AsyncTestRunner extends TestRunner {
constructor(frame, page, params, suite, test, callback, type) {
super(frame, page, params, suite, test, callback, type);
}
async _runSyncStep(test, page) {
await test.run(page);
}
}
export const TEST_RUNNER_LOOKUP = {
__proto__: null,
default: TestRunner,
async: AsyncTestRunner,
remote: TestRunner,
};