Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/classes/Test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ const INHERITED_PROPS = [
* Represents a single test or a group of tests
*/
export default class Test {
/**
* Source file of each test definition, e.g. `{ label: "check.js", path: "file:///…/check.js" }`.
* Populated by the environment, which cannot store it on the test object itself:
* test definitions come from modules that may hold unrelated data.
* @type {WeakMap<object, { label: string, path: string }>}
*/
static files = new WeakMap();

data = {};

constructor (test, parent) {
Expand Down Expand Up @@ -63,6 +71,12 @@ export default class Test {
}
Object.defineProperties(this, descriptors);

// Only set it if tagged: an own `file: undefined` would block inheriting the parent's below
let file = Test.files.get(test);
if (file) {
this.file = file;
}

if (typeof this.data === "function") {
this.getData = this.data;
this.data = {};
Expand Down
10 changes: 5 additions & 5 deletions src/env/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ async function rerun (options, urls) {

let test = module.default ?? Object.values(module);

if (Object.isExtensible(test)) {
test.file = old.test.file;
if (old.test.file && typeof test === "object") {
Test.files.set(test, old.test.file);
}

test = new Test(test, currentRoot.test);
Expand Down Expand Up @@ -342,16 +342,16 @@ export default {
[...loadedFiles].map(async url => {
let module = await import(url);
let test = module.default ?? module;
if (test && typeof test === "object" && Object.isExtensible(test) && !test.file) {
if (test && typeof test === "object") {
let fileUrl = new URL(url);
fileUrl.search = "";
test.file = {
Test.files.set(test, {
label: path.relative(
isDirectory ? location : path.dirname(location),
fileURLToPath(url),
),
path: fileUrl.href,
};
});
}
}),
);
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/plain-module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// A plain data module, not a test definition. Imported by ../module-tagging.js
// to verify hTest doesn't tag it with source file metadata.
export default { a: 1 };
27 changes: 27 additions & 0 deletions tests/module-tagging.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Test from "../src/classes/Test.js";
import data from "./fixtures/plain-module.js";

export default {
name: "Module tagging",
tests: [
{
name: "Non-test modules are left untouched",
description:
"The Node env tags every loaded module with its source file. Data modules must not be mutated: a `file` key would shadow a real one and leak into Object.keys().",
run: () => Object.keys(data),
expect: ["a"],
},
{
name: "Tests get their file from the registry",
description: "Source file metadata reaches the Test instance without touching the spec.",
run () {
let spec = { name: "Tagged" };
Test.files.set(spec, { label: "tagged.js", path: "file:///tagged.js" });
let test = new Test(spec);

return [test.file?.label, "file" in spec];
},
expect: ["tagged.js", false],
},
],
};