DEV Community

Cover image for The toBeEnabled() test that passed and lied to me
Darya Belaya
Darya Belaya

Posted on

The toBeEnabled() test that passed and lied to me

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

Top comments (0)