Skip to main content

Test conventions

How the tests in this repository are written. The shipped suite follows these rules, and so should every test you or an agent add. They are specified in openspec/specs/workshop/test-conventions/spec.md.

1. The criterion ID comes first​

A test that verifies a story criterion starts its name with the criterion's ID, <STORY>_<AC>:

WEB-006_AC-1 Order Total Adds Up

When one criterion needs several tests, they share the prefix: WEB-002_AC-1 Every Card Offers Add To Cart and WEB-002_AC-1 Card Prices Are The Product Prices. A test that verifies no criterion carries the smoke tag instead.

Why: a failing test names the requirement it protects, and results trace back to openspec/specs/shop/.

2. Locators live in resources​

Test files call keywords. Every locator is written inside a keyword under resources/.

Why: when the page changes, there is exactly one place to fix, and a test reads as behaviour rather than markup.

3. The stable contract first​

Build locators from what a person or an assistive technology perceives. That doesn't change when the markup is restyled:

UseExample
a role and its accessible namerole=button[name="Apply filters"]
a form field by its label, inside its formform[action="/checkout"] >> role=textbox[name="Email"]
visible textbutton:has-text("Add to cart")
a link targeta[href="/cart"]

Element ids, CSS classes and data-test hooks are implementation details. Pages rename and remove them.

Known deviation: resources/legacy.resource still finds some elements by id, class or data-test hook. It is kept deliberately: Module 8 heals exactly these locators. Never add a new keyword to it.

4. Quoted text means visible text​

When a specification quotes UI text, match it against the element's visible text, ignoring case, as a phrase contained in that text. Use the accessible name only where the specification speaks of a label or a name. openspec/specs/shop/interpretation-rules/spec.md has the full rules, with examples.

Example: the quoted "Add to Cart" is a button whose visible text reads "Add to cart". Its accessible name is the longer "Add Aurora Neural Headphones to cart", so a lookup by the exact name "Add to Cart" finds nothing.

5. Scope form fields to their form​

Always locate a field inside its form: form[action="/checkout"] >> role=textbox[name="Email"], not [name="email"].

Why: the same field name can appear in more than one form on a page. The checkout page also contains the sign-in dialog, which has its own email field.

6. Every test is isolated​

Every UI test runs in its own browser context, and therefore with its own cookies and its own empty cart. A test never applies a preset or resets a space. It creates the state it needs through the shop's pages or API, inside its own session.

Why: tests can run in any order, alone or together, locally or on the shared instance.

7. Tags​

TagOn
the story ID, for example WEB-002every test that verifies a criterion
smokeevery test that verifies no criterion
ui or apievery test, exactly one of the two
brokenthe two tests that are broken on purpose, and no other