ATK Selector Hooks
When to Use
Use selector hooks when targeting Drupal-rendered markup from tests. Use volatile class names only when no selector hook exists and the element has no other stable identifier.
Decision
| Test target | Selector |
|---|---|
| ATK-supplied UI (login, content edit, admin menu) | ATK's data-qa-id attribute |
| Your custom markup | Add data-qa-id in a preprocess hook in your module/theme |
| Third-party module's rendered markup | Add a preprocess hook in your module/theme to inject the attribute |
The Problem
Drupal's rendered DOM contains classes and IDs that change across versions, themes, and modules:
- #edit-name becomes #edit-name--BG87f2qDxQk after Form API mangling
- .node--type-article varies by render context
- View list classes change when the view's machine name does
Pattern
Use ATK's selector hook (Playwright)
await page.locator('[data-qa-id="login-form-submit"]').click();
await page.locator('[data-qa-id="user-menu-account"]').hover();
Use ATK's selector hook (Cypress)
cy.get('[data-qa-id="login-form-submit"]').click();
Extend in your module/theme
function mytheme_preprocess_node(array &$variables): void {
if ($variables['node']->bundle() === 'article') {
$variables['attributes']['data-qa-id'] = 'article-' . $variables['view_mode'];
}
}
Your tests now select [data-qa-id="article-teaser"] reliably across theme changes.
Common Mistakes
- Wrong: Using Drupal-generated class names in selectors → Right: they break on every Drupal upgrade or theme change
- Wrong: Adding
data-qa-idto render arrays directly → Right: preprocess hooks are the right place; render arrays get rebuilt and lose attribute additions - Wrong: Reusing the same
data-qa-idvalue across multiple elements → Right: selectors return ambiguous matches
See Also
- Custom Tests
- Anti-Patterns
- Reference:
automated_testing_kit.modulein the canonical repo