paperlint 10.3.0 → 10.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +15 -4
- package/README.md +1 -0
- package/dist/CLAUDE.md.spec.d.ts.map +1 -1
- package/dist/CLAUDE.md.spec.js +6 -3
- package/dist/CLAUDE.md.spec.js.map +1 -1
- package/dist/adapters/git/index.d.ts +10 -0
- package/dist/adapters/git/index.d.ts.map +1 -0
- package/dist/adapters/git/index.js +151 -0
- package/dist/adapters/git/index.js.map +1 -0
- package/dist/adapters/latex/captions.d.ts +9 -0
- package/dist/adapters/latex/captions.d.ts.map +1 -0
- package/dist/adapters/latex/captions.js +40 -0
- package/dist/adapters/latex/captions.js.map +1 -0
- package/dist/adapters/latex/index.d.ts +1 -0
- package/dist/adapters/latex/index.d.ts.map +1 -1
- package/dist/adapters/latex/index.js +1 -0
- package/dist/adapters/latex/index.js.map +1 -1
- package/dist/adapters/latex/parse.d.ts.map +1 -1
- package/dist/adapters/latex/parse.js +6 -0
- package/dist/adapters/latex/parse.js.map +1 -1
- package/dist/adapters/memory/index.d.ts +6 -0
- package/dist/adapters/memory/index.d.ts.map +1 -1
- package/dist/adapters/memory/index.js +8 -0
- package/dist/adapters/memory/index.js.map +1 -1
- package/dist/adapters/node/index.d.ts +1 -0
- package/dist/adapters/node/index.d.ts.map +1 -1
- package/dist/adapters/node/index.js +1 -0
- package/dist/adapters/node/index.js.map +1 -1
- package/dist/adapters/node/list-entries.io.d.ts +3 -0
- package/dist/adapters/node/list-entries.io.d.ts.map +1 -0
- package/dist/adapters/node/list-entries.io.js +26 -0
- package/dist/adapters/node/list-entries.io.js.map +1 -0
- package/dist/adapters/study-registry/index.d.ts +70 -10
- package/dist/adapters/study-registry/index.d.ts.map +1 -1
- package/dist/adapters/study-registry/index.js +84 -10
- package/dist/adapters/study-registry/index.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -3
- package/dist/cli.js.map +1 -1
- package/dist/domain/decide-first.d.ts +110 -0
- package/dist/domain/decide-first.d.ts.map +1 -0
- package/dist/domain/decide-first.js +108 -0
- package/dist/domain/decide-first.js.map +1 -0
- package/dist/domain/index-columns.d.ts +45 -0
- package/dist/domain/index-columns.d.ts.map +1 -0
- package/dist/domain/index-columns.js +64 -0
- package/dist/domain/index-columns.js.map +1 -0
- package/dist/domain/study-mode.d.ts +37 -0
- package/dist/domain/study-mode.d.ts.map +1 -0
- package/dist/domain/study-mode.js +45 -0
- package/dist/domain/study-mode.js.map +1 -0
- package/dist/domain/study-registry.d.ts +116 -12
- package/dist/domain/study-registry.d.ts.map +1 -1
- package/dist/domain/study-registry.js +164 -44
- package/dist/domain/study-registry.js.map +1 -1
- package/dist/outputs-before-decision-rule.d.ts +17 -0
- package/dist/outputs-before-decision-rule.d.ts.map +1 -0
- package/dist/outputs-before-decision-rule.js +94 -0
- package/dist/outputs-before-decision-rule.js.map +1 -0
- package/dist/ports/git-history.d.ts +44 -0
- package/dist/ports/git-history.d.ts.map +1 -0
- package/dist/ports/git-history.js +2 -0
- package/dist/ports/git-history.js.map +1 -0
- package/dist/ports/list-entries.d.ts +20 -0
- package/dist/ports/list-entries.d.ts.map +1 -0
- package/dist/ports/list-entries.js +2 -0
- package/dist/ports/list-entries.js.map +1 -0
- package/dist/ports/study-deps.d.ts +14 -0
- package/dist/ports/study-deps.d.ts.map +1 -0
- package/dist/ports/study-deps.js +2 -0
- package/dist/ports/study-deps.js.map +1 -0
- package/dist/ports/study-folder.d.ts +43 -0
- package/dist/ports/study-folder.d.ts.map +1 -0
- package/dist/ports/study-folder.js +2 -0
- package/dist/ports/study-folder.js.map +1 -0
- package/dist/ports/study-registry-codec.d.ts +6 -3
- package/dist/ports/study-registry-codec.d.ts.map +1 -1
- package/dist/presets.d.ts +1 -2
- package/dist/presets.d.ts.map +1 -1
- package/dist/presets.js +1 -2
- package/dist/presets.js.map +1 -1
- package/dist/studies.d.ts +4 -0
- package/dist/studies.d.ts.map +1 -1
- package/dist/studies.js +52 -21
- package/dist/studies.js.map +1 -1
- package/dist/study-folder.d.ts +14 -0
- package/dist/study-folder.d.ts.map +1 -0
- package/dist/study-folder.js +123 -0
- package/dist/study-folder.js.map +1 -0
- package/dist/study-gate.d.ts +14 -0
- package/dist/study-gate.d.ts.map +1 -0
- package/dist/study-gate.js +132 -0
- package/dist/study-gate.js.map +1 -0
- package/dist/study-index-input.d.ts +5 -0
- package/dist/study-index-input.d.ts.map +1 -0
- package/dist/study-index-input.js +18 -0
- package/dist/study-index-input.js.map +1 -0
- package/dist/study-modes.d.ts +56 -0
- package/dist/study-modes.d.ts.map +1 -0
- package/dist/study-modes.js +59 -0
- package/dist/study-modes.js.map +1 -0
- package/dist/study-registry-rule.d.ts +13 -1
- package/dist/study-registry-rule.d.ts.map +1 -1
- package/dist/study-registry-rule.js +33 -21
- package/dist/study-registry-rule.js.map +1 -1
- package/dist/study-run.d.ts +19 -0
- package/dist/study-run.d.ts.map +1 -0
- package/dist/study-run.js +9 -0
- package/dist/study-run.js.map +1 -0
- package/docs/design/study-modes.md +265 -0
- package/docs/design/study-registry.md +6 -0
- package/docs/e2e.md +8 -4
- package/docs/layout.md +22 -19
- package/docs/rules/repro/outputs-before-decision.md +122 -0
- package/docs/rules/repro/study-registered.md +49 -15
- package/docs/rules/tex/heading-case.md +1 -1
- package/docs/rules.md +54 -53
- package/docs/testing.md +1 -0
- package/eslint.config.mjs +36 -24
- package/package.json +2 -1
- package/scripts/check.ts +6 -0
- package/skills/camera-ready/SKILL.md +6 -1
- package/skills/camera-ready/SKILL.md.spec.ts +5 -0
- package/skills/draft-paper/SKILL.md +11 -3
- package/skills/draft-paper/SKILL.md.spec.ts +10 -2
- package/skills/grade-paper-writing/SKILL.md +4 -2
- package/skills/grade-paper-writing/SKILL.md.spec.ts +3 -1
- package/skills/grade-paper-writing/__snapshots__/prose-lint.test.ts.snap +16 -3
- package/skills/grade-paper-writing/prose-lint.mjs +127 -95
- package/skills/grade-paper-writing/prose-lint.test.ts +119 -10
- package/skills/harden-paper/SKILL.md +12 -2
- package/skills/harden-paper/SKILL.md.spec.ts +11 -1
- package/skills/osf-artifact-upload/SKILL.md +7 -1
- package/skills/osf-artifact-upload/SKILL.md.spec.ts +6 -0
- package/skills/pc-panel-review/SKILL.md +10 -3
- package/skills/pc-panel-review/SKILL.md.spec.ts +9 -2
- package/skills/tighten-paper/SKILL.md +5 -1
- package/skills/tighten-paper/SKILL.md.spec.ts +4 -0
- package/src/CLAUDE.md +7 -4
- package/src/CLAUDE.md.spec.ts +6 -3
- package/src/adapters/git/history.test.ts +121 -0
- package/src/adapters/git/index.test.ts +268 -0
- package/src/adapters/git/index.ts +227 -0
- package/src/adapters/latex/captions.test.ts +57 -0
- package/src/adapters/latex/captions.ts +56 -0
- package/src/adapters/latex/index.ts +1 -0
- package/src/adapters/latex/parse.ts +7 -0
- package/src/adapters/memory/index.test.ts +15 -1
- package/src/adapters/memory/index.ts +13 -0
- package/src/adapters/node/index.ts +1 -0
- package/src/adapters/node/list-entries.io.ts +27 -0
- package/src/adapters/node/list-entries.test.ts +61 -0
- package/src/adapters/study-registry/index.test.ts +12 -9
- package/src/adapters/study-registry/index.ts +130 -24
- package/src/adapters/study-registry/mode.test.ts +162 -0
- package/src/cli.ts +47 -3
- package/src/domain/decide-first.test.ts +291 -0
- package/src/domain/decide-first.ts +258 -0
- package/src/domain/index-columns.test.ts +261 -0
- package/src/domain/index-columns.ts +107 -0
- package/src/domain/study-measurements.test.ts +16 -8
- package/src/domain/study-mode.test.ts +55 -0
- package/src/domain/study-mode.ts +78 -0
- package/src/domain/study-registry.test.ts +72 -37
- package/src/domain/study-registry.ts +281 -50
- package/src/new-paper.test.ts +5 -1
- package/src/outputs-before-decision-rule.test.ts +251 -0
- package/src/outputs-before-decision-rule.ts +133 -0
- package/src/ports/git-history.ts +44 -0
- package/src/ports/list-entries.ts +21 -0
- package/src/ports/study-deps.ts +14 -0
- package/src/ports/study-folder.ts +38 -0
- package/src/ports/study-registry-codec.ts +10 -3
- package/src/presets.ts +1 -2
- package/src/rule-cases.test.ts +78 -3
- package/src/studies-index-modes.test.ts +129 -0
- package/src/studies.test.ts +35 -11
- package/src/studies.ts +96 -26
- package/src/study-folder.test.ts +237 -0
- package/src/study-folder.ts +176 -0
- package/src/study-gate.test.ts +467 -0
- package/src/study-gate.ts +234 -0
- package/src/study-index-input.ts +31 -0
- package/src/study-modes.ts +107 -0
- package/src/study-registry-rule.test.ts +285 -11
- package/src/study-registry-rule.ts +64 -35
- package/src/study-run.ts +21 -0
|
@@ -19,14 +19,19 @@ import {
|
|
|
19
19
|
studyIndex,
|
|
20
20
|
studyMessage,
|
|
21
21
|
studyReadme,
|
|
22
|
+
studyById,
|
|
22
23
|
studySlug,
|
|
23
24
|
withIndex,
|
|
24
25
|
type RegistryState,
|
|
25
26
|
type Study,
|
|
26
27
|
type StudyInput,
|
|
27
28
|
type StudyRegistry,
|
|
29
|
+
type IndexInput,
|
|
28
30
|
} from "./study-registry.ts";
|
|
29
31
|
|
|
32
|
+
/** No plan and no history: the index with every mode column `—`. */
|
|
33
|
+
const NO_FACTS: IndexInput = { plans: new Map(), history: new Map() };
|
|
34
|
+
|
|
30
35
|
const study = (over: Partial<Study> = {}): Study => ({
|
|
31
36
|
id: "P1-S01",
|
|
32
37
|
folder: "P1-S01-baseline",
|
|
@@ -56,8 +61,8 @@ const input = (
|
|
|
56
61
|
): StudyInput => ({
|
|
57
62
|
folders: r.studies.map((s) => s.folder),
|
|
58
63
|
registry: read(r),
|
|
59
|
-
readme: withIndex("# Reproduction index\n", r),
|
|
60
|
-
|
|
64
|
+
readme: withIndex("# Reproduction index\n", r, NO_FACTS),
|
|
65
|
+
results: new Map(),
|
|
61
66
|
plans: new Map(),
|
|
62
67
|
...over,
|
|
63
68
|
});
|
|
@@ -84,7 +89,9 @@ describe("judgeStudies — silent", () => {
|
|
|
84
89
|
expect(
|
|
85
90
|
judgeStudies(
|
|
86
91
|
input(r, {
|
|
87
|
-
|
|
92
|
+
results: new Map([
|
|
93
|
+
["P1-S02-ablation/out/results.tsv", { kind: "file" as const }],
|
|
94
|
+
]),
|
|
88
95
|
}),
|
|
89
96
|
),
|
|
90
97
|
).toEqual([]);
|
|
@@ -96,7 +103,7 @@ describe("judgeStudies — silent", () => {
|
|
|
96
103
|
folders: [],
|
|
97
104
|
registry: { kind: "absent" },
|
|
98
105
|
readme: null,
|
|
99
|
-
|
|
106
|
+
results: new Map(),
|
|
100
107
|
plans: new Map(),
|
|
101
108
|
}),
|
|
102
109
|
).toEqual([]);
|
|
@@ -125,7 +132,7 @@ describe("judgeStudies — the registry itself", () => {
|
|
|
125
132
|
folders: ["a-2026-10-01", "b-2026-10-02"],
|
|
126
133
|
registry: { kind: "absent" },
|
|
127
134
|
readme: null,
|
|
128
|
-
|
|
135
|
+
results: new Map(),
|
|
129
136
|
plans: new Map(),
|
|
130
137
|
}),
|
|
131
138
|
).toEqual([
|
|
@@ -139,7 +146,7 @@ describe("judgeStudies — the registry itself", () => {
|
|
|
139
146
|
folders: ["a-2026-10-01"],
|
|
140
147
|
registry: { kind: "unreadable", why: "prefix: required" },
|
|
141
148
|
readme: null,
|
|
142
|
-
|
|
149
|
+
results: new Map(),
|
|
143
150
|
plans: new Map(),
|
|
144
151
|
}),
|
|
145
152
|
).toEqual([{ problem: "unreadable", why: "prefix: required" }]);
|
|
@@ -255,7 +262,11 @@ describe("judgeStudies — status", () => {
|
|
|
255
262
|
const r = reg(study({ status: "running", result_file: "r.tsv" }));
|
|
256
263
|
expect(
|
|
257
264
|
judgeStudies(
|
|
258
|
-
input(r, {
|
|
265
|
+
input(r, {
|
|
266
|
+
results: new Map([
|
|
267
|
+
["P1-S01-baseline/r.tsv", { kind: "file" as const }],
|
|
268
|
+
]),
|
|
269
|
+
}),
|
|
259
270
|
),
|
|
260
271
|
).toEqual([]);
|
|
261
272
|
});
|
|
@@ -310,16 +321,16 @@ describe("judgeStudies — the index", () => {
|
|
|
310
321
|
|
|
311
322
|
it("a table prettier padded by display width, with wide characters, is in sync", async () => {
|
|
312
323
|
const wide = reg(study({ title: "測定 A" }));
|
|
313
|
-
const readme = await format(withIndex("# index\n", wide), {
|
|
324
|
+
const readme = await format(withIndex("# index\n", wide, NO_FACTS), {
|
|
314
325
|
parser: "markdown",
|
|
315
326
|
});
|
|
316
|
-
expect(readme).not.toBe(withIndex("# index\n", wide));
|
|
327
|
+
expect(readme).not.toBe(withIndex("# index\n", wide, NO_FACTS));
|
|
317
328
|
expect(judgeStudies(input(wide, { readme }))).toEqual([]);
|
|
318
329
|
});
|
|
319
330
|
|
|
320
331
|
it("a title with a backslash before a pipe stays one cell through prettier", async () => {
|
|
321
332
|
const bs = reg(study({ title: "foo\\|bar" }));
|
|
322
|
-
const readme = await format(withIndex("# index\n", bs), {
|
|
333
|
+
const readme = await format(withIndex("# index\n", bs, NO_FACTS), {
|
|
323
334
|
parser: "markdown",
|
|
324
335
|
});
|
|
325
336
|
expect(judgeStudies(input(bs, { readme }))).toEqual([]);
|
|
@@ -328,22 +339,25 @@ describe("judgeStudies — the index", () => {
|
|
|
328
339
|
|
|
329
340
|
it("a title carrying the end marker's text: in sync, and regenerating changes nothing", () => {
|
|
330
341
|
const m = reg(study({ title: `x ${INDEX_END} y` }));
|
|
331
|
-
const readme = withIndex("# index\n", m);
|
|
342
|
+
const readme = withIndex("# index\n", m, NO_FACTS);
|
|
332
343
|
expect(judgeStudies(input(m, { readme }))).toEqual([]);
|
|
333
|
-
expect(withIndex(readme, m)).toBe(readme);
|
|
344
|
+
expect(withIndex(readme, m, NO_FACTS)).toBe(readme);
|
|
334
345
|
});
|
|
335
346
|
|
|
336
347
|
it("prose that mentions the begin marker is kept, and the real block is the one judged", () => {
|
|
337
348
|
const r1 = reg(study());
|
|
338
349
|
const prose = `# index\n\nThe table between \`${INDEX_BEGIN}\` and its end marker is generated.\n`;
|
|
339
|
-
const readme = withIndex(prose, r1);
|
|
350
|
+
const readme = withIndex(prose, r1, NO_FACTS);
|
|
340
351
|
expect(judgeStudies(input(r1, { readme }))).toEqual([]);
|
|
341
|
-
expect(withIndex(readme, r1)).toBe(readme);
|
|
352
|
+
expect(withIndex(readme, r1, NO_FACTS)).toBe(readme);
|
|
342
353
|
expect(readme.startsWith(prose.trimEnd())).toBe(true);
|
|
343
354
|
});
|
|
344
355
|
|
|
345
356
|
it("a block that differs from the registry by one character", () => {
|
|
346
|
-
const readme = withIndex("# index\n", r).replace(
|
|
357
|
+
const readme = withIndex("# index\n", r, NO_FACTS).replace(
|
|
358
|
+
"designing",
|
|
359
|
+
"running",
|
|
360
|
+
);
|
|
347
361
|
expect(judgeStudies(input(r, { readme }))).toEqual([
|
|
348
362
|
{ problem: "stale-index", why: "differs" },
|
|
349
363
|
]);
|
|
@@ -363,12 +377,12 @@ describe("the index", () => {
|
|
|
363
377
|
in_paper: "yes",
|
|
364
378
|
}),
|
|
365
379
|
);
|
|
366
|
-
expect(studyIndex(r)).toBe(
|
|
380
|
+
expect(studyIndex(r, NO_FACTS)).toBe(
|
|
367
381
|
[
|
|
368
|
-
"| id | title | status | result | in paper | folder |",
|
|
369
|
-
"| ------ | ------------------- | --------- | -------- | -------- | ------------------------------------ |",
|
|
370
|
-
"| P1-S01 | Baseline pass rate | designing | — | no | [P1-S01-baseline/](P1-S01-baseline/) |",
|
|
371
|
-
"| P1-S02 | Pipes \\| and breaks | done | 41 of 60 | yes | [P1-S02-a-b/](P1-S02-a-b/) |",
|
|
382
|
+
"| id | title | status | result | in paper | mode | decision | first output | folder |",
|
|
383
|
+
"| ------ | ------------------- | --------- | -------- | -------- | ---- | -------- | ------------ | ------------------------------------ |",
|
|
384
|
+
"| P1-S01 | Baseline pass rate | designing | — | no | — | — | — | [P1-S01-baseline/](P1-S01-baseline/) |",
|
|
385
|
+
"| P1-S02 | Pipes \\| and breaks | done | 41 of 60 | yes | — | — | — | [P1-S02-a-b/](P1-S02-a-b/) |",
|
|
372
386
|
].join("\n"),
|
|
373
387
|
);
|
|
374
388
|
});
|
|
@@ -390,30 +404,30 @@ describe("the index", () => {
|
|
|
390
404
|
}),
|
|
391
405
|
].slice(0, n),
|
|
392
406
|
);
|
|
393
|
-
const md = withIndex("# Reproduction index\n\nSome text.\n", r);
|
|
407
|
+
const md = withIndex("# Reproduction index\n\nSome text.\n", r, NO_FACTS);
|
|
394
408
|
expect(await format(md, { parser: "markdown" })).toBe(md);
|
|
395
409
|
});
|
|
396
410
|
});
|
|
397
411
|
|
|
398
412
|
describe("the index of an empty registry, and its markers", () => {
|
|
399
413
|
it("of an empty registry is the header alone", () => {
|
|
400
|
-
expect(studyIndex(reg())).toBe(
|
|
414
|
+
expect(studyIndex(reg(), NO_FACTS)).toBe(
|
|
401
415
|
[
|
|
402
|
-
"| id | title | status | result | in paper | folder |",
|
|
403
|
-
"| --- | ----- | ------ | ------ | -------- | ------ |",
|
|
416
|
+
"| id | title | status | result | in paper | mode | decision | first output | folder |",
|
|
417
|
+
"| --- | ----- | ------ | ------ | -------- | ---- | -------- | ------------ | ------ |",
|
|
404
418
|
].join("\n"),
|
|
405
419
|
);
|
|
406
420
|
});
|
|
407
421
|
|
|
408
422
|
it("sits between the two markers, a blank line on each side", () => {
|
|
409
|
-
expect(indexBlock(reg())).toBe(
|
|
410
|
-
`${INDEX_BEGIN}\n\n${studyIndex(reg())}\n\n${INDEX_END}`,
|
|
423
|
+
expect(indexBlock(reg(), NO_FACTS)).toBe(
|
|
424
|
+
`${INDEX_BEGIN}\n\n${studyIndex(reg(), NO_FACTS)}\n\n${INDEX_END}`,
|
|
411
425
|
);
|
|
412
426
|
});
|
|
413
427
|
|
|
414
428
|
it("is read back from a README as the text between the markers, or null", () => {
|
|
415
|
-
expect(embeddedIndex(`# x\n\n${indexBlock(reg())}\n`)).toBe(
|
|
416
|
-
`\n\n${studyIndex(reg())}\n\n`,
|
|
429
|
+
expect(embeddedIndex(`# x\n\n${indexBlock(reg(), NO_FACTS)}\n`)).toBe(
|
|
430
|
+
`\n\n${studyIndex(reg(), NO_FACTS)}\n\n`,
|
|
417
431
|
);
|
|
418
432
|
expect(embeddedIndex("# x\n")).toBeNull();
|
|
419
433
|
expect(embeddedIndex(`${INDEX_END}\n${INDEX_BEGIN}\n`)).toBeNull();
|
|
@@ -424,27 +438,27 @@ describe("withIndex", () => {
|
|
|
424
438
|
const r = reg(study());
|
|
425
439
|
|
|
426
440
|
it("replaces the block in place and keeps the rest", () => {
|
|
427
|
-
const before = `# Index\n\nintro\n\n${indexBlock(reg())}\n\ntail\n`;
|
|
428
|
-
expect(withIndex(before, r)).toBe(
|
|
429
|
-
`# Index\n\nintro\n\n${indexBlock(r)}\n\ntail\n`,
|
|
441
|
+
const before = `# Index\n\nintro\n\n${indexBlock(reg(), NO_FACTS)}\n\ntail\n`;
|
|
442
|
+
expect(withIndex(before, r, NO_FACTS)).toBe(
|
|
443
|
+
`# Index\n\nintro\n\n${indexBlock(r, NO_FACTS)}\n\ntail\n`,
|
|
430
444
|
);
|
|
431
445
|
});
|
|
432
446
|
|
|
433
447
|
it("appends the block to a README without markers", () => {
|
|
434
|
-
expect(withIndex("# Index\n\nintro", r)).toBe(
|
|
435
|
-
`# Index\n\nintro\n\n${indexBlock(r)}\n`,
|
|
448
|
+
expect(withIndex("# Index\n\nintro", r, NO_FACTS)).toBe(
|
|
449
|
+
`# Index\n\nintro\n\n${indexBlock(r, NO_FACTS)}\n`,
|
|
436
450
|
);
|
|
437
451
|
});
|
|
438
452
|
|
|
439
453
|
it("writes a README when there is none", () => {
|
|
440
|
-
expect(withIndex(null, r)).toBe(
|
|
441
|
-
`# Reproduction index\n\n${indexBlock(r)}\n`,
|
|
454
|
+
expect(withIndex(null, r, NO_FACTS)).toBe(
|
|
455
|
+
`# Reproduction index\n\n${indexBlock(r, NO_FACTS)}\n`,
|
|
442
456
|
);
|
|
443
457
|
});
|
|
444
458
|
|
|
445
459
|
it("is a fixed point: a second run changes nothing", () => {
|
|
446
|
-
const once = withIndex("# Index\n", r);
|
|
447
|
-
expect(withIndex(once, r)).toBe(once);
|
|
460
|
+
const once = withIndex("# Index\n", r, NO_FACTS);
|
|
461
|
+
expect(withIndex(once, r, NO_FACTS)).toBe(once);
|
|
448
462
|
});
|
|
449
463
|
});
|
|
450
464
|
|
|
@@ -664,3 +678,24 @@ describe("studyReadme", () => {
|
|
|
664
678
|
);
|
|
665
679
|
});
|
|
666
680
|
});
|
|
681
|
+
|
|
682
|
+
describe("studyById — the one way to address a study by its id", () => {
|
|
683
|
+
const a = study({ id: "P1-S01", folder: "P1-S01-a" });
|
|
684
|
+
const b = study({ id: "P1-S01", folder: "P1-S01-b" });
|
|
685
|
+
const c = study({ id: "P1-S02", folder: "P1-S02-c" });
|
|
686
|
+
|
|
687
|
+
it("exactly one row: that row; no row: none; two or more: all of them, in registry order", () => {
|
|
688
|
+
// Guards: a caller can take a row only from `one`, never "the first" of an ambiguous id.
|
|
689
|
+
expect([
|
|
690
|
+
studyById([a, c], "P1-S01"),
|
|
691
|
+
studyById([a, c], "P1-S09"),
|
|
692
|
+
studyById([], "P1-S01"),
|
|
693
|
+
studyById([a, c, b], "P1-S01"),
|
|
694
|
+
]).toEqual([
|
|
695
|
+
{ kind: "one", row: a },
|
|
696
|
+
{ kind: "none" },
|
|
697
|
+
{ kind: "none" },
|
|
698
|
+
{ kind: "ambiguous", rows: [a, b] },
|
|
699
|
+
]);
|
|
700
|
+
});
|
|
701
|
+
});
|