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.
In this guide11 sections
- 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
- Understand persistent input state
- Coordinate timing without fixed sleeps
- Assert the composite result
- Diagnose tick alignment failures
- Choose the right abstraction level
- Apply a multi-input tick checklist
- 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.
01 / keyboard source
Keyboard Source
Build key presses, pauses, and releases under one stable source ID.
02 / pointer source
Pointer Source
Build moves, button actions, and pauses under a distinct source ID.
03 / action ticks
Synchronized Action Ticks
Align both source lists so prerequisites precede dependent input.
04 / browser dispatch
Browser Input Dispatch
The remote end dispatches each tick and maintains device state.
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:
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:
| Tick | Keyboard source | Pointer source | Required state after tick |
|---|---|---|---|
| 0 | Shift down | Move to range end | Shift is held and pointer is positioned |
| 1 | Pause | Left button down | Shift remains held during press |
| 2 | Pause | Left button up | Click completes while Shift remains held |
| 3 | Shift up | Pause | Every 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.
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.
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.
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.
- 01Selenium documentation
Selenium Project
Canonical WebDriver, Grid, waits, element, and browser automation guidance.
- 02WebDriver standard
W3C
The browser automation protocol specification behind WebDriver implementations.
- 03
- 04
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.
RELATED GUIDES
Continue the learning route
GUIDE 01
Selenium Pointer Actions: Viewport, Element, and Offset Coordinates
Master Selenium pointer action coordinates across viewport, element, and current-pointer origins, with bounds diagnosis, hit testing, and stable offsets.
GUIDE 02
Reliable Keyboard Chords and Modifier State with Selenium Actions
Build reliable Selenium keyboard Actions chords with explicit focus, balanced modifier state, Grid-aware platform keys, outcome assertions, and cleanup.
GUIDE 03
Mouse, Keyboard, and Drag-and-Drop Workflows in Playwright
Model reliable Playwright mouse, keyboard, and drag-and-drop workflows with semantic actions, deliberate event sequences, and stable coordinates.
GUIDE 04
Selenium Java Tutorial: Build a Maintainable Test Suite
Selenium Java tutorial for beginners covering Maven setup, WebDriver, waits, TestNG, JUnit, page objects, debugging, CI, and reliable UI tests.