Public: anyone can read this; the crew can change it.
The map's counts line draws §5's badge, .map-counts leaves the markup, and the limbs test reads the count, not the old markup #4327
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/4313-the-map-s-counts-line-draws-5-s-badge-ma → main 1 commit fork
3 files
changed +48
−10
src/web/pages/map.ts +10 −2
| @@ -918,10 +918,18 @@ export function countWords(words: readonly Word[]): Record<Word, number> { | ||
| 918 | 918 | |
| 919 | 919 | function countsLine(counts: Record<Word, number>): Html { |
| 920 | 920 | const always = new Set<Word>(["ready", "working", "blocked", "done"]); |
| 921 | return html`<span class="map-counts"> | |
| 921 | const tone: Partial<Record<Word, string>> = { | |
| 922 | ready: "success", | |
| 923 | working: "attn", | |
| 924 | waiting: "merged", | |
| 925 | parked: "attn", | |
| 926 | held: "attn", | |
| 927 | done: "outline", | |
| 928 | }; | |
| 929 | return html`<span data-counts> | |
| 922 | 930 | ${WORDS.filter((w) => always.has(w) || counts[w] > 0).map( |
| 923 | 931 | (w) => |
| 924 | html`<span><i data-state="${w}"></i>${w} <b>${counts[w]}</b></span>`, | |
| 932 | html`<span class="badge" data-variant="${tone[w] ?? "secondary"}" data-state="${w}">${w} <b>${counts[w]}</b></span> `, | |
| 925 | 933 | )} |
| 926 | 934 | </span>`; |
| 927 | 935 | } |
test/limbs.test.ts +3 −1
| @@ -452,7 +452,9 @@ describe("limb actions", () => { | ||
| 452 | 452 | expect(mapPage).toContain( |
| 453 | 453 | `aria-label="#${extra.number} One home: the footer — deferred: ${through}"`, |
| 454 | 454 | ); |
| 455 | expect(mapPage).toMatch(/data-state="deferred"><\/i>deferred <b>5<\/b>/); | |
| 455 | expect(mapPage).toMatch( | |
| 456 | /class="badge"[^>]*data-state="deferred"[^>]*>deferred <b>5<\/b><\/span>/, | |
| 457 | ); | |
| 456 | 458 | const issuePage = await ( |
| 457 | 459 | await get("alice", `/alice/engine/issues/${extra.number}`) |
| 458 | 460 | ).text(); |
test/map.test.ts +35 −7
| @@ -317,6 +317,20 @@ describe("the map, per app and across the forge", () => { | ||
| 317 | 317 | await rm(dataDir, { recursive: true, force: true }); |
| 318 | 318 | }); |
| 319 | 319 | |
| 320 | test("the counts line is §5 badges: every word and count present, the old name absent", async () => { | |
| 321 | const cookie = await signIn("alice"); | |
| 322 | const page = await ( | |
| 323 | await fetch(`${base}/alice/engine/map`, { headers: { cookie } }) | |
| 324 | ).text(); | |
| 325 | expect(page).not.toContain('class="map-counts"'); | |
| 326 | expect(page).not.toMatch(/<[^>]*map-counts/); | |
| 327 | for (const w of ["ready", "working", "blocked", "done"]) | |
| 328 | expect(page).toMatch( | |
| 329 | new RegExp(`class="badge"[^>]*data-state="${w}"[^>]*>${w} <b>\\d+</b></span>`), | |
| 330 | ); | |
| 331 | expect(page).toContain('class="map-node"'); | |
| 332 | }); | |
| 333 | ||
| 320 | 334 | test("a card says its word, who is on it, and wears a ring while the crew works", async () => { |
| 321 | 335 | const cookie = await signIn("alice"); |
| 322 | 336 | const page = await ( |
| @@ -345,12 +359,24 @@ describe("the map, per app and across the forge", () => { | ||
| 345 | 359 | // The edge from a done issue recedes with it. |
| 346 | 360 | expect(page).toContain('class="map-edge map-edge-done"'); |
| 347 | 361 | // The line under the drawing counts the words. |
| 348 | expect(page).toContain('<i data-state="ready"></i>ready <b>2</b>'); | |
| 349 | expect(page).toContain('<i data-state="working"></i>working <b>1</b>'); | |
| 350 | expect(page).toContain('<i data-state="blocked"></i>blocked <b>1</b>'); | |
| 351 | expect(page).toContain('<i data-state="done"></i>done <b>1</b>'); | |
| 352 | expect(page).toContain('<i data-state="waiting"></i>waiting <b>1</b>'); | |
| 353 | expect(page).toContain('<i data-state="parked"></i>parked <b>1</b>'); | |
| 362 | expect(page).toMatch( | |
| 363 | /data-state="ready"[^>]*>ready <b>2<\/b><\/span>/, | |
| 364 | ); | |
| 365 | expect(page).toMatch( | |
| 366 | /data-state="working"[^>]*>working <b>1<\/b><\/span>/, | |
| 367 | ); | |
| 368 | expect(page).toMatch( | |
| 369 | /data-state="blocked"[^>]*>blocked <b>1<\/b><\/span>/, | |
| 370 | ); | |
| 371 | expect(page).toMatch( | |
| 372 | /data-state="done"[^>]*>done <b>1<\/b><\/span>/, | |
| 373 | ); | |
| 374 | expect(page).toMatch( | |
| 375 | /data-state="waiting"[^>]*>waiting <b>1<\/b><\/span>/, | |
| 376 | ); | |
| 377 | expect(page).toMatch( | |
| 378 | /data-state="parked"[^>]*>parked <b>1<\/b><\/span>/, | |
| 379 | ); | |
| 354 | 380 | // The lists beside it still read the graph; the critical path is the |
| 355 | 381 | // accent on the canvas alone, and spend and flow live on their boards. |
| 356 | 382 | expect(page).toContain("Ready now"); |
| @@ -410,7 +436,9 @@ describe("the map, per app and across the forge", () => { | ||
| 410 | 436 | // dormant has nothing ready and nobody working: it folds to its header. |
| 411 | 437 | expect(lane("lane-alice-dormant")).toContain('data-idle="1"'); |
| 412 | 438 | expect(lane("lane-alice-dormant")).not.toMatch(/\sopen\s*>/); |
| 413 | expect(page).toContain('<i data-state="parked"></i>parked <b>1</b>'); | |
| 439 | expect(page).toMatch( | |
| 440 | /data-state="parked"[^>]*>parked <b>1<\/b><\/span>/, | |
| 441 | ); | |
| 414 | 442 | // The lanes are §5's list and rows, not a page-local component (#3651). |
| 415 | 443 | expect(page).toMatch(/id="lanes"[^>]*>\s*<div\s+class="card"[^>]*>\s*<ul class="list">/); |
| 416 | 444 | expect(page).toMatch(/<details[^>]*id="lane-alice-engine"[^>]*>\s*<summary\s+class="row"/); |
new above
offline