DEV Community

Your automation says "submitted". Here's how to verify a form was actually submitted

To verify a form was actually submitted in browser automation, you have to stop trusting the one signal every framework hands you for free: your own success log. I spent a long time debugging a pipeline that fills and submits real application forms on employer hiring systems. Not a test fixture, not a mock. Real Greenhouse, Lever, Ashby, Workday, iCIMS forms, each one a different shape, most of them behind at least one thing that does not want a script there. The bug I could not find for weeks turned out not to be a bug in the filling. It was a bug in how I was measuring the filling. This post is the part I wish someone had written for me.

The core mistake: scoring a run on a read-back of your own input

Here is the shape of almost every automation log I have written, and probably yours:

await page.setInputFiles('input[type=file]', resumePath)
log.info('resume staged', { staged: true, files: 1 })
await page.click('button[type=submit]')
log.info('submitted', { submitted: true })

Both of those lines are lies, in a specific and dangerous way. They are not false. The calls really did return without throwing. They are lies because of what they claim to measure. staged: true does not mean a file was staged. It means I asked for a file to be staged and got no exception. submitted: true does not mean anything was submitted. It means I clicked at some coordinates. Every value in those log lines originated on my side of the wire. Not one of them is the other side answering. Once you see it you cannot unsee it. A tool that types a value into a box and then prints the value it typed has proved nothing. My dashboard was full of green.

The concrete failure that taught me this

The specific thing that was happening, and it is worth knowing if you run any browser automation against a remote browser rather than a local one: setInputFiles has more than one code path. Against a local browser it hands over a real filesystem path. Against a remote browser it cannot do that, because the file lives on your machine and the browser is somewhere else, so the library takes a different branch that ships the bytes over the protocol. If that branch does not get what it expects, you can end up with a file input that is technically populated and contains zero bytes. No exception. No warning. files: 1.

Twenty-three separate debugging sessions of reading my own logs found nothing, because my logs were built entirely out of my own inputs and my own inputs were all fine. The thing that found it in about ten minutes was opening a real browser next to the automated one, doing the same upload by hand, and diffing the two network panels. One request carried a multipart body of a few hundred kilobytes. The other carried nothing. The divergence between a human doing it and your rig doing it IS the bug. If you are stuck, stop reading logs and go get that diff.

Three signals that are not a read-back of your own input

Ranked by how much I trust them.

  1. The request on the wire - Not "did I click submit" but "did a request leave, where did it go, what status came back, and how big was the body". You can get this from CDP, from page.on('request') / page.on('response'), or from a proxy. This is the cheapest real signal available and most people skip straight past it to assertions about the DOM. One trap that cost me real time: a `%{http
Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.