ce8cbfc3d2ef6853f94bf80b0f8b5f389c60025b braney Sat Sep 5 09:21:32 2026 -0700 docent: two more expect: checks, a positional click:, and three fixes found writing tests Everything here was needed by a regression script that could not otherwise be written, or by one that failed for a reason that was not a bug. expect: {url:, noUrl:} is a substring check on the current address. Some things are visible nowhere else: which CGI a click reached, and what a form put in a query string. #36387's fix strips zero-width characters out of a search term before the position box submits it, and the character is invisible in the rendered page, so whether %E2%80%8B survives into the URL is the only evidence either way. expect: {has:, noHas:} takes a CSS selector, for a bug whose whole signature is WHERE something sits. #37785 attached a squishyPack track's center label to the wrong row: same rows drawn, same image height, same pixels, and only the row the label's image map hangs off changed, so rows:, height: and text: are all blind to it. Documented as a last resort, since an assertion on hgTracks' own ids breaks easily for reasons that are not bugs. click: now takes the positional forms mouseover: already had (at:/frac:/x:) and follows the item box nearest that point. Some tracks have no item that can be named at all: every GIAB Problematic Regions subtrack is type bigBed 3, so hgTracks writes an EMPTY i= into the hgc href and gives every box the title "Start of Exon (1/1)". Neither item: nor title: can pick one, and a raw mouse click on the data area is swallowed by the drag-select handler. The "item not found" message now picks the row's map boxes by MAP NAME instead of by a y-band, reports how many boxes are in the row, and falls back to a box's title when it has no name. On a quickLift target the band test dropped every item box, so the message said the GIAB row held three things when it held twenty-six, and the three it named were a density control and two exon arrows. goShow now scrolls to the top and takes the Login/Share links out of the way before clicking Search. Those links sit in an absolutely-positioned container at the top right of the header bar and on a wide page land on top of #goButton; Playwright then retries for the full 30s and fails with " subtree intercepts pointer events", which reads like a broken Search button rather than a covered one. Pressing Enter in the position box is not a substitute: by the time a 30s click timeout has been caught, the navigation wait armed before it has already expired. tests/pagechecks and tests/pagechecks.xfail cover all of it, per the rule in tests/README.txt that a verb we touch and find untested belongs on its list. The xfail aims all four new checks the wrong way at once, because a check that cannot fail is not a check. refs #38252 diff --git src/hg/utils/docent/tests/README.txt src/hg/utils/docent/tests/README.txt index bf7ca335cad..e8e240a92f8 100644 --- src/hg/utils/docent/tests/README.txt +++ src/hg/utils/docent/tests/README.txt @@ -1,103 +1,112 @@ Docent tests ------------ Run by hand, not by the kent tree's `make test`: make test # every *.docent.yaml here make test T=composite # just one make parity # one script FAST and slow, and twice over make derive # the derivation alone, against expected/ (no browser) make derive-accept # rewrite those baselines, then read `git diff expected/` Most tests drive a real browser against a real server, so they need the network and the shared Playwright install (/hive/groups/browser/uiTest/pw; see ../README.md). That is why none of this is part of the tree-wide test target: a broken network would fail the build. A test is an ordinary Docent script that asserts with `expect:`. It passes by exiting 0. `expect:` is the only verb that CHECKS anything, so a test with no `expect:` step in it tests nothing: `track:` accepts a name no assembly has and still exits 0. Other verbs do fail a run, so do not read the line above as "nothing else can stop it". A verb throws when it cannot do what it was told -- `mouseover:` cannot find the item, `loadSession:` cannot find the file, `drag:`, `convert:` and `go:` likewise -- and docent.js turns any step's throw into `step N (verb) failed` and exit 1. None of them looks at whether the page came out right, which is the part only `expect:` does. A script named *.xfail.docent.yaml is expected to FAIL, and the run fails if it passes. That is how a trap gets pinned rather than merely written down. `make derive` is the cheap half: DOCENT_DERIVE=1 resolves the `track:` steps against trackDb and prints the cart variables without opening a browser, in about a second. It is where Docent's own decisions live, and the baselines in expected/ are what catch a change to visVars() or tdbHideTargets() that a rendered page would hide. The trackDb listing is cached in $TMPDIR for a day (docent.js, TDB_TTL), and a cold fetch prints one provenance line that a warm run does not. That line would make the first `make derive` of the day differ from a baseline captured warm, for a reason that has nothing to do with trackDb changing, so the makefile strips it from both the run and the baseline. Everything else trackDb says about itself is kept, including the two lines that report a hub genome or an unreachable hubApi. What is covered --------------- selftest session: -> expect: -> loadSession:, on hg38 at SHH. Saves the cart, changes the view, restores it from the local file, checks rows both times. composite clinvar with clinvarCnv hidden: the two-request split (#37953). One request would leave clinvarCnv_sel=1 and the CNV row drawn. views hideKids on the VIEW that holds the subtrack, with the sibling views hidden by name. Also covers the `_sel` checkbox, since the subtrack is `parent wgEncodeRegDnaseSignal off`, and pins the superTrack side effect below. views.xfail the same thing aimed at the COMPOSITE instead, which loses the row. Expected to fail. supertrack varsInPubs hideKids + one member: `exact: true`, because a test that only checked the member was present would pass with all six drawn. urllen {cCREs: hideKids} must not become the 1701-variable, 42,020-character GET that Apache answered with 414. Checks `noText: "Too Long"`, since a 414 renders as a perfectly good page; the derive baseline pins it at 3. customtrack addCustomTrack: with inline BED, tabs and newlines surviving the trip. scale a 3x run draws the same rows as a 1x one. ordered `ordered: true` on rows:, and the fact that a row which was not drawn is reported by rows: alone rather than failing the order check as well. ordered.xfail the same two rows named the wrong way round. Expected to fail -- a flag that cannot fail is not a check, it is a second copy of the set test. + pagechecks the `expect:` checks that read the PAGE rather than the track image -- + `url:`/`noUrl:` on the address, `has:`/`noHas:` on a CSS selector -- plus + the positional form of `click:` (`{track:, frac:}`), which follows the + item box nearest a point. All four exist for bugs that rows:, height: and + text: cannot see: a search term's zero-width space stripped out of a URL + (#36387), a center label attached to the wrong row (#37785), and an item + that cannot be named at all because its track is `type bigBed 3` (#36335). + pagechecks the same four aimed the wrong way at once. Expected to fail. The message + .xfail names every check that failed, so one run says which of the four broke. expectfail an assertion that is plainly false. Expected to fail -- if it ever passes, .xfail `expect:` has stopped throwing and every other test here means nothing. make parity FAST vs slow, and a rerun, on composite. FAST drops the dwells and the recording and must not change what the page ends up showing; the rerun catches state left behind in the cart. Two things these tests found ---------------------------- Worth knowing before writing more: * Turning on anything under a superTrack sends `=show`, and every OTHER member then comes up at its own trackDb visibility -- so `hide: all` is undone for them. views asserts wgEncodeRegMarkH3k27ac comes back, rather than working around it. Whether Docent should be cleverer here is an open question, not a settled one. * `hideKids` on a VIEW has to enumerate its leaves (a view holds no sub-containers to stop at), so views sends 188 variables in a 6,986-character request. That is under Apache's 8,190 limit with less room than is comfortable. Its `noText: "Too Long"` is what turns a future overflow into a clear failure instead of a strange one. Still to write -------------- mouseover: by item: on stacked items, and the timing case where a neighbour's tooltip is still up on arrival pinShot: several tooltips in one figure, cursors drawn convert: quickLift onto a GenArk haplotype, hideDefaults re-checked -- note a session taken after it cannot be checked in, see #38046 drag: each of then: zoom / highlight / cancel addHub:, addPublicHub: the two hub attach paths (a stable hub URL is the hard part) montage: panel order, lettering, a named shot that was never taken goShow: the suggestion menu, including a `pick:` that matches nothing loadSession: the three remote forms -- only the local-file form is covered the YAML lint `{item:name}` with no space warns and drops the argument. This needs a test that reads stderr, which the harness does not do yet. A test that needs a stable server-side fixture (a hub, a custom track) should carry it in the script rather than assume something on disk.