2b8d9275548bcc9bd161b68f24848b545e1069ef
braney
Sun Sep 6 14:00:28 2026 -0700
docent: click raw:, and the first two scripts that do anything twice
Every one of the 29 scripts in the suite was a straight line: fresh cart, a few
steps, assert once. A bug that only exists on the repeat was invisible to that
shape, and the closed-bug pool holds several. These are the first two that
repeat a gesture.
rm36805 click a TOGA item, dismiss the pop-up, click the same item again.
One click passes on the broken build as happily as on the fixed one,
so the second click is the test.
rm27113 three clicks on the centre of the base-position ruler. A single
click zooms 3x about the base under the cursor, and the bug drifted
that centre one base left each time.
Both needed a gesture the language could not express. click: {track, item}
follows the item's own map-box href, which is right when the assertion is about
the hgc PAGE, but hgTracks answers a real click with an ajax DIALOG
(popUpHgcOrHgGene.hgc) and following the href never opens one. raw: true
presses the mouse where a user presses it and lets the page answer -- a
navigation, a dialog, or a new image in place, whichever arrives, waited for
rather than slept through. With no item name it is a bare point on the row,
which is the only way to click the ruler at all: the ruler carries no hgc map
boxes for areaXY to snap to.
Two things measured while writing these, both in the script comments. jQuery UI
HIDES a dialog on close rather than removing it, so the assertion has to be
has: "#hgcDialog:visible" -- without :visible the second half of rm36805 passes
whether or not the pop-up ever comes back. And one base is invisible at the
13kb windows the rest of the suite uses, where a pixel is fourteen bases, so
rm27113 starts at 100 bases and its three expected windows are arithmetic rather
than three observed strings: every one of them is centred on base 155,806,200.
#37014 is the other repeat-click bug in the pool and is deliberately NOT written.
Its reproducer no longer reaches the code it was about: 9e9ee32a4c8 (#37878)
later excluded crossTissue* tracks from the pop-up path altogether, and the
session's track is crossTissueMapsTissueCellType, so the click now navigates and
no dialog is involved. Writing it against a different bar chart track would be
pinning the ticket to something it was never about.
refs #38252
diff --git src/hg/utils/docent/README.md src/hg/utils/docent/README.md
index 49ddf53f3b7..89eb35bc4b5 100644
--- src/hg/utils/docent/README.md
+++ src/hg/utils/docent/README.md
@@ -1,436 +1,437 @@
# Docent — a language for authoring guided tours of the Genome Browser
A **docent** leads a tour and explains what you are looking at. A Docent script is
that tour, written down: an ordered list of high-level verbs (`go`, `hide`,
`track`, `mouseover`, `convert`, `drag`, `shot`) that drive a real browser against a
real server.
`docent.js` renders one script into three things at once:
- a **silent mp4** of the whole tour,
- a named **PNG still** at every `shot:` marker, and
- a loadable **session file** at every `session:` marker.
So a published figure is literally a frame of the tour, and the two can never drift
apart. A session file goes further: it hands back the state itself, so a reader can open
the view the figure was taken from instead of only looking at it. The surface syntax is YAML — so ordinary editors highlight it and no one has to
learn a new parser — but the language is the verb vocabulary layered on top, not the
serialization. Scripts are named `.docent.yaml` (a bare `.docent` works too).
The verbs deliberately encode *browser mechanics* rather than selectors: `convert:`
knows that the Hide-defaults checkbox reverts when the Assembly menu reloads,
`track: {clinvar: pack}` asks trackDb which containers and checkboxes that implies, and
`mouseover:` knows that a lifted track's DOM id gains a per-run `hub__` prefix. The author writes
intent; the renderer deals with the UI.
## Run
```
PW=/hive/groups/browser/uiTest/pw
PLAYWRIGHT_BROWSERS_PATH=$PW/browsers NODE_PATH=$PW/node_modules \
node docent.js AP1.docent.yaml
```
Needs `playwright`, `js-yaml`, and `ffmpeg`. At UCSC these live in one pinned shared
install at `/hive/groups/browser/uiTest/pw` (`browsers` for Chromium, `node_modules`
for the modules), which every browser-driving test in the tree uses; its `README.md`
records the pinned versions. Point the two variables above anywhere you have them.
Outputs, relative to the script's own directory:
- mp4 → `../.mp4` (override with `mp4:` or a second argument)
- stills → `stills//.png` (override with `stills:`)
- sessions → `sessions//.txt` (override the parent with `sessions:`)
`docent.mk` in this directory has the make rules — include it from a project that keeps
a set of scripts and it rebuilds only the ones whose source changed. See the usage
comment at the top of that file.
## Top of file (all optional)
```yaml
target: genome-test # or rr, hgwdev, hgwbeta, hgwdev-, or a full https://.../cgi-bin
db: hg38 # source assembly
position: chr7:155.8M # starting position (tracked from then on; `go:` sets it too)
reset: true # cartReset first (clean cart + fresh quickLift hub)
size: [1000, 760] # viewport
pix: 850 # browser image width
pace: 1.2 # seconds to dwell after each step
shotHold: 2.2 # extra seconds the video pauses at a shot
trackAnim: false # skip the visible dropdown gesture on `track:` (nav only)
fast: false # true = figures only: no dwells, no cursor animation, no mp4
scale: 1 # >1 = print-resolution stills (see Print resolution)
pinMouseovers: false # record every mouseover for `pinShot:` (see below)
mp4: ../myname.mp4 # override output paths if you want
stills: stills/AP1
sessions: sessions # PARENT of the session dirs; files land in //
sessionUrlBase: https://hgwdev-you.gi.ucsc.edu/~you/docent/sessions # see Sessions
```
`target:` defaults to genome-test, so a script that forgets to say where it runs will
not quietly hit someone's personal sandbox.
## Print resolution
A screen still is about 120 dpi across a journal's 7-inch column — fine on a monitor,
too coarse to print. `scale:` (or `DOCENT_SCALE=k`, or `make hires`) renders **the same
tour with k times the pixels**, which is not the same thing as enlarging the stills
afterwards: a still only ever has the pixels it was drawn with, so every layer is asked
to draw more of them.
```
make hires # every scenario at 3x -> stills.hires//
make hires SCALE=2 # 2x
make hires BASES=BP1 # one scenario
DOCENT_SCALE=3 DOCENT_STILLS=stills.hires DOCENT_FAST=1 node docent.js BP1.docent.yaml
```
What k does:
- **deviceScaleFactor: k**, with the viewport left at its 1x CSS size. The page lays out
exactly as at 1x — same line breaks, same jQuery-dialog width, same tooltip placement —
and all of it rasterizes with k times the pixels.
- **`pix` × k, `textSize` stepped up with it** (hgTracks' size ladder: 6, 8, 10, 12, 14,
18, 24, 34; 3x lands exactly on 24). The server draws a genuinely wider image, and the
bigger font keeps its layout decisions proportional — tick spacing, room for labels,
how features pack into rows. A wider image with an 8px font would be a different
picture, not a bigger one.
- **`zoom: 1/k` on the image table**, handing that wider image the 1x amount of layout
space, so one image pixel lands on one device pixel. Native resolution, no resampling.
- **The tooltip font is pinned back to its 1x size.** hgTracks takes the tooltip's
font-size from the browser text size (`window.browserTextSize` → `hg/js/utils.js`
`addMouseover`), which `textSize × k` has just tripled — and then the device pixel ratio
scales the same text a second time. Left alone, a 3x still gets tooltips 3x too big: the
popups swamp the figure and the last one pinned falls off the crop.
- **Every drawn row asks for k times its height** (`