Skip to content

Test ID Strategy for dhanman-app

This document outlines the conventions and best practices for using data-testid attributes in the dhanman-app React application to support reliable E2E testing with Playwright.

Overview

The data-testid attribute is used to provide stable, semantic selectors for automated testing. Unlike CSS classes or element types that might change due to styling or refactoring, data-testid attributes are specifically intended for testing and remain stable across UI changes.

Naming Conventions

Format

Use kebab-case for all test IDs:

data-testid="auth0-login-button"
data-testid="invoice-create-form"
data-testid="bill-status-badge"

Component-Based Naming

Structure test IDs to reflect the component hierarchy and purpose:

[feature]-[component]-[element]

Examples: - auth0-login-button - Auth0 login button - invoice-create-form - Invoice creation form - bill-list-table - Bills listing table - payment-approve-modal - Payment approval modal - ledger-export-button - Ledger export functionality

Specific Naming Patterns

Forms

  • {feature}-{action}-form - Main form container
  • {feature}-{field}-input - Input fields
  • {feature}-{action}-submit - Submit buttons
  • {feature}-{action}-cancel - Cancel buttons

Examples:

<form data-testid="invoice-create-form">
  <input data-testid="invoice-amount-input" />
  <input data-testid="invoice-description-input" />
  <button data-testid="invoice-create-submit">Create Invoice</button>
  <button data-testid="invoice-create-cancel">Cancel</button>
</form>

Lists and Tables

  • {feature}-list-container - List wrapper
  • {feature}-list-item - Individual list items
  • {feature}-table-row - Table rows
  • {feature}-{action}-button - Action buttons within lists

Examples:

<div data-testid="bill-list-container">
  <div data-testid="bill-list-item">
    <button data-testid="bill-view-button">View</button>
    <button data-testid="bill-edit-button">Edit</button>
  </div>
</div>

  • {section}-nav-link - Navigation links
  • {feature}-status-badge - Status indicators
  • {feature}-filter-{type} - Filter controls
  • {feature}-search-input - Search inputs

Examples:

<nav>
  <a data-testid="bills-nav-link">Bills</a>
  <a data-testid="invoices-nav-link">Invoices</a>
  <a data-testid="payments-nav-link">Payments</a>
</nav>

<span data-testid="bill-status-badge">Approved</span>
<input data-testid="invoice-search-input" placeholder="Search invoices..." />

Implementation Guidelines

1. Add to Interactive Elements

Prioritize adding data-testid to elements that users interact with: - Buttons - Form inputs - Links - Dropdowns - Modals - Tables

2. Use Semantic IDs

Make test IDs descriptive of the element's purpose, not its appearance:

// Good
<button data-testid="invoice-approve-button">Approve</button>

// Avoid
<button data-testid="green-button">Approve</button>

3. Maintain Consistency

Use consistent patterns across similar components:

// All approval buttons follow the same pattern
<button data-testid="invoice-approve-button">Approve Invoice</button>
<button data-testid="bill-approve-button">Approve Bill</button>
<button data-testid="payment-approve-button">Approve Payment</button>

4. Dynamic Test IDs

For dynamic content (like lists), include unique identifiers:

{bills.map(bill => (
  <div key={bill.id} data-testid={`bill-item-${bill.id}`}>
    <button data-testid={`bill-edit-button-${bill.id}`}>Edit</button>
    <button data-testid={`bill-delete-button-${bill.id}`}>Delete</button>
  </div>
))}

Best Practices

1. Keep Test IDs Stable

  • Don't include dynamic values that change frequently (like timestamps)
  • Don't use auto-generated IDs unless they're deterministic
  • Prefer business logic identifiers over technical ones

2. Document Complex Components

For complex components with multiple interactive elements, document the test ID structure:

/**
 * Invoice Form Component Test IDs:
 * - invoice-create-form: Main form container
 * - invoice-amount-input: Amount input field
 * - invoice-date-picker: Date picker component
 * - invoice-vendor-select: Vendor dropdown
 * - invoice-create-submit: Form submission button
 * - invoice-create-cancel: Cancel button
 */

3. Avoid Over-Testing

Don't add test IDs to every element - focus on: - User interaction points - Critical business logic elements - Elements that are hard to select otherwise

4. Coordinate with Tests

Ensure test IDs are added before writing the corresponding E2E tests, and keep the test file documentation in sync with component changes.

Current Test Coverage

Implemented

  • Auth0 Login Button (auth0-login-button) - Login page authentication

Planned Extensions

Future areas for test ID implementation: - Bill creation and management forms - Invoice workflow and approval processes - Payment processing interfaces - Ledger and reporting features - User management and settings - Dashboard and analytics components

Testing Integration

Playwright Usage

// Using test IDs in Playwright tests
await page.getByTestId('auth0-login-button').click();
await page.getByTestId('invoice-create-form').waitFor();
await page.getByTestId('bill-amount-input').fill('100.00');

Test Organization

Organize tests to match the test ID naming structure:

e2e/
├── auth/
│   └── login.spec.ts
├── bills/
│   ├── create.spec.ts
│   └── approve.spec.ts
├── invoices/
│   ├── create.spec.ts
│   └── workflow.spec.ts
└── payments/
    └── process.spec.ts

Maintenance

Regular Review

  • Review test IDs during code reviews
  • Update this document when adding new patterns
  • Ensure test IDs remain consistent across feature additions
  • Remove obsolete test IDs when refactoring components

Versioning

When making breaking changes to test IDs: 1. Update this document with the new patterns 2. Update corresponding tests 3. Consider backward compatibility for gradual migration


Note: This strategy document should be updated as the testing infrastructure grows and new patterns emerge. All team members should follow these conventions to maintain consistency across the application.