Skip to content

[#646] Added focus assertions to 'ElementTrait'.#650

Merged
AlexSkrypnyk merged 1 commit into
mainfrom
feature/646-focus-assertions
Jun 1, 2026
Merged

[#646] Added focus assertions to 'ElementTrait'.#650
AlexSkrypnyk merged 1 commit into
mainfrom
feature/646-focus-assertions

Conversation

@AlexSkrypnyk
Copy link
Copy Markdown
Member

@AlexSkrypnyk AlexSkrypnyk commented Jun 1, 2026

Closes #646

Summary

Added four focus-related assertions to ElementTrait: two for keyboard focus (should have keyboard focus / should not have keyboard focus) and two for visible focus indicators (should have a visible focus outline / should not have a visible focus outline). The keyboard-focus check tests whether an element is the current document.activeElement; the outline check inspects computed outline-style, outline-width, and box-shadow to catch WCAG 2.4.7 (Focus Visible) regressions. Both pairs delegate to a shared protected helper with an $is_inverted flag, following the same DRY pattern used by elementAssertAttributeWithValue. Three fixture buttons were added to elements.html to exercise all three CSS states (outline, box-shadow, none), and ten new Behat scenarios cover every positive path and error branch.

Changes

src/ElementTrait.php

  • Added elementAssertHasKeyboardFocus() and elementAssertNotHasKeyboardFocus() — public step methods that delegate to elementAssertKeyboardFocus().
  • Added elementAssertHasVisibleFocusOutline() and elementAssertNotHasVisibleFocusOutline() — public step methods that delegate to elementAssertVisibleFocusOutline().
  • Added elementAssertKeyboardFocus() — protected helper; returns __OK__, __NONE__, or the focused element's outer HTML to build context-rich failure messages.
  • Added elementAssertVisibleFocusOutline() — protected helper; reads outline-style, outline-width, and box-shadow via getComputedStyle and considers an element visible when either the outline or box-shadow is non-none.

tests/behat/fixtures/elements.html

  • Added #focus-button-outline (CSS outline), #focus-button-shadow (box-shadow indicator), and #focus-button-no-outline (no visible indicator) to the Focus Testing section.

tests/behat/features/element.feature

  • Added 3 @javascript positive scenarios covering focus detection, outline detection, and box-shadow detection.
  • Added 7 @trait:ElementTrait negative scenarios covering: missing element (both step types), wrong element focused, no element focused, element unexpectedly focused, missing element for outline, no outline when expected, outline present when not expected.

STEPS.md

  • Regenerated to include documentation for all four new step definitions.

Before / After

Before (ElementTrait public step surface, focus section):

  elementFocusOnElement()          @When  the element :selector gets focus

After (ElementTrait public step surface, focus section):

  elementFocusOnElement()                  @When  the element :selector gets focus
  elementAssertHasKeyboardFocus()          @Then  the element :selector should have keyboard focus
  elementAssertNotHasKeyboardFocus()       @Then  the element :selector should not have keyboard focus
  elementAssertHasVisibleFocusOutline()    @Then  the element :selector should have a visible focus outline
  elementAssertNotHasVisibleFocusOutline() @Then  the element :selector should not have a visible focus outline

Summary

Added four public step methods to ElementTrait for asserting keyboard focus and visible focus outline states on DOM elements. These steps provide WCAG 2.4.7 (Focus Visible) validation capabilities to prevent accidental outline: none regressions.

Changes

Core Implementation (src/ElementTrait.php, +152 lines)

Public step methods:

  • elementAssertHasKeyboardFocus(string $selector) — Step: Then the element :selector should have keyboard focus
  • elementAssertNotHasKeyboardFocus(string $selector) — Step: Then the element :selector should not have keyboard focus
  • elementAssertHasVisibleFocusOutline(string $selector) — Step: Then the element :selector should have a visible focus outline
  • elementAssertNotHasVisibleFocusOutline(string $selector) — Step: Then the element :selector should not have a visible focus outline

Protected helper methods:

  • elementAssertKeyboardFocus(string $selector, bool $is_inverted) — Verifies element matches document.activeElement. Returns __OK__, __NONE__ (no focused element), or focused element's truncated outerHTML (first 200 chars) for failure messages. Throws ElementNotFoundException if selector matches no elements; ExpectationException on assertion failure.
  • elementAssertVisibleFocusOutline(string $selector, bool $is_inverted) — Checks computed style outline-style, outline-width, and box-shadow. Considers element visible when outline is non-none with width > 0, or box-shadow is non-none. Throws same exceptions as keyboard focus helper.

All methods follow step definition guidelines: use tuple format (#[Then(...)]), start with element, include Assert prefix, use should/should not in step text, and start step with entity being asserted.

Feature Tests (tests/behat/features/element.feature, +128 lines)

Positive scenarios (@javascript):

  • Keyboard focus detection: verify focused and non-focused elements
  • CSS outline detection: verify element with outline-style
  • Box-shadow detection: verify element with box-shadow focus ring

Negative scenarios (@trait:ElementTrait):

  • Keyboard focus: element not found, wrong element focused, no element focused, element incorrectly asserted as unfocused
  • Visible outline: element not found, no visible indicator present, outline incorrectly asserted as absent

Test Fixtures (tests/behat/fixtures/elements.html, +3 lines)

Extended Focus Testing section with three button elements:

  • #focus-button-outline — inline style with outline: 2px solid blue
  • #focus-button-shadow — inline style with box-shadow: 0 0 0 2px rgb(0, 0, 255) and outline: none
  • #focus-button-no-outline — inline style with both outline: none and box-shadow: none

Documentation (STEPS.md, +56 lines)

Updated ElementTrait section to document the four new step definitions with parameter and execution context details.

Compliance

All step definitions follow CONTRIBUTING.md guidelines:

  • ✓ Use tuple format annotations instead of regex
  • ✓ Method names begin with trait name (element) and include Assert prefix
  • ✓ Step text uses should/should not and starts with entity being asserted
  • ✓ Use existing elementExecuteJs() infrastructure
  • ✓ Throw ElementNotFoundException for missing selectors, ExpectationException for assertion failures
  • ✓ Descriptive placeholder name (:selector) used consistently

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 1, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 5fbb5ce9-7608-4707-b7fa-d8d99e92b69e

📥 Commits

Reviewing files that changed from the base of the PR and between 5f2030d and db36f3f.

📒 Files selected for processing (4)
  • STEPS.md
  • src/ElementTrait.php
  • tests/behat/features/element.feature
  • tests/behat/fixtures/elements.html

Walkthrough

This PR adds keyboard focus and visible focus outline assertions to ElementTrait. Four new Behat step methods enable feature files to verify an element has keyboard focus or a visible focus indicator, with supporting protected helpers that read document.activeElement and computed CSS outline/box-shadow properties. Feature tests cover both positive paths and failure scenarios.

Changes

Focus Assertions for ElementTrait

Layer / File(s) Summary
Focus check helpers
src/ElementTrait.php
elementAssertKeyboardFocus() executes JS to check if the resolved element is document.activeElement; elementAssertVisibleFocusOutline() reads computed outlineStyle, outlineWidth, and boxShadow to detect a visible focus indicator. Both throw ElementNotFoundException or ExpectationException based on inverted expectations.
Focus assertion steps
src/ElementTrait.php
Four new public Behat Then step methods (elementAssertHasKeyboardFocus, elementAssertNotHasKeyboardFocus, elementAssertHasVisibleFocusOutline, elementAssertNotHasVisibleFocusOutline) accept a CSS selector and delegate to the appropriate helper with an inverted flag.
Focus assertion scenarios
tests/behat/features/element.feature
Seven scenario blocks covering positive paths (element has focus, element has outline, box-shadow detection) and failure cases (element not found, focus on different element, no focus anywhere, no outline, inverted assertions), each with expected error messages.
Documentation and fixtures
STEPS.md, tests/behat/fixtures/elements.html
STEPS.md documents all four step definitions; elements.html adds three test buttons with distinct focus styling (outline, box-shadow, none).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through focus rings,
Keyboard keys and outline things!
Can you see that element shine?
Is it yours? That's the line!
Four new steps to verify with grace,
Which button's proud in focus place! 🎯

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the primary change: adding focus assertions to ElementTrait, matching the main objective in issue #646.
Linked Issues check ✅ Passed The PR implements all required focus assertion steps with proper error handling, documentation, and feature tests as specified in issue #646.
Out of Scope Changes check ✅ Passed All changes are directly aligned with issue #646 objectives: four focus assertion steps, helpers, feature tests, and documentation updates with no unrelated modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/646-focus-assertions

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.57%. Comparing base (5f2030d) to head (db36f3f).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #650      +/-   ##
==========================================
+ Coverage   96.54%   96.57%   +0.03%     
==========================================
  Files          44       44              
  Lines        3356     3389      +33     
==========================================
+ Hits         3240     3273      +33     
  Misses        116      116              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@AlexSkrypnyk AlexSkrypnyk merged commit f121d1e into main Jun 1, 2026
15 checks passed
@AlexSkrypnyk AlexSkrypnyk deleted the feature/646-focus-assertions branch June 1, 2026 01:34
@AlexSkrypnyk AlexSkrypnyk modified the milestones: v3.9, v3.10 Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Extend 'ElementTrait' with focus assertions.

1 participant