skip to content

A team wants every Playwright form interaction to use pressSequentially for realism. How do you evaluate that?

level: principalimportance: nice to knowfreq 28%

answer

  1. The instinct is right, the rule is wrong
  2. Count what realism actually costs
  3. Per field, from the page's behaviour
  4. Name the mechanism in a comment
  5. A long exception list means wrong layer

basics

~20 s

Replace the blanket rule with a criterion. Typing costs one round trip per character and adds a re-render window between each, so reserve it for fields whose own behaviour depends on key events and use fill everywhere else.

solid answer

~50 s

The instinct is sound — masks, mention menus and search-as-you-type only exist under real key events, and a suite of nothing but `locator.fill()` is blind to them — but the blanket rule is the wrong shape. `locator.pressSequentially()` is one protocol round trip per character, so five twenty-five-character fields become roughly a hundred and twenty-five steps, each a window for a re-render or focus steal. That is runtime and flakiness bought on fields that only read `value`. Offer a per-field criterion instead: type when the page binds a key handler, rewrites the value per keystroke, or fires something per character; `fill` otherwise. Make the exception self-documenting with a comment naming the mechanism. Where the exception list keeps growing, that is the signal to move key-handling coverage down to the component layer and let the end-to-end suite prove the integration once.

code

typescript · 13 lines
typescript
import { test, expect } from '@playwright/test';

test('filter the board as the user types', async ({ page }) => {
  await page.goto('https://tracker.example.com/board');

  const title = page.getByLabel('Issue title');
  await title.fill('Crash on board drag');       // no key handling: fill

  const search = page.getByRole('searchbox', { name: 'Filter issues' });
  await search.pressSequentially('cra', { delay: 100 }); // debounced per keystroke

  await expect(page.getByRole('listitem')).toHaveCount(3);
});

go deeper

for a junior

Know that typing character by character is slower than filling and is reserved for fields that genuinely react to keys, rather than being the more careful option everywhere.

for a middle

Be able to quantify the cost: one round trip per character, plus a window between every keystroke where a re-render can interfere with the field.

for a senior

Turn the proposal into a per-field criterion tied to the page's actual handlers, and make each exception carry a comment naming the mechanism that justifies it.

for a principal

Own the tradeoff explicitly. Set the cheap default, keep a justified exception list, and treat a growing list as the signal to push key-handling coverage down a layer.

## Take the proposal seriously first The argument for typing everything is not stupid. `locator.pressSequentially()` produces the key events a human produces, and there are real widgets — input masks, mention menus, search-as-you-type, keyboard shortcut handlers — whose behaviour only exists under those events. A suite that uses `locator.fill()` everywhere is blind to that class of bug, and someone who has been burnt by it will reach for the blanket rule. The job is not to say no; it is to replace a blanket rule with a criterion. ## What the blanket rule costs - **Runtime.** Typing is one protocol round trip per character. A form with five fields averaging twenty-five characters goes from five steps to roughly a hundred and twenty-five. Across a suite running on several workers, that is measured in minutes per run and hours per week. - **Flakiness.** Every keystroke is a window in which a re-render, a focus steal, a toast or a late-arriving autocomplete can land between characters. Interleaving more steps with the page strictly increases the number of interleavings that can go wrong. - **False confidence.** Typing does not make a test more truthful about a field that only reads `value`; it makes it slower. Realism you do not need is cost without coverage. - **Debuggability.** A failure mid-string leaves a half-typed field, which reads as a product bug and burns triage time before anyone notices the mechanism. ## The criterion to offer instead Choose per field, from the page's behaviour, not per suite from a preference: 1. **Does the page have a handler bound to a key event on this field?** If not, `fill` is complete and typing adds nothing. 2. **Does the value get rewritten as it is entered** — a mask, a formatter, a character limit that truncates per keystroke? Then type it. 3. **Does something fire per character** — a debounced lookup, a counter, a menu? Then type at least the characters that trigger it, and only those. 4. **Is the answer "yes" across dozens of edge cases?** Then the coverage belongs closer to the component than to the end-to-end suite; prove the integration once here. | Field | Verb | Reason | |---|---|---| | Issue title | `fill` | value read on submit | | Description textarea | `fill` | no key handling | | Mention trigger in a comment | `pressSequentially` (trigger only) | menu opens on `keydown` | | Due-date mask | `pressSequentially` | value rewritten per keystroke | | Board search box | `pressSequentially` with `delay` | debounced per character | ## How to land it as a convention State the default in the team's testing guide — `fill` unless the field's own behaviour requires keys — and make the exception self-documenting with a one-line comment naming the mechanism (`// mask reformats per keystroke`). That comment is what stops the next person from "optimising" it back to `fill` and reintroducing the bug, and equally what stops typing spreading by copy-paste into fields that never needed it. Then measure rather than argue. If the concern is a real class of missed bugs, look for it: have the team name the widgets on the product that are key-driven, and cover exactly those. That list is usually short — a handful of fields on an issue tracker — and converting a handful is a very different proposal from converting everything. ## The tradeoff worth naming out loud Every end-to-end suite trades fidelity against runtime and stability, and the only wrong answer is choosing one axis globally without looking at the other. Blanket typing maximises fidelity at a runtime and flakiness cost paid on every field, most of which cannot benefit. Blanket `fill` minimises cost but leaves key-driven widgets untested. The principled position is a cheap default plus a named, justified exception list — and, where the exception list starts growing, a push to move that coverage down a layer where a keystroke costs microseconds instead of a round trip.

  • How would you measure whether the blanket rule is actually costing anything?
    Convert one representative spec both ways and compare wall-clock time and retry rate over a run of the full suite, not a single execution. The interesting number is not the average but the tail: how often a keystroke run needs a retry that a fill run does not.
  • The team says typing catches bugs fill would miss. How do you test that claim?
    Ask them to name the widgets. Key-driven behaviour is a property of specific controls — masks, mention menus, debounced search — and the list on a given product is usually short. Cover exactly those, which is a very different proposal from converting every field.
  • When does key-driven behaviour stop belonging in the end-to-end suite at all?
    When the exception list keeps growing, or when a widget needs dozens of keystroke permutations to exercise. At that point a component test covers the key handling in microseconds, and the end-to-end test is left proving the integration once: one handle typed, one selection, one saved record.

saying these in an interview costs you the question

  • Typing is always more realistic, so always better
  • Runtime does not matter because tests run in parallel
  • fill leaves framework state stale, so typing is required
  • One suite-wide rule is simpler than a per-field judgment
  • Adding a delay to every field makes tests more stable
  • Key-driven widgets can only be covered end to end