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 "<a id=loginLink ...>
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/pagechecks.docent.yaml src/hg/utils/docent/tests/pagechecks.docent.yaml
new file mode 100644
index 00000000000..e1837901d12
--- /dev/null
+++ src/hg/utils/docent/tests/pagechecks.docent.yaml
@@ -0,0 +1,56 @@
+# The three `expect:` checks that look at the page rather than at the track image, plus
+# the positional form of `click:`. All four were added for the regression suite next door
+# (tests/regress), and tests/README.txt's rule is that a verb we touch and find untested
+# belongs here.
+#
+#   url: / noHas:   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 the URL is the only evidence.
+#   has: / noHas:   a CSS selector matches / matches nothing. For a bug whose whole
+#                   signature is WHERE something sits: #37785 drew the same rows, the same
+#                   height and the same pixels either way, and only the row its center
+#                   label's image map hung off changed.
+#   click: {track:, frac:}   the item box nearest a point, and its hgc link. The only way
+#                   in for a track whose items cannot be named -- a `type bigBed 3` gets
+#                   an EMPTY i= in its hgc href and the same title on every box.
+#
+# pagechecks.xfail.docent.yaml is the other half: the same view with each of the four
+# aimed the wrong way, because a check that cannot fail is not a check.
+target: genome-test
+db: hg38
+position: chr7:155799529-155812871
+reset: true
+fast: true
+steps:
+  - go: chr7:155799529-155812871
+  - hide: all
+  - track: {mane: pack}
+
+  # url: and noUrl: on a plain tracks view. `go:` navigates hgTracks directly, so the
+  # address carries the CGI and the position and no hgSearch.
+  - expect:
+      url: "/hgTracks"
+      noUrl: "/hgSearch"
+      rows: [ruler, mane]
+
+  # has: and noHas:. hgTracks puts each center label in a MAP named map_center_<track>,
+  # inside the data cell of the row it belongs to -- which is the structure #37785 broke.
+  # The ruler has no center label of its own, so that pair is the negative case.
+  - expect:
+      has:
+        - "#td_data_mane map[name=map_center_mane]"
+        - "#imgTbl tr#tr_mane"
+      noHas:
+        - "#td_data_ruler map[name=map_center_mane]"
+        - "map[name=map_center_noSuchTrack]"
+
+  # The positional click. frac: 0.5 is the middle of the row; mane HAS named items here,
+  # so this is not the case that needs it, but it is the case where the result can be
+  # checked -- the details page has to be the one for a MANE transcript.
+  - click: {track: mane, frac: 0.5}
+  - expect:
+      url: "/hgc"
+      text: "Position:"
+      noText: "Warning/Error"