When is Playwright's locator.first() or nth() a legitimate fix for a strict mode violation rather than a cover-up?
answer
- An opt-out, not an answer
- Ask what the other match is
- Position must be the requirement itself
- Document order, not on-screen order
- Future duplicates stop being reported
basics
~20 sUse first, last or nth when position genuinely is the requirement, such as the newest comment in a thread. Using them to silence an ambiguity you have not explained leaves a test that passes while acting on the wrong element.
solid answer
~40 s`first()`, `last()` and `nth(i)` are explicit opt-outs from strictness: each returns a new locator with a positional step appended, so `first()` is the zero-indexed first match, `last()` is the final one, and `nth(2)` is the third. They are legitimate when position is the actual specification -- the newest comment appended to an issue thread, the top row of a deliberately sorted backlog -- and the test would be wrong if it acted on any other match. They are a cover-up when you added one because a violation appeared and the candidate list looked confusing: the underlying locator still matches an unknown number of nodes, so the test silently follows whatever lands at that index after the next markup change. Playwright's locators guide calls these methods not recommended for exactly that reason.
code
typescript · 9 linesimport { test, expect } from '@playwright/test';
test('the newest comment is a position, not an ambiguity', async ({ page }) => {
await page.goto('/issues/4821');
const comments = page.locator('[data-comment]');
await expect(comments).toHaveCount(3);
await comments.last().locator('button.edit').click();
});go deeper
Know that these three methods exist and that they pick by position: first is index zero, last is the final match, and nth takes a zero-based index.
Explain that the opt-out appends a positional step, so the resulting locator always resolves to one node and can never report a growing set again.
Show the judgment: name the second match before you index past it, and reserve positional locators for cases where the position is the requirement under test.
Set the team's default that a violation is a finding, not a nuisance, and decide when a repeated duplicate should be fixed in the product rather than absorbed test by test.
## What the opt-outs actually do `first()`, `last()` and `nth(i)` are the sanctioned way to tell Playwright which element to use when several match. Each returns a **new locator** with a positional step appended -- `first()` is the zero-indexed first match, `last()` is the final match, and `nth(2)` is the third. The resulting locator resolves to at most one element, so strictness can never fire on it again. That last sentence is the whole tradeoff in one line. The opt-out does not resolve the ambiguity; it removes your ability to be told about it. - The underlying locator may still match two elements, or twenty, and nothing reports that. - The index is evaluated in document order, not visual order, so a CSS reordering can change which element you get without changing the DOM count. - `nth(i)` is zero-based: `nth(0)` and `first()` select the same element. ## When position is the specification There is a legitimate case, and it is narrower than it looks: the position *is* what the test means. On an issue tracker's issue detail page: - "the newest comment in the thread" is the last `article` in the list -- `comments.last()` is a faithful translation of the requirement, not a dodge; - "the top row of the backlog after sorting by priority" is genuinely `rows.first()`, because the ordering is the behaviour under test; - "the third step of the onboarding checklist" is `steps.nth(2)` when the step order is fixed by the product. In each case the test would be *wrong* if it acted on a different match, and a reader can tell that from the line alone. ## When it is a cover-up The failure mode is mechanical: a strict mode violation appears in CI, the candidate list looks confusing, someone appends `.first()`, the suite goes green, and nobody establishes what the second match was. Three things are now true and none of them are written down: 1. The page contains a duplicate that nobody has explained, and it may be a product bug -- a stale modal, a control rendered twice, an accessibility problem. 2. The test now depends on DOM order, an implementation detail no requirement mentions. 3. If the duplicate multiplies, the test keeps passing while acting on whatever ended up at index 0. ## The two responses side by side | | Narrow the locator to one element | Append `first()` / `nth(i)` | |---|---|---| | Match count afterwards | exactly one | still unknown | | Future duplicate | fails loudly again | absorbed silently | | Depends on document order | no | yes | | Reader can tell what it targets | from the locator | only from the index | | Right when | the element is identifiable | the position is the requirement | ## A review rule that works Ask one question of any diff that adds a positional opt-out: **what is the other match?** If the author can name it -- "the second Delete is in the composer, we want the one on the comment" -- the change is informed, and if position really is the requirement the answer belongs in the test's name or a comment. If the author cannot name it, the violation was information and it has just been muted. Playwright's own locators guide flags these three methods as not recommended for exactly this reason: when the page changes, they may act on an element you did not intend, and they will do it without complaining.
- A teammate fixes every strict mode violation with `.first()`. What do you say in review?Ask what the other match is. If they cannot name it, the fix is unexamined: the locator still matches an unknown set and the test now rides on document order. If they can name it, position may well be right, and the reason belongs in the test.
- Is `nth(0)` any different from `first()` in Playwright?No. `first()` selects the zero-indexed first match, which is exactly what `nth(0)` selects, and `last()` is the counterpart for the final match. Prefer `first()` and `last()` for readability, and `nth(i)` when the index itself carries meaning.
saying these in an interview costs you the question
- Treating first as the standard fix for any strict violation
- Assuming first means the topmost element on screen
- Believing nth re-checks that only one element matched
- Claiming a positional opt-out makes the test more stable
- Saying nth indexes start at one