testbotron
v0.0.4
Published
Declarative UI test authoring on top of Webotron.
Readme
testbotron
Declarative UI tests on top of Webotron.
Testbotron lets you describe browser tests as plain TypeScript objects today, with a DSL shape that is intentionally friendly to YAML and JSON later. It is useful when you want tests to read like durable test cases rather than hand-written browser scripts.
Features
- Declarative test documents with reusable named locators
- Navigation, waits, clicks, typing, fixed delays, screenshots, and HTML snapshots
- Text, attribute, and snapshot assertions with string matchers
- Per-assertion failure policies:
continue,stopTest, andstopRun - JSON reports plus a small static HTML report viewer
- Built on Webotron, so you still keep direct control of the browser configuration
Install
bun add testbotron webotronwebotron is a peer dependency. Testbotron uses it to launch and control the browser.
CLI
bunx testbotron --help
bunx testbotron run ./tests/example.yaml
bunx testbotron run ./tests/example.json --test "data url example" --json-reportSupported CLI options:
--chrome-path <path>--test <name>--headless--headed--user-data-dir <dir>--report-dir <dir>--open-report--json-report--html-report
Quick start
import { runTest, type TestbotronDocument } from "testbotron";
const document: TestbotronDocument = {
version: 1,
defaults: {
browser: {
headless: true,
},
},
locators: {
learnMoreLink: { css: "a" },
},
tests: [
{
name: "example.com exposes its learn more link",
baseUrl: "https://example.com",
steps: [
{
navigate: {
url: "/",
waitUntil: "domcontentloaded",
timeoutMs: 15000,
},
},
{
waitFor: {
exists: "@learnMoreLink",
timeoutMs: 10000,
},
},
{
expect: {
text: {
target: "@learnMoreLink",
equals: "Learn more",
},
},
},
{
expect: {
attribute: {
target: "@learnMoreLink",
name: "href",
contains: "iana.org",
},
},
},
],
},
],
};
await runTest(document, {
chromePath: "C:/Program Files/Google/Chrome/Application/chrome.exe",
testName: "example.com exposes its learn more link",
report: {
outputDir: "reports",
},
});Core DSL
Each step is a one-key object, which keeps documents readable and simple to serialize:
steps: [
{ navigate: { url: "/", waitUntil: "domcontentloaded" } },
{ waitFor: { exists: "@searchBox", timeoutMs: 10000 } },
{
type: {
target: "@searchBox",
text: "solidjs",
perCharacterDelayMs: 120,
},
},
{ click: { target: "@submitButton" } },
{ wait: 5000 },
{ screenshot: { path: "artifacts/results.png" } },
]Current executable step support includes:
navigatewaitwaitFor.readyStatewaitFor.existsclicktypepressexpect.textexpect.attributesnapshotHtmlexpect.snapshotHtmlscreenshot
The DSL types already sketch additional future verbs, but the runner is still intentionally small in this early version.
Locators
Named locators make test steps easier to read and reuse:
locators: {
searchBox: { css: "textarea[name='q']" },
submitButton: { css: "input[name='btnK']" },
}{ waitFor: { exists: "@searchBox" } }
{ click: { target: "@submitButton" } }The broader DSL type model includes semantic locator shapes such as byRole, byText, and byLabel. The current executable runner slice supports CSS locators for the implemented browser actions and assertions.
Assertions
String-based assertions support:
equalsnotEqualscontainsnotContainsstartsWithendsWithmatches
{
expect: {
text: {
target: "@heading",
startsWith: "Dash",
},
},
}Assertions default to stopTest, but you can change that at the document, test, or individual assertion level:
defaults: {
assertions: {
onFailure: "continue",
},
}HTML snapshots
Capture markup at one moment in time, then assert against that exact captured value later:
{
snapshotHtml: {
as: "learnMoreMarkup",
target: "@learnMoreLink",
mode: "outer",
},
},
{
expect: {
snapshotHtml: {
source: "@learnMoreMarkup",
contains: "iana.org",
},
},
}This is useful when you want reports to show the exact DOM fragment involved in a failure.
Reports
runTest() can write both machine-readable JSON and a simple HTML report:
await runTest(document, {
chromePath,
testName: "example.com exposes its learn more link",
report: {
outputDir: "reports",
openHtml: true,
},
});Every step is recorded with status, timing, input, result, and failure metadata. Reports are still written when a test fails, including failures that stop the current test.
For multi-test documents, use runTests() to execute the full suite and honor stopRun assertion policies.
If you want report content without writing files, pass writeToFiles: false and use the returned artifacts strings:
const result = await runTest(document, {
chromePath,
testName: "example.com exposes its learn more link",
report: {
writeToFiles: false,
},
});
console.log(result.artifacts?.json);
console.log(result.artifacts?.html);Browser configuration
Browser startup options are passed through to Webotron:
await runTest(document, {
chromePath,
testName,
browser: {
headless: false,
userDataDir: "./.tmp-browser-profile",
},
});Pass userDataDir when you want repeat runs to reuse a browser profile, or use Webotron profile options when you need isolated browser state.
Status
Testbotron is early and deliberately small. The current focus is a clear declarative model, reliable reporting, and a compact set of browser primitives that are easy to grow without turning the DSL into a disguised programming language.
