Booking a practical driving assessment through a government transport portal means a login, a multi-factor code, a form, and a captcha, every single time a slot needs to be checked or re-booked. We wrote a 104-line Playwright script that handles the parts worth automating and stops, deliberately, at the parts that are not.
The problem
The booking flow itself is simple but repetitive: log in, get past MFA, fill in the same four fields (instructor number, licence number, licence expiry, date of birth), clear a captcha, submit. None of that is hard to do by hand once. It is tedious to do every time a slot needs checking, and slots can be booked up to six months out, so the same manual sequence gets repeated often for something genuinely time-sensitive.
What we built
A single Node.js file, 104 lines, with exactly one dependency: Playwright. It reads six required values from a local environment file and refuses to run if any are missing, naming the gap rather than failing partway through. It logs in, waits up to ten seconds to see if a multi-factor prompt appears, and if one does, waits up to two minutes in a visible browser window for a person to clear it; if no prompt appears, it carries on. It then fills the four booking-form fields by their exact field names and stops at a terminal prompt for a person to solve the captcha before continuing.
How it runs
The whole thing runs headed, not headless: the browser stays on screen the entire time, because a form this consequential should never submit somewhere a person isn't watching. After the form is filled and the captcha is cleared by hand, the script clicks through and then calls Playwright's pause function, which opens an inspector and leaves the result sitting on screen for a manual check before anyone closes the window. The whole build, start to finish, took about 24 minutes.
What changed
This is the smallest honest example of a rule we hold everywhere else: automate the mechanical steps, never the identity checks. MFA and the captcha exist specifically because a human needs to be there, so the script does not try to get around either. It fills four fields and waits at a login screen so a person doesn't have to sit through the boring middle of a form they still need to finish themselves.
