b9491daad0
This is a minimal version of qtloader. The load function accepts the same arguments as emscripten runtime with a few additions: - qt.environment - qt.onExit - qt.containerElements - qt.fontDpi - qt.onLoaded - qt.entryFunction State handling has been removed in favor of making the load async (assume loading when the promise is live). Public APIs getting crashed status, exit text and code have been refactored into the new qt.onExit event fed to load. No need for keeping the state in the loader. The loader is integration-tested. A test module with test APIs has been created as a test harness. The runtime APIs exposed by Qt (font dpi and screen API) are handled by the qtloader seamlessly. Change-Id: Iaee65702667da0349a475feae6b83244d966d98d Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
146 lines
3.9 KiB
JavaScript
146 lines
3.9 KiB
JavaScript
// Copyright (C) 2022 The Qt Company Ltd.
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
|
|
|
export class assert
|
|
{
|
|
static isFalse(value)
|
|
{
|
|
if (value !== false)
|
|
throw new Error(`Assertion failed, expected to be false, was ${value}`);
|
|
}
|
|
|
|
static isTrue(value)
|
|
{
|
|
if (value !== true)
|
|
throw new Error(`Assertion failed, expected to be true, was ${value}`);
|
|
}
|
|
|
|
static isUndefined(value)
|
|
{
|
|
if (typeof value !== 'undefined')
|
|
throw new Error(`Assertion failed, expected to be undefined, was ${value}`);
|
|
}
|
|
|
|
static isNotUndefined(value)
|
|
{
|
|
if (typeof value === 'undefined')
|
|
throw new Error(`Assertion failed, expected not to be undefined, was ${value}`);
|
|
}
|
|
|
|
static equal(expected, actual)
|
|
{
|
|
if (expected !== actual)
|
|
throw new Error(`Assertion failed, expected to be ${expected}, was ${actual}`);
|
|
}
|
|
|
|
static notEqual(expected, actual)
|
|
{
|
|
if (expected === actual)
|
|
throw new Error(`Assertion failed, expected not to be ${expected}`);
|
|
}
|
|
}
|
|
|
|
export class Mock extends Function
|
|
{
|
|
#calls = [];
|
|
|
|
constructor()
|
|
{
|
|
super()
|
|
const proxy = new Proxy(this, {
|
|
apply: (target, _, args) => target.onCall(...args)
|
|
});
|
|
proxy.thisMock = this;
|
|
|
|
return proxy;
|
|
}
|
|
|
|
get calls()
|
|
{
|
|
return this.thisMock.#calls;
|
|
}
|
|
|
|
onCall(...args)
|
|
{
|
|
this.#calls.push(args);
|
|
}
|
|
}
|
|
|
|
function output(message)
|
|
{
|
|
const outputLine = document.createElement('div');
|
|
outputLine.style.fontFamily = 'monospace';
|
|
outputLine.innerText = message;
|
|
|
|
document.body.appendChild(outputLine);
|
|
|
|
console.log(message);
|
|
}
|
|
|
|
export class TestRunner
|
|
{
|
|
#testClassInstance
|
|
#timeoutSeconds
|
|
|
|
constructor(testClassInstance, config)
|
|
{
|
|
this.#testClassInstance = testClassInstance;
|
|
this.#timeoutSeconds = config?.timeoutSeconds ?? 2;
|
|
}
|
|
|
|
async run(testCase)
|
|
{
|
|
const prototype = Object.getPrototypeOf(this.#testClassInstance);
|
|
try {
|
|
output(`Running ${testCase}`);
|
|
if (!prototype.hasOwnProperty(testCase))
|
|
throw new Error(`No such testcase ${testCase}`);
|
|
|
|
if (prototype.beforeEach) {
|
|
await prototype.beforeEach.apply(this.#testClassInstance);
|
|
}
|
|
|
|
await new Promise((resolve, reject) =>
|
|
{
|
|
let rejected = false;
|
|
const timeout = window.setTimeout(() =>
|
|
{
|
|
rejected = true;
|
|
reject(new Error(`Timeout after ${this.#timeoutSeconds} seconds`));
|
|
}, this.#timeoutSeconds * 1000);
|
|
prototype[testCase].apply(this.#testClassInstance).then(() =>
|
|
{
|
|
if (!rejected) {
|
|
window.clearTimeout(timeout);
|
|
output(`✅ Test passed ${testCase}`);
|
|
resolve();
|
|
}
|
|
}).catch(reject);
|
|
});
|
|
} catch (e) {
|
|
output(`❌ Failed ${testCase}: exception ${e} ${e.stack}`);
|
|
} finally {
|
|
if (prototype.afterEach) {
|
|
await prototype.afterEach.apply(this.#testClassInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
async runAll()
|
|
{
|
|
const SPECIAL_FUNCTIONS =
|
|
['beforeEach', 'afterEach', 'beforeAll', 'afterAll', 'constructor'];
|
|
const prototype = Object.getPrototypeOf(this.#testClassInstance);
|
|
const testFunctions =
|
|
Object.getOwnPropertyNames(prototype).filter(
|
|
entry => SPECIAL_FUNCTIONS.indexOf(entry) === -1);
|
|
|
|
if (prototype.beforeAll)
|
|
await prototype.beforeAll.apply(this.#testClassInstance);
|
|
for (const fn of testFunctions)
|
|
await this.run(fn);
|
|
if (prototype.afterAll)
|
|
await prototype.afterAll.apply(this.#testClassInstance);
|
|
}
|
|
}
|