use boa_engine::{Context, Source};
const HARNESS: &str = r#"
const __results = [];
function test(name, fn) {
try {
fn();
__results.push({ name, ok: true, message: '' });
} catch (e) {
__results.push({ name, ok: false, message: String(e && e.message ? e.message : e) });
}
}
const assert = {
equal(actual, expected, note) {
if (!Object.is(actual, expected)) {
throw new Error(
(note ? note + ': ' : '') + 'expected ' + JSON.stringify(expected) +
', got ' + JSON.stringify(actual)
);
}
},
ok(value, note) {
if (!value) throw new Error(note || 'expected a truthy value');
},
// A whole sequence in one assertion. `clock.test.js` checks what a
// binding *saw over time*, and comparing that element by element would
// report the first difference and hide the shape of the rest.
deepEqual(actual, expected, note) {
const a = JSON.stringify(actual);
const b = JSON.stringify(expected);
if (a !== b) {
throw new Error((note ? note + ': ' : '') + 'expected ' + b + ', got ' + a);
}
},
};
"#;
const REPORT: &str = r#"
__results.map(r => (r.ok ? 'PASS ' : 'FAIL ') + r.name + (r.ok ? '' : ' :: ' + r.message)).join('\n')
"#;
fn flatten(source: &str) -> String {
source
.lines()
.filter(|line| !line.trim_start().starts_with("import "))
.map(|line| line.strip_prefix("export ").unwrap_or(line))
.collect::<Vec<_>>()
.join("\n")
}
fn run_suite(
name: &str,
suite: &str,
modules: &[(&str, String)],
floor: usize,
mode: zdc_runtime::Mode,
) {
let mut context = Context::default();
let release = mode == zdc_runtime::Mode::Release;
let mut sources = vec![
("harness", HARNESS.to_string()),
("mode", format!("const RELEASE = {release};\n")),
(
"dom shim",
include_str!("../runtime/dom-shim.js").to_string(),
),
];
sources.extend(modules.iter().map(|(what, source)| (*what, source.clone())));
sources.push((name, flatten(suite)));
for (what, source) in sources {
context
.eval(Source::from_bytes(source.as_bytes()))
.unwrap_or_else(|e| panic!("{what} failed to evaluate: {e}"));
}
let report = context
.eval(Source::from_bytes(REPORT.as_bytes()))
.expect("collecting results")
.to_string(&mut context)
.expect("report is a string")
.to_std_string_escaped();
let lines: Vec<&str> = report.lines().filter(|l| !l.trim().is_empty()).collect();
assert!(
!lines.is_empty(),
"{name} reported nothing, so it did not run"
);
for line in &lines {
println!("{line}");
}
let failures: Vec<&&str> = lines.iter().filter(|l| l.starts_with("FAIL")).collect();
assert!(
failures.is_empty(),
"{} of {} cases in {name} failed:\n{}",
failures.len(),
lines.len(),
failures
.iter()
.map(|l| format!(" {l}"))
.collect::<Vec<_>>()
.join("\n")
);
assert!(
lines.len() >= floor,
"expected at least {floor} cases in {name}, found {}",
lines.len()
);
}
#[test]
fn the_javascript_renderer_suite_passes() {
renderer_suite(zdc_runtime::Mode::Development);
}
#[test]
fn the_release_renderer_still_passes_the_suite() {
renderer_suite(zdc_runtime::Mode::Release);
}
fn renderer_suite(mode: zdc_runtime::Mode) {
run_suite(
"dom.test.js",
include_str!("../runtime/dom.test.js"),
&[
(
"signal.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::SIGNAL_JS, mode)),
),
(
"dom.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::DOM_JS, mode)),
),
(
"markup.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::MARKUP_JS, mode)),
),
(
"list.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::LIST_JS, mode)),
),
],
38,
mode,
);
}
#[test]
fn the_foreign_lifecycle_suite_passes() {
run_suite(
"foreign.test.js",
include_str!("../runtime/foreign.test.js"),
&[
("signal.js", flatten(zdc_runtime::SIGNAL_JS)),
("foreign.js", flatten(zdc_runtime::FOREIGN_JS)),
],
7,
zdc_runtime::Mode::Development,
);
}
#[test]
fn the_handler_failure_suite_passes() {
handler_suite(zdc_runtime::Mode::Development);
}
#[test]
fn the_release_handler_failure_suite_passes() {
handler_suite(zdc_runtime::Mode::Release);
}
fn handler_suite(mode: zdc_runtime::Mode) {
run_suite(
"handler.test.js",
include_str!("../runtime/handler.test.js"),
&[
(
"signal.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::SIGNAL_JS, mode)),
),
(
"dom.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::DOM_JS, mode)),
),
],
4,
mode,
);
}
#[test]
fn the_document_key_suite_passes() {
keys_suite(zdc_runtime::Mode::Development);
}
#[test]
fn the_release_document_key_suite_passes() {
keys_suite(zdc_runtime::Mode::Release);
}
fn keys_suite(mode: zdc_runtime::Mode) {
run_suite(
"keys.test.js",
include_str!("../runtime/keys.test.js"),
&[
(
"signal.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::SIGNAL_JS, mode)),
),
(
"keys.js",
flatten(&zdc_runtime::for_mode(zdc_runtime::KEYS_JS, mode)),
),
],
7,
mode,
);
}
#[test]
fn the_list_reconciler_suite_passes() {
run_suite(
"list.test.js",
include_str!("../runtime/list.test.js"),
&[
("signal.js", flatten(zdc_runtime::SIGNAL_JS)),
("dom.js", flatten(zdc_runtime::DOM_JS)),
("list.js", flatten(zdc_runtime::LIST_JS)),
],
7,
zdc_runtime::Mode::Development,
);
}
#[test]
fn the_clock_suite_passes() {
run_suite(
"clock.test.js",
include_str!("../runtime/clock.test.js"),
&[
("signal.js", flatten(zdc_runtime::SIGNAL_JS)),
("clock.js", flatten(zdc_runtime::CLOCK_JS)),
],
12,
zdc_runtime::Mode::Development,
);
}
#[test]
fn the_element_library_suite_passes() {
run_suite(
"elements.test.js",
include_str!("../runtime/elements.test.js"),
&[
("signal.js", flatten(zdc_runtime::SIGNAL_JS)),
("dom.js", flatten(zdc_runtime::DOM_JS)),
("markup.js", flatten(zdc_runtime::MARKUP_JS)),
("list.js", flatten(zdc_runtime::LIST_JS)),
("elements.js", flatten(zdc_runtime::ELEMENTS_JS)),
],
6,
zdc_runtime::Mode::Development,
);
}