b4e78d426dcdb47a20f1979025d5a0969ea79ac4
braney
Thu Sep 17 17:07:49 2026 -0700
docent: a box: check for where an element sits, and lists for text:/noText:, refs #37892
`expect:` could say what was in a page and never where it was on the screen.
has:/noHas: take a CSS selector, which describes the tree; color: reads pixels
but only inside a track's row. #38251 moved the narrow-window menu icon out of
the blue bar with every selector still matching and every word of the page still
there, and nothing in the language could ask about it.
box: takes `inside:` (every edge within another element's box, with `tolerance:`
px of slack), `clear:` (no overlap with anything a selector matches, `gap:` for a
minimum separation) and `height:`/`width:`. Every element `sel:` matches has to
satisfy every clause, so {sel: "ul.nice-menu > li", inside: "#main-menu-whole"}
reads as "every menu item is in the bar". Boxes are read in document
coordinates; an element with no box at all is skipped rather than treated as a
zero-sized box at the origin, which would sit "inside" anything. A failure
prints the measurement:
#topRightLinks is not inside #main-menu-whole: 114px above it
-- it is at 666,0 34x32, #main-menu-whole at 0,114 1000x32
The image height: and box's own height:/width: now share one cmpSize(), so the
two cannot drift into different comparison grammars.
text: and noText: take a list, the way rows:, has: and noHas: always did. They
have to: a list handed to a check that stringifies its argument fails OPEN --
["a", "b"] becomes "a,b", which no page contains, so it passes on anything, and
passes silently. Six scripts in one batch were written that way and all six
looked green.
pagechecks and its .xfail twin cover both, the xfail with one entry aimed wrongly
and one aimed rightly in each list, so it fails only if every entry is really
looked at on its own.
diff --git src/hg/utils/docent/tests/pagechecks.xfail.docent.yaml src/hg/utils/docent/tests/pagechecks.xfail.docent.yaml
index 6f70ae570d3..d7f9039280f 100644
--- src/hg/utils/docent/tests/pagechecks.xfail.docent.yaml
+++ src/hg/utils/docent/tests/pagechecks.xfail.docent.yaml
@@ -1,30 +1,54 @@
# EXPECTED TO FAIL. The other half of pagechecks.docent.yaml.
#
-# The same view with all four new checks aimed the wrong way at once. If this run ever
-# exits 0, at least one of url:, noUrl:, has: and noHas: has stopped comparing anything --
-# and since the regression suite next door leans on them for bugs that rows:, height: and
-# text: cannot see, they would pass silently over exactly those bugs.
+# The same view with every page-level check aimed the wrong way at once. If this run ever
+# exits 0, the checks below have stopped comparing anything -- and since the regression
+# suite next door leans on them for bugs that rows:, height: and text: cannot see, they
+# would pass silently over exactly those bugs.
#
-# One script rather than four because each check was also verified on its own by hand when
-# it was written, and four near-identical scripts is four things to keep in step. The
-# failure names every check that failed, so a single run says which of the four broke.
+# One script rather than one per check because each was also verified on its own by hand
+# when it was written, and a pile of near-identical scripts is a pile of things to keep in
+# step. The failure names every check that failed, so a single run says which broke. Note
+# what that does and does not buy: `expect:` throws when ANY check fails, so this script
+# goes green as long as at least one of them still works. It catches all of them breaking
+# at once -- a change to how `bad` is collected, say -- not one of them breaking alone.
#
-# The failure it should print, all four in one message:
-# url is "https://genome-test.gi.ucsc.edu/cgi-bin/hgTracks?...", wanted it to contain
+# The two list checks are here for a particular failure. text: and noText: took one string
+# until 2026-09-17, and a list handed to a check that stringifies its argument fails OPEN:
+# ["a", "b"] becomes "a,b", which no page contains, so the check passes on anything and
+# passes silently. Each list below holds one entry aimed wrongly and one aimed rightly, so
+# it fails only if every entry is really looked at on its own.
+#
+# The failure it should print, all eight in one message, in this order:
+# page does not contain "no page says this"; page contains "Visible Tracks"; url is
+# "https://genome-test.gi.ucsc.edu/cgi-bin/hgTracks?...", wanted it to contain
# "/hgSearch"; url contains "/hgTracks": ...; nothing matches
# "#td_data_ruler map[name=map_center_mane]"; 1 element(s) match
-# "#td_data_mane map[name=map_center_mane]", wanted none
+# "#td_data_mane map[name=map_center_mane]", wanted none; #topRightLinks is not inside
+# #main-menu: 32px below it -- it is at 964,0 30x32, #main-menu at 30,0 970x0;
+# #topRightLinks at 964,0 30x32 is not clear of 1 of 1 "#trToggle": 970,0 18x32;
+# #main-menu-whole height is 32px, wanted >=500
target: genome-test
db: hg38
position: chr7:155799529-155812871
reset: true
fast: true
steps:
- go: chr7:155799529-155812871
- hide: all
- track: {mane: pack}
- expect:
url: "/hgSearch"
noUrl: "/hgTracks"
+ # One entry each way, so a list that is not really iterated cannot satisfy this.
+ text: ["chr7:155,799,529-155,812,871", "no page says this"]
+ noText: ["no page says this either", "Visible Tracks"]
has: "#td_data_ruler map[name=map_center_mane]"
noHas: "#td_data_mane map[name=map_center_mane]"
+ box:
+ # #topRightLinks is a SIBLING of #main-menu inside the bar, to the right of it, so
+ # it can never be inside it.
+ - {sel: "#topRightLinks", inside: "#main-menu"}
+ # The icon overlaps its own hamburger button by construction.
+ - {sel: "#topRightLinks", clear: "#trToggle"}
+ # The bar is one 32px row and is not going to be 500 tall.
+ - {sel: "#main-menu-whole", height: ">=500"}