Skip to content

The Confirmation That Disappears: Wisata Admin QA

Adityo Guni Waluyo

A QA session hit a delete click that vanished without deleting anything. The Playwright dialog contract explains why.

TL;DR

The disappearing confirm dialog wasn't an app bug; Playwright auto-dismisses native dialogs when no handler is registered, so destructive actions must go through the MCP browser_handle_dialog tool. Testing also found phone validation missing in two modules, folded into one cross-module ticket instead of two. Lesson: verify against tool contracts and database artifacts, not impressions.

I clicked the delete button on /admincms/wisata through a script, and the native browser confirmation dialog appeared briefly, then vanished on its own. No record was deleted, no error in the console. The wisata admin QA session got stuck right there: the delete action felt like "it never happened," even though the button had clearly been clicked.

My first guess: the dialog itself was broken, or the QA environment was flaky. I reran it several times with identical results. Yet in another session, the one running through the [3] MCP browser with the browser_handle_dialog tool, the same dialog could be accepted normally. The difference wasn't luck.

The official Playwright documentation spells out the contract [1], with no page.on('dialog') handler registered, every native dialog (alert, confirm, prompt) is auto-dismissed. A click going through the plain JavaScript .click() path doesn't pass through the layer that recognizes MCP dialogs, so the confirm gets answered as cancel before anyone can decide it. Web modals are blocking; if they weren't auto-dismissed, page execution would halt until the dialog is handled. The framework closes them automatically so scripts don't hang forever; the consequence is that the "accept" intent has to be stated explicitly, through a handler or through the MCP path.

From there, the session's working pattern makes sense: destructive actions through MCP with browser_handle_dialog, in-page actions through plain clicks without native dialogs. The mistake of "the button doesn't work" turned into the understanding that the click path was wrong.

21 Scenarios, 2 Findings, One Generalization Pattern

The full session covered 21 scenarios, and most were about evidence rather than interaction luck: negative-first forms submitted empty until aria-invalid appeared without a single request hitting the endpoint, latitude 999 rejected with a range message, a Scheduled status without a publish schedule blocked. What slipped through was the easiest thing to overlook: the form accepted bukan-angka as a phone number, the record was created, and the phone column in the database held exactly that non-digit text. Scenario N-4 became finding R4-7, and its twin R4-1 in the informasi module used a different layer, entityadmin vs announcementadmin, with a single root cause: phone format validation simply absent from both editors.

Because the layers and modules differ but the root cause matches, the two findings didn't become separate per-module tickets. Both were folded into one cross-module owner decision: R4-1+R4-7 for phone validation, R4-5 for media orphans. One decision, two modules closed.

The session report also recorded things that had nothing to do with dialogs but follow the same logic: trust the artifacts, not the impression. The CRUD lifecycle was verified column by column: category, address, lat/lng parsed automatically from the Maps URL, the seven-day opening-hours JSON, the edited password cross-checked with bcrypt.checkpw [2] so the hash matches the new password, down to the en-dash U+2013 checked at the HEX byte level right in the database row to make sure it hadn't turned into a different character somewhere along its JSON journey [4]. The media orphan from the R4-5 extension was proven with database rows and physical files left behind after an entity was permanently deleted, then cleaned up through the media endpoint (200 response). These numbers came out of machine artifacts, not from a tester's memory.

The technical lesson is simple: when automation "doesn't work," check the tool's contract before accusing the application. A disappearing dialog isn't an application bug; it's the Playwright contract working exactly as intended. And when your own QA findings start to overlap across modules, don't rush to file per-module tickets. Fold them first: find the shared root, put one decision to the owner, and let two modules be closed by a single answer.

Sources

[1] Playwright docs: Dialogs
[2] pyca/bcrypt README
[3] Playwright MCP
[4] Go spec

Related articles