Skip to content

Commit 7389f42

Browse files
committed
refactor: remove one extra layer around test batches
1 parent eb6e1f4 commit 7389f42

File tree

9 files changed

+97
-51
lines changed

9 files changed

+97
-51
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
"@league-of-foundry-developers/foundry-vtt-types": "github:League-of-Foundry-Developers/foundry-vtt-types#e787013319b46f7465cce2c71263083abba70ce8"
5858
},
5959
"devDependencies": {
60-
"@biomejs/biome": "^1.8.3",
60+
"@biomejs/biome": "^1.9.0",
6161
"@commitlint/cli": "^19.3.0",
6262
"@commitlint/config-conventional": "^19.2.2",
6363
"@guanghechen/rollup-plugin-copy": "^6.0.0-alpha.9",

src/module/apps/quench-results.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as Diff from "diff";
22

33
import type { Quench, QuenchBatchKey } from "../quench";
4-
import type { RUNNABLE_STATE } from "../utils/quench-utils";
4+
import { type RUNNABLE_STATE, getBatchKey } from "../utils/quench-utils";
55

66
import { MissingSnapshotError } from "../utils/quench-snapshot-error";
77
import {
@@ -18,7 +18,9 @@ import {
1818
} from "../utils/quench-utils";
1919
import { pause } from "../utils/user-utils";
2020

21-
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
21+
import ApplicationV2 = foundry.applications.api.ApplicationV2;
22+
import HandlebarsApplicationMixin = foundry.applications.api.HandlebarsApplicationMixin;
23+
// const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
2224

2325
/**
2426
* The visual UI for representing Quench test batches and the tests results thereof.
@@ -36,10 +38,7 @@ export class QuenchResults extends HandlebarsApplicationMixin(ApplicationV2) {
3638
* @param quench - The `Quench` instance this `Application` belongs to
3739
* @param options - Additional options
3840
*/
39-
constructor(
40-
quench: Quench,
41-
options: Partial<foundry.applications.api.ApplicationV2.Configuration> = {},
42-
) {
41+
constructor(quench: Quench, options: Partial<ApplicationV2.Configuration> = {}) {
4342
super(options);
4443
this.quench = quench;
4544
}
@@ -539,7 +538,7 @@ export class QuenchResults extends HandlebarsApplicationMixin(ApplicationV2) {
539538
* @param suite - The starting Mocha suite
540539
*/
541540
handleSuiteBegin(suite: Mocha.Suite) {
542-
const batchkey = suite._quench_parentBatch;
541+
const batchkey = getBatchKey(suite);
543542
const isBatchRoot = suite._quench_batchRoot || suite.root;
544543

545544
// If this suite is the root of a test batch or does not belong to a test batch, don't show in the UI.
@@ -582,7 +581,7 @@ export class QuenchResults extends HandlebarsApplicationMixin(ApplicationV2) {
582581
* @param test - The starting test
583582
*/
584583
handleTestBegin(test: Mocha.Test) {
585-
const batchKey = test._quench_parentBatch;
584+
const batchKey = getBatchKey(test);
586585
const parentId = test.parent?.id;
587586

588587
const batchLi: HTMLLIElement | null = this.element.querySelector(
@@ -667,7 +666,7 @@ export class QuenchResults extends HandlebarsApplicationMixin(ApplicationV2) {
667666
* @param error - The error thrown by the hook
668667
*/
669668
handleBatchFail(hook: Mocha.Hook, error: Error) {
670-
const batchKey = hook.parent?._quench_parentBatch;
669+
const batchKey = getBatchKey(hook);
671670
const isBatchRoot = hook.parent?._quench_batchRoot === true;
672671

673672
if (!batchKey || !isBatchRoot) return;

src/module/quench-reporter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
RUNNABLE_STATE,
99
RUNNABLE_STATES,
1010
createDirectory,
11+
getBatchKey,
1112
getGame,
1213
getTestState,
1314
logPrefix,
@@ -98,7 +99,7 @@ export class QuenchReporter extends Mocha.reporters.Base {
9899

99100
// Log detailed results in console
100101
if (QuenchReporter._shouldLogTestDetails() && !suite.root) {
101-
const batchKey = suite._quench_parentBatch;
102+
const batchKey = getBatchKey(suite);
102103
const isBatchRoot = suite._quench_batchRoot;
103104
if (isBatchRoot) {
104105
console.group(quench._testBatches.get(batchKey)?.displayName);

src/module/quench-snapshot.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { MissingSnapshotError } from "./utils/quench-snapshot-error";
33
import {
44
createDirectoryTree,
55
enforce,
6+
getBatchKey,
67
getBatchNameParts,
78
localize,
89
logPrefix,
@@ -110,7 +111,7 @@ export class QuenchSnapshotManager {
110111
const updateSnapshot = quench.snapshots.enableUpdates;
111112
const currentRunnable = quench._currentRunner?.currentRunnable;
112113
if (!currentRunnable) throw new Error("No Runner found");
113-
const quenchBatch = currentRunnable._quench_parentBatch;
114+
const quenchBatch = getBatchKey(currentRunnable);
114115
const [, ...titleParts] = currentRunnable.titlePath();
115116
// Slugify non-Quench batch test name (describe and it parts)
116117
const fullTitle = titleParts.join("-").trim().slugify();

src/module/quench-tests/nonsense-tests.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export function registerExampleTests(quench: Quench) {
1414
registerSnapshotTestBatch,
1515
registerPropertyTestBatch,
1616
registerPromiseTestBatch,
17+
registerAsyncDynamicTestBatch,
1718
registerNonQuenchTestBatch,
1819
]) {
1920
batchFunction(quench);
@@ -261,6 +262,43 @@ function registerPromiseTestBatch(quench: Quench) {
261262
);
262263
}
263264

265+
function registerAsyncDynamicTestBatch(quench: Quench) {
266+
quench.registerBatch(
267+
"quench.examples.dynamicasync",
268+
async function (context) {
269+
const { it, assert, expect, before } = context;
270+
271+
const addIt = (title: string, fn: Mocha.Func): void => {
272+
const test = new (quench.mocha.constructor as typeof Mocha).Test(title, fn);
273+
this.addTest(test);
274+
};
275+
276+
before(async function () {
277+
const tests: { args: number[]; expected: number }[] = await new Promise((resolve) => {
278+
setTimeout(resolve, 1000, [
279+
{ args: [1, 2], expected: 3 },
280+
{ args: [1, 2, 3], expected: 6 },
281+
{ args: [1, 2, 3, 4], expected: 10 },
282+
]);
283+
});
284+
285+
for (const { args, expected } of tests) {
286+
addIt(`should equal ${expected}`, function () {
287+
const sum = args.reduce((a, b) => a + b, 0);
288+
expect(sum).to.equal(expected);
289+
});
290+
}
291+
});
292+
293+
it("requires a single test to register as non-empty suite", function () {
294+
assert.ok(true);
295+
});
296+
},
297+
298+
{ displayName: "QUENCH: Asynchronously Dynamically Generated Tests" },
299+
);
300+
}
301+
264302
// ============================ //
265303
// Additional Quench self tests //
266304
// ============================ //

src/module/quench.ts

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import * as quenchUserUtils from "./utils/user-utils";
1111
declare global {
1212
namespace Mocha {
1313
interface Runnable {
14-
_quench_parentBatch: string;
14+
_quench_parentBatch?: string;
1515
}
1616
interface Suite {
17-
_quench_parentBatch: string;
18-
_quench_batchRoot: boolean;
17+
_quench_parentBatch?: string;
18+
_quench_batchRoot?: boolean;
1919
get id(): string;
2020
}
2121
interface Test {
@@ -135,29 +135,6 @@ export class Quench {
135135
});
136136
return batches.map(({ key }) => key);
137137
}
138-
/**
139-
* A helper function adding a reference to a test's Quench Batch to a given Mocha function's result
140-
*
141-
* @internal
142-
* @param fn - The Mocha function to add the batch to
143-
* @param key - The key of the batch to add
144-
* @returns The Mocha function with the batch reference added
145-
*/
146-
protected static _quenchify<Fn extends Mocha.TestFunction | Mocha.SuiteFunction>(
147-
fn: Fn,
148-
key: string,
149-
): Fn {
150-
const quenchFn = function quenchFn(...args: Parameters<Fn>) {
151-
// @ts-expect-error Args are passed through as-is
152-
const result = fn(...args);
153-
result._quench_parentBatch = key;
154-
return result;
155-
};
156-
quenchFn.only = fn.only;
157-
quenchFn.skip = fn.skip;
158-
if ("retries" in fn) quenchFn.retries = fn.retries;
159-
return quenchFn as Fn;
160-
}
161138

162139
/**
163140
* Registers a new Quench test batch which will show up in the quench window to be enabled/disabled and run.
@@ -284,11 +261,13 @@ export class Quench {
284261
// Run should to patch object prototype
285262
const should = this.chai.should();
286263

287-
const baseContext: Omit<QuenchBatchContext, "describe" | "it"> = {
264+
const baseContext: QuenchBatchContext = {
288265
after,
289266
afterEach,
290267
before,
291268
beforeEach,
269+
describe,
270+
it,
292271
utils,
293272
assert,
294273
expect,
@@ -303,18 +282,15 @@ export class Quench {
303282

304283
// Register suites and tests for provided batches
305284
for (const key of batchKeys) {
306-
const context: QuenchBatchContext = {
307-
...baseContext,
308-
describe: (this.constructor as typeof Quench)._quenchify(describe, key), // typecasting necessary, see #3841
309-
it: (this.constructor as typeof Quench)._quenchify(it, key), // see above; check again ~2030
310-
};
311-
285+
const context = { ...baseContext };
312286
// Create a wrapper suite to contain this test batch
313-
const testBatchRoot = context.describe(`${key}_root`, async () => {
287+
const quench = this;
288+
const testBatchRoot = context.describe(`${key}_root`, async function () {
314289
// Call the batch's registration function
315-
await this._testBatches.get(key)?.fn(context);
290+
await quench._testBatches.get(key)?.fn.apply(this, [context]);
316291
});
317292
testBatchRoot._quench_batchRoot = true;
293+
testBatchRoot._quench_parentBatch = key;
318294
}
319295

320296
// Run the tests and hold on to the runner
@@ -370,7 +346,10 @@ export interface QuenchRegisterBatchOptions {
370346
* @public
371347
* @param context - Various Mocha and Chai functions
372348
*/
373-
export type QuenchRegisterBatchFunction = (context: QuenchBatchContext) => void | Promise<void>;
349+
export type QuenchRegisterBatchFunction = (
350+
this: Mocha.Suite,
351+
context: QuenchBatchContext,
352+
) => void | Promise<void>;
374353

375354
/**
376355
* A context object passed to batch registration functions, containing functions usually

src/module/settings.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ export function registerSettings(): void {
3535
config: true,
3636
type: Boolean,
3737
default: false,
38-
onChange: foundry.utils.debounce(() => {
39-
location.reload();
40-
}, 500),
38+
requiresReload: true,
4139
});
4240

4341
game.settings.register(MODULE_ID, "collapseSuccessful", {

src/module/utils/quench-utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,30 @@ export function getSuiteState(suite: Mocha.Suite): RUNNABLE_STATE {
6565
return allSuitesSucceed ? RUNNABLE_STATES.SUCCESS : RUNNABLE_STATES.FAILURE;
6666
}
6767

68+
/**
69+
* Get the batch key for a test, suite, or hook.
70+
*
71+
* @param test - The test, suite, or hook to get the batch key for
72+
* @returns The batch key, or "" if none was found.
73+
*/
74+
export function getBatchKey(runnable: Mocha.Runnable | Mocha.Test | Mocha.Suite | Mocha.Hook) {
75+
// Given runnable already is the batch root providing the key
76+
if (runnable._quench_parentBatch) return runnable._quench_parentBatch;
77+
78+
// Find key by traversing the parent chain
79+
let key = "";
80+
let parent: Mocha.Runnable | Mocha.Test | Mocha.Suite | Mocha.Hook | null = runnable;
81+
let depth = 0;
82+
do {
83+
depth += 1;
84+
if (parent.parent) parent = parent.parent;
85+
else if (parent.ctx?.parent) parent = parent.ctx.parent;
86+
else parent = null;
87+
if (parent?._quench_parentBatch) key = parent._quench_parentBatch;
88+
} while (depth < 30 && parent && !key);
89+
return key as QuenchBatchKey;
90+
}
91+
6892
/**
6993
* Returns a tuple containing the package name and the batch identifier
7094
*

src/styles/quench.css

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,12 @@
145145
font-weight: bold;
146146
}
147147

148+
#quench-results .summary .runnable-title {
149+
white-space: nowrap;
150+
overflow: hidden;
151+
text-overflow: ellipsis;
152+
}
153+
148154
#quench-results .expandable>.summary {
149155
margin-left: 0.4rem;
150156
}

0 commit comments

Comments
 (0)