RS-21803: Banded Rows test - #69
Conversation
Adds unit tests for CreateCustomTable's banded.rows/banded.cols behaviour: no CSS emitted by default, the odd/even row-banding rule (pinning the confirmed unscoped tr:nth-child(even) clause), custom fill colours, per-cell background suppression shared by both flags, the verbatim td:nth-child(2n+3)/even column rule, both flags together, and row-count independence of the emitted rule count. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…plan-mistake comment Pins the whole banded.cols verbatim CSS rule (both the 2n+3 and even clauses with their fill values) instead of only half of it, and adds a scoping-leak check mirroring the existing banded.rows one, confirming the unscoped `td:nth-child(even)` clause is a real, unticketed defect. Removes the incorrect "defect"/"corrected odd/even split" framing for the 2n+3/even selector pair, which is disjoint and correct as coded - that was a plan-authoring mistake, not a production bug. Guards the container-selector regex extraction with expect_length() so a failed match can't pass vacuously. Pins the paired custom banded.odd.fill / banded.even.fill values so swapping them would fail. Tightens two cell-fill assertions to include the trailing " ;" for anchoring. Moves the banded.rows/banded.cols section to after the last row.spans test it had been accidentally inserted into the middle of. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Merge the odd/even grepl pairs for banded.rows (default and custom fill cases) into single contiguous-string assertions, mirroring the existing banded.cols test. As two independent greps, inserting content between the odd and even clauses in production would not have failed these assertions. Also drop "deliberately" from a comment asserting unestablished author intent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds characterization tests for row and column banding in CreateCustomTable.
Changes:
- Verifies generated banding CSS, fill behavior, selector scoping, and defaults.
- Covers combined banding and row-count independence.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| res <- CreateCustomTable(x2, cell.fill = "rgb(3,3,3)", banded.cols = TRUE) | ||
| h <- normWs(tableHtml(res)) | ||
| expect_true(grepl(paste0("tbody td:nth-child(2n+3){background-color: rgb(250,250,250) ;}", | ||
| " td:nth-child(even){background-color: rgb(245,245,245) ;}"), | ||
| h, fixed = TRUE)) |
There was a problem hiding this comment.
Confirmed by mutation, and it was a real gap. banded.rows and banded.cols are separate cata() calls (createcustomtable.R:626-631) that each interpolate banded.odd.fill/banded.even.fill independently, and only the row branch was exercised with non-default values.
I hard-coded the defaults in the banded.cols call exactly as you describe:
cata(container.selector.name, 'tbody td:nth-child(2n+3){background-color:', 'rgb(250,250,250)',
';} td:nth-child(even){background-color:', 'rgb(245,245,245)', ';}')The suite stayed fully green at 181 passing. Against the same mutant the extended test now fails on 6 assertions.
Added three cases rather than one: banded.cols alone with rgb(1,1,1)/rgb(2,2,2), and banded.rows + banded.cols together. The both-at-once case matters because the two branches take the same argument pair — a regression that forwarded them in only one of the two rules would otherwise still pass the single-branch tests.
188 passing, 0 failures. Production is unchanged; the mutation was reverted.
| { | ||
| resBanded <- CreateCustomTable(x2, cell.fill = "rgb(3,3,3)", banded.rows = TRUE) | ||
| hBanded <- normWs(tableHtml(resBanded)) | ||
| expect_false(grepl("background: rgb(3,3,3) ;", hBanded, fixed = TRUE)) |
There was a problem hiding this comment.
You are right, the summary had it backwards. The test pins that enabling banding removes the configured cell.fill, not that cell.fill suppresses the banding.
Confirmed in production (createcustomtable.R:397-400):
if (!banded.rows && !banded.cols)
cell.fill <- matrix(rep(paste("background:", cell.fill, ";"), length = nrows * ncols), nrows, ncols)
else
cell.fill <- matrix("", nrows, ncols)Banding wins: the moment either banding argument is set, the cell-fill matrix is blanked, so the banded background is what shows. Summary corrected to say that, with the line reference and a note that the single-variable contrast against the unbanded case is what makes the assertion meaningful — with banded.rows = FALSE the same cell.fill value does reach the celldefault declaration.
The test names themselves already described the right direction ("banded.rows = TRUE drops the per-cell background from the celldefault CSS"), so only the PR body needed the fix.
…t column From a review pass over this PR. - The beforeEven/afterOddRule assertions in both banding scope tests could not fail for the reason they existed. If the even clause ever disappeared, regexpr(..., fixed = TRUE) returns -1L, substr(h, 1, -2) yields "", and expect_false(grepl(sel, "")) passes trivially. They were also strictly weaker than the exact-string pin above them, which asserts the odd selector through to the even one as one contiguous string and so already fails the moment a container prefix is inserted between the clauses. Removed, keeping the genuinely additive assertion that the first clause carries the prefix. - The comment in the banded.cols test noted that show.row.headers = FALSE leaves column 1 matching neither nth-child(2n+3) nor nth-child(even), so the first column is never banded - but nothing asserted it, leaving the gap with no regression anchor. Added a test asserting the emitted rule is byte-identical with and without row headers, which pins the defect and trips if anyone makes the selectors header-aware. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both banding defects this file pins were previously marked as having no ticket, which leaves whoever fixes them facing a red, deliberately-inverted test with no trail back to why. - RS-23594: the second clause of each banding rule is emitted without the container prefix, so it is not scoped to the table and applies to every table on the page. - RS-23595: the column selectors assume a row-header cell at nth-child(1) and are not adjusted when row headers are hidden, so the first data column matches neither 2n+3 nor even and is never banded. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Responds to a review comment on PR #69. banded.rows and banded.cols are separate cata() calls (createcustomtable.R:626-631) that each interpolate banded.odd.fill and banded.even.fill independently. Only the row branch was exercised with non-default fills; the banded.cols test used the defaults, so the column branch could have stopped forwarding those arguments and hard-coded rgb(250,250,250)/rgb(245,245,245) without any test failing. Confirmed by mutation: hard-coding the defaults in the banded.cols cata() call left the suite fully green at 181 passing. The extended test now fails on 6 assertions against the same mutant. Adds three cases to the custom-fills test: banded.cols alone, and both banded.rows and banded.cols together, so a regression that forwarded the arguments in only one of the two rules is caught as well. 188 passing, 0 failures, 0 warnings. R/createcustomtable.R is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Adds unit test coverage for the
banded.rowsandbanded.colsarguments ofCreateCustomTable(R/createcustomtable.R).banded.odd.fillwithbanded.even.fill, fails the test.nth-childrule is emitted at all when banding is off.td:nth-child(2n+3)/td:nth-child(even)pair as coded.banded.odd.fill/banded.even.fillreach both emitted rules. These are separatecata()calls that interpolate the arguments independently, so the row branch, the column branch, and both-at-once are each exercised with non-default values.cell.fillfrom thecelldefaultdeclaration, so the banded background is what shows. Banding wins overcell.fill, not the other way round (createcustomtable.R:397-400blanks the cell-fill matrix whenever either banding argument is set). Pinned as a single-variable contrast against the unbanded case, where the samecell.fillvalue does reach the declaration.expect_length(containerSel, 1).Pinned production defects
Two tests deliberately pin current buggy behaviour and will fail loudly when fixed. Both are ticketed.
1. The second clause of each banding rule is unscoped — RS-23594. Every other rule this function emits is namespaced under the generated container class. The banding rules are the exception: only the first clause carries the prefix, so
tr:nth-child(even)/td:nth-child(even)is emitted bare and applies to every table on the page. Affected tables need neither banding enabled nor to beCreateCustomTableoutput. Because the odd clause is scoped, the two halves split — unrelated tables pick up the banded fill on even rows only, which reads as inconsistent striping rather than an obvious fault.2.
banded.colsleaves the first column unbanded when row headers are hidden — RS-23595. The selectors assume a row-header cell atnth-child(1). Withshow.row.headers = FALSEthe data columns start at position 1, which matches neither2n+3({3,5,…}) noreven({2,4,…}). Verified: the emitted rule is byte-identical with and without row headers, so the selectors are not header-aware. The test asserts that equality, which is what trips if anyone makes them adapt.Plan corrections
Two of the plan's expectations did not survive checking and were corrected rather than encoded:
;.cata()writes viacat(..., sep = " "), so real output isbackground-color: rgb(250,250,250) ;— the plan's string never occurs. Tests pin the real text.2n+3/evencolumn selectors as overlapping and asks for that to be logged as a defect. They are provably disjoint:2n+3= {3, 5, 7, …} andeven= {2, 4, …}. With row headers shown the header cell occupiesnth-child(1), so data columns land on 2, 3, 4 and alternate correctly. Not a defect — the real defect in that rule is the header-hidden case above, which the plan did not raise.No production code is changed in this PR.
Jira: https://numbers.atlassian.net/browse/RS-21803
Stacked PR
Based on
RS-21803-rowspans(#68), notmaster— these plans all add tests totests/testthat/test-createcustomtable.Rand share its helpers. #66 has already merged to master, so the remaining merge order is #67, #68, then this. This branch is up to date with its base, and the diff against it is this plan's work only.Test plan
188 passing, 0 failures, 0 warnings (48
test_thatblocks; 8 added here). Also run underLANGUAGE=frwith identical results.🤖 Generated with Claude Code