Skip to content

PDD-CLI Bug: E2E Tests Don't Account for Dynamic UI Text #570

@jiaminc-cmu

Description

@jiaminc-cmu

PDD-CLI Bug: E2E Tests Don't Account for Dynamic UI Text

PDD-CLI generates E2E test selectors using exact string matching for UI elements that contain dynamic content (counts, dates, user names). Tests fail when the dynamic content changes.

Why this matters: Tests become flaky, failing whenever dynamic values change, even though the functionality is correct.

Concrete Example

For a page that displays action counts:

// PDD generated test (WRONG):
test('shows actions section', async ({ page }) => {
  await page.goto('/dashboard');
  
  // Expects exact text match
  await expect(page.getByText('Actions')).toBeVisible();
});

But the actual UI renders dynamic content:

// dashboard.tsx (actual implementation)
export function Dashboard({ actionCount }: Props) {
  return (
    <div>
      <h2>Actions ({actionCount} configured)</h2>  {/* Dynamic count */}
    </div>
  );
}

What went wrong: PDD generated exact match for "Actions" but UI renders "Actions (3 configured)" or "Actions (5 configured)" depending on data.

Impact: Test fails with locator.getByText('Actions') not found even though the heading exists.

Why PDD Makes This Mistake

PDD-CLI currently:

  • Uses exact string matching by default
  • Doesn't identify which text is static vs dynamic
  • Doesn't consider data-driven UI variations

But it should:

  1. Recognize patterns that indicate dynamic content (counts, dates, names)
  2. Use flexible matching for dynamic text
  3. Prefer data-testid for elements with dynamic content

How to Prevent This in PDD-CLI

What PDD should do differently:

  1. Detect dynamic content patterns: Recognize {variable}, {count}, template strings in UI code.

  2. Use regex or partial matching:

    // Instead of exact match
    page.getByText('Actions')
    
    // Use pattern matching
    page.getByText(/Actions \(\d+ configured\)/)
  3. Prefer data-testid for dynamic content:

    // In component
    <h2 data-testid="actions-heading">Actions ({count} configured)</h2>
    
    // In test
    page.getByTestId('actions-heading')

Example improvement:

Current: See "Actions ({actionCount} configured)" in code
       → Generate: getByText('Actions')
       → Test fails

Improved: See "Actions ({actionCount} configured)" in code
        → Detect dynamic content
        → Generate: getByText(/Actions \(\d+ configured\)/)
        → Or: getByTestId('actions-heading')

Severity

P2 - Medium Priority

  • Frequency: High - common in data-driven UIs
  • Impact: Flaky tests, false negatives
  • Detectability: Medium - fails inconsistently based on data
  • Prevention cost: Low - regex matching is straightforward

Category

test-generation

Related Issues


For Contributors: Discovered in frontend/e2e/crm.spec.ts where heading rendered as "Actions (N configured)", fixed in commit 34a651d5.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions