self_test_bridge 2.3.1 copy "self_test_bridge: ^2.3.1" to clipboard
self_test_bridge: ^2.3.1 copied to clipboard

The socket and the report server for self_test: drives a running Flutter app from outside it, and serves each run as a local web page.

2.3.1 - 2026-10-15 #

  • tap waits for its widget to stop moving before it acts (SelfTestManager.tap(settleFirst: true)). A client taps straight after an exists that answered while a route was still sliding in, and that tap was lost: on a slower emulator a Dismantle button opened nothing.
  • self_test is now ^2.3.1, the first version with settleFirst.

2.3.0 - 2026-09-22 #

  • find answers through [SelfTestManager.tryFind] instead of catching [WidgetNotFoundError] and throwing it away. The error builds its candidate list eagerly, a full walk of the tree, and this command never read the message: measured on a real Sites list, a miss cost 12.1s against 0.2s for a hit. A client that walks indices until it runs out paid that on every walk, which is what made a list-ordering case slower than the code it replaced. Still not findAll, which would drop the index the caller sent.
  • The importer reads which keys a step may carry from [StepVocabulary.keysFor] rather than from a list kept here. The same list now backs the published schema, so the schema and the refusal cannot disagree.
  • Breaking for the constraint, not for the API: self_test is now ^2.3.0. keysFor and tryFind are both 2.3.0, and a resolve against 2.2.0 would compile against neither.

2.2.0 - 2026-09-22 #

  • getFacts, setFacts and clearFacts: the values a run captured, read and seeded over the socket, so the half of an end-to-end case that judges a value can be another process. A non-string fact is refused rather than stringified.
  • A recorded step keeps a locator's within scope.
  • The report page carries a scoped locator through an edit instead of dropping it, and shows the scope in the one-line rendering of a step.
  • Breaking for the constraint, not for the API: self_test is now ^2.2.0, not ^2.0.0. The facts commands call SelfTestManager.facts, seedFacts and forgetFacts, which 2.2.0 introduced, so the old constraint said a resolution would work that cannot compile.

2.1.0 - 2026-09-22 #

Everything here was found while driving two real apps against a live tenant, and every entry carries the test that pins it.

The tap recorder can be switched on over the wire #

recordScenarioStart and recordScenarioStop turn on the app's own recorder, the one that watches pointer events at the root and resolves each one against the element tree. That is the part a driver cannot do for itself: a driven tap already knows the locator it was given, so recording it proves nothing, and only a tap nobody named exercises the recorder at all. Until now the recorder could be reached only from the in-app panel, which a headless machine has no way to press.

Starting a second recording while one is open is refused rather than accepted, because a replay that runs while a recording is open feeds itself into that recording and the run still reports a pass. recordScenarioStop answers with the step count, since a recording that captured nothing looks exactly like a successful one from the outside.

A wait can watch what the app draws #

wait with exists, absent, gone or not_exists now polls a locator instead of reading the registered-widget map. That map is filled by wrapping widgets in SelfTestableWidget, so an app that is only ever driven from outside could wait for exactly one thing: a fixed number of milliseconds. A fixed pause long enough for the worst run is spent on every good one, and it is still a guess, which is how a slow screen turns into a red run and how the next person ends up raising the number.

Running out of budget throws. Answering {'success': false} would have been read as a pass, because the step reader only looks at passed, result and error.

A wait may name the condition it already means #

saveScript refuses a wait step that names a condition a stored script cannot watch, and says so with the assertion to write instead. duration is accepted as harmless company, because it is the one condition a script can carry out and it is read from the same field either way. Refusing it too would have rejected scripts that were already correct.

A step whose value would be dropped is refused by name #

saveScript read a step's value from value, text or duration and silently dropped every other key. A pause written {"action":"wait","ms":6000} imported with no value at all and replayed as the 500ms default: the scenario asked for six seconds, got half of one, and still reported green. Measured: 2.34s wall clock against 8.51s for the same pause written the documented way. Importing such a step now fails and names the key. mockChannel is exempt on purpose, because it validates its own spec.

A pause standing in front of an assertion is that assertion's budget #

Pinned with a test rather than changed. Folding the pause into the assertion that follows it is what makes a lengthened assertion cheap, and withdrawing it would make every fold in the suite silently spend its time twice.

2.0.0 - 2026-09-16 #

First release on pub.dev. Before this the bridge was publish_to: none and a consumer had to name a git dependency, which is a poor answer for the half of self_test that carries the socket, the report page and everything the command line talks to.

Fixed #

  • A back press at the root no longer closes the app. goBack answered an empty navigator with SystemNavigator.pop(). On iOS that does nothing, which is why it survived; on Android it puts the activity behind the launcher, and the bridge lives inside the app, so leaving the app takes the bridge with it. Every scenario opens with five or six goBacks, so the first one backgrounded the app and the next command timed out against a socket nobody was left to answer, while the pid, the port and adb forward all still looked healthy. Nothing left to pop is now a no-op reported as popped: false.

  • The runs list no longer squashes the scripts list. With flex: 0 1 auto the runs pane, which grows all day, pushed the scripts pane down to a third of its own content, and three scripts already had to be scrolled.

  • A step that names no widget is shown without a locator line, instead of an empty one.

Added #

  • The report page shows what the app threw during a run, under the step that caused it and in the exported HTML, apart from the step's own error. A replay refusing and an app breaking are different failures, and reading one as the other sends a reader looking for a widget that was never the problem.

  • An imported script may carry a compound mockChannel step, so one step can answer a plugin whose generated channel differs per platform.

  • assertAbove is accepted by the importer and drawn in the check family.

Changed #

  • The page builds its colours from SelfTestPalette, which the in-app panel also uses, so a run reads the same in a browser and on the device. A test pins the two together.

1.0.0 #

Shipped alongside self_test 1.0.0, from git rather than from pub.dev: the WebSocket bridge, the stored-script commands, and the report server with its page, export and script editor.

0
likes
150
points
515
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

The socket and the report server for self_test: drives a running Flutter app from outside it, and serves each run as a local web page.

Repository (GitHub)
View/report issues

Topics

#testing #flutter #automation #websocket

License

MIT (license)

Dependencies

dio, flutter, self_test, shared_preferences, web_socket_channel

More

Packages that depend on self_test_bridge