<!DOCTYPE html>
<html>
<head>
<style>
.pass, .fail, .running {
height: 30px;
line-height: 30px;
color: white;
display: block;
border: 1px solid black;
margin: 3px;
padding: 3px;
}
.pass { background-color: green; }
.fail { background-color: red; }
.running { background-color: blue; }
</style>
</head>
<body>
<script>
const bindMethodsForTest = (method, maxInvocations) => {
let failures = 0;
const span = document.createElement("span");
span.innerHTML = `Running: ${method}`;
span.setAttribute("class", "running");
span.id = method;
const pass = () => {
if (failures > 0) {
return;
}
++pass.invocationCount;
if (pass.invocationCount > maxInvocations) {
fail(`setTimeout callback triggered more than ${maxInvocations} time(s)`);
} else if (pass.invocationCount === maxInvocations) {
const span = document.getElementById(method);
span.innerHTML = `Pass! ${method}`;
span.setAttribute("class", "pass");
}
};
const fail = message => {
const span = document.getElementById(method);
span.innerHTML = `Fail! ${method}: ${message}`;
span.setAttribute("class", "fail");
++failures;
};
document.body.appendChild(span);
pass.invocationCount = 0;
return [pass, fail];
};
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout triggered immediately", 1);
setTimeout(pass, 0);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout triggered after 1 second", 1);
setTimeout(pass, 1000);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout triggered with string callback", 1);
window.setTimeoutStringCallback = pass;
setTimeout("setTimeoutStringCallback();", 500);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout cancelled before execution", 1);
pass();
const id = setTimeout(pass, 1000);
clearTimeout(id);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout triggered with callback that throws", 1);
// The error should be reported, but should not halt execution of other JavaScript.
setTimeout(() => {
setTimeout(pass, 500);
throw new Error();
}, 500);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout callback unaffected by cancelling another timer", 1);
const id = setTimeout(pass, 1000);
clearTimeout(id + 1);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setTimeout callback triggered with extra arguments", 1);
setTimeout((integer, string, date) => {
if (integer !== 12389) {
fail(`Expected first argument to be the integer 12389, got ${integer}`);
} else if (string !== "foo") {
fail(`Expected second argument to be the string foo, got ${string}`);
} else if (!(date instanceof Date)) {
fail(`Expected third argument to be a date, got ${date}`);
}
pass();
}, 600, 12389, "foo", new Date());
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval repeats callback until cancelled", 5);
const id = setInterval(pass, 500);
setTimeout(() => clearInterval(id), 2600);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval cancelled before execution", 1);
pass();
const id = setInterval(pass, 1000);
clearInterval(id);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval cancelled during execution", 1);
const id = setInterval(() => {
pass();
clearInterval(id);
}, 500);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval triggered with string callback", 6);
window.setIntervalStringCallback = pass;
const id = setInterval("setIntervalStringCallback();", 300);
setTimeout(() => clearInterval(id), 1900);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval triggered with callback that throws", 3);
let callCount = 0;
const id = setInterval(() => {
++callCount;
pass();
if (callCount === 2) {
throw new Error();
} else if (callCount === 3) {
clearInterval(id);
}
}, 500);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval callback unaffected by cancelling another timer", 1);
const id = setInterval(() => {
pass();
clearInterval(id);
}, 1000);
clearInterval(id + 1);
})();
(() => {
const [pass, fail] = bindMethodsForTest("setInterval callback triggered with extra arguments", 1);
const id = setInterval((integer, string, date) => {
if (integer !== 12389) {
fail(`Expected first argument to be the integer 12389, got ${integer}`);
} else if (string !== "foo") {
fail(`Expected second argument to be the string foo, got ${string}`);
} else if (!(date instanceof Date)) {
fail(`Expected third argument to be a date, got ${date}`);
}
pass();
clearInterval(id);
}, 600, 12389, "foo", new Date());
})();
</script>
</body>
</html>