Record a test with the agent
You say what the test must prove; the agent drives the app and every step that works is recorded. export_recorded_test saves the recording as a recorded test, a JSON file in uitest/ that replays from the palette, the agent, Maven and CI. This page records a short test on Order Desk with the real answers. A Java test, for more checks, is the last section.
With the skills installed, you ask in plain words, for example: "Record a test that opens a new order for Ada Shop Ltd and checks her account terms." The swing-ui-record-test skill takes the agent through the steps below.
1. Explore with reads
Section titled “1. Explore with reads”Before recording, the agent finds the route and every target with reads: snapshot, read, screenshot. Reads are never recorded.
2. Reset
Section titled “2. Reset”$UI session '{"op":"reset"}'{"reset":"dialogs"}A reset starts a fresh recording. Without a plugin it moves the pointer home and closes modal dialogs, nothing more: the app may still be on any screen. recording {"op":"start","reset":true} resets and starts a recording too, and the palette shows it. recording {"op":"stop"} ends it. The person watching can delete, redo or undo steps meanwhile: Record together with your agent.
3. Reach the starting screen
Section titled “3. Reach the starting screen”The first step must get to the test's own starting screen from anywhere: a menu item, a key shortcut (press) or, with a plugin, navigate. Reset and the starting screen explains why.
$UI menu '{"path":["File","New order"]}'4. Act
Section titled “4. Act”$UI type '{"target":{"role":"text","name":"Customer","scope":["tab:New order"]},"text":"Ada"}'$UI press '{"keys":"Enter"}'Each answer carries recorded, the step as it will be exported:
{"passed":true,"action":"type(text 'Customer' [tab:New order] 'Ada')","message":"","ms":18,"recorded":{"action":"type","target":{"scope":["tab:New order"],"role":"text","name":"Customer"},"text":"Ada"}}A step that fails is not recorded. A step that ran but was wrong is: edit the recording rather than patching it with more steps. recording {"op":"delete","n":<step>} removes it, recording {"op":"redo","from":<step>} cuts that step and the ones after it, replays the earlier ones from a reset and records on. Resetting and starting over is no longer needed.
5. Check with expect
Section titled “5. Check with expect”Checks are steps too, so they land in the test:
$UI expect '{"target":{"role":"tab","name":"New order · Ada Shop Ltd"},"state":["selected"]}'$UI expect '{"target":{"role":"label","name":"Invoice 30 days","match":"contains","scope":["tab:New order · Ada Shop Ltd"]},"state":["!hidden"]}'expect takes exactly one of state, text, value, tooltip, rows, cell, selected, shown or absent, and retries until it holds (10 seconds by default). "shown": true checks that the element is there and shown; "absent": true waits until it is gone, for example after a tab is closed. The agent asserts what you asked for, not everything it can see.
6. Save it as a recorded test
Section titled “6. Save it as a recorded test”Before saving, the agent gives each step and check a short sentence in plain words (recording {"op":"edit",…}, see Sentences and groups). Then:
$UI export_recorded_test '{"name":"new-order-for-ada","description":"Opens a new order for Ada Shop Ltd and checks her account terms"}'- It writes
uitest/new-order-for-ada.jsonand refuses a name that already exists. The steps are the ones recorded since the last reset. recording {"op":"export","as":"recorded"}writes the same file and keeps the recording, for the next test.- Order Desk's
place-order.jsonwas saved this way. The file's format is in Recorded test format.
7. Prove it from another screen
Section titled “7. Prove it from another screen”Put the app on some other screen, such as another tab, then run only the new test while the app is up: Load recorded test… and ▶ Run in the palette, or run_recorded_test '{"name":"new-order-for-ada"}'. It must pass from there. More ways to run it are in Run and edit a recorded test.
Java tests, for more checks
Section titled “Java tests, for more checks”When a test must read a value that changes per run, or check something the screen does not show, the same recording can be exported as a Java class instead. Which one to choose: Recorded tests or Java tests. export_test writes it as a JUnit 5 @UiTest class, which you review like any other code.
Export a Java test
Section titled “Export a Java test”$UI export_test '{"class":"NewOrderForAdaUiIT","package":"io.github.kormcrete.orderdesk","method":"opensANewOrderForAda"}'The answer names the file (savedTo, under -Dswingui.testsDir), the number of steps and how many were fragile, and holds the source:
package io.github.kormcrete.orderdesk;
import io.github.kormcrete.swingui.junit.Client;import io.github.kormcrete.swingui.junit.UiTest;import org.junit.jupiter.api.Test;
import static io.github.kormcrete.swingui.junit.UiAssertions.assertThat;
/** Recorded through swing-ui-agent. Replace literals the test creates with ui.runId(). */@UiTestclass NewOrderForAdaUiIT {
@Test void opensANewOrderForAda(Client ui) { ui.menu("File", "New order"); ui.tab("New order").field("Customer").type("Ada"); ui.press("Enter");
assertThat(ui.tab("New order · Ada Shop Ltd")).isSelected(); assertThat(ui.tab("New order · Ada Shop Ltd").label("Invoice 30 days").contains()).isVisible(); }}classis required and ends inUiIT.methoddefaults torecorded,packagetorecorded(or the plugin's export package).- An export clears the recording. Reset again before the next test.
export_testwrites underdirif you pass it (the test sources root, e.g.<project>/src/test/java; a relative one is under the app's working directory), else under-Dswingui.testsDir, else under the plugin's module, else under the app's working directory. In that last case, a working directory withoutpom.xmlorbuild.gradleadds awarningnaming the path.export_testrefuses a recording that did not begin at a reset, an empty one, bad names and an existing file. A refusal keeps the recording, so fix the problem and export again.- A test that does not begin by reaching its own screen is still written, with a
startWarning(see Reset and the starting screen). Reset and record again with amenuorpressfirst step.
Run it
Section titled “Run it”Put the app on some other screen, then run only the new class while the app is up. It must pass from there. How to run it is in Run the tests.
Reading a longer Java test
Section titled “Reading a longer Java test”PlaceOrderUiIT in the Order Desk repository was recorded the same way: File ▸ New order, Ada Shop Ltd, two products in the cart, the four checkout steps and the placed order on the Orders tab. Its opening steps:
@UiTestclass PlaceOrderUiIT {
@Test void placesAnOrderForAda(Client ui) { ui.menu("File", "New order");
assertThat(ui.tab("New order")).isSelected(); ui.inWindow("main").saveScreenshot("PlaceOrderUiIT/01-new-order.png", 2);
ui.tab("New order").field("Customer").type("Ada"); ui.press("Enter");
assertThat(ui.tab("New order · Ada Shop Ltd")).isSelected(); assertThat(ui.tab("New order · Ada Shop Ltd") .label("Account · Invoice 30 days · Credit limit €2,000.00").contains()).isVisible(); ui.inWindow("main").saveScreenshot("PlaceOrderUiIT/02-customer.png", 2);
ui.tab("New order · Ada Shop Ltd").table().row("Product", "A4 paper, 500 sheets").cell("SKU").click(); ui.tab("New order · Ada Shop Ltd").button("Add to cart").click(); ui.tab("New order · Ada Shop Ltd").table().row("Product", "Gel pen, black").cell("SKU").click(); ui.tab("New order · Ada Shop Ltd").button("Add to cart").click(); ui.tab("New order · Ada Shop Ltd").table().row("Product", "A4 paper, 500 sheets").cell("Qty") .type("3", null, Boolean.TRUE); ui.tab("New order · Ada Shop Ltd").table().row("Product", "Gel pen, black").cell("Qty") .type("10", null, Boolean.TRUE);
assertThat(ui.tab("New order · Ada Shop Ltd").label("Subtotal")).hasText("€26.70"); assertThat(ui.tab("New order · Ada Shop Ltd").label("Volume discount")).hasText("−€0.60");What a reviewer looks for:
- It begins at its own screen:
ui.menu("File", "New order")works from any tab. - Each check is something the test must prove: the account terms, the cart totals, the order on the Orders tab.
- Values the app makes up are not hard-coded. The order number is the app's next one, so the review replaced the recorded
ORD-1640with a read of the status bar. This is the one hand-written part:
// the number is the app's next one: wait for the status bar to name the order whose tab is selected, so a// rerun against a running app passes too (the last run's "Order … placed" shows until this one replaces it)String order = eventually("the status bar naming the new order", Duration.ofMillis(8000L), () -> { String placed = ui.main().label("placed").contains().text(); // "Order ORD-1640 placed" String number = placed.substring("Order ".length(), placed.indexOf(" placed")); return ui.tab(number).states().contains("selected") ? number : null;}, number -> number != null);assertThat(ui.tab(order)).isSelected();Add by hand only checks no tool expresses, as here with UiAssertions.eventually. To change a recorded step, fix it in the recording before the export (recording delete or redo, as in step 4), or record it again. The whole test, step by step with its screenshots, is in PlaceOrderUiIT, step by step.
What's next
Section titled “What's next”- Run and edit a recorded test, and Run the tests for Java tests.
- Record together with your agent: you say it in chat, the agent records, you watch.
- Record a test by showing it: record the steps yourself, and let the agent save them.
- Waiting and Reset and the starting screen.
Apache-2.0 · A Kormcrete project · For coding agents:llms.txt, llms-full.txt