Link Steps

The link steps assert the anchors on a page: that a link with a given visible text points at the right href, that it lives inside the right region of the page, that a link carries a specific title attribute, and whether an href is absolute or relative. They are quick wins for content QA and SEO reviews - a navigation menu that silently lost its Contact link, a footer Privacy link pointing at a stale path, or an internal link accidentally hardcoded to an absolute production URL all become one-line assertions.

Under the hood each step builds a Playwright locator from the visible text (a:has-text("..."), a substring match on the link's text) combined with an attribute selector. The href check is a contains match ([href*="..."]), so "/about" matches /about, /about-us, and https://example.com/about - quote as much of the path as you need to be unambiguous. The title checks, by contrast, require an exact attribute value. The "within the element" variants scope the search to a parent CSS selector, which is the right tool when the same link text appears in both the header and the footer.

These are count-based existence assertions: they check the DOM immediately rather than retrying, so run them after the page (or the AJAX update) has finished loading - pair with a wait step when asserting links injected by JavaScript. The one action step in the group, clicking a link by its title, is handy for icon-only links that have no readable text.

Quick reference

StepWhat it does
Then the link "About" with the href "/about" should existAssert a link with that visible text and an href containing the fragment exists.
Then the link "Home" with the href "/" within the element "#main-nav" should existSame assertion, scoped to a parent element.
Then the link "Sign in" with the href "/login" should not existAssert no link with that text + href fragment exists.
Then the link "Logout" with the href "/logout" within the element ".guest-nav" should not existNegative assertion scoped to a parent element.
Then the link with the title "Open menu" should existAssert at least one link has that exact title attribute.
Then the link with the title "Delete user" should not existAssert no link has that exact title attribute.
Then the link "Twitter" should be an absolute linkAssert the link's href starts with http:// or https://.
Then the link "Home" should not be an absolute linkAssert the link's href is relative (no scheme).
When I click on the link with the title "Open menu"Click the first link with that exact title attribute.

Steps in detail

Then the link "About" with the href "/about" should exist / should not exist

Asserts that at least one (or, for the negative form, zero) anchor whose visible text contains the first argument has an href containing the second argument. Both matches are substring matches, which keeps scenarios stable across query strings and domain changes - but be specific enough: asserting "/" matches almost every link, and the negative form of a too-loose fragment can never pass.

Given I am on "/links.html"
 Then the link "About" with the href "/about" should exist
  And the link "About" with the href "/missing" should not exist

Then the link "Home" with the href "/" within the element "#main-nav" should exist / should not exist

Scoped variants that only search inside the parent matched by the third argument (any CSS selector - #main-nav, footer, .breadcrumb). Use these to disambiguate repeated link text and to assert region-specific rules, such as no Logout link inside the guest navigation.

Then the link "Contact" with the href "/contact" within the element "#nav" should exist
 And the link "Logout" with the href "/logout" within the element ".guest-nav" should not exist

Then the link with the title "Open menu" should exist / should not exist

Asserts on the title attribute instead of visible text - the natural addressing scheme for icon links (hamburger menus, download buttons, print links). Unlike the href steps, the title must match exactly, including case.

Then the link with the title "About us" should exist
 And the link with the title "Nonexistent" should not exist

Then the link "Twitter" should be an absolute link / should not be an absolute link

Reads the href of the first link whose text matches and tests it against ^https?://. The positive form catches external links written as relative paths; the negative form catches internal links accidentally hardcoded with a full domain - a classic source of broken links after a domain or environment change. Both forms fail if the link has no href at all, and the failure message includes the actual href found.

Then the link "Example" should be an absolute link
 And the link "About" should not be an absolute link

When I click on the link with the title "Open menu"

Clicks the first anchor with the exact title attribute. Because it is a Playwright click, it auto-waits for the link to be visible and stable before clicking. Reach for it when the link has no usable visible text - otherwise the plain When I follow "..." navigation step reads better.

When I click on the link with the title "Open menu"
Then I should see "Navigation"

Complete example

Adapted from tests/features/link.feature, running against the examples/links.html fixture:

Feature: Link step definitions

  Scenario: Link href + title assertions
    Given I am on "/links.html"
     Then the link "About" with the href "/about" should exist
      And the link "Contact" with the href "/contact" within the element "#nav" should exist
      And the link "About" with the href "/missing" should not exist
      And the link with the title "About us" should exist
      And the link with the title "Nonexistent" should not exist
      And the link "Example" should be an absolute link
      And the link "About" should not be an absolute link

Tips

  • Link text matching uses :has-text(), which matches substrings - "About" also matches "About us". When several links share a prefix, add the href fragment or a parent scope to pin down the one you mean.
  • For asserting a link's other attributes (target, rel, class), use the generic element-attribute steps in the element steps group.
  • Links rendered by JavaScript after load need a wait first - see wait steps.

Back to all step groups