mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-12-16 16:28:58 +00:00
Migrate some tests to the new htmlRunner
Fix events.get_timeStamp (was events.get_timestamp, wrong casing). Rename `newRunner` to `htmlRunner`. move tests to src/tests (from src/browser/tests). src/runtime and possibly other parts might want to have html tests too.
This commit is contained in:
5
src/tests/browser.html
Normal file
5
src/tests/browser.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<script src="testing.js"></script>
|
||||
<script id=intl>
|
||||
// this will crash if ICU isn't properly configured / ininitialized
|
||||
testing.expectEqual("[object Intl.DateTimeFormat]", new Intl.DateTimeFormat().toString());
|
||||
</script>
|
||||
25
src/tests/crypto.html
Normal file
25
src/tests/crypto.html
Normal file
@@ -0,0 +1,25 @@
|
||||
<script src="testing.js"></script>
|
||||
<script id=crypto>
|
||||
const a = crypto.randomUUID();
|
||||
const b = crypto.randomUUID();
|
||||
testing.expectEqual(36, a.length);
|
||||
testing.expectEqual(36, b.length);
|
||||
testing.expectEqual(false, a == b)
|
||||
|
||||
testing.expectError('Error: QuotaExceededError', () => {
|
||||
crypto.getRandomValues(new BigUint64Array(8193));
|
||||
});
|
||||
|
||||
let r1 = new Int32Array(5)
|
||||
let r2 = crypto.getRandomValues(r1)
|
||||
testing.expectEqual(5, new Set(r1).size);
|
||||
testing.expectEqual(5, new Set(r2).size);
|
||||
testing.expectEqual(true, r1.every((v, i) => v === r2[i]));
|
||||
|
||||
var r3 = new Uint8Array(16);
|
||||
let r4 = crypto.getRandomValues(r3);
|
||||
|
||||
r4[6] = 10;
|
||||
testing.expectEqual(10, r4[6]);
|
||||
testing.expectEqual(10, r3[6]);
|
||||
</script>
|
||||
5
src/tests/css.html
Normal file
5
src/tests/css.html
Normal file
@@ -0,0 +1,5 @@
|
||||
<script src="testing.js"></script>
|
||||
<script id=support>
|
||||
testing.expectEqual(true, CSS.supports('display: flex'));
|
||||
testing.expectEqual(true, CSS.supports('text-decoration-style', 'blink'));
|
||||
</script>
|
||||
17
src/tests/encoding/decoder.html
Normal file
17
src/tests/encoding/decoder.html
Normal file
@@ -0,0 +1,17 @@
|
||||
<script src="../testing.js"></script>
|
||||
<script id=decoder>
|
||||
let d1 = new TextDecoder();
|
||||
testing.expectEqual('utf-8', d1.encoding);
|
||||
testing.expectEqual(false, d1.fatal);
|
||||
testing.expectEqual(false, d1.ignoreBOM);
|
||||
|
||||
testing.expectEqual('𠮷', d1.decode(new Uint8Array([240, 160, 174, 183])));
|
||||
testing.expectEqual('𠮷', d1.decode(new Uint8Array([0xEF, 0xBB, 0xBF, 240, 160, 174, 183])));
|
||||
testing.expectEqual('<27>2', d1.decode(new Uint8Array([249, 50])));
|
||||
|
||||
let d2 = new TextDecoder('utf8', {fatal: true})
|
||||
testing.expectError('Error: InvalidUtf8', () => {
|
||||
let data = new Uint8Array([240, 240, 160, 174, 183]);
|
||||
d2.decode(data);
|
||||
});
|
||||
</script>
|
||||
13
src/tests/encoding/encoder.html
Normal file
13
src/tests/encoding/encoder.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<script src="../testing.js"></script>
|
||||
<script id=encoder>
|
||||
var encoder = new TextEncoder();
|
||||
testing.expectEqual('utf-8', encoder.encoding);
|
||||
testing.expectEqual([226, 130, 172], Array.from(encoder.encode('€')));
|
||||
|
||||
// Invalid utf-8 sequence.
|
||||
// Browsers give a different result for this, they decode it to:
|
||||
// 50, 50, 54, 44, 52, 48, 44, 49, 54, 49
|
||||
testing.expectError('Error: InvalidUtf8', () => {
|
||||
encoder.encode(new Uint8Array([0xE2,0x28,0xA1]));
|
||||
});
|
||||
</script>
|
||||
16
src/tests/events/custom.html
Normal file
16
src/tests/events/custom.html
Normal file
@@ -0,0 +1,16 @@
|
||||
<script src="../testing.js"></script>
|
||||
<script id=custom>
|
||||
let capture = null;
|
||||
const el = document.createElement('div');
|
||||
el.addEventListener('c1', (e) => { capture = 'c1-' + new String(e.detail)});
|
||||
el.addEventListener('c2', (e) => { capture = 'c2-' + new String(e.detail.over)});
|
||||
|
||||
el.dispatchEvent(new CustomEvent('c1'));
|
||||
testing.expectEqual("c1-null", capture);
|
||||
|
||||
el.dispatchEvent(new CustomEvent('c1', {detail: '123'}));
|
||||
testing.expectEqual("c1-123", capture);
|
||||
|
||||
el.dispatchEvent(new CustomEvent('c2', {detail: {over: 9000}}));
|
||||
testing.expectEqual("c2-9000", capture);
|
||||
</script>
|
||||
138
src/tests/events/event.html
Normal file
138
src/tests/events/event.html
Normal file
@@ -0,0 +1,138 @@
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<div id=content>
|
||||
<p id="para"></p>
|
||||
</div>
|
||||
|
||||
<script id=dispatch>
|
||||
const startTime = new Date().getTime();
|
||||
|
||||
let content = $('#content');
|
||||
// let para = document.getElementById('para');
|
||||
var nb = 0;
|
||||
var evt = null;
|
||||
|
||||
const incrementCallback = function(e) {
|
||||
evt = e;
|
||||
nb += 1;
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
content.addEventListener('dispatch', incrementCallback);
|
||||
|
||||
content.dispatchEvent(new Event('dispatch', {bubbles: true, cancelable: true}));
|
||||
testing.expectEqual(1, nb);
|
||||
testing.expectEqual(content, evt.target);
|
||||
testing.expectEqual(true, evt.bubbles);
|
||||
testing.expectEqual(true, evt.cancelable);
|
||||
testing.expectEqual(true, evt.defaultPrevented);
|
||||
testing.expectEqual(true, evt.isTrusted);
|
||||
testing.expectEqual(true, evt.timeStamp >= Math.floor(startTime/1000));
|
||||
</script>
|
||||
|
||||
<script id=propogate>
|
||||
nb = 0;
|
||||
let para = $('#para');
|
||||
// the stop listener is capturing, so it propogates down
|
||||
content.addEventListener('stop',function(e) {
|
||||
e.stopPropagation();
|
||||
nb += 1;
|
||||
}, true)
|
||||
|
||||
para.addEventListener('stop',function(e) {
|
||||
nb += 10;
|
||||
});
|
||||
|
||||
para.dispatchEvent(new Event('stop'));
|
||||
// didn't propogate down (because of capturing) to para handler
|
||||
testing.expectEqual(1, nb);
|
||||
</script>
|
||||
|
||||
<script id=immediate>
|
||||
nb = 0;
|
||||
|
||||
content.addEventListener('immediate', function(e) {
|
||||
e.stopImmediatePropagation();
|
||||
nb += 1;
|
||||
});
|
||||
|
||||
// the following event listener will not be invoked
|
||||
content.addEventListener('immediate', function(e) {
|
||||
nb += 10;
|
||||
});
|
||||
|
||||
content.dispatchEvent(new Event('immediate'));
|
||||
testing.expectEqual(1, nb);
|
||||
</script>
|
||||
|
||||
<script id=legacy>
|
||||
nb = 0;
|
||||
content.addEventListener('legacy', incrementCallback);
|
||||
let evtLegacy = document.createEvent('Event');
|
||||
evtLegacy.initEvent('legacy');
|
||||
content.dispatchEvent(evtLegacy);
|
||||
testing.expectEqual(1, nb);
|
||||
</script>
|
||||
|
||||
<script id=removeListener>
|
||||
nb = 0;
|
||||
document.addEventListener('count', incrementCallback);
|
||||
document.removeEventListener('count', incrementCallback);
|
||||
document.dispatchEvent(new Event('count'));
|
||||
testing.expectEqual(0, nb);
|
||||
</script>
|
||||
|
||||
<script id=once>
|
||||
document.addEventListener('count', incrementCallback, {once: true});
|
||||
document.dispatchEvent(new Event('count'));
|
||||
document.dispatchEvent(new Event('count'));
|
||||
document.dispatchEvent(new Event('count'));
|
||||
testing.expectEqual(1, nb);
|
||||
</script>
|
||||
|
||||
<script id=abortController>
|
||||
nb = 0;
|
||||
|
||||
let ac = new AbortController()
|
||||
document.addEventListener('count', incrementCallback, {signal: ac.signal})
|
||||
document.dispatchEvent(new Event('count'));
|
||||
document.dispatchEvent(new Event('count'));
|
||||
ac.abort();
|
||||
document.dispatchEvent(new Event('count'));
|
||||
testing.expectEqual(2, nb);
|
||||
document.removeEventListener('count', incrementCallback);
|
||||
</script>
|
||||
|
||||
<script id=composedPath>
|
||||
testing.expectEqual([], new Event('').composedPath());
|
||||
|
||||
let div1 = document.createElement('div');
|
||||
let sr1 = div1.attachShadow({mode: 'open'});
|
||||
sr1.innerHTML = "<p id=srp1></p>";
|
||||
document.getElementsByTagName('body')[0].appendChild(div1);
|
||||
|
||||
let cp = null;
|
||||
const shadowCallback = function(e) {
|
||||
cp = e.composedPath().map((n) => n.id || n.nodeName || n.toString());
|
||||
}
|
||||
|
||||
div1.addEventListener('click', shadowCallback);
|
||||
sr1.getElementById('srp1').click();
|
||||
testing.expectEqual(
|
||||
['srp1', '#document-fragment', 'DIV', 'BODY', 'HTML', '#document', '[object Window]'],
|
||||
cp
|
||||
);
|
||||
|
||||
let div2 = document.createElement('div');
|
||||
let sr2 = div2.attachShadow({mode: 'closed'});
|
||||
sr2.innerHTML = "<p id=srp2></p>";
|
||||
document.getElementsByTagName('body')[0].appendChild(div2);
|
||||
|
||||
cp = null;
|
||||
div2.addEventListener('click', shadowCallback);
|
||||
sr2.getElementById('srp2').click();
|
||||
testing.expectEqual(
|
||||
['DIV', 'BODY', 'HTML', '#document', '[object Window]'],
|
||||
cp
|
||||
);
|
||||
</script>
|
||||
33
src/tests/events/mouse.html
Normal file
33
src/tests/events/mouse.html
Normal file
@@ -0,0 +1,33 @@
|
||||
<script src="../testing.js"></script>
|
||||
<script id=default>
|
||||
let event = new MouseEvent('click');
|
||||
testing.expectEqual('click', event.type);
|
||||
testing.expectEqual(true, event instanceof MouseEvent);
|
||||
testing.expectEqual(true, event instanceof Event);
|
||||
testing.expectEqual(0, event.clientX);
|
||||
testing.expectEqual(0, event.clientY);
|
||||
testing.expectEqual(0, event.screenX);
|
||||
testing.expectEqual(0, event.screenY);
|
||||
</script>
|
||||
|
||||
<script id=parameters>
|
||||
let new_event = new MouseEvent('click', { 'button': 0, 'clientX': 10, 'clientY': 20 });
|
||||
testing.expectEqual(0, new_event.button);
|
||||
testing.expectEqual(10, new_event.x);
|
||||
testing.expectEqual(20, new_event.y);
|
||||
testing.expectEqual(10, new_event.screenX);
|
||||
testing.expectEqual(20, new_event.screenY);
|
||||
</script>
|
||||
|
||||
<script id=listener>
|
||||
let me = new MouseEvent('click');
|
||||
testing.expectEqual(true, me instanceof Event);
|
||||
|
||||
var evt = null;
|
||||
document.addEventListener('click', function (e) {
|
||||
evt = e;
|
||||
});
|
||||
document.dispatchEvent(me);
|
||||
testing.expectEqual('click', evt.type);
|
||||
testing.expectEqual(true, evt instanceof MouseEvent);
|
||||
</script>
|
||||
175
src/tests/testing.js
Normal file
175
src/tests/testing.js
Normal file
@@ -0,0 +1,175 @@
|
||||
// Note: this code tries to make sure that we don't fail to execute a <script>
|
||||
// block without reporting an error. In other words, if the test passes, you
|
||||
// should be confident that the code actually ran.
|
||||
// We do a couple things to ensure this.
|
||||
// 1 - We make sure that ever script with an id has at least 1 assertion called
|
||||
// 2 - We add an onerror handler to every script and, on error, fail.
|
||||
//
|
||||
// This is pretty straightforward, with the only complexity coming from "eventually"
|
||||
// assertions, which are assertions we lazily check in `getStatus()`. We
|
||||
// do this because, by the time `getStatus()`, `page.wait()` will have been called
|
||||
// and any timer (setTimeout, requestAnimation, MutationObserver, etc...) will
|
||||
// have been evaluated. Test which use/test these behavior will use `eventually`.
|
||||
(() => {
|
||||
function expectEqual(expected, actual) {
|
||||
_recordExecution();
|
||||
if (_equal(expected, actual)) {
|
||||
return;
|
||||
}
|
||||
testing._status = 'fail';
|
||||
let msg = `expected: ${JSON.stringify(expected)}, got: ${JSON.stringify(actual)}`;
|
||||
|
||||
console.warn(
|
||||
`id: ${testing._captured?.script_id || document.currentScript.id}`,
|
||||
`msg: ${msg}`,
|
||||
`stack: ${testing._captured?.stack || new Error().stack}`,
|
||||
);
|
||||
}
|
||||
|
||||
function expectError(expected, fn) {
|
||||
withError((err) => {
|
||||
expectEqual(expected, err.toString());
|
||||
}, fn);
|
||||
}
|
||||
|
||||
function withError(cb, fn) {
|
||||
try{
|
||||
fn();
|
||||
} catch (err) {
|
||||
cb(err);
|
||||
return;
|
||||
}
|
||||
expectEqual('an error', null);
|
||||
}
|
||||
|
||||
// Should only be called by the test runner
|
||||
function getStatus() {
|
||||
// if we're already in a fail state, return fail, nothing can recover this
|
||||
if (testing._status === 'fail') return 'fail';
|
||||
|
||||
// run any eventually's that we've captured
|
||||
for (const ev of testing._eventually) {
|
||||
testing._captured = ev[1];
|
||||
ev[0]();
|
||||
testing._captured = null;
|
||||
}
|
||||
|
||||
// Again, if we're in a fail state, we can immediately fail
|
||||
if (testing._status === 'fail') return 'fail';
|
||||
|
||||
// make sure that any <script id=xyz></script> tags we have have had at least
|
||||
// 1 assertion. This helps ensure that if a script tag fails to execute,
|
||||
// we'll report an error, even if no assertions failed.
|
||||
const scripts = document.getElementsByTagName('script');
|
||||
for (script of scripts) {
|
||||
const id = script.id;
|
||||
if (!id) {
|
||||
continue;
|
||||
}
|
||||
if (!testing._executed_scripts[id]) {
|
||||
console.warn(`Failed to execute any expectations for <script id="${id}">...</script>`),
|
||||
testing._status = 'fail';
|
||||
}
|
||||
}
|
||||
|
||||
return testing._status;
|
||||
}
|
||||
|
||||
// Set expectations to happen at some point in the future. Necessary for
|
||||
// testing callbacks which will only be executed after page.wait is called.
|
||||
function eventually(fn) {
|
||||
// capture the current state (script id, stack) so that, when we do run this
|
||||
// we can display more meaningful details on failure.
|
||||
testing._eventually.push([fn, {
|
||||
script_id: document.currentScript.id,
|
||||
stack: new Error().stack,
|
||||
}]);
|
||||
|
||||
_registerErrorCallback();
|
||||
}
|
||||
|
||||
function _recordExecution() {
|
||||
if (testing._status === 'fail') {
|
||||
return;
|
||||
}
|
||||
testing._status = 'ok';
|
||||
|
||||
const script_id = testing._captured?.script_id || document.currentScript.id;
|
||||
testing._executed_scripts[script_id] = true;
|
||||
_registerErrorCallback();
|
||||
}
|
||||
|
||||
// We want to attach an onError callback to each <script>, so that we can
|
||||
// properly fail it.
|
||||
function _registerErrorCallback() {
|
||||
const script = document.currentScript;
|
||||
if (!script) {
|
||||
// can be null if we're executing an eventually assertion, but that's ok
|
||||
// because the errorCallback would have been registered for this script
|
||||
// already
|
||||
return;
|
||||
}
|
||||
|
||||
if (script.onerror) {
|
||||
// already registered
|
||||
return;
|
||||
}
|
||||
|
||||
script.onerror = function(err, b) {
|
||||
testing._status = 'fail';
|
||||
console.warn(
|
||||
`id: ${script.id}`,
|
||||
`msg: There was an error executing the <script id=${script.id}>...</script>.\n There should be a eval error printed above this.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function _equal(a, b) {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (a === null || b === null) {
|
||||
return false;
|
||||
}
|
||||
if (typeof a !== 'object' || typeof b !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Object.keys(a).length != Object.keys(b).length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (property in a) {
|
||||
if (b.hasOwnProperty(property) === false) {
|
||||
return false;
|
||||
}
|
||||
if (_equal(a[property], b[property]) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
window.testing = {
|
||||
_status: 'empty',
|
||||
_eventually: [],
|
||||
_executed_scripts: {},
|
||||
_captured: null,
|
||||
getStatus: getStatus,
|
||||
eventually: eventually,
|
||||
expectEqual: expectEqual,
|
||||
expectError: expectError,
|
||||
withError: withError,
|
||||
};
|
||||
|
||||
// Helper, so you can do $(sel) in a test
|
||||
window.$ = function(sel) {
|
||||
return document.querySelector(sel);
|
||||
}
|
||||
|
||||
// Helper, so you can do $$(sel) in a test
|
||||
window.$$ = function(sel) {
|
||||
return document.querySelectorAll(sel);
|
||||
}
|
||||
})();
|
||||
12
src/tests/window/frames.html
Normal file
12
src/tests/window/frames.html
Normal file
@@ -0,0 +1,12 @@
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<body>
|
||||
<iframe src="https://httpbin.io/html" title="iframea"></iframe>
|
||||
<iframe src="https://httpbin.io/html" title="iframeb"></iframe>
|
||||
</body>
|
||||
|
||||
<script id=frames>
|
||||
testing.expectEqual(2, frames.length);
|
||||
testing.expectEqual(undefined, frames[3])
|
||||
testing.expectError('Error: TODO', () => { frames[1] });
|
||||
</script>
|
||||
105
src/tests/window/window.html
Normal file
105
src/tests/window/window.html
Normal file
@@ -0,0 +1,105 @@
|
||||
<script src="../testing.js"></script>
|
||||
<body></body>
|
||||
<script id=aliases>
|
||||
testing.expectEqual(window, window.self);
|
||||
testing.expectEqual(window, window.parent);
|
||||
testing.expectEqual(window, window.top);
|
||||
testing.expectEqual(window, window.frames);
|
||||
testing.expectEqual(0, window.frames.length);
|
||||
</script>
|
||||
|
||||
<script id=request_animation>
|
||||
let start = 0;
|
||||
function step(timestamp) {
|
||||
start = timestamp;
|
||||
}
|
||||
requestAnimationFrame(step);
|
||||
testing.eventually(() => testing.expectEqual(true, start > 0));
|
||||
|
||||
let request_id = requestAnimationFrame(() => {
|
||||
start = 0;
|
||||
});
|
||||
cancelAnimationFrame(request_id);
|
||||
testing.eventually(() => testing.expectEqual(true, start > 0));
|
||||
</script>
|
||||
|
||||
<script id=dimensions>
|
||||
testing.expectEqual(1, innerHeight);
|
||||
// Width is 1 even if there are no elements
|
||||
testing.expectEqual(1, innerWidth);
|
||||
|
||||
let div1 = document.createElement('div');
|
||||
document.body.appendChild(div1);
|
||||
div1.getClientRects()
|
||||
|
||||
let div2 = document.createElement('div');
|
||||
document.body.appendChild(div2);
|
||||
div2.getClientRects();
|
||||
|
||||
testing.expectEqual(1, innerHeight);
|
||||
testing.expectEqual(2, innerWidth);
|
||||
</script>
|
||||
|
||||
<script id=setTimeout>
|
||||
let longCall = false;
|
||||
window.setTimeout(() => {longCall = true}, 5001);
|
||||
testing.eventually(() => testing.expectEqual(false, longCall));
|
||||
|
||||
let wst1 = 0;
|
||||
window.setTimeout(() => {wst1 += 1}, 1);
|
||||
testing.eventually(() => testing.expectEqual(1, wst1));
|
||||
|
||||
let wst2 = 1;
|
||||
window.setTimeout((a, b) => {wst2 = a + b}, 1, 2, 3);
|
||||
testing.eventually(() => testing.expectEqual(5, wst2));
|
||||
</script>
|
||||
|
||||
<script id=eventTarget>
|
||||
let called = false;
|
||||
|
||||
window.addEventListener("ready", (e) => {
|
||||
called = (e.currentTarget == window);
|
||||
}, {capture: false, once: false});
|
||||
|
||||
const evt = new Event("ready", { bubbles: true, cancelable: false });
|
||||
window.dispatchEvent(evt);
|
||||
testing.expectEqual(true, called);
|
||||
</script>
|
||||
|
||||
<script id=btoa_atob>
|
||||
const b64 = btoa('https://ziglang.org/documentation/master/std/#std.base64.Base64Decoder')
|
||||
testing.expectEqual('aHR0cHM6Ly96aWdsYW5nLm9yZy9kb2N1bWVudGF0aW9uL21hc3Rlci9zdGQvI3N0ZC5iYXNlNjQuQmFzZTY0RGVjb2Rlcg==', b64);
|
||||
|
||||
const str = atob(b64)
|
||||
testing.expectEqual('https://ziglang.org/documentation/master/std/#std.base64.Base64Decoder', str);
|
||||
|
||||
testing.expectError('Error: InvalidCharacterError', () => {
|
||||
atob('b');
|
||||
});
|
||||
</script>
|
||||
|
||||
<script id=scroll>
|
||||
let scroll = false;
|
||||
let scrolend = false
|
||||
window.addEventListener('scroll', () => {scroll = true});
|
||||
document.addEventListener('scrollend', () => {scrollend = true});
|
||||
window.scrollTo(0);
|
||||
|
||||
testing.expectEqual(true, scroll);
|
||||
testing.expectEqual(true, scrollend);
|
||||
</script>
|
||||
|
||||
<script id=queueMicroTask>
|
||||
var qm = false;
|
||||
window.queueMicrotask(() => {qm = true });
|
||||
testing.eventually(() => testing.expectEqual(true, qm));
|
||||
</script>
|
||||
|
||||
<script id=DOMContentLoaded>
|
||||
let dcl = false;
|
||||
window.queueMicrotask(() => {qm = true });
|
||||
window.addEventListener('DOMContentLoaded', (e) => {
|
||||
dcl = e.target == document;
|
||||
});
|
||||
testing.eventually(() => testing.expectEqual(true, dcl));
|
||||
</script>
|
||||
Reference in New Issue
Block a user