Skip to content

0.11.0

Latest

Choose a tag to compare

@mishan mishan released this 05 Nov 19:14
· 46 commits to main since this release
v0.11.0
1b8b73f

shotbox mutate runs a browser test against small changes to its page,
to find a test that breaks on one that shouldn't matter or passes on
one that should. wait node waits for a node's state, value or count,
and wait gone for one to go; wait idle waits until each program
answers, free on its main thread. capture --tree saves the tree
beside a picture, so compare says what each change is in;
--ignore-node leaves out a clock by its name, and compare --update
takes a reviewed change in one command. a11y diff says what changed
between two trees, and shoot waits for a node or an announcement.
record --sound keeps picture and sound in step on X11. A session's
end now stops what it started that left its process group, and a11y lint and contrast fail, rather than pass, when a program doesn't
answer the accessibility bus: see the changes to check.

Changes to check when upgrading

  • A session's end stops what it started that left its process
    group
    , as setsid does (a daemon, a test runner's helper, a
    browser's own launcher), as a session killed outright already did.
    That covers shotbox run, exec, shoot, stop, MCP's
    stop_session, Python's Session and each run of shotbox mutate.
    It also stops a lasting session started from inside it (shotbox start within shotbox run). Such a program kept running after the
    session ended. A recording's keeper finishing its file is spared, and
    so is a command put in the session from outside with --session.
  • A program that doesn't answer the accessibility bus (busy, or hung)
    fails a check rather than passing it.
    a11y lint and a11y contrast without --window fail with status 2, not all checked
    (Python's raise, MCP's say so as an error), rather than finding
    nothing wrong. Every a11y and wait node command that gives up adds
    ; program 1234 didn't answer: busy, or hung to its error, which
    alone read as a window, node or focus not being there. Each gives
    such a program its --timeout (15 s at most) to answer, not
    libatspi's 15 s: tree and lint took 16 s with --timeout 2, and
    with a11y's default --timeout of 10, a program slower than that to
    answer now counts as not answering.

Added

  • shotbox wait node RE --state STATE (enabled, focused, checked,
    expanded, any the tree says, or not-STATE), --value V (a field's
    text, a progress bar's percent) and --count N wait for a node in
    that state, with that value, or that many of them; shotbox wait gone RE until no showing node matches: a spinner, a dialog after OK. Each
    says what it saw last when it gives up. Python's wait_node(states=, value=, count=) and wait_gone, MCP's wait_for node and gone.
  • --ignore-node NAME leaves out a node by its name, where there's a
    tree: compare's from the tree beside either picture, wait match and
    wait change's from the live one as the wait starts; a clock or a
    spinner needn't be given as coordinates that drift with the layout.
    Python's wait_match and wait_change take ignore_node, MCP's
    wait_for ignore_node.
  • wait match --click [X,Y] clicks where the picture matched, at its
    middle or at X,Y in it, for a canvas or a game with no tree to click
    by; Python's wait_match(click=), MCP's wait_for match with click.
  • shotbox capture --tree (with --a11y) and shoot --tree write the
    accessible tree beside the picture, OUT.json, its bounds in the
    picture's pixels; compare, given pictures with one, says what each
    part that differs is in, on --diff's page too: 412 pixels, in 80x24 at 12,40: button "Save", and a change in text GTK 4 draws partly
    outside its widget is named by that widget, not the panel round it.
    --update takes the tree with the picture, and removes the
    reference's own if the new picture has none. Python's capture takes
    tree=True.
  • A recipe, in docs/recipes.md: a web app in Chrome, served
    once in a lasting session and pictured as many times as wanted, with
    the flags it needs and why, and what it doesn't.
  • shotbox compare REFDIR NEWDIR --update makes each new picture that
    fails the reference, and adds each with no reference yet, leaving those
    that pass untouched: a reviewed change is one command. --diff DIR
    also writes index.html, each failure's reference, new picture and
    diff side by side, with copies of both sides, so the directory alone is
    a CI artifact.
  • shotbox wait stable --crop G watches just that part, and --ignore G (repeatable) leaves out a box, so a page with a clock or a live
    chart can hold still round it; shoot --wait stable watches its
    --crop, and takes --ignore too. Python's wait_stable and MCP's
    wait_for stable take crop and ignore.
  • shotbox shoot --wait node:RE and --wait announce:RE wait, in
    order with the other waits, for a showing node with RE in its name or
    text, or for something said to a screen reader, as wait node and
    wait announce do: a one-shot picture of a page once a line says
    it's drawn. Either gives the session --a11y.
  • shotbox a11y diff BEFORE [AFTER] says what changed from a tree
    saved with a11y tree --json to another, or to the live one, as
    MCP's actions with changes say it: status 1 if anything did, 0 if
    nothing, 2 if it couldn't tell.
  • shotbox wait idle [--window RE] waits until each program with a
    window shown has answered just now, three times in a row: free on its
    main thread, which wait stable can't tell, as a busy program holds
    as still as an idle one. It asks by X11's _NET_WM_PING, and with
    --a11y GTK programs on the accessibility bus too, on Wayland as well;
    a browser's page, and a window nothing can ask, are said on stderr not
    to have been checked. Python has Screen.wait_idle(), and MCP's
    wait_for idle.
  • shotbox mutate runs a browser test against small changes to the page
    it tests, each run in a session of its own, to find a test that fails
    on a change that shouldn't matter (BRITTLE: the page at another size,
    a button moved 40 px) or passes on one that should (BLIND: a button
    hidden or dead, a total a cent off). The test opts in with await mutated(context) from the npm package; each change to a target is
    proven by Playwright looking for it as the test would, and a run whose
    change can't be proven is UNAPPLIED, with why. Python has
    shotbox.mutate().

Fixed

  • record --sound on X11 drifted from the sound on a busy machine, a
    tone up to a tenth of a second or more from its flash, and by
    different amounts within one file: ffmpeg's x11grab took the frames.
    shotbox takes them itself now, straight from the X server, with the
    pointer drawn on for --pointer, and hands them to ffmpeg raw, as
    wf-recorder's are on Wayland.
  • record --sound on Wayland lost the first second or so of sound:
    ffmpeg opened the sound only once it had looked over the first frames.
  • With --usage, a recording still finishing its file as its session
    ended was cut off, leaving an empty or unfinished file and no error:
    the session's slice was stopped with the recording's keeper in it.
    The keeper, and what it started, now go to a scope of their own
    first, and finish as they do without --usage.
  • With XDG_RUNTIME_DIR set to a directory that isn't there, as after a
    logout, or one you can't make a directory in, as under su, start,
    stop and sessions ended in a traceback. They now say so:
    XDG_RUNTIME_DIR is /run/user/1000, which isn't a directory: unset it, or point it at your runtime directory, or can't make /run/user/1000/shotbox: Permission denied.
  • a11y click couldn't click a Firefox check box inside its <label>
    by name, with the pointer or --action: Firefox puts a label of the
    same name round it, and each ruled the other out. Nor a Chromium field
    named by a <label for>, whose text in the label matched too. The
    field is clicked.
  • The focus renamed just as its window came up ("Check" to "Checked")
    was sometimes not announced under load, so wait announce for it
    gave up: the recorder looked at the focus again after the rename but
    before its event, and took the new name for the one it already had.
    A focus first looked at that late can still be missed.
  • With --failed and without --a11y, wait announce and Python's
    wait_focus, giving up, said the screen twice (the screen then: F; the screen then: F) and took the --failed picture twice. Once now.
  • MCP's actions with changes, and shotbox.a11y.changes(), said
    nothing of a program that didn't answer as the tree was read before,
    only after: what changed in its windows was left out unsaid, and
    could read as "Nothing changed". It's said either way.