test(harness): a grouped, multi-level fixture, and the first browser run of anr9
web/demo.improv has no groups and one category per visible axis, deliberately (gotcha 46), so the multi-level header, the group span, the collapse indicator and the inline group row -- exactly the code improvise-anr9 added -- had never been rendered anywhere a test could see (improvise-plcx). harness/fixtures/grouped.improv is served beside the shipped demo by harness/build.sh; web/demo.improv is untouched. Two categories on each visible axis, three row groups with one collapsed, two column groups. A missing seed is NOT an error -- web/worker.js answers a 404 with an empty model -- so every assertion is about labels the fixture really carries, never that a grid exists. Five injections, five distinct failures, patched in harness/serve so no rebuild stands between the fault and the run: slash-joining the level tuples; colSpan = 1 on the group headers; a stray cell on the inline group row; indicator(false) hard-coded; and the collapse removed from the document. One thing the ticket did not predict: the row frame's FIRST group header is never drawn, so the spec asserts two group rows where the document has three, with improvise-0o6x named on the line. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
@@ -16,6 +16,9 @@ bench/*.txt
|
|||||||
# The document the static site ships and seeds a fresh browser from
|
# The document the static site ships and seeds a fresh browser from
|
||||||
# (improvise-bck). A shipped asset, not somebody's working file.
|
# (improvise-bck). A shipped asset, not somebody's working file.
|
||||||
!web/*.improv
|
!web/*.improv
|
||||||
|
# The harness's own documents. A test fixture, not somebody's working file:
|
||||||
|
# the grouped, multi-level model no shipped document is (improvise-plcx).
|
||||||
|
!harness/fixtures/*.improv
|
||||||
|
|
||||||
# Beads / Dolt files (added by bd init)
|
# Beads / Dolt files (added by bd init)
|
||||||
.dolt/
|
.dolt/
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ serve="$root/harness/serve"
|
|||||||
"$root/web/build.sh" >/dev/null
|
"$root/web/build.sh" >/dev/null
|
||||||
"$root/web/assemble.sh" "$serve" harness >/dev/null
|
"$root/web/assemble.sh" "$serve" harness >/dev/null
|
||||||
|
|
||||||
|
# The harness's own documents, beside the shipped one. `web/demo.improv` is
|
||||||
|
# the front page and stays as it is (gotcha 46); a grouped, multi-level model
|
||||||
|
# is the only way to reach the header-level, group-span and collapse paths
|
||||||
|
# `improvise-anr9` added (improvise-plcx). A seed that 404s is NOT an error --
|
||||||
|
# worker.js answers it with an empty model -- so every spec over these asserts
|
||||||
|
# real labels rather than that a grid exists.
|
||||||
|
cp -f "$root"/harness/fixtures/*.improv "$serve/app/"
|
||||||
|
|
||||||
wasm-pack build "$root/harness/opfs-contract" \
|
wasm-pack build "$root/harness/opfs-contract" \
|
||||||
--target web --out-dir "$root/harness/www/opfs/pkg"
|
--target web --out-dir "$root/harness/www/opfs/pkg"
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
v2025-04-09
|
||||||
|
# Harness grouped fixture
|
||||||
|
Initial View: Grouped
|
||||||
|
|
||||||
|
## View: Grouped
|
||||||
|
Region: row
|
||||||
|
Store: row
|
||||||
|
Month: column
|
||||||
|
Channel: column
|
||||||
|
_Measure: page, Revenue
|
||||||
|
_Index: none
|
||||||
|
_Dim: none
|
||||||
|
collapsed: Region/Upland
|
||||||
|
format: ,.0
|
||||||
|
|
||||||
|
## Category: Region
|
||||||
|
- North[Coastal]
|
||||||
|
- South[Coastal]
|
||||||
|
- East[Inland]
|
||||||
|
- West[Inland]
|
||||||
|
- Ridge[Upland]
|
||||||
|
- Valley[Upland]
|
||||||
|
|
||||||
|
## Category: Store
|
||||||
|
- Main, Annex
|
||||||
|
|
||||||
|
## Category: Month
|
||||||
|
- Jan[Q1]
|
||||||
|
- Feb[Q1]
|
||||||
|
- Mar[Q2]
|
||||||
|
|
||||||
|
## Category: Channel
|
||||||
|
- Web, Shop
|
||||||
|
|
||||||
|
## Category: _Measure
|
||||||
|
- Revenue, Cost
|
||||||
|
|
||||||
|
## Data
|
||||||
|
Region=North, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 1000
|
||||||
|
Region=North, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 333
|
||||||
|
Region=North, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 1040
|
||||||
|
Region=North, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 346
|
||||||
|
Region=North, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 1110
|
||||||
|
Region=North, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 370
|
||||||
|
Region=North, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 1150
|
||||||
|
Region=North, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 383
|
||||||
|
Region=North, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 1220
|
||||||
|
Region=North, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 406
|
||||||
|
Region=North, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 1260
|
||||||
|
Region=North, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 420
|
||||||
|
Region=North, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 1300
|
||||||
|
Region=North, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 433
|
||||||
|
Region=North, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 1340
|
||||||
|
Region=North, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 446
|
||||||
|
Region=North, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 1410
|
||||||
|
Region=North, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 470
|
||||||
|
Region=North, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 1450
|
||||||
|
Region=North, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 483
|
||||||
|
Region=North, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 1520
|
||||||
|
Region=North, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 506
|
||||||
|
Region=North, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 1560
|
||||||
|
Region=North, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 520
|
||||||
|
Region=South, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 1700
|
||||||
|
Region=South, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 566
|
||||||
|
Region=South, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 1740
|
||||||
|
Region=South, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 580
|
||||||
|
Region=South, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 1810
|
||||||
|
Region=South, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 603
|
||||||
|
Region=South, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 1850
|
||||||
|
Region=South, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 616
|
||||||
|
Region=South, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 1920
|
||||||
|
Region=South, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 640
|
||||||
|
Region=South, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 1960
|
||||||
|
Region=South, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 653
|
||||||
|
Region=South, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 2000
|
||||||
|
Region=South, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 666
|
||||||
|
Region=South, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 2040
|
||||||
|
Region=South, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 680
|
||||||
|
Region=South, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 2110
|
||||||
|
Region=South, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 703
|
||||||
|
Region=South, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 2150
|
||||||
|
Region=South, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 716
|
||||||
|
Region=South, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 2220
|
||||||
|
Region=South, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 740
|
||||||
|
Region=South, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 2260
|
||||||
|
Region=South, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 753
|
||||||
|
Region=East, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 2400
|
||||||
|
Region=East, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 800
|
||||||
|
Region=East, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 2440
|
||||||
|
Region=East, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 813
|
||||||
|
Region=East, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 2510
|
||||||
|
Region=East, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 836
|
||||||
|
Region=East, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 2550
|
||||||
|
Region=East, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 850
|
||||||
|
Region=East, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 2620
|
||||||
|
Region=East, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 873
|
||||||
|
Region=East, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 2660
|
||||||
|
Region=East, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 886
|
||||||
|
Region=East, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 2700
|
||||||
|
Region=East, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 900
|
||||||
|
Region=East, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 2740
|
||||||
|
Region=East, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 913
|
||||||
|
Region=East, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 2810
|
||||||
|
Region=East, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 936
|
||||||
|
Region=East, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 2850
|
||||||
|
Region=East, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 950
|
||||||
|
Region=East, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 2920
|
||||||
|
Region=East, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 973
|
||||||
|
Region=East, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 2960
|
||||||
|
Region=East, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 986
|
||||||
|
Region=West, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 3100
|
||||||
|
Region=West, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 1033
|
||||||
|
Region=West, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 3140
|
||||||
|
Region=West, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 1046
|
||||||
|
Region=West, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 3210
|
||||||
|
Region=West, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 1070
|
||||||
|
Region=West, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 3250
|
||||||
|
Region=West, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 1083
|
||||||
|
Region=West, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 3320
|
||||||
|
Region=West, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 1106
|
||||||
|
Region=West, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 3360
|
||||||
|
Region=West, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 1120
|
||||||
|
Region=West, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 3400
|
||||||
|
Region=West, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 1133
|
||||||
|
Region=West, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 3440
|
||||||
|
Region=West, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 1146
|
||||||
|
Region=West, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 3510
|
||||||
|
Region=West, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 1170
|
||||||
|
Region=West, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 3550
|
||||||
|
Region=West, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 1183
|
||||||
|
Region=West, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 3620
|
||||||
|
Region=West, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 1206
|
||||||
|
Region=West, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 3660
|
||||||
|
Region=West, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 1220
|
||||||
|
Region=Ridge, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 3800
|
||||||
|
Region=Ridge, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 1266
|
||||||
|
Region=Ridge, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 3840
|
||||||
|
Region=Ridge, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 1280
|
||||||
|
Region=Ridge, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 3910
|
||||||
|
Region=Ridge, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 1303
|
||||||
|
Region=Ridge, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 3950
|
||||||
|
Region=Ridge, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 1316
|
||||||
|
Region=Ridge, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 4020
|
||||||
|
Region=Ridge, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 1340
|
||||||
|
Region=Ridge, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 4060
|
||||||
|
Region=Ridge, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 1353
|
||||||
|
Region=Ridge, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 4100
|
||||||
|
Region=Ridge, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 1366
|
||||||
|
Region=Ridge, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 4140
|
||||||
|
Region=Ridge, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 1380
|
||||||
|
Region=Ridge, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 4210
|
||||||
|
Region=Ridge, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 1403
|
||||||
|
Region=Ridge, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 4250
|
||||||
|
Region=Ridge, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 1416
|
||||||
|
Region=Ridge, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 4320
|
||||||
|
Region=Ridge, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 1440
|
||||||
|
Region=Ridge, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 4360
|
||||||
|
Region=Ridge, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 1453
|
||||||
|
Region=Valley, Store=Main, Month=Jan, Channel=Web, _Measure=Revenue = 4500
|
||||||
|
Region=Valley, Store=Main, Month=Jan, Channel=Web, _Measure=Cost = 1500
|
||||||
|
Region=Valley, Store=Main, Month=Jan, Channel=Shop, _Measure=Revenue = 4540
|
||||||
|
Region=Valley, Store=Main, Month=Jan, Channel=Shop, _Measure=Cost = 1513
|
||||||
|
Region=Valley, Store=Main, Month=Feb, Channel=Web, _Measure=Revenue = 4610
|
||||||
|
Region=Valley, Store=Main, Month=Feb, Channel=Web, _Measure=Cost = 1536
|
||||||
|
Region=Valley, Store=Main, Month=Feb, Channel=Shop, _Measure=Revenue = 4650
|
||||||
|
Region=Valley, Store=Main, Month=Feb, Channel=Shop, _Measure=Cost = 1550
|
||||||
|
Region=Valley, Store=Main, Month=Mar, Channel=Web, _Measure=Revenue = 4720
|
||||||
|
Region=Valley, Store=Main, Month=Mar, Channel=Web, _Measure=Cost = 1573
|
||||||
|
Region=Valley, Store=Main, Month=Mar, Channel=Shop, _Measure=Revenue = 4760
|
||||||
|
Region=Valley, Store=Main, Month=Mar, Channel=Shop, _Measure=Cost = 1586
|
||||||
|
Region=Valley, Store=Annex, Month=Jan, Channel=Web, _Measure=Revenue = 4800
|
||||||
|
Region=Valley, Store=Annex, Month=Jan, Channel=Web, _Measure=Cost = 1600
|
||||||
|
Region=Valley, Store=Annex, Month=Jan, Channel=Shop, _Measure=Revenue = 4840
|
||||||
|
Region=Valley, Store=Annex, Month=Jan, Channel=Shop, _Measure=Cost = 1613
|
||||||
|
Region=Valley, Store=Annex, Month=Feb, Channel=Web, _Measure=Revenue = 4910
|
||||||
|
Region=Valley, Store=Annex, Month=Feb, Channel=Web, _Measure=Cost = 1636
|
||||||
|
Region=Valley, Store=Annex, Month=Feb, Channel=Shop, _Measure=Revenue = 4950
|
||||||
|
Region=Valley, Store=Annex, Month=Feb, Channel=Shop, _Measure=Cost = 1650
|
||||||
|
Region=Valley, Store=Annex, Month=Mar, Channel=Web, _Measure=Revenue = 5020
|
||||||
|
Region=Valley, Store=Annex, Month=Mar, Channel=Web, _Measure=Cost = 1673
|
||||||
|
Region=Valley, Store=Annex, Month=Mar, Channel=Shop, _Measure=Revenue = 5060
|
||||||
|
Region=Valley, Store=Annex, Month=Mar, Channel=Shop, _Measure=Cost = 1686
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
// The grid paths no shipped document reaches: two categories on an axis, a
|
||||||
|
// group spanning its members, a collapsed one, and the inline group row that
|
||||||
|
// carries no cells. This is the first test anywhere that runs improvise-anr9's
|
||||||
|
// header-level code in a browser (improvise-plcx).
|
||||||
|
|
||||||
|
import { test, expect } from "@playwright/test";
|
||||||
|
|
||||||
|
/// `harness/fixtures/grouped.improv`, served beside the shipped demo by
|
||||||
|
/// `harness/build.sh`. A seed that 404s is NOT an error -- `web/worker.js`
|
||||||
|
/// answers a missing seed with an EMPTY model -- so everything below asserts
|
||||||
|
/// labels the fixture really carries. "A grid is drawn" would pass on the
|
||||||
|
/// empty document a typo in the filename produces.
|
||||||
|
async function openGrouped(page, doc) {
|
||||||
|
await page.goto(`/app/index.html?doc=${doc}&seed=grouped.improv`);
|
||||||
|
await expect(page.locator("#grid table")).toBeVisible({ timeout: 20_000 });
|
||||||
|
await expect(page.locator("#view-name")).toHaveText("Grouped");
|
||||||
|
await page.locator("#grid").focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The head as lines of text, group line included: `thead > tr` in order, each
|
||||||
|
/// as the labels it draws with the corner dropped. Read in one pass, because
|
||||||
|
/// what these tests are about is the RELATION between the lines.
|
||||||
|
const headLines = (page) =>
|
||||||
|
page.locator("#grid thead").evaluate((head) =>
|
||||||
|
[...head.rows].map((tr) => ({
|
||||||
|
group: tr.cells[0].className === "group" || tr.cells[1]?.className === "group",
|
||||||
|
cells: [...tr.cells].slice(1).map((th) => ({
|
||||||
|
text: th.textContent,
|
||||||
|
span: th.colSpan,
|
||||||
|
})),
|
||||||
|
cornerSpan: tr.cells[0].colSpan,
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
|
||||||
|
test.describe("a grouped, multi-level document", () => {
|
||||||
|
// The defect this is aimed at is the one improvise-anr9 fixed: a label tuple
|
||||||
|
// slash-joined into one string. That draws the RIGHT number of columns with
|
||||||
|
// the right words in them, so only the SHAPE of the head can see it.
|
||||||
|
test("two categories on an axis are two header lines, not one slash-joined one", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await openGrouped(page, "grouped-levels.improv");
|
||||||
|
const lines = await headLines(page);
|
||||||
|
|
||||||
|
// One group line and one line per column level: Month over Channel.
|
||||||
|
expect(lines.length).toBe(3);
|
||||||
|
expect(lines.map((l) => l.group)).toEqual([true, false, false]);
|
||||||
|
|
||||||
|
// Two row categories, so the corner spans two — and the row headers below
|
||||||
|
// must agree with it, or the table is ragged.
|
||||||
|
expect(lines.every((l) => l.cornerSpan === 2)).toBe(true);
|
||||||
|
await expect(page.locator("#grid tbody tr").first().locator("th")).toHaveCount(2);
|
||||||
|
|
||||||
|
// The level lines. The blanks are repeat suppression, decided in Rust by
|
||||||
|
// `AxisFrame::header_labels` — NOT an absent label, and not the page's
|
||||||
|
// idea. A slash-join would put "Jan/Web" on ONE line and none of this
|
||||||
|
// would hold; so would a page that drew only the innermost level.
|
||||||
|
expect(lines[1].cells.map((c) => c.text)).toEqual(["Jan", "", "Feb", "", "Mar", ""]);
|
||||||
|
expect(lines[2].cells.map((c) => c.text)).toEqual([
|
||||||
|
"Web",
|
||||||
|
"Shop",
|
||||||
|
"Web",
|
||||||
|
"Shop",
|
||||||
|
"Web",
|
||||||
|
"Shop",
|
||||||
|
]);
|
||||||
|
// Stated as well as implied: a joined label would satisfy a length check.
|
||||||
|
expect(lines.flatMap((l) => l.cells).every((c) => !c.text.includes("/"))).toBe(true);
|
||||||
|
|
||||||
|
// The row labels are the same rule on the other axis: North is drawn
|
||||||
|
// once and its second store's line is blank.
|
||||||
|
const rowHeads = await page
|
||||||
|
.locator("#grid tbody tr")
|
||||||
|
.evaluateAll((rows) =>
|
||||||
|
rows
|
||||||
|
.filter((tr) => tr.cells[0].className !== "group")
|
||||||
|
.map((tr) => [...tr.cells].filter((c) => c.tagName === "TH").map((c) => c.textContent)),
|
||||||
|
);
|
||||||
|
expect(rowHeads.slice(0, 4)).toEqual([
|
||||||
|
["North", "Main"],
|
||||||
|
["", "Annex"],
|
||||||
|
["South", "Main"],
|
||||||
|
["", "Annex"],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// A column group header spans exactly the columns under it. The span is
|
||||||
|
// `col_groups`' run length, and a wrong one is not a wrong word: it is a
|
||||||
|
// table whose header sits over the wrong data.
|
||||||
|
test("a column group spans its members, and the spans tile the columns", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await openGrouped(page, "grouped-spans.improv");
|
||||||
|
const lines = await headLines(page);
|
||||||
|
const groups = lines[0].cells;
|
||||||
|
|
||||||
|
// Q1 holds Jan and Feb, each split by Channel: 2 x 2. Q2 holds Mar: 1 x 2.
|
||||||
|
expect(groups.map((c) => c.text)).toEqual(["▼ Q1", "▼ Q2"]);
|
||||||
|
expect(groups.map((c) => c.span)).toEqual([4, 2]);
|
||||||
|
|
||||||
|
// And they tile: every column is under exactly one group header. A span
|
||||||
|
// that were merely "big enough" passes the pair above and fails here.
|
||||||
|
const columns = lines[2].cells.length;
|
||||||
|
expect(groups.reduce((n, c) => n + c.span, 0)).toBe(columns);
|
||||||
|
await expect(page.locator("#grid tbody tr").first().locator("td")).toHaveCount(columns);
|
||||||
|
});
|
||||||
|
|
||||||
|
// `RenderRow::Group` carries no cells, so the page CANNOT draw one with
|
||||||
|
// cells — which is worth asserting from outside, because the DOM is where
|
||||||
|
// that guarantee is cashed and the table goes ragged if it is lost.
|
||||||
|
test("an inline group row is a header alone, and its indicator reports the collapse", async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
await openGrouped(page, "grouped-collapse.improv");
|
||||||
|
|
||||||
|
const groupRows = page.locator("#grid tbody tr", {
|
||||||
|
has: page.locator("th.group"),
|
||||||
|
});
|
||||||
|
// Two, not three: `Coastal` is the frame's FIRST entry and a leading group
|
||||||
|
// header is dropped before it reaches the page — improvise-0o6x, open. If
|
||||||
|
// this count becomes 3, that bug is fixed and this line is the record of
|
||||||
|
// when. It is not an endorsement.
|
||||||
|
await expect(groupRows).toHaveCount(2);
|
||||||
|
await expect(groupRows).toHaveText(["▼ Inland", "▶ Upland"]);
|
||||||
|
// The glyph is `indicator(collapsed)`: a page that hard-coded either one
|
||||||
|
// passes on one of these rows and fails on the other.
|
||||||
|
expect(await groupRows.locator("td").count()).toBe(0);
|
||||||
|
await expect(groupRows.first().locator("th")).toHaveCount(1);
|
||||||
|
|
||||||
|
// The collapse is the document's, and it must have REMOVED rows: Upland's
|
||||||
|
// two regions are absent while Inland's two are here. Without this, a
|
||||||
|
// page that drew the indicator and ignored the frame would pass.
|
||||||
|
const labels = await page.locator("#grid tbody tr th[scope=row]").allTextContents();
|
||||||
|
expect(labels).toContain("East");
|
||||||
|
expect(labels).toContain("West");
|
||||||
|
expect(labels).not.toContain("Ridge");
|
||||||
|
expect(labels).not.toContain("Valley");
|
||||||
|
// 4 regions visible x 2 stores. Stated as a count as well, because a row
|
||||||
|
// drawn with empty labels would satisfy the four assertions above.
|
||||||
|
await expect(page.locator("#grid tbody tr").filter({ hasNot: page.locator("th.group") })).toHaveCount(
|
||||||
|
8,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user