PRACTICAL GUIDE / W3C Actions API ticks Selenium

Synchronizing Keyboard and Pointer Devices with W3C Action Ticks

Understand W3C Actions API ticks in Selenium, align keyboard and pointer sources with pauses, preserve modifier state, and diagnose composite gestures.

By The Testing AcademyUpdated July 11, 20268 min read
All field guides
In this guide11 sections
  1. Read the action payload as columns and rows
  2. Start with the high-level Actions builder
  3. Design ticks before writing low-level code
  4. Encode aligned Java input sequences
  5. Understand persistent input state
  6. Coordinate timing without fixed sleeps
  7. Assert the composite result
  8. Diagnose tick alignment failures
  9. Choose the right abstraction level
  10. Apply a multi-input tick checklist
  11. Close on an explicit input timeline

What you will learn

  • Read the action payload as columns and rows
  • Start with the high-level Actions builder
  • Design ticks before writing low-level code
  • Encode aligned Java input sequences

W3C Actions API ticks in Selenium are the timeline that keeps virtual keyboard and pointer devices aligned. Each device owns an action list, and actions at the same list index share a tick. If one source has nothing to do, a pause holds its place while the other source moves, presses, or releases.

Use that model for composite gestures whose correctness depends on state across devices. Put prerequisite state in an earlier tick, align every later phase deliberately, release all inputs, and assert the application's interpretation of the gesture. Protocol completion alone does not prove that the right item was selected or dragged.

Read the action payload as columns and rows

Selenium describes the Actions API as virtualized input sources executed through one perform command. Think of each source as a column and each tick as a row. The remote end processes one row before advancing to the next.

Animated field map

Multi-Input Tick Dispatch

Keyboard and pointer lists align by tick before the browser interprets the resulting composite gesture.

  1. 01 / keyboard source

    Keyboard Source

    Build key presses, pauses, and releases under one stable source ID.

  2. 02 / pointer source

    Pointer Source

    Build moves, button actions, and pauses under a distinct source ID.

  3. 03 / action ticks

    Synchronized Action Ticks

    Align both source lists so prerequisites precede dependent input.

  4. 04 / browser dispatch

    Browser Input Dispatch

    The remote end dispatches each tick and maintains device state.

  5. 05 / gesture assert

    Composite Gesture Assertion

    Verify the application result and that no input remains pressed.

This alignment is conceptual synchronization, not a claim that two operating-system devices generated events at an identical timestamp. When event ordering matters, place the prerequisite in a completed earlier tick.

Start with the high-level Actions builder

For common gestures, Selenium's Actions builder creates the underlying source sequences and alignment. A Shift-click range selection is readable as a user workflow:

Java
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;

WebElement rangeEnd = driver.findElement(
    By.cssSelector("[data-row-id='ROW-108']")
);

new Actions(driver)
    .keyDown(Keys.SHIFT)
    .click(rangeEnd)
    .keyUp(Keys.SHIFT)
    .perform();

The modifier is established before the click and released afterward. Prefer this form unless the requirement needs exact source-level durations or unusual parallel composition. Low-level code has more ways to misalign releases and pauses, so its added detail must buy an observable behavior.

Focus still matters. If the range component requires an anchor selection, click the anchor and assert its selected identity before performing the Shift-click. The multi-input sequence cannot repair missing application preconditions.

Design ticks before writing low-level code

Write a small table for the gesture. For a Shift-click, one safe design is:

TickKeyboard sourcePointer sourceRequired state after tick
0Shift downMove to range endShift is held and pointer is positioned
1PauseLeft button downShift remains held during press
2PauseLeft button upClick completes while Shift remains held
3Shift upPauseEvery input is released

The pointer move and key-down share tick zero, but the click begins only in tick one. That avoids relying on which source action the browser exposes first within the shared tick. Each pause is meaningful alignment, not waiting for the application.

If pointer movement has a duration, that tick's progression accounts for the duration before the next tick begins. Do not add an equal sleep to the keyboard source; its pause already occupies that aligned tick.

Encode aligned Java input sequences

The low-level Java classes expose the two devices and their action lists. Keep the sequences equal in length so a reviewer can compare tick by tick.

Java
import java.time.Duration;
import java.util.List;
import org.openqa.selenium.Keys;
import org.openqa.selenium.interactions.KeyInput;
import org.openqa.selenium.interactions.PointerInput;
import org.openqa.selenium.interactions.Sequence;
import org.openqa.selenium.remote.RemoteWebDriver;

PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "range-mouse");
KeyInput keyboard = new KeyInput("range-keyboard");

Sequence keySequence = new Sequence(keyboard, 0);
keySequence.addAction(keyboard.createKeyDown(Keys.SHIFT.getCodePoint()));
keySequence.addAction(keyboard.createPause(Duration.ZERO));
keySequence.addAction(keyboard.createPause(Duration.ZERO));
keySequence.addAction(keyboard.createKeyUp(Keys.SHIFT.getCodePoint()));

Sequence pointerSequence = new Sequence(mouse, 0);
pointerSequence.addAction(mouse.createPointerMove(
    Duration.ofMillis(100),
    PointerInput.Origin.fromElement(rangeEnd),
    0,
    0
));
pointerSequence.addAction(
    mouse.createPointerDown(PointerInput.MouseButton.LEFT.asArg())
);
pointerSequence.addAction(
    mouse.createPointerUp(PointerInput.MouseButton.LEFT.asArg())
);
pointerSequence.addAction(mouse.createPause(Duration.ZERO));

((RemoteWebDriver) driver).perform(List.of(keySequence, pointerSequence));

The source IDs are distinct and stable within the payload. The pointer destination uses an element origin so layout is resolved at execution. Both sources contain four actions, making the tick table visible in code.

This sequence does not include an application wait. After perform, use an explicit condition for the final selected range, copied item, or drag result.

Understand persistent input state

Input source state belongs to the WebDriver session. A key pressed in one perform request remains down until released, and pointer location and button state also persist. Creating a new Java Actions object does not reset the remote devices.

That persistence enables complex gestures but creates failure risk. Keep the complete gesture in one perform payload when possible. If the remote end reports an error after a press, call resetInputState() through the concrete remote driver before sending unrelated input, or discard the session when its state is uncertain.

Do not intentionally leave a modifier down across test methods. Test ordering and retries will turn that hidden dependency into nondeterministic behavior. Teardown can provide defensive reset, but every successful sequence should still balance its own keys and buttons.

Coordinate timing without fixed sleeps

Action durations describe input, such as how long a pointer move takes. Explicit pauses inside a source describe tick alignment or an intentional held gesture. Neither proves the application has rendered the result.

For a drag that should reveal a drop zone, use a deliberate pointer move duration if hover behavior is part of the requirement. After the action sequence completes, wait for the domain result. Do not insert Thread.sleep between separately performed key-down and click commands; Grid latency then becomes part of modifier lifetime and an exception can strand the key.

One combined perform request also reduces client-to-Grid command boundaries. It does not guarantee hardware-level simultaneity or remove browser event-loop scheduling. Keep assertions at the application level rather than timing individual DOM events to milliseconds.

Assert the composite result

For range selection, read stable row IDs from selected elements and compare the intended set. A count alone can pass when the wrong contiguous range was selected.

Java
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
wait.until(current -> current.findElements(
    By.cssSelector("[data-row-id][aria-selected='true']")
).size() == 5);

List<String> selectedIds = driver.findElements(
    By.cssSelector("[data-row-id][aria-selected='true']")
).stream()
    .map(element -> element.getAttribute("data-row-id"))
    .toList();

List<String> expected = List.of(
    "ROW-104", "ROW-105", "ROW-106", "ROW-107", "ROW-108"
);
if (!selectedIds.equals(expected)) {
    throw new AssertionError("Unexpected selected range: " + selectedIds);
}

If visual order can differ from DOM order, compare the appropriate ordered or unordered contract explicitly. After assertion, send a plain character or pointer move only when it is a meaningful check that input state was released; routine teardown reset is a cleaner defensive measure.

Diagnose tick alignment failures

Log a compact tick table with source IDs, action types, origins, durations, key values, and button values. Capture the active element, target rectangle, selected state before the gesture, browser capabilities, and the state after failure. This evidence reveals whether the problem occurred before dispatch, during input, or in application handling.

If Shift appears inactive during the click, verify it is down in an earlier completed tick. If Shift remains active afterward, inspect the final key-up and whether processing stopped before that tick. If the pointer misses, diagnose element origin and layout rather than modifying keyboard pauses. If the correct input events occur but the range is wrong, investigate the application's anchor model.

Stale element references can arise between locating the pointer target and performing the payload. Build and perform close together, after the component is stable. A retry must rebuild both sequences with a fresh element; replaying only the pointer source can inherit keyboard state and change the gesture.

Choose the right abstraction level

Use WebElement convenience methods for ordinary clicks and typing. Use the high-level Actions builder for standard chords, hover, drag, and modifier-click flows. Drop to KeyInput, PointerInput, and Sequence only when the tick structure itself is part of the requirement or diagnosis.

Wrap low-level composition in a narrowly named domain helper such as shiftSelectRangeEnd. Do not expose raw sequence mutation to every page object. The helper should own source IDs, alignment, releases, and failure reporting while the test owns the business assertion.

Apply a multi-input tick checklist

  • Define each virtual input source and its persistent state.
  • Sketch tick alignment before writing low-level sequences.
  • Put prerequisite modifier state in an earlier completed tick.
  • Add pauses to keep inactive sources aligned.
  • Keep source IDs distinct and sequence lengths reviewable.
  • Prefer one perform request for the complete gesture.
  • Release every key and pointer button explicitly.
  • Reset input state after an interrupted sequence.
  • Rebuild sequences after a stale target or rerender.
  • Assert exact business identities, not only event counts.

Close on an explicit input timeline

Multi-device actions stop being mysterious when the timeline is visible. Align keyboard and pointer sources by tick, establish state before dependent input, release everything at the end, and judge success by the application's composite result. The protocol supplies synchronized virtual devices; the test still owns the meaning of their gesture.

// LIVE COURSE / THE TESTING ACADEMY

Playwright Automation Mastery

Go beyond Selenium. Master Playwright with JS/TS in 90 days.

From the instructor behind this guide.

Playwright jobs are growing 8x faster than Selenium. 90 days / 75+ live hrs / Tue-Thu-Sat 7 AM IST.

Code PROMODE / 10% offJoin the batch

The Testing Academy editorial desk

Practical QA guidance built around test evidence, production tradeoffs, and interview-ready explanations.

Published July 11, 2026 / Reviewed July 11, 2026

PRIMARY REFERENCES

Verify the details at the source

QABattle guides are practical explanations. Product behavior, standards, and APIs can change, so use these primary references for the canonical details.

  1. 01
    Selenium documentation

    Selenium Project

    Canonical WebDriver, Grid, waits, element, and browser automation guidance.

  2. 02
    WebDriver standard

    W3C

    The browser automation protocol specification behind WebDriver implementations.

  3. 03
    HTTP Semantics

    IETF

    The normative semantics for methods, status codes, fields, and HTTP behavior.

  4. 04
    OWASP API Security Top 10

    OWASP Foundation

    Primary API-specific risk taxonomy and defensive guidance.

FAQ / QUICK ANSWERS

Questions testers ask

What is a tick in the W3C Actions API?

A tick is one aligned position across the action lists for all input sources in a perform request. Each source contributes an action or pause at that position. The remote end dispatches the tick before advancing to the next aligned position.

Do actions in the same tick happen at exactly the same instant?

Treat them as synchronized within one protocol tick, not as a guarantee of identical hardware timestamps or a specific cross-source event order. Put prerequisite state, such as a modifier key down, in an earlier tick than the pointer button that depends on it.

Why are pauses needed in multi-device sequences?

A pause keeps one source idle while another source acts, preserving column alignment. Without explicit alignment in low-level code, a key release or pointer event can occur in the wrong phase. High-level Actions builders often insert the needed pauses for convenience operations.

Does input state persist after perform returns?

Yes. Depressed keys, pressed pointer buttons, and pointer location are remote session state. Release every key and button in the sequence. If processing is interrupted, use the binding's release-all or reset-input-state operation before continuing or discard the session.

When should Selenium tests use low-level Sequence objects?

Use them when the requirement depends on explicit source alignment, custom pointer duration, or a composite gesture not expressed clearly by Actions conveniences. For ordinary Shift-click or drag operations, a high-level Actions chain is usually easier to review and maintain.