The toBeEnabled() test that passed and lied to me

typescript dev.to

Writing disabled-state tests for a cascading form — and what I found when one failed

I wrote the test. It passed. And it lied to me.


The form

Booking form: specialty → doctor → day → time. A classic cascading select. Each step unlocks the next.

The mental model writes itself: pick a specialty, the doctor dropdown enables. Pick a doctor, the day selector enables. Pick a day — and only then — the time slot selector enables.

I was testing the last step. Time select should be disabled until the user picks a day.

await page.selectOption('[data-testid="booking-specialty"]',
'Cardiology');
await page.selectOption('[data-testid="booking-doctor"]', { index: 0 });
await page.waitForSelector('[data-testid="booking-slot-day"]');

await expect(page.locator('[data-testid="booking-slot-time"]')
  ).toBeDisabled();
Enter fullscreen mode Exit fullscreen mode

The test failed.

received: enabled


What I expected to find

Timing. A missing waitFor. The element hadn't fully loaded yet.

Added waitForLoadState. Added an explicit waitFor for the time element. Still: enabled.

Not a timing issue.


What was actually happening

Opened the page source. Two lines in the slot handler:

slotDayEl.value = dayKeys[0];
fillTimeOptionsForDay(dayKeys[0]);
Enter fullscreen mode Exit fullscreen mode

When slots loaded, the page automatically selected the first available day — without any user input. Then it immediately filled the time options for that day.

The state "time select disabled before user picks a day" didn't exist in this product. By the time slots appeared, a day was already chosen.

This was an intentional UX decision — reduce clicks, pre-select the most sensible default.


The lie

I had previously written a different version of this test:

await expect(page.locator('[data-testid="booking-slot-time"]')
).toBeEnabled();
Enter fullscreen mode Exit fullscreen mode

That test passed. I marked the behavior as verified.

What did it actually verify? That the time select was enabled after slots loaded.

What it didn't verify: whether the user had done anything to make it enabled.

The test confirmed the element's state. It said nothing about how the system got there.

If I had only written the toBeEnabled() version — if I'd never tried to test the disabled state — the CI would have stayed green, and I would have shipped a complete misunderstanding of how the form worked.

The failure taught me more than the passing test did.


The fix

The actual behavior: time select is disabled at page load, before any doctor is chosen. It enables when slots load — because the page auto-selects the first available day.

The correct test captures what actually happens:

// page load — no doctor selected yet, time is disabled
await expect(timeSelect).toBeDisabled();

// doctor selected, slots load, first day auto-selected — time enables
await page.selectOption(doctorSelect, { index: 0 });
await page.waitForSelector(slotDaySelect);
await expect(timeSelect).toBeEnabled();
Enter fullscreen mode Exit fullscreen mode

This test documents what the system does — not what I assumed it would do.


Why it matters

A passing test doesn't tell you the system works the way you think it works.

It tells you the assertion succeeded given the actual system state — whatever produced that state.

The toBeEnabled() test confirmed a state. Not a behavior. The difference only became visible when I tried to test the opposite and the test failed.

Some of the most useful information about a system comes from tests that fail in unexpected ways.


The hidden assumption "I assumed a cascading form means each step requires explicit user selection. The product had a different model — reduce clicks by pre-selecting sensible defaults."


Part of the "Hidden Assumptions in Test Automation" series.

Full project (API + UI + E2E + CI + AI endpoint): GitHub

Source: dev.to

arrow_back Back to Tutorials