Problem
Currently, user-authored functions (run, beforeEach, afterEach, map, check, getName, getExpect) receive the full Test instance as this. This exposes internals that test authors should not need to access or mutate.
A more limited object would:
- Restrict what test functions can see and do
- Give flexibility around what
this means — it can be different per function, tailored to each function's role
- Decouple user-authored code from
Test implementation details
Example of what this enables
One concrete benefit: the this.parent resolution problem. When a function is inherited and calls this.parent.run(x), it re-enters itself because this.parent resolves to the literal parent (which has the same inherited function):
{
name: "Test",
run (x) { return x * 2 }, // Level 0: defines run
tests: [
{
name: "Level 1",
run (x) { // Level 1: defines its own run, calls parent's
return this.parent.run(x) + 1;
},
tests: [
{
name: "Level 2",
// No run here — inherited from Level 1
arg: 3,
expect: 7, // Actual: 8
},
],
},
],
}
Level 2 inherits Level 1's run. The runner calls it with this = Level 2. Inside, this.parent.run(3) hits Level 1's own run (same function), which runs again with this = Level 1, calls Level 0's run(3) = 6, +1 = 7, +1 = 8.
A view object could define this.parent differently per function — e.g., skipping to the nearest ancestor with a different version — without special-casing parent resolution on Test itself.
Source
Proposed by @LeaVerou in the discussion on PR #141.
Problem
Currently, user-authored functions (
run,beforeEach,afterEach,map,check,getName,getExpect) receive the fullTestinstance asthis. This exposes internals that test authors should not need to access or mutate.A more limited object would:
thismeans — it can be different per function, tailored to each function's roleTestimplementation detailsExample of what this enables
One concrete benefit: the
this.parentresolution problem. When a function is inherited and callsthis.parent.run(x), it re-enters itself becausethis.parentresolves to the literal parent (which has the same inherited function):Level 2 inherits Level 1's
run. The runner calls it withthis= Level 2. Inside,this.parent.run(3)hits Level 1's ownrun(same function), which runs again withthis= Level 1, calls Level 0'srun(3)= 6, +1 = 7, +1 = 8.A view object could define
this.parentdifferently per function — e.g., skipping to the nearest ancestor with a different version — without special-casingparentresolution onTestitself.Source
Proposed by @LeaVerou in the discussion on PR #141.