Web Automation
/automate writes tests into the harness the project
already has. Set one up first with /tests browser — see
Testing Setup.
Syntaxis never migrates a project from one runner to another as a side effect of automating a case.
What gets automated
Section titled “What gets automated”A case is automated when all of these hold:
- its behaviour is stable — a flow still being redesigned churns the test
- it is deterministic — same input, same observable outcome
- it will repeat — as a regression guard or across platforms
- the harness can actually observe its expected result
Cases stay manual, and are reported as such, when they need visual judgement, a real payment or third-party account, or a browser permission dialog the runner cannot reach.
Selector policy
Section titled “Selector policy”This is the single biggest cause of a suite people stop trusting. In priority order:
- Role and accessible name —
getByRole("button", { name: "Checkout" }). It breaks when the user-visible contract breaks, which is correct, and it fails when a control is inaccessible, which is a real defect. - Label, placeholder, or visible text for controls without a clear role.
- A dedicated test attribute such as
data-testidwhen nothing stable exists. Adding it to the application is a small legitimate change. - Never CSS structure, generated class names,
nth-child, or DOM XPath. Those encode layout, and layout is meant to change.
Playwright
Section titled “Playwright”The default for a new harness.
import { expect, test } from "@playwright/test";
test("TC-014 checkout rejects an expired card", async ({ page }) => { await page.goto("/cart"); await page.getByRole("button", { name: "Checkout" }).click(); await page.getByRole("radio", { name: /ending 4242/ }).check(); await page.getByRole("button", { name: "Place order" }).click();
// The expected result, both halves. await expect(page.getByRole("alert")).toContainText("expired"); await expect(page.getByRole("listitem")).toHaveCount(1);});expectauto-waits and retries until its timeout.page.waitForTimeout(...)is the fixed sleep Syntaxis forbids.- Reuse authentication through a storage-state setup project, and keep the state file out of Git.
- The case’s Platforms field decides which browsers it needs. Three engines do not run by default when the plan names one.
Cypress
Section titled “Cypress”Used when the project already has it.
it("TC-014 rejects an expired card", () => { cy.visit("/cart"); cy.findByRole("button", { name: "Checkout" }).click(); cy.findByRole("radio", { name: /ending 4242/ }).check(); cy.findByRole("button", { name: "Place order" }).click();
cy.findByRole("alert").should("contain.text", "expired"); cy.findByRole("listitem").should("have.length", 1);});Cypress cannot cross origins in one test without cy.origin. A case that routes
through an external identity provider may have to stay manual, and that is said
plainly rather than weakening the case.
WebdriverIO
Section titled “WebdriverIO”Used when the project already has it — often because the same harness also drives mobile through Appium. One harness and one reporting format across web and device is a real reason to reuse it, but not a reason to migrate to it.
Rules that apply to every harness
Section titled “Rules that apply to every harness”- Assert the full expected result, including its negative half.
- Reach the state the way a user would. Seeding through the database skips the code the case exists to exercise.
- Never add a fixed sleep, and never add a retry to mask non-determinism. A flaky test is not a passing test.
- Keep tests independent. Each creates and cleans up its own data; a suite that only passes in order is already broken.
- Never commit credentials. Read them from environment variables and document the names.
Shared setup goes in the harness’s existing fixture layer. Syntaxis does not build a page-object framework or a custom assertion library unless the project already has one.
Proving a test can fail
Section titled “Proving a test can fail”Required before any test is reported as covering a case:
- Invert the assertion.
- Run that single test and confirm it fails for the expected reason — not from a selector error or a timeout.
- Restore it and confirm it passes.
A test that fails with “element not found” during this check was asserting on a selector that never matched, and never covered the case at all.
Artifacts and CI
Section titled “Artifacts and CI”Traces, videos, screenshots, reports, and stored authentication stay out of Git.
Browser suites are not added to the project’s Verify command or a GitHub
workflow without a separate request. That gate belongs to
/ci.