Skip to navigation

CSS selectors

CSS selectors are the recommended way to target elements in Doc Detective tests. They’re widely used, well-documented, and work with most web elements. This guide explains how to choose and define effective CSS selectors for your tests.

When to use CSS selectors

Use CSS selectors in Doc Detective actions like find, click, and others that accept a selector property. CSS selectors are ideal when

  • You need to target elements by their attributes, classes, or IDs
  • You want a selector that’s more reasonable to read and maintain
  • The element doesn’t have identifiable display text

Basic CSS selector patterns

Target by element type

If there’s only one instance of your target element on the page, you can specify the element type directly:

{
"find": {
"selector": "input"
}
}

Target by ID

Elements with unique id attributes are ideal selector targets. Use the # prefix followed by the ID value:

{
"find": {
"selector": "#username"
}
}
Example

An element like <input id="username"> can be targeted with #username.

Target by class

You can target elements by their CSS class using the . prefix. However, classes are often shared across multiple elements, so use them carefully:

{
"find": {
"selector": ".submit-button"
}
}

If multiple elements share the same class, Doc Detective uses the first matching element. For unique targeting, combine classes with other selectors.

Target by attribute

Custom attributes provide reliable, unique selectors. Use square brackets with the attribute name and value:

{
"find": {
"selector": "[type='password']"
}
}
Common attributes to target
  • type (for input fields)
  • name
  • data-testid or data-test (common in test frameworks)
  • aria-label (for accessibility-labeled elements)
  • placeholder

Combining selectors

When a single attribute isn’t unique, combine multiple selectors to create a more specific target:

Element with attribute

Combine an element type with an attribute selector to narrow your target:

{
"find": {
"selector": "input[type='password']"
}
}

This finds an input element specifically with type="password".

Element with class

Target a specific element type that has a particular class:

{
"find": {
"selector": "button.primary"
}
}

This finds a button element with the class primary.

Multiple attributes

Combine multiple attribute selectors to create more specific targets:

{
"find": {
"selector": "input[type='text'][name='email']"
}
}

This finds an input element with both type="text" and name="email".

Class combinations

Target elements that have multiple classes by chaining class selectors:

{
"find": {
"selector": ".btn.btn-primary"
}
}

This finds elements with both btn and btn-primary classes.

Advanced patterns

Child selectors

Use the > combinator to select only direct children in a specific hierarchy:

{
"find": {
"selector": "nav > ul > li > a"
}
}

This finds an a element that’s a direct child of an li, which is a direct child of a ul, which is a direct child of a nav.

Descendant selectors

Use a space between selectors to match elements nested at any depth:

{
"find": {
"selector": "div.content a"
}
}

This finds any a element within a div with class content, regardless of nesting depth.

Attribute contains

Use the *= operator to match attributes that contain a specific substring:

{
"find": {
"selector": "[class*='button']"
}
}

This matches any element whose class attribute contains the text “button”.

Attribute starts with

Use the ^= operator to match attributes that begin with a specific prefix:

{
"find": {
"selector": "[id^='user-']"
}
}

This matches elements with IDs like user-profile, user-settings, etc.

nth-child

Use the :nth-child() pseudo-class to select elements by their position:

{
"find": {
"selector": "li:nth-child(2)"
}
}

This selects the second li element within its parent.

Best practices

Use unique attributes when possible

The best selectors target elements with unique identifiers:

Good practice

#submit-button or [data-testid='submit-btn']

Avoid

.button (likely not unique)

Key into test-specific attributes

Work with your engineering team to add data-testid or similar attributes to important elements. These attributes are designed for testing and won’t change with UI updates.

{
"find": {
"selector": "[data-testid='login-form-submit']"
}
}

Avoid fragile selectors

Selectors that are too specific can break when the page structure changes:

Fragile

div > div > div > span.text

Better

[aria-label='User menu'] or #user-menu

Combine selectors for uniqueness

When no single attribute is unique, combine multiple attributes:

{
"find": {
"selector": "button[type='submit'].primary"
}
}

Finding selectors in the browser

Most modern browsers provide tools to help you identify selectors:

  1. Inspect element: Right-click an element and select “Inspect” or “Inspect Element”
  2. Copy selector: In the developer tools, right-click the highlighted element in the HTML pane and select “Copy” → “Copy selector”
  3. Test selector: In the browser console, use document.querySelector('your-selector') to verify it targets the correct element

Examples

Find and click a submit button

{
"steps": [
{
"description": "Click the form submit button",
"find": {
"selector": "button[type='submit']",
"click": true
}
}
]
}

Type into a password field

{
"steps": [
{
"description": "Enter password",
"find": {
"selector": "input[type='password']",
"click": true,
"type": "mySecurePassword123"
}
}
]
}
{
"steps": [
{
"description": "Navigate to Downloads page",
"click": {
"selector": "nav a[href='/downloads']"
}
}
]
}

Find an element by custom data attribute

{
"steps": [
{
"description": "Find the user profile button",
"find": {
"selector": "[data-testid='user-profile-button']"
}
}
]
}