Action steps are the minimum vocabulary a team needs to drive a page: press, click, follow, attach. They read like plain English, so product owners can review feature files without translation, yet each one maps to a real Playwright interaction underneath. Every action goes through Playwright's actionability checks - the element must be attached, visible, stable, and enabled before the click fires - so you almost never need an explicit wait before an action step.
Each step comes in two flavors. The text variants (press "Log In", click "Contact Us", follow "About") match the exact visible label, which keeps scenarios readable and resilient to markup changes. The attribute variants (... by attr) reach elements that have no readable text - icon buttons, generated markup - by id, class, name, placeholder, or any data-* attribute. Attribute targets also resolve through the selector registry, so a registered friendly name like "buy button" works anywhere a raw selector does.
When a text-based action fails, webship-js does not dump a raw Playwright stack trace. It raises a friendly error naming the step and the target, plus hints: check that the visible text matches exactly (matching is case-sensitive and anchored to the whole label), switch to the by attr variant if the element has no readable text, or add a wait for ... to appear step if the element renders late.
Quick reference
| Step | What it does |
|---|---|
When I press "Log In" | Press a button, submit input, or link by its visible text. |
When I press "btn-pressid" by attr | Press a button by its attribute (id, class, name, placeholder, data-*). |
When I click "Contact Us" | Click a link or button by its visible text. |
When I click "#about-us-id" by attr | Click a link or button by its attribute (id, class, name, data-*). |
When I click "Edit" in the "John Smith" row | Click a clickable element inside a table row identified by text. |
When I follow "Contact Us" | Follow a link by its visible text. |
When I attach the file "profile-icon.jpg" to "#profile-icon-upload" | Attach a file from tests/assets/ to a file input. |
Steps in detail
When I press "Log In"
Presses a button-like element - <button>, <input type="button">, <input type="submit">, anything with role="button", or a link styled as a button - whose visible text matches the quoted label exactly. The pronoun and the words "the" and "button" are optional, so When I press "Submit", And I press the "Save as" button, and When we press "Cancel" all hit the same definition. If several elements share the label, the first match wins.
Given I am on "/test--when--i-press-button.html"
When I press the "Submit" button
Then I should see "Button Pressed Successfully"
When I press "btn-pressid" by attr
Presses a button located by attribute rather than label. With a bare value like "btn-pressid", the step fans out across id, class, name, data-testid, data-test-id, data-test, data-cy, aria-label, value, placeholder, and title and clicks the first hit. Name a specific attribute - by "placeholder" attribute, by "data-selector" attr - to match only that attribute (case-insensitively). Verbatim CSS like #id, .class, or [attr=value] passes through untouched. Note that submit buttons usually carry their label in the value attribute, hence the common pattern I press "Submit" by "value" attr.
Given I am on "/test--when--i-attach-file-to-feild.html"
When I press "Submit" by attr
And I press "Register" by "value" attr
When I click "Contact Us"
Clicks a link or button by its visible text. The searched pool covers <a>, <button>, role="button", and button/submit inputs, so it is the general-purpose "activate whatever carries this label" step. The text must match the whole label exactly (case-sensitive); a partial like "Contact" will not match "Contact Us". Failures raise the friendly hint block described in the intro.
Given I am on "/test--when--i-click-link.html"
When I click "About us link"
And I wait max of 2 seconds
Then I should see "About Us"
When I click "#about-us-id" by attr
The attribute-targeted counterpart of click, using the same selector fan-out as press ... by attr. Ideal for icon links, elements identified only by data-* hooks, and names registered in the selector registry. All of these are equivalent ways to name the same variant: by attr, by attribute, by "class" attr, by its "id" attribute.
Given I am on "/test--when--i-click-link.html"
When I click "#aboutUsid" by attr
When I click "aboutUsid" by its "id" attribute
When I click "aboutUs" by "class" attr
When I click "Edit" in the "John Smith" row
Scopes the click to a single table row: it finds the first <tr> containing the row-identifier text, then clicks the link or button inside that row whose label matches exactly. If the row holds no matching clickable element, the step falls back to clicking the exact text itself - useful for cells that toggle behavior via a click handler. This is the step for admin listings and data grids, where a dozen rows each carry their own "Edit" and "Delete" actions.
Given I am on "/test--when--i-click-text-in-table-row.html"
When I click "Edit" in the "John Smith" row
Then I should see "Performed (edit) action on (John Smith)" in the "#action-message" element by attr
When I click "Track" in the "Order #67890" row
When I follow "Contact Us"
Follows a link by its visible text. Unlike click, this step is accessibility-first: it tries Playwright's getByRole('link', { name }) with an exact accessible name, then falls back to an exact text match on <a> elements. Use it when you specifically mean "navigate via this link" - it will never accidentally press a button that happens to share the label.
Given I am on "/links.html"
When I follow "Read more"
Then I should be on "/about-us.html"
When I attach the file "profile-icon.jpg" to "#profile-icon-upload"
Uploads a file into an <input type="file">. The file name is resolved against the project's assets folder (default tests/assets/), so keep test fixtures there and reference them by bare name. The second argument is a CSS selector that must address the file input itself - #resume, input[name=logo], or [data-testid=csv-upload] all work. The word "the" is optional: attach file "resume.pdf" to "#resume" is equally valid.
Given I am on "/test--when--i-attach-file-to-feild.html"
When I attach the file "webshipco.png" to "#fileUpload"
And I press "Submit" by attr
Complete example
Adapted from the suite's own feature files, exercising every step in this group against the shipped fixture pages in examples/:
Feature: Drive a page with the core action steps
As a tester
I want to press, click, follow, and attach
So that I can automate any interactive flow
Scenario: Press a button by label and by attribute
Given I am on "/test--when--i-press-button.html"
When I press the "Submit" button
Then I should see "Button Pressed Successfully"
Scenario: Click a link by text, then by attribute
Given I am on "/test--when--i-click-link.html"
When I click "About us link"
And I wait max of 2 seconds
Then I should see "About Us"
Given I am on "/test--when--i-click-link.html"
When I click "#aboutUsid" by attr
Then I should see "About Us"
Scenario: Row-scoped actions in a data table
Given I am on "/test--when--i-click-text-in-table-row.html"
When I click "Edit" in the "John Smith" row
Then I should see "Performed (edit) action on (John Smith)" in the "#action-message" element by attr
When I click "Download" in the "Product A" row
Then I should see "Performed (download) action on (Product A)" in the "#action-message" element by attr
Scenario: Attach a file and submit
Given I am on "/test--when--i-attach-file-to-feild.html"
When I attach the file "webshipco.png" to "#fileUpload"
And I press "Submit" by attr
And I wait 2 seconds
Tips
- Text matching is exact and case-sensitive.
press "log in"will not find a button labeled Log In. - Regex metacharacters in labels are escaped automatically -
click "View Details" in the "Order #12345" rowand labels containing(,., or?just work. - If an element renders after an AJAX call, pair the action with a smart wait such as
When I wait for "#dashboard" to appear; the BBR wait engine returns as soon as the page settles, never a fixed sleep. - Prefer
followfor navigation links andpressfor form submission; keepclickfor mixed cases. The intent reads better in review. - Register long selectors once with
I add "buy button" selector for "..." css selectorand then useI click "buy button" by attreverywhere.