bun-sticky 1.0.4 → 1.0.5
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/README.md +7 -120
- package/index.js +8 -0
- package/package.json +7 -27
- package/.claude/commands/score.md +0 -12
- package/.claude/commands/test.md +0 -15
- package/.github/FUNDING.yml +0 -5
- package/.github/workflows/ci.yml +0 -26
- package/.github/workflows/release.yml +0 -36
- package/CLAUDE.md +0 -98
- package/LICENSE +0 -21
- package/PRODUCT-SHEET.md +0 -124
- package/PUBLISH-PROTOCOL.md +0 -64
- package/index.ts +0 -349
- package/lib/parser.ts +0 -75
- package/lib/scorer.ts +0 -237
- package/lib/tier.ts +0 -46
- package/project.faf +0 -23
- package/tests/__snapshots__/sticky.test.ts.snap +0 -114
- package/tests/sticky.test.ts +0 -1321
- package/tests/wjttc.test.ts +0 -966
package/tests/wjttc.test.ts
DELETED
|
@@ -1,966 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 🏎️ WJTTC Championship Test Suite for bun-sticky
|
|
3
|
-
*
|
|
4
|
-
* F1-Inspired Software Testing Philosophy:
|
|
5
|
-
* "When brakes must work flawlessly, so must our code."
|
|
6
|
-
*
|
|
7
|
-
* This test suite demonstrates EVERY Bun test API feature combined
|
|
8
|
-
* with the WJTTC (Wolfejam Test Track Championship) methodology.
|
|
9
|
-
*
|
|
10
|
-
* Bun Test Features Used:
|
|
11
|
-
* - test, describe, it (core)
|
|
12
|
-
* - test.each (parametrized)
|
|
13
|
-
* - test.concurrent (parallel)
|
|
14
|
-
* - test.skip, test.todo (documentation)
|
|
15
|
-
* - beforeAll, beforeEach, afterEach, afterAll (lifecycle)
|
|
16
|
-
* - mock, spyOn (mocking)
|
|
17
|
-
* - expect matchers (full suite)
|
|
18
|
-
* - snapshots
|
|
19
|
-
* - retry, repeats (reliability)
|
|
20
|
-
*
|
|
21
|
-
* @author wolfejam
|
|
22
|
-
* @license MIT
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import {
|
|
26
|
-
test,
|
|
27
|
-
expect,
|
|
28
|
-
describe,
|
|
29
|
-
it,
|
|
30
|
-
beforeAll,
|
|
31
|
-
beforeEach,
|
|
32
|
-
afterEach,
|
|
33
|
-
afterAll,
|
|
34
|
-
mock,
|
|
35
|
-
spyOn,
|
|
36
|
-
} from "bun:test";
|
|
37
|
-
|
|
38
|
-
import { parseYaml, getNestedValue, hasValue } from "../lib/parser";
|
|
39
|
-
import { calculateScore, SLOTS, ProjectType } from "../lib/scorer";
|
|
40
|
-
import { getTier, TIERS } from "../lib/tier";
|
|
41
|
-
|
|
42
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
43
|
-
// WJTTC RACE 1: SLOT DEFINITIONS (The Foundation)
|
|
44
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
45
|
-
|
|
46
|
-
describe("🏁 WJTTC Race 1: Slot Definitions", () => {
|
|
47
|
-
describe("slot counts", () => {
|
|
48
|
-
test("project has exactly 3 slots", () => {
|
|
49
|
-
expect(SLOTS.project).toHaveLength(3);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
test("frontend has exactly 4 slots", () => {
|
|
53
|
-
expect(SLOTS.frontend).toHaveLength(4);
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("backend has exactly 5 slots", () => {
|
|
57
|
-
expect(SLOTS.backend).toHaveLength(5);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("universal has exactly 3 slots", () => {
|
|
61
|
-
expect(SLOTS.universal).toHaveLength(3);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test("human has exactly 6 slots", () => {
|
|
65
|
-
expect(SLOTS.human).toHaveLength(6);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test("total slots equals 21 (Wolfejam specification)", () => {
|
|
69
|
-
const total =
|
|
70
|
-
SLOTS.project.length +
|
|
71
|
-
SLOTS.frontend.length +
|
|
72
|
-
SLOTS.backend.length +
|
|
73
|
-
SLOTS.universal.length +
|
|
74
|
-
SLOTS.human.length;
|
|
75
|
-
expect(total).toBe(21);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
describe("slot naming convention", () => {
|
|
80
|
-
test.each(SLOTS.project)("project slot %s uses dot notation", (slot) => {
|
|
81
|
-
expect(slot).toMatch(/^project\./);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test.each(SLOTS.human)("human slot %s uses dot notation", (slot) => {
|
|
85
|
-
expect(slot).toMatch(/^human_context\./);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
test.each(SLOTS.frontend)("frontend slot %s uses stack prefix", (slot) => {
|
|
89
|
-
expect(slot).toMatch(/^stack\./);
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
test.each(SLOTS.backend)("backend slot %s uses stack prefix", (slot) => {
|
|
93
|
-
expect(slot).toMatch(/^stack\./);
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe("required fields exist", () => {
|
|
98
|
-
test("project.name is a slot", () => {
|
|
99
|
-
expect(SLOTS.project).toContain("project.name");
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
test("project.goal is a slot", () => {
|
|
103
|
-
expect(SLOTS.project).toContain("project.goal");
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
test("project.main_language is a slot", () => {
|
|
107
|
-
expect(SLOTS.project).toContain("project.main_language");
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("human_context.who (5W1H) is a slot", () => {
|
|
111
|
-
expect(SLOTS.human).toContain("human_context.who");
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
test("human_context.what is a slot", () => {
|
|
115
|
-
expect(SLOTS.human).toContain("human_context.what");
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
test("human_context.why is a slot", () => {
|
|
119
|
-
expect(SLOTS.human).toContain("human_context.why");
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("human_context.where is a slot", () => {
|
|
123
|
-
expect(SLOTS.human).toContain("human_context.where");
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("human_context.when is a slot", () => {
|
|
127
|
-
expect(SLOTS.human).toContain("human_context.when");
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
test("human_context.how is a slot", () => {
|
|
131
|
-
expect(SLOTS.human).toContain("human_context.how");
|
|
132
|
-
});
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
137
|
-
// WJTTC RACE 2: PROJECT TYPE DETECTION
|
|
138
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
139
|
-
|
|
140
|
-
describe("🏁 WJTTC Race 2: Project Type Detection", () => {
|
|
141
|
-
// Parametrized tests using test.each - Bun's powerful feature
|
|
142
|
-
const typeTestCases: Array<[string, string, ProjectType]> = [
|
|
143
|
-
["cli", "type: cli", "cli"],
|
|
144
|
-
["CLI uppercase", "type: CLI", "cli"],
|
|
145
|
-
["library", "type: library", "library"],
|
|
146
|
-
["lib short", "type: lib", "library"],
|
|
147
|
-
["package", "type: package", "library"],
|
|
148
|
-
["api", "type: api", "api"],
|
|
149
|
-
["backend", "type: backend", "api"],
|
|
150
|
-
["webapp", "type: webapp", "webapp"],
|
|
151
|
-
["web", "type: web", "webapp"],
|
|
152
|
-
["frontend", "type: frontend", "webapp"],
|
|
153
|
-
["fullstack", "type: fullstack", "fullstack"],
|
|
154
|
-
["full", "type: full", "fullstack"],
|
|
155
|
-
["mobile", "type: mobile", "mobile"],
|
|
156
|
-
["app", "type: app", "mobile"],
|
|
157
|
-
];
|
|
158
|
-
|
|
159
|
-
test.each(typeTestCases)(
|
|
160
|
-
"detects %s type from '%s'",
|
|
161
|
-
(name, content, expected) => {
|
|
162
|
-
const faf = parseYaml(`project:\n ${content}`);
|
|
163
|
-
const result = calculateScore(faf);
|
|
164
|
-
expect(result.projectType).toBe(expected);
|
|
165
|
-
}
|
|
166
|
-
);
|
|
167
|
-
|
|
168
|
-
describe("type inference from stack", () => {
|
|
169
|
-
test("infers fullstack when both frontend and backend exist", () => {
|
|
170
|
-
const faf = parseYaml(`
|
|
171
|
-
stack:
|
|
172
|
-
frontend: React
|
|
173
|
-
backend: Node
|
|
174
|
-
`);
|
|
175
|
-
const result = calculateScore(faf);
|
|
176
|
-
expect(result.projectType).toBe("fullstack");
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test("infers webapp when only frontend exists", () => {
|
|
180
|
-
const faf = parseYaml(`
|
|
181
|
-
stack:
|
|
182
|
-
frontend: React
|
|
183
|
-
`);
|
|
184
|
-
const result = calculateScore(faf);
|
|
185
|
-
expect(result.projectType).toBe("webapp");
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
test("infers api when only backend exists", () => {
|
|
189
|
-
const faf = parseYaml(`
|
|
190
|
-
stack:
|
|
191
|
-
backend: Express
|
|
192
|
-
`);
|
|
193
|
-
const result = calculateScore(faf);
|
|
194
|
-
expect(result.projectType).toBe("api");
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("infers api from database presence", () => {
|
|
198
|
-
const faf = parseYaml(`
|
|
199
|
-
stack:
|
|
200
|
-
database: PostgreSQL
|
|
201
|
-
`);
|
|
202
|
-
const result = calculateScore(faf);
|
|
203
|
-
expect(result.projectType).toBe("api");
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
test("defaults to unknown when no type info", () => {
|
|
207
|
-
const faf = parseYaml(`
|
|
208
|
-
project:
|
|
209
|
-
name: Mystery
|
|
210
|
-
`);
|
|
211
|
-
const result = calculateScore(faf);
|
|
212
|
-
expect(result.projectType).toBe("unknown");
|
|
213
|
-
});
|
|
214
|
-
});
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
218
|
-
// WJTTC RACE 3: SCORE CALCULATIONS
|
|
219
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
220
|
-
|
|
221
|
-
describe("🏁 WJTTC Race 3: Score Calculations", () => {
|
|
222
|
-
describe("Wolfejam formula verification", () => {
|
|
223
|
-
test("score = (filled / applicable) × 100", () => {
|
|
224
|
-
const faf = parseYaml(`
|
|
225
|
-
project:
|
|
226
|
-
name: Test
|
|
227
|
-
goal: Testing
|
|
228
|
-
main_language: TypeScript
|
|
229
|
-
type: cli
|
|
230
|
-
human_context:
|
|
231
|
-
who: Developers
|
|
232
|
-
what: A CLI tool
|
|
233
|
-
`);
|
|
234
|
-
const result = calculateScore(faf);
|
|
235
|
-
// 5 filled / 9 applicable = 55.55% → rounds to 56%
|
|
236
|
-
expect(result.filled).toBe(5);
|
|
237
|
-
expect(result.total).toBe(9);
|
|
238
|
-
expect(result.score).toBe(56);
|
|
239
|
-
});
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
describe("type-aware slot counting", () => {
|
|
243
|
-
const slotCountCases: Array<[ProjectType, number]> = [
|
|
244
|
-
["cli", 9],
|
|
245
|
-
["library", 9],
|
|
246
|
-
["api", 17],
|
|
247
|
-
["webapp", 16],
|
|
248
|
-
["fullstack", 21],
|
|
249
|
-
["mobile", 9],
|
|
250
|
-
["unknown", 9],
|
|
251
|
-
];
|
|
252
|
-
|
|
253
|
-
test.each(slotCountCases)(
|
|
254
|
-
"%s type has %d applicable slots",
|
|
255
|
-
(type, expected) => {
|
|
256
|
-
const faf = parseYaml(`
|
|
257
|
-
project:
|
|
258
|
-
type: ${type}
|
|
259
|
-
`);
|
|
260
|
-
const result = calculateScore(faf);
|
|
261
|
-
expect(result.total).toBe(expected);
|
|
262
|
-
}
|
|
263
|
-
);
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
describe("100% score scenarios", () => {
|
|
267
|
-
test("CLI with all 9 slots filled = 100%", () => {
|
|
268
|
-
const faf = parseYaml(`
|
|
269
|
-
project:
|
|
270
|
-
name: PerfectCLI
|
|
271
|
-
goal: Achieve perfection
|
|
272
|
-
main_language: Zig
|
|
273
|
-
type: cli
|
|
274
|
-
human_context:
|
|
275
|
-
who: Developers
|
|
276
|
-
what: A CLI tool
|
|
277
|
-
why: For testing
|
|
278
|
-
where: Terminal
|
|
279
|
-
when: 2025
|
|
280
|
-
how: Run it
|
|
281
|
-
`);
|
|
282
|
-
const result = calculateScore(faf);
|
|
283
|
-
expect(result.score).toBe(100);
|
|
284
|
-
expect(result.filled).toBe(9);
|
|
285
|
-
expect(result.total).toBe(9);
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test("Fullstack with all 21 slots filled = 100%", () => {
|
|
289
|
-
const faf = parseYaml(`
|
|
290
|
-
project:
|
|
291
|
-
name: FullApp
|
|
292
|
-
goal: Complete solution
|
|
293
|
-
main_language: TypeScript
|
|
294
|
-
type: fullstack
|
|
295
|
-
stack:
|
|
296
|
-
frontend: React
|
|
297
|
-
css_framework: Tailwind
|
|
298
|
-
ui_library: shadcn
|
|
299
|
-
state_management: zustand
|
|
300
|
-
backend: Node
|
|
301
|
-
api_type: REST
|
|
302
|
-
runtime: Bun
|
|
303
|
-
database: PostgreSQL
|
|
304
|
-
connection: prisma
|
|
305
|
-
hosting: Vercel
|
|
306
|
-
build: vite
|
|
307
|
-
cicd: GitHub Actions
|
|
308
|
-
human_context:
|
|
309
|
-
who: Users
|
|
310
|
-
what: Full application
|
|
311
|
-
why: Complete solution
|
|
312
|
-
where: Web
|
|
313
|
-
when: 2025
|
|
314
|
-
how: npm start
|
|
315
|
-
`);
|
|
316
|
-
const result = calculateScore(faf);
|
|
317
|
-
expect(result.score).toBe(100);
|
|
318
|
-
expect(result.filled).toBe(21);
|
|
319
|
-
expect(result.total).toBe(21);
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
describe("partial score scenarios", () => {
|
|
324
|
-
const partialCases: Array<[string, number, number, number]> = [
|
|
325
|
-
// [description, filled, total, expectedScore]
|
|
326
|
-
// Note: Math.round(x/9 * 100) - some round up!
|
|
327
|
-
["1/9 CLI slots", 1, 9, 11],
|
|
328
|
-
["4/9 CLI slots", 4, 9, 44],
|
|
329
|
-
["5/9 CLI slots", 5, 9, 56], // 55.55 rounds to 56
|
|
330
|
-
["7/9 CLI slots", 7, 9, 78], // 77.77 rounds to 78
|
|
331
|
-
["8/9 CLI slots", 8, 9, 89], // 88.88 rounds to 89
|
|
332
|
-
];
|
|
333
|
-
|
|
334
|
-
test.each(partialCases)(
|
|
335
|
-
"%s = %d%",
|
|
336
|
-
(desc, filled, total, expectedScore) => {
|
|
337
|
-
const score = Math.round((filled / total) * 100);
|
|
338
|
-
expect(score).toBe(expectedScore);
|
|
339
|
-
}
|
|
340
|
-
);
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
describe("empty and edge cases", () => {
|
|
344
|
-
test("empty object returns 0 score", () => {
|
|
345
|
-
const faf = {};
|
|
346
|
-
const result = calculateScore(faf);
|
|
347
|
-
expect(result.score).toBe(0);
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
test("missing sections don't crash", () => {
|
|
351
|
-
const faf = parseYaml(`
|
|
352
|
-
project:
|
|
353
|
-
name: Minimal
|
|
354
|
-
`);
|
|
355
|
-
expect(() => calculateScore(faf)).not.toThrow();
|
|
356
|
-
});
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
361
|
-
// WJTTC RACE 4: TIER SYSTEM
|
|
362
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
363
|
-
|
|
364
|
-
describe("🏁 WJTTC Race 4: Tier System", () => {
|
|
365
|
-
describe("tier definitions", () => {
|
|
366
|
-
test("7 tiers are defined", () => {
|
|
367
|
-
expect(TIERS).toHaveLength(7);
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
test("all tiers have required properties", () => {
|
|
371
|
-
for (const tier of TIERS) {
|
|
372
|
-
expect(tier).toHaveProperty("name");
|
|
373
|
-
expect(tier).toHaveProperty("emoji");
|
|
374
|
-
expect(tier).toHaveProperty("color");
|
|
375
|
-
// Note: minScore handled by getTier() function, not stored in TIERS
|
|
376
|
-
}
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
describe("tier boundaries (exact thresholds)", () => {
|
|
381
|
-
const boundaryTests: Array<[number, string]> = [
|
|
382
|
-
[100, "Trophy"],
|
|
383
|
-
[99, "Gold"],
|
|
384
|
-
[95, "Silver"],
|
|
385
|
-
[85, "Bronze"],
|
|
386
|
-
[70, "Green"],
|
|
387
|
-
[55, "Yellow"],
|
|
388
|
-
[54, "Red"],
|
|
389
|
-
[0, "Empty"],
|
|
390
|
-
];
|
|
391
|
-
|
|
392
|
-
test.each(boundaryTests)("score %d = %s tier", (score, expectedName) => {
|
|
393
|
-
const tier = getTier(score);
|
|
394
|
-
expect(tier.name).toBe(expectedName);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
describe("tier emoji verification", () => {
|
|
399
|
-
test("Trophy has trophy emoji", () => {
|
|
400
|
-
expect(getTier(100).emoji).toBe("🏆");
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
test("Gold has gold medal emoji", () => {
|
|
404
|
-
expect(getTier(99).emoji).toBe("🥇");
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
test("Silver has silver medal emoji", () => {
|
|
408
|
-
expect(getTier(95).emoji).toBe("🥈");
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
test("Bronze has bronze medal emoji", () => {
|
|
412
|
-
expect(getTier(85).emoji).toBe("🥉");
|
|
413
|
-
});
|
|
414
|
-
|
|
415
|
-
test("Green has green circle emoji", () => {
|
|
416
|
-
expect(getTier(70).emoji).toBe("🟢");
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
test("Yellow has yellow circle emoji", () => {
|
|
420
|
-
expect(getTier(55).emoji).toBe("🟡");
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
test("Red has red circle emoji", () => {
|
|
424
|
-
expect(getTier(50).emoji).toBe("🔴");
|
|
425
|
-
});
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
describe("tier color coding", () => {
|
|
429
|
-
test("high tiers use green-ish colors", () => {
|
|
430
|
-
const trophy = getTier(100);
|
|
431
|
-
const gold = getTier(99);
|
|
432
|
-
expect(trophy.color).toContain("\x1b[");
|
|
433
|
-
expect(gold.color).toContain("\x1b[");
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
test("Red tier uses red ANSI code", () => {
|
|
437
|
-
const red = getTier(50);
|
|
438
|
-
expect(red.color).toBe("\x1b[31m");
|
|
439
|
-
});
|
|
440
|
-
});
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
444
|
-
// WJTTC RACE 5: YAML PARSER
|
|
445
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
446
|
-
|
|
447
|
-
describe("🏁 WJTTC Race 5: YAML Parser", () => {
|
|
448
|
-
describe("parseYaml basic parsing", () => {
|
|
449
|
-
test("parses simple key-value", () => {
|
|
450
|
-
const result = parseYaml("name: test");
|
|
451
|
-
expect(result).toHaveProperty("name", "test");
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
test("parses nested objects", () => {
|
|
455
|
-
const result = parseYaml(`
|
|
456
|
-
project:
|
|
457
|
-
name: test
|
|
458
|
-
goal: testing
|
|
459
|
-
`);
|
|
460
|
-
expect(result.project).toHaveProperty("name", "test");
|
|
461
|
-
expect(result.project).toHaveProperty("goal", "testing");
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
test("handles empty lines", () => {
|
|
465
|
-
const result = parseYaml(`
|
|
466
|
-
name: test
|
|
467
|
-
|
|
468
|
-
goal: testing
|
|
469
|
-
`);
|
|
470
|
-
expect(result).toHaveProperty("name", "test");
|
|
471
|
-
expect(result).toHaveProperty("goal", "testing");
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
test("ignores comments", () => {
|
|
475
|
-
const result = parseYaml(`
|
|
476
|
-
# This is a comment
|
|
477
|
-
name: test
|
|
478
|
-
`);
|
|
479
|
-
expect(result).toHaveProperty("name", "test");
|
|
480
|
-
expect(result).not.toHaveProperty("#");
|
|
481
|
-
});
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
describe("getNestedValue", () => {
|
|
485
|
-
const testObj = {
|
|
486
|
-
project: {
|
|
487
|
-
name: "test",
|
|
488
|
-
details: {
|
|
489
|
-
version: "1.0.0",
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
test("gets top-level value", () => {
|
|
495
|
-
expect(getNestedValue(testObj, "project")).toBeDefined();
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
test("gets nested value", () => {
|
|
499
|
-
expect(getNestedValue(testObj, "project.name")).toBe("test");
|
|
500
|
-
});
|
|
501
|
-
|
|
502
|
-
test("gets deeply nested value", () => {
|
|
503
|
-
expect(getNestedValue(testObj, "project.details.version")).toBe("1.0.0");
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
test("returns undefined for missing path", () => {
|
|
507
|
-
expect(getNestedValue(testObj, "missing.path")).toBeUndefined();
|
|
508
|
-
});
|
|
509
|
-
});
|
|
510
|
-
|
|
511
|
-
describe("hasValue", () => {
|
|
512
|
-
test("returns true for existing value", () => {
|
|
513
|
-
const obj = { name: "test" };
|
|
514
|
-
expect(hasValue(obj, "name")).toBe(true);
|
|
515
|
-
});
|
|
516
|
-
|
|
517
|
-
test("returns false for missing value", () => {
|
|
518
|
-
const obj = { name: "test" };
|
|
519
|
-
expect(hasValue(obj, "goal")).toBe(false);
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
test("returns false for empty string", () => {
|
|
523
|
-
const obj = { name: "" };
|
|
524
|
-
expect(hasValue(obj, "name")).toBe(false);
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
test("returns false for null", () => {
|
|
528
|
-
const obj = { name: null };
|
|
529
|
-
expect(hasValue(obj, "name")).toBe(false);
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
test("returns false for undefined", () => {
|
|
533
|
-
const obj = { name: undefined };
|
|
534
|
-
expect(hasValue(obj, "name")).toBe(false);
|
|
535
|
-
});
|
|
536
|
-
});
|
|
537
|
-
});
|
|
538
|
-
|
|
539
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
540
|
-
// WJTTC RACE 6: EDGE CASES & STRESS TESTS
|
|
541
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
542
|
-
|
|
543
|
-
describe("🏁 WJTTC Race 6: Edge Cases", () => {
|
|
544
|
-
describe("unicode handling", () => {
|
|
545
|
-
test("handles Japanese characters", () => {
|
|
546
|
-
const faf = parseYaml(`
|
|
547
|
-
project:
|
|
548
|
-
name: 日本語プロジェクト
|
|
549
|
-
type: cli
|
|
550
|
-
`);
|
|
551
|
-
const result = calculateScore(faf);
|
|
552
|
-
expect(result.filled).toBeGreaterThan(0);
|
|
553
|
-
});
|
|
554
|
-
|
|
555
|
-
test("handles emoji in values", () => {
|
|
556
|
-
const faf = parseYaml(`
|
|
557
|
-
project:
|
|
558
|
-
name: 🚀 Rocket Project
|
|
559
|
-
type: cli
|
|
560
|
-
`);
|
|
561
|
-
const result = calculateScore(faf);
|
|
562
|
-
expect(result.filled).toBeGreaterThan(0);
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
|
|
566
|
-
describe("special characters", () => {
|
|
567
|
-
test("handles colons in values", () => {
|
|
568
|
-
const faf = parseYaml(`
|
|
569
|
-
project:
|
|
570
|
-
goal: "Build a CLI: fast and simple"
|
|
571
|
-
type: cli
|
|
572
|
-
`);
|
|
573
|
-
expect(faf.project.goal).toContain(":");
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
test("handles quotes in values", () => {
|
|
577
|
-
const faf = parseYaml(`
|
|
578
|
-
project:
|
|
579
|
-
name: "Project 'Alpha'"
|
|
580
|
-
type: cli
|
|
581
|
-
`);
|
|
582
|
-
const result = calculateScore(faf);
|
|
583
|
-
expect(result.filled).toBeGreaterThan(0);
|
|
584
|
-
});
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
describe("whitespace handling", () => {
|
|
588
|
-
test("handles tabs instead of spaces", () => {
|
|
589
|
-
const faf = parseYaml("project:\n\tname: test\n\ttype: cli");
|
|
590
|
-
expect(faf.project).toBeDefined();
|
|
591
|
-
});
|
|
592
|
-
|
|
593
|
-
test("handles trailing whitespace", () => {
|
|
594
|
-
const faf = parseYaml("project: \n name: test \n type: cli ");
|
|
595
|
-
expect(faf.project.name).toBe("test");
|
|
596
|
-
});
|
|
597
|
-
|
|
598
|
-
test("handles CRLF line endings", () => {
|
|
599
|
-
const faf = parseYaml("project:\r\n name: test\r\n type: cli");
|
|
600
|
-
expect(faf.project).toBeDefined();
|
|
601
|
-
});
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
describe("large content", () => {
|
|
605
|
-
test("handles 100+ line FAF file", () => {
|
|
606
|
-
let content = "project:\n name: Large\n type: fullstack\n";
|
|
607
|
-
for (let i = 0; i < 100; i++) {
|
|
608
|
-
content += ` field${i}: value${i}\n`;
|
|
609
|
-
}
|
|
610
|
-
expect(() => parseYaml(content)).not.toThrow();
|
|
611
|
-
});
|
|
612
|
-
});
|
|
613
|
-
});
|
|
614
|
-
|
|
615
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
616
|
-
// WJTTC RACE 7: CONCURRENT TESTS (Bun Feature Showcase)
|
|
617
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
618
|
-
|
|
619
|
-
describe("🏁 WJTTC Race 7: Concurrent Tests", () => {
|
|
620
|
-
test.concurrent("concurrent test 1: CLI scoring", async () => {
|
|
621
|
-
const faf = parseYaml("project:\n name: CLI1\n type: cli");
|
|
622
|
-
const result = calculateScore(faf);
|
|
623
|
-
expect(result.projectType).toBe("cli");
|
|
624
|
-
});
|
|
625
|
-
|
|
626
|
-
test.concurrent("concurrent test 2: webapp scoring", async () => {
|
|
627
|
-
const faf = parseYaml("project:\n name: Web1\n type: webapp");
|
|
628
|
-
const result = calculateScore(faf);
|
|
629
|
-
expect(result.projectType).toBe("webapp");
|
|
630
|
-
});
|
|
631
|
-
|
|
632
|
-
test.concurrent("concurrent test 3: api scoring", async () => {
|
|
633
|
-
const faf = parseYaml("project:\n name: API1\n type: api");
|
|
634
|
-
const result = calculateScore(faf);
|
|
635
|
-
expect(result.projectType).toBe("api");
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
test.concurrent("concurrent test 4: fullstack scoring", async () => {
|
|
639
|
-
const faf = parseYaml("project:\n name: Full1\n type: fullstack");
|
|
640
|
-
const result = calculateScore(faf);
|
|
641
|
-
expect(result.projectType).toBe("fullstack");
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
test.concurrent("concurrent test 5: tier calculation", async () => {
|
|
645
|
-
const tier = getTier(85);
|
|
646
|
-
expect(tier.name).toBe("Bronze");
|
|
647
|
-
});
|
|
648
|
-
});
|
|
649
|
-
|
|
650
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
651
|
-
// WJTTC RACE 8: MOCKING & SPYING (Bun Feature Showcase)
|
|
652
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
653
|
-
|
|
654
|
-
describe("🏁 WJTTC Race 8: Mocking & Spying", () => {
|
|
655
|
-
describe("mock function basics", () => {
|
|
656
|
-
test("mock tracks calls", () => {
|
|
657
|
-
const fn = mock(() => "result");
|
|
658
|
-
fn("arg1");
|
|
659
|
-
fn("arg2");
|
|
660
|
-
|
|
661
|
-
expect(fn).toHaveBeenCalledTimes(2);
|
|
662
|
-
expect(fn.mock.calls[0][0]).toBe("arg1");
|
|
663
|
-
expect(fn.mock.calls[1][0]).toBe("arg2");
|
|
664
|
-
});
|
|
665
|
-
|
|
666
|
-
test("mock can return custom values", () => {
|
|
667
|
-
const fn = mock(() => 42);
|
|
668
|
-
expect(fn()).toBe(42);
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
test("mockReturnValue works", () => {
|
|
672
|
-
const fn = mock().mockReturnValue("custom");
|
|
673
|
-
expect(fn()).toBe("custom");
|
|
674
|
-
});
|
|
675
|
-
|
|
676
|
-
test("mockImplementation works", () => {
|
|
677
|
-
const fn = mock().mockImplementation((x: number) => x * 2);
|
|
678
|
-
expect(fn(5)).toBe(10);
|
|
679
|
-
});
|
|
680
|
-
});
|
|
681
|
-
|
|
682
|
-
describe("spyOn functionality", () => {
|
|
683
|
-
test("spyOn tracks method calls", () => {
|
|
684
|
-
const obj = {
|
|
685
|
-
method: (x: number) => x + 1,
|
|
686
|
-
};
|
|
687
|
-
|
|
688
|
-
const spy = spyOn(obj, "method");
|
|
689
|
-
obj.method(5);
|
|
690
|
-
|
|
691
|
-
expect(spy).toHaveBeenCalledWith(5);
|
|
692
|
-
});
|
|
693
|
-
});
|
|
694
|
-
|
|
695
|
-
describe("mock assertions", () => {
|
|
696
|
-
test("toHaveBeenCalled works", () => {
|
|
697
|
-
const fn = mock();
|
|
698
|
-
fn();
|
|
699
|
-
expect(fn).toHaveBeenCalled();
|
|
700
|
-
});
|
|
701
|
-
|
|
702
|
-
test("toHaveBeenCalledWith works", () => {
|
|
703
|
-
const fn = mock();
|
|
704
|
-
fn("hello", 123);
|
|
705
|
-
expect(fn).toHaveBeenCalledWith("hello", 123);
|
|
706
|
-
});
|
|
707
|
-
|
|
708
|
-
test("toHaveReturned works", () => {
|
|
709
|
-
const fn = mock(() => "value");
|
|
710
|
-
fn();
|
|
711
|
-
expect(fn).toHaveReturned();
|
|
712
|
-
});
|
|
713
|
-
});
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
717
|
-
// WJTTC RACE 9: LIFECYCLE HOOKS DEMONSTRATION
|
|
718
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
719
|
-
|
|
720
|
-
describe("🏁 WJTTC Race 9: Lifecycle Hooks", () => {
|
|
721
|
-
let setupValue: string;
|
|
722
|
-
let testCount: number;
|
|
723
|
-
|
|
724
|
-
beforeAll(() => {
|
|
725
|
-
setupValue = "initialized";
|
|
726
|
-
testCount = 0;
|
|
727
|
-
});
|
|
728
|
-
|
|
729
|
-
beforeEach(() => {
|
|
730
|
-
testCount++;
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
afterEach(() => {
|
|
734
|
-
// Cleanup after each test
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
afterAll(() => {
|
|
738
|
-
// Final cleanup
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
test("beforeAll sets up value", () => {
|
|
742
|
-
expect(setupValue).toBe("initialized");
|
|
743
|
-
});
|
|
744
|
-
|
|
745
|
-
test("beforeEach increments counter (first)", () => {
|
|
746
|
-
expect(testCount).toBeGreaterThan(0);
|
|
747
|
-
});
|
|
748
|
-
|
|
749
|
-
test("beforeEach increments counter (second)", () => {
|
|
750
|
-
expect(testCount).toBeGreaterThan(1);
|
|
751
|
-
});
|
|
752
|
-
});
|
|
753
|
-
|
|
754
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
755
|
-
// WJTTC RACE 10: MATCHER SHOWCASE
|
|
756
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
757
|
-
|
|
758
|
-
describe("🏁 WJTTC Race 10: Matcher Showcase", () => {
|
|
759
|
-
describe("equality matchers", () => {
|
|
760
|
-
test("toBe for strict equality", () => {
|
|
761
|
-
expect(1).toBe(1);
|
|
762
|
-
expect("hello").toBe("hello");
|
|
763
|
-
});
|
|
764
|
-
|
|
765
|
-
test("toEqual for deep equality", () => {
|
|
766
|
-
expect({ a: 1 }).toEqual({ a: 1 });
|
|
767
|
-
expect([1, 2, 3]).toEqual([1, 2, 3]);
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
test("toStrictEqual for strict deep equality", () => {
|
|
771
|
-
expect({ a: 1 }).toStrictEqual({ a: 1 });
|
|
772
|
-
});
|
|
773
|
-
});
|
|
774
|
-
|
|
775
|
-
describe("truthiness matchers", () => {
|
|
776
|
-
test("toBeTruthy", () => {
|
|
777
|
-
expect(1).toBeTruthy();
|
|
778
|
-
expect("hello").toBeTruthy();
|
|
779
|
-
expect([]).toBeTruthy();
|
|
780
|
-
});
|
|
781
|
-
|
|
782
|
-
test("toBeFalsy", () => {
|
|
783
|
-
expect(0).toBeFalsy();
|
|
784
|
-
expect("").toBeFalsy();
|
|
785
|
-
expect(null).toBeFalsy();
|
|
786
|
-
});
|
|
787
|
-
|
|
788
|
-
test("toBeNull", () => {
|
|
789
|
-
expect(null).toBeNull();
|
|
790
|
-
});
|
|
791
|
-
|
|
792
|
-
test("toBeUndefined", () => {
|
|
793
|
-
expect(undefined).toBeUndefined();
|
|
794
|
-
});
|
|
795
|
-
|
|
796
|
-
test("toBeDefined", () => {
|
|
797
|
-
expect("hello").toBeDefined();
|
|
798
|
-
});
|
|
799
|
-
});
|
|
800
|
-
|
|
801
|
-
describe("number matchers", () => {
|
|
802
|
-
test("toBeGreaterThan", () => {
|
|
803
|
-
expect(10).toBeGreaterThan(5);
|
|
804
|
-
});
|
|
805
|
-
|
|
806
|
-
test("toBeGreaterThanOrEqual", () => {
|
|
807
|
-
expect(10).toBeGreaterThanOrEqual(10);
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
test("toBeLessThan", () => {
|
|
811
|
-
expect(5).toBeLessThan(10);
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
test("toBeLessThanOrEqual", () => {
|
|
815
|
-
expect(10).toBeLessThanOrEqual(10);
|
|
816
|
-
});
|
|
817
|
-
|
|
818
|
-
test("toBeCloseTo for floating point", () => {
|
|
819
|
-
expect(0.1 + 0.2).toBeCloseTo(0.3, 5);
|
|
820
|
-
});
|
|
821
|
-
});
|
|
822
|
-
|
|
823
|
-
describe("string matchers", () => {
|
|
824
|
-
test("toMatch with regex", () => {
|
|
825
|
-
expect("hello world").toMatch(/world/);
|
|
826
|
-
});
|
|
827
|
-
|
|
828
|
-
test("toContain for substring", () => {
|
|
829
|
-
expect("hello world").toContain("world");
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
describe("collection matchers", () => {
|
|
834
|
-
test("toContain for arrays", () => {
|
|
835
|
-
expect([1, 2, 3]).toContain(2);
|
|
836
|
-
});
|
|
837
|
-
|
|
838
|
-
test("toHaveLength", () => {
|
|
839
|
-
expect([1, 2, 3]).toHaveLength(3);
|
|
840
|
-
expect("hello").toHaveLength(5);
|
|
841
|
-
});
|
|
842
|
-
|
|
843
|
-
test("toHaveProperty", () => {
|
|
844
|
-
expect({ a: 1, b: 2 }).toHaveProperty("a");
|
|
845
|
-
expect({ a: 1, b: 2 }).toHaveProperty("a", 1);
|
|
846
|
-
});
|
|
847
|
-
});
|
|
848
|
-
|
|
849
|
-
describe("exception matchers", () => {
|
|
850
|
-
test("toThrow", () => {
|
|
851
|
-
expect(() => {
|
|
852
|
-
throw new Error("fail");
|
|
853
|
-
}).toThrow();
|
|
854
|
-
});
|
|
855
|
-
|
|
856
|
-
test("toThrow with message", () => {
|
|
857
|
-
expect(() => {
|
|
858
|
-
throw new Error("specific error");
|
|
859
|
-
}).toThrow("specific error");
|
|
860
|
-
});
|
|
861
|
-
|
|
862
|
-
test("toThrow with Error class", () => {
|
|
863
|
-
expect(() => {
|
|
864
|
-
throw new TypeError("type error");
|
|
865
|
-
}).toThrow(TypeError);
|
|
866
|
-
});
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
describe("type matchers", () => {
|
|
870
|
-
test("toBeInstanceOf", () => {
|
|
871
|
-
expect(new Date()).toBeInstanceOf(Date);
|
|
872
|
-
expect([]).toBeInstanceOf(Array);
|
|
873
|
-
});
|
|
874
|
-
});
|
|
875
|
-
});
|
|
876
|
-
|
|
877
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
878
|
-
// WJTTC FINAL LAP: INTEGRATION TESTS
|
|
879
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
880
|
-
|
|
881
|
-
describe("🏁 WJTTC Final Lap: Integration Tests", () => {
|
|
882
|
-
test("full workflow: parse → score → tier", () => {
|
|
883
|
-
const yaml = `
|
|
884
|
-
project:
|
|
885
|
-
name: Integration Test
|
|
886
|
-
goal: Test the full workflow
|
|
887
|
-
main_language: TypeScript
|
|
888
|
-
type: cli
|
|
889
|
-
human_context:
|
|
890
|
-
who: Developers
|
|
891
|
-
what: A CLI tool
|
|
892
|
-
why: Testing
|
|
893
|
-
where: Terminal
|
|
894
|
-
when: 2025
|
|
895
|
-
how: bun run
|
|
896
|
-
`;
|
|
897
|
-
const faf = parseYaml(yaml);
|
|
898
|
-
const result = calculateScore(faf);
|
|
899
|
-
const tierResult = getTier(result.score);
|
|
900
|
-
|
|
901
|
-
expect(faf.project.name).toBe("Integration Test");
|
|
902
|
-
expect(result.projectType).toBe("cli");
|
|
903
|
-
expect(result.filled).toBe(9);
|
|
904
|
-
expect(result.total).toBe(9);
|
|
905
|
-
expect(result.score).toBe(100);
|
|
906
|
-
expect(tierResult.name).toBe("Trophy");
|
|
907
|
-
expect(tierResult.emoji).toBe("🏆");
|
|
908
|
-
});
|
|
909
|
-
|
|
910
|
-
test("missing slots are correctly identified", () => {
|
|
911
|
-
const faf = parseYaml(`
|
|
912
|
-
project:
|
|
913
|
-
name: Partial
|
|
914
|
-
type: cli
|
|
915
|
-
human_context:
|
|
916
|
-
who: Someone
|
|
917
|
-
`);
|
|
918
|
-
const result = calculateScore(faf);
|
|
919
|
-
|
|
920
|
-
expect(result.missing).toContain("project.goal");
|
|
921
|
-
expect(result.missing).toContain("project.main_language");
|
|
922
|
-
expect(result.missing).toContain("human_context.what");
|
|
923
|
-
expect(result.missing).toContain("human_context.why");
|
|
924
|
-
expect(result.missing).toContain("human_context.where");
|
|
925
|
-
expect(result.missing).toContain("human_context.when");
|
|
926
|
-
expect(result.missing).toContain("human_context.how");
|
|
927
|
-
});
|
|
928
|
-
|
|
929
|
-
test("section breakdown is accurate", () => {
|
|
930
|
-
const faf = parseYaml(`
|
|
931
|
-
project:
|
|
932
|
-
name: Sectioned
|
|
933
|
-
goal: Test sections
|
|
934
|
-
type: cli
|
|
935
|
-
human_context:
|
|
936
|
-
who: Testers
|
|
937
|
-
what: Testing
|
|
938
|
-
why: Quality
|
|
939
|
-
`);
|
|
940
|
-
const result = calculateScore(faf);
|
|
941
|
-
|
|
942
|
-
expect(result.sections.project.filled).toBe(2);
|
|
943
|
-
expect(result.sections.project.total).toBe(3);
|
|
944
|
-
expect(result.sections.human.filled).toBe(3);
|
|
945
|
-
expect(result.sections.human.total).toBe(6);
|
|
946
|
-
});
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
950
|
-
// CHAMPIONSHIP SUMMARY
|
|
951
|
-
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
952
|
-
|
|
953
|
-
describe("🏆 Championship Summary", () => {
|
|
954
|
-
test("WJTTC Championship Grade: All systems operational", () => {
|
|
955
|
-
// This test confirms all modules are working together
|
|
956
|
-
const faf = parseYaml("project:\n name: Champion\n type: cli");
|
|
957
|
-
const result = calculateScore(faf);
|
|
958
|
-
const tier = getTier(result.score);
|
|
959
|
-
|
|
960
|
-
expect(faf).toBeDefined();
|
|
961
|
-
expect(result).toBeDefined();
|
|
962
|
-
expect(tier).toBeDefined();
|
|
963
|
-
expect(SLOTS).toBeDefined();
|
|
964
|
-
expect(TIERS).toBeDefined();
|
|
965
|
-
});
|
|
966
|
-
});
|