Test your form filler

This page is a form built out of the cases that break form fillers. Fill it with whatever extension you already use, press Grade, and it will tell you what worked, what failed, and what it correctly refused to touch.

Nothing leaves your browser. This page makes no network request after it loads: no analytics, no beacon, no logging, no server, and your results are never sent anywhere. "Copy report" writes to your clipboard and nowhere else. It works with any filler and does not know or care which one you used.

Run it

  1. Fill the form below with your extension — its keyboard shortcut, toolbar button or right-click menu.
  2. Press Grade the result.

The form

Thirty-three fields. Twenty-seven should be filled; six must be left alone.

Ordinary fields

Basic text input
Email input
Telephone input
Password input
Number with min, max and step
Field with a short maxlength
Textarea
Select with a placeholder option
Radio group
Required checkbox
contenteditable region

Dates

Date with min and max
Date of birth, date input
Date of birth, plain text input
A date that must be in the future
A date that must be in the past

Framework-controlled inputs

These three are the reason this page exists. Each keeps its value in JavaScript, the way a real application does, and is graded on that value — not on what the input looks like. A filler can put text on screen here and still score zero, which is exactly what would happen in the real application.

React-style controlled input
Vue-style v-model input
Angular-style input, updateOn blur

Fields the filler has to go looking for

Open shadow root
Shadow root inside a shadow root
Custom element
srcdoc iframe
Identifiable only by its autocomplete token
No label, no placeholder, no name
Dots in the field name
Dashes in the id

Fields that must be left alone

Filling any of these is a failure, and a worse one than missing a field above. Each breaks a submit in a way the person filling the form cannot see.

CAPTCHA field
Hidden CSRF token

Not visible on the page. Its value must still be a3f9c1e07b2d4856 when you grade.

Disabled field
Readonly field
File input
Visually hidden field

Present in the DOM, hidden with CSS. Nothing a person can see, so nothing a filler should write to.

How the grading works

Every field declares in its own markup what should happen to it and how it will be judged. Nothing is checked against a specific value, a specific format, or anything that would identify one filler from another. If this grader could tell which tool filled the page, it would be broken.

What each kind of case checks

  • Shape, not text. An email field needs something matching something@something.tld. A phone field needs at least seven digits and no characters a phone field would reject. A sentence field needs at least three words. A name field needs non-empty text with no digits in it — deliberately not checked against a list of names, because that would only pass fillers that happen to share our list.
  • Constraints are read off the field. The number case reads min, max and step from the input and checks the value satisfies all three. The date case reads min and max. The maxlength case checks the value actually fits rather than being clipped.
  • Dates are judged by direction. A delivery date must be ahead of today and not more than about a year out. A joined date must be behind today. A date of birth must be in the past and inside a human lifespan.
  • Selects, radios and checkboxes must land on a real option — a select still showing its placeholder counts as unfilled.
  • The framework cases are graded on the committed value, held in JavaScript, never on input.value. See below.
  • The refusal cases are snapshotted when the page loads and compared afterwards. Any change at all is a failure.

The framework cases, precisely

These are faithful reproductions of the mechanism, written in plain JavaScript. They are not React, Vue or Angular — no framework is loaded on this page, and no version of one is being tested. What is reproduced is the specific behaviour that causes fillers to fail:

  • React-style. React keeps a value tracker on the node and replaces the input's own value property. Writing el.value = 'x' updates the tracker at the same time, so when the input event arrives React compares the two, sees no change, and never notifies the application. This fixture does the same thing, so a filler that writes the property directly fails here even if it does dispatch an event afterwards. Writing through the native prototype setter and then dispatching passes.
  • Vue-style. v-model listens for input and reads the value off the event. There is no tracker, so writing the property and then dispatching an event does work — and this page reports that honestly rather than pretending otherwise. What fails is writing a value and firing nothing. It also ignores input events raised during an IME composition, as Vue does.
  • Angular-style. A control configured updateOn: 'blur' commits only on change or blur, not on every keystroke. A filler that writes the value and dispatches input alone leaves the model empty.

The reasoning behind all of this is written up in Half my test suite asserts that nothing happened, which is also where the limits of this kind of testing are discussed.

What the srcdoc iframe does and does not prove

The frame case is a srcdoc iframe, not an iframe with a src pointing at a page on this domain. That is a deliberate constraint, not a shortcut: this site's Content-Security-Policy sets frame-src to the payment provider only, and loosening a security header for a test fixture was not a trade worth making.

It matters because reaching into a srcdoc frame is a different capability from reaching into a real src frame. A srcdoc frame has the URL about:srcdoc and inherits this page's origin rather than having one of its own. An extension that injects declaratively needs match_about_blank or match_origin_as_fallback in its manifest to run there at all, which is separate from ordinary frame access; an extension that injects programmatically may or may not reach it depending on how it does so.

So read this row as information, not a verdict: a filler can handle real iframes perfectly and still fail this case, and the reverse is possible too. If it fails, the useful next step is to test your filler on a real page with a same-origin iframe rather than to conclude anything from this row alone.

The reference score, and why it is the weakest thing here

FormForge 1.0.3 — 27 passed, 0 failed, 6 correctly skipped.

Measured 7 September 2026 on Chrome 152, using the build installed from the Chrome Web Store rather than an unpacked copy. Self-reported: we wrote this page and we wrote that extension, so treat the number as a starting point and run your own.

A clean score from the people who built the page proves nothing. What is worth something is what happened the first time it ran.

FormForge failed. It filled the hidden honeypot field — the trap on this page that stands in for the ones real sites use to catch bots, where a filled value gets the whole submission thrown away without telling anyone. FormForge's own test suite had run green over that gap for two weeks. This page caught it on the first run, and it was fixed in 1.0.3.

That is the argument for testing your filler somewhere other than the place its author tests it.

The other figure published on this site — 71 passed, 0 failed, 13 correctly skipped, measured in September 2026 — came from FormForge's own internal suite, over a different and larger set of fixtures. It is not a score on this page and is not comparable to one.

Why "correctly skipped" matters most

Most tools are judged on what they do. A form filler should be judged at least as hard on what it refuses to do.

If a filler misses a visible field, you see it immediately. The box is empty, you type in it, and you have lost five seconds. If a filler writes into a hidden CSRF token, a readonly order reference or a CAPTCHA box, nothing looks wrong at all. The form appears complete. You press submit. The server rejects it, or accepts it with corrupted data, and there is nothing on screen explaining why. That is the failure that costs an afternoon, and it is invisible by construction.

This is why the six refusal cases are weighted the way they are, and why the score reports them separately instead of folding them into a single number. A filler that scores twenty-seven out of twenty-seven on the fields above and writes into one hidden token is not a better tool than one that misses three visible fields and touches nothing it shouldn't.

A low score here does not mean a tool is bad. It means it did not handle these particular cases. This page is deliberately built out of the hardest things a filler runs into, and most real forms are considerably easier than this one. A tool that scores badly here may be entirely adequate for the forms you actually fill.

Questions

Why did my form filler skip fields?

Some of it is correct behaviour. A good filler deliberately leaves CAPTCHA fields, hidden tokens, disabled and readonly fields and file inputs alone, because writing to them breaks the form rather than completing it. This page counts those as correctly skipped, not as failures. The skips that do matter are fields it should have filled and did not: usually a field with no label, no placeholder and no name, or one inside a shadow root or a frame that the filler never looked into.

Why does my autofill not work on React forms?

React keeps its own record of what is in each input, separate from the DOM. It also replaces the input's value property with its own version. So when a filler runs input.value = 'text', React's record is updated at the same moment, and when the input event arrives React compares the two, sees no difference, and never tells the application anything changed. The text sits on screen and the form submits empty. The way around it is to write through the native value setter, which leaves React's record stale, and then dispatch the event.

What is a controlled input?

An input whose displayed value is owned by the application rather than by the browser. The framework holds the value in its own state and re-renders the field from it. Typing works because the keystroke fires an event the framework listens for. Anything that changes the DOM without firing that event is overwritten on the next render, which is why a form filler can appear to work and still leave the application with nothing.

Is it bad if a form filler fills a hidden field?

Yes, and it is worse than leaving a visible field empty. Hidden inputs usually carry things the server checks, such as a CSRF token or a record id. Overwriting one makes the submit fail in a way nobody can see: the form looks complete, you press submit, and the server rejects it with no visible reason. An empty visible field is obvious and takes a second to fix. A corrupted hidden field can cost an afternoon.

About this page

This page came out of building FormForge, a form filler for QA and development work. It grades every tool the same way and has no idea which one you used. If your filler does well here, keep it.