2e5054b09838bacf29b31bb64fbd234471e7cb71
braney
  Wed Sep 16 13:00:17 2026 -0700
docent: a test for hgFind's copy of isParentVisible, on hgSearch, refs #37892

The third and last of the three.  isParentVisible() exists in the tree character
for character three times -- hg/lib/trackHub.c:1818, hg/hgCollection/hgCollection.c:269
and hg/lib/hgFind.c:2957 -- and each copy asks the same question, are this track's
containers visible, by reading the cart itself rather than going through the
accessor.  Nine scripts in regress/ cover the first, collection.docent.yaml covers
the second, and this covers the third.  A change to how visibility is stored has to
reach all three, or two of them quietly start answering from the trackDb default.

What this copy decides: isTrackVisible() at hgFind.c:2977 sets
category->visibility, and hgSearch groups its results by it -- "Visible Tracks"
when it is set, "Currently Hidden Tracks" when it is not (hgSearch.c:174,
js/hgSearch.js:44).  The symptom is a search result for a track you have on in the
browser filed under the hidden heading, where nobody looking for it will open it.

This one needs NO LOGIN, which is why it is worth having even with the other two
covered: it is the cheap one, about eight seconds.

wgEncodeGencodeBasicV49 is the track because it satisfies both constraints at
once.  It is in hgFindSpec, so it has search results at all; and its chain is
wgEncodeGencodeV49ViewGenes -> wgEncodeGencodeV49 (visibility 0) ->
wgEncodeGencodeSuper, which is `superTrack on` with no `show`, so isShow is FALSE
and a build that reads the cart wrongly stops the walk at the superTrack.  A
top-level or default-visible track would have passed on the broken build.

The hideKids step is not tidiness.  Showing the superTrack brings every other
archived version up at its own visibility -- V50 draws alongside and lands in
Visible Tracks too -- so without it the `exact:` would need rewriting every time
GENCODE ships a version.  It costs 31 variables, one per archived composite.

ENST00000297261 rather than a gene symbol: one of SHH's transcripts, it reaches
the GENCODE sets, and it searches 65 categories where SHH searches 319.  No count
is asserted anywhere -- every one of those numbers moves when a track is reloaded.

Same wait discipline as collection.docent.yaml, and for the same reason: wait for
EITHER heading, so a build that files the track on the wrong side fails at the
expect: naming the selector it wanted rather than timing out 15 seconds later with
a Playwright message.

Measured both ways.  The same script with the track left hidden fails at the
assertion:

step 7 (expect) failed: nothing matches
"li[id="Visible Tracks"] li#wgEncodeGencodeBasicV49"

No derive baseline on purpose: the hideKids expansion is one variable per archived
GENCODE version, so a baseline would go red twice a year for a reason that is not
a bug.  views is left out of expected/ for the same reason.

tests/ is 19 of 19 with four fixtures resolved and the derive baselines matching.

diff --git src/hg/utils/docent/tests/README.txt src/hg/utils/docent/tests/README.txt
index 08df45d7358..e2ccb73b8c9 100644
--- src/hg/utils/docent/tests/README.txt
+++ src/hg/utils/docent/tests/README.txt
@@ -95,30 +95,36 @@
                 shows up in. A path that dropped four rows left selftest green.
   firstrequest  a track turned on has to be drawn by the request that turned it on, with
                 no `go:`, `open:` or `convert:` in between. The bug it exists for lags by
                 exactly one request, so any script that navigates before asserting reads a
                 correct image and passes. It names wgEncodeRegMarkH3k4me1 for the reason
                 in its header: a top-level track or a default-visible child would pass on
                 the broken build too.
   collection    hgCollection, which no other script here or in regress/ reaches. It shares
                 visibility logic with hgTracks by COPY rather than by call:
                 hg/hgCollection/hgCollection.c carries its own isParentVisible(), a
                 verbatim copy of the one in hg/lib/trackHub.c, and it decides what goes in
                 the builder's "Visible Tracks" folder. Needs a login, so it is the one
                 script here that uses `login:`. Asserts on the folder's own jsTree class
                 first -- open when checkForVisible() found something, leaf when it did not
                 -- and then on the leaves inside it.
+  search        hgSearch, and the THIRD copy of isParentVisible() -- the one in
+                hg/lib/hgFind.c at line 2957, feeding isTrackVisible() at 2977. It sets
+                category->visibility, which is what files a result under "Visible Tracks"
+                rather than "Currently Hidden Tracks". Turns on a searchable GENCODE
+                archive subtrack, whose containers are hidden by default, and asserts the
+                result lands on the visible side. No login needed, unlike collection.
   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.
@@ -168,37 +174,30 @@
 --------------
 
   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.
-  hgFind's copy   there is a THIRD copy of isParentVisible(), in hg/lib/hgFind.c at line
-                  2957, feeding isTrackVisible() at 2977. It decides category->visibility,
-                  which is what puts a search result under "Visible Tracks" rather than
-                  "Hidden Tracks" on hgSearch (hgSearch.c:174, js/hgSearch.js:44). Same
-                  bug, same shape, and no login needed. wgEncodeGencodeBasicV49 is a
-                  searchable subtrack three levels under a hidden container, which is the
-                  right shape for it.
 
 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.
 
 The one fixture that cannot be carried anywhere is a login. hgCollection refuses a
 visitor who is not signed in, and the login cookie is checked against a salted hash, so
 a script that needs that page uses the `login:` step, and the step reads an account from
 ~/.docentLogin. That file is one [section] per HGCENTRAL DATABASE, because an account is
 a row in gbMembers in one of them: genome-test, hgwdev, every sandbox and every ticket
 park read hgcentraltest and share one account, while hgwbeta and the RR are separate sets
 of accounts. Which central a server reads is read from its hg.conf rather than guessed
 from the host -- a sandbox can point itself somewhere else, and two on hgwdev do today.
 `make preflight` says which account and which central it resolved for the server being
 driven, and refuses a file that is readable by group or other. No password is ever
 printed and none can be written in a script. ../README.md under `login` has the format.