Files
cremote/docs/REAL_KEYBOARD_SIMULATION.md

5.9 KiB

Real Keyboard Simulation for Focus Indicator Testing

Date: 2025-11-20 Updated: 2025-12-09 Status: IMPLEMENTED (Now the only method - legacy programmatic method removed)


Overview

Cremote always uses real Tab key simulation for keyboard navigation testing. The legacy programmatic .focus() method has been removed because it produces false negatives.

The Problem

Programmatic .focus() cannot trigger :focus-within or :focus-visible on elements, causing false negatives for:

  • Dropdown menus that rely on CSS :focus-within pseudo-class
  • Modern focus indicators using :focus-visible pseudo-class
  • Accessibility plugins that inject universal focus styles

The Solution

Always use real Tab key presses via Chrome DevTools Protocol to simulate actual user keyboard navigation, which properly triggers all CSS pseudo-classes including :focus-within and :focus-visible.


Implementation Details

New Function: testKeyboardNavigationWithRealKeys()

Located in daemon/daemon.go (lines 10849-11148), this function:

  1. Scans all interactive elements using JavaScript to identify focusable elements
  2. Focuses the body to start from a known state
  3. Presses Tab key repeatedly using d.performSpecialKey(tabID, "Tab")
  4. Checks the focused element after each Tab press
  5. Detects focus indicators by examining computed styles (outline, box-shadow, border)
  6. Builds tab order based on actual keyboard navigation flow
  7. Stops when cycling back to body/document or after finding all expected elements

Key Advantages

Triggers :focus-within - Parent elements receive the pseudo-class
Opens dropdowns automatically - CSS rules like .menu-item-has-children:focus-within > .sub-menu work
Tests real user experience - Simulates actual keyboard navigation
Accurate focus indicators - Detects indicators on elements inside hidden dropdowns
Proper tab order - Follows browser's natural tab navigation flow


API Changes

Client Function: TestKeyboardNavigation()

Current signature:

func (c *Client) TestKeyboardNavigation(tabID string, useRealKeys bool, timeout int) (*KeyboardTestResult, error)

Parameters:

  • tabID - Tab ID (empty string uses current tab)
  • useRealKeys - Ignored (always uses real Tab simulation for accuracy)
  • timeout - Timeout in seconds

Note: The useRealKeys parameter is maintained for backward compatibility but is ignored. All keyboard testing now uses real Tab key simulation.

Client Function: GetKeyboardAudit()

Current signature:

func (c *Client) GetKeyboardAudit(tabID string, checkFocusIndicators, checkTabOrder, checkKeyboardTraps, useRealKeys bool, timeout int) (*KeyboardAuditResult, error)

Parameters:

  • useRealKeys - Ignored (always uses real Tab simulation for accuracy)

Note: The useRealKeys parameter is maintained for backward compatibility but is ignored.


MCP Tools Updated

web_keyboard_test_cremotemcp

Parameters:

  • tab (string, optional) - Tab ID
  • timeout (integer, default: 15) - Timeout in seconds

Note: The use_real_keys parameter has been removed. Real Tab key simulation is always used.

Example:

{
  "tool": "web_keyboard_test_cremotemcp",
  "arguments": {
    "timeout": 15
  }
}

web_keyboard_audit_cremotemcp

Parameters:

  • tab (string, optional) - Tab ID
  • check_focus_indicators (boolean, default: true)
  • check_tab_order (boolean, default: true)
  • check_keyboard_traps (boolean, default: true)
  • timeout (integer, default: 15) - Timeout in seconds

Note: The use_real_keys parameter has been removed. Real Tab key simulation is always used.

Example:

{
  "tool": "web_keyboard_audit_cremotemcp",
  "arguments": {
    "check_focus_indicators": true,
    "timeout": 15
  }
}

Backward Compatibility

⚠️ Breaking Change (Simplified):

  • The use_real_keys parameter has been removed from MCP tools
  • Client functions still accept the parameter for backward compatibility but ignore it
  • All keyboard testing now uses real Tab key simulation for accurate results
  • Legacy programmatic .focus() method has been removed

Rationale: The programmatic method produced false negatives for :focus-visible and :focus-within, making it unreliable for accessibility testing.


Testing Recommendations

For Dropdown Menus

{
  "tool": "web_keyboard_audit_cremotemcp",
  "arguments": {
    "check_focus_indicators": true
  }
}

For Standard Pages

{
  "tool": "web_keyboard_test_cremotemcp",
  "arguments": {
    "timeout": 15
  }
}

Legacy Testing (if needed)

{
  "tool": "web_keyboard_test_cremotemcp",
  "arguments": {
    "use_real_keys": false
  }
}

Expected Results

Before (Programmatic Focus)

  • 29.2% pass rate on pages with dropdown menus
  • False negatives for elements in hidden dropdowns
  • :focus-within not triggered

After (Real Tab Simulation)

  • ~49%+ pass rate on pages with dropdown menus
  • Accurate detection of focus indicators
  • :focus-within properly triggered
  • Dropdowns open automatically during testing

Performance Considerations

  • Slightly slower than programmatic focus (adds ~50ms per element for Tab press + style check)
  • More accurate results justify the small performance trade-off
  • Timeout increased to 15 seconds by default to accommodate the additional time
  • Safety limits in place (max 200 Tab presses to prevent infinite loops)

Next Steps

  1. Implementation complete
  2. Test on pages with dropdown menus
  3. Update documentation
  4. Deploy to production

  • docs/FOCUS_INDICATORS_VALIDATION_SUCCESS.md - Original issue identification
  • mcp/LLM_USAGE_GUIDE.md - MCP tool usage guide
  • docs/ADA_TESTING_GUIDE.md - Accessibility testing guide