Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →Use a specific selector, wait for the radio to be visible, click it, and verify the resulting state. In an Angular form, also verify the relevant FormControl, NgModel, or rendered result. A successful page.click() only proves that Pyppeteer performed a mouse action; it does not prove that Angular accepted the value or that a submission finished.
The reliable sequence
- Identify the exact radio with stable attributes such as
nameandvalue. - Wait for that element to be present and visible.
- Click it with Pyppeteer.
- Wait for a meaningful postcondition, normally the native
checkedproperty. - If Angular state is the real test target, verify the form value or a UI result that depends on it.
- If the click causes navigation, wait for navigation concurrently with the click.
This order avoids the most common failures: clicking a hidden duplicate, selecting the wrong radio group, racing Angular change detection, and missing a navigation event.
Before you write the test
Confirm the installed APIs
The Python examples here use the Pyppeteer API documented for version 0.0.25. That documentation is old, and Pyppeteer is an unofficial Python port; JavaScript Puppeteer examples are not guaranteed to have identical Python signatures. Check the version installed in your project and use its API reference when adapting these snippets.
Inspect the actual Angular markup
Do not guess the selector from a component label. In browser developer tools, inspect the rendered <input type="radio">. Confirm its name, value, whether it is disabled, and whether a custom component hides the native input. A typical group looks like this:
#1 Best Overall
<input type="radio" name="delivery" value="standard">
<input type="radio" name="delivery" value="express">
The name groups mutually exclusive choices. The value distinguishes the option. Prefer attributes that are stable across styling changes instead of generated classes or DOM positions.
A minimal Pyppeteer implementation
This complete example opens a page, waits for the express option to be visible, clicks it, and verifies the browser’s native state.
import asyncio
from pyppeteer import launch
async def select_delivery():
browser = await launch()
page = await browser.newPage()
await page.goto("https://example.com/checkout")
selector = 'input[type="radio"][name="delivery"][value="express"]'
await page.waitForSelector(selector, {"visible": True})
await page.click(selector)
await page.waitForFunction(
"selector => document.querySelector(selector)?.checked === true",
{},
selector,
)
await browser.close()
asyncio.get_event_loop().run_until_complete(select_delivery())
waitForSelector waits for DOM presence by default. Passing visible=True adds a visibility requirement. waitForFunction evaluates a browser-side function until it returns a truthy value, so the assertion is tied to an observable state rather than an arbitrary sleep.
Make the selector narrower when necessary
If the page contains several groups, scope the selector to a form or section:
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11selector = '#shipping-options input[type="radio"][name="delivery"][value="express"]'
When IDs are unstable, use a stable ancestor attribute supplied by the application. A broad selector such as input[type="radio"] can match the first option in an unrelated group and still produce a technically successful click.
Verify Angular’s form state, not only checked
Angular’s RadioControlValueAccessor is the bridge that writes radio values and listens for radio changes. It is used by FormControlDirective, FormControlName, and NgModel. Therefore, a checked DOM input is important evidence, but it is not proof that the application’s intended model or submit path has completed.
Verify a rendered application result
The most portable end-to-end assertion is an element that Angular renders from the selected value, such as a summary or validation message. For example, after clicking, wait for a summary containing the expected option:
Rank #2
await page.waitForFunction(
"expected => document.querySelector('#delivery-summary')?.textContent.includes(expected)",
{},
"Express",
)
Use the application’s real result selector and text. Avoid asserting an implementation detail that users never see.
Verify a form control when your test exposes one
With reactive forms, assert the value through the application’s observable output or a test-only diagnostic surface rather than reaching into Angular internals from the browser. With template-driven forms, NgModel binds a FormControl to the element and emits ngModelChange. The default update mode is change; an application can instead configure updateOn as blur or submit.
That setting changes what “complete” means. For updateOn: 'blur', a click may update the native control while the model waits for a blur. For updateOn: 'submit', the model may not be committed until submission. In those cases, perform the user action that the application requires and assert the resulting form behavior, rather than adding a fixed delay.
When selecting the radio navigates
A change handler may submit a form or route to another page. Register the navigation wait at the same time as the click. If you await the click first, the navigation can begin and finish before the wait is registered.
import asyncio
selector = 'input[type="radio"][name="delivery"][value="express"]'
await page.waitForSelector(selector, {"visible": True})
await asyncio.gather(
page.waitForNavigation(),
page.click(selector),
)
After navigation, assert the destination or a page-specific element. If the action updates the current page without navigation, use a postcondition such as checked, a changed summary, or a form error disappearing.
The Tool Desk
Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Choosing a dependable interaction strategy
Direct input selector
Use name plus value when the native input is present and those attributes are stable. This is usually the clearest and least ambiguous approach.
Label or accessible name
A visible label can be preferable when the input is visually hidden and the application exposes a proper label association. Verify that the label points to the intended input and that duplicate labels do not exist. If the markup does not provide a reliable association, improve the markup or use a stable input selector rather than guessing by text.
Presence versus visibility
Presence only means an element exists in the DOM. It may still be covered, hidden, or a template duplicate. Request visibility when a real user must see and click it. If a component intentionally reveals the option after another action, wait for that reveal condition first, then wait for the radio itself.
Condition versus fixed sleep
Fixed sleeps make tests slower when the page is ready quickly and flaky when it is not ready yet. Prefer waitForFunction for a state transition you can name: checked becomes true, a summary appears, or a submit result is rendered. Angular performs asynchronous model/view synchronization and an additional change-detection pass in the NgModel flow, so an explicit condition is more meaningful than a guessed number of milliseconds.
Reusable helper with diagnostics
A small helper can standardize waiting, clicking, and verification while retaining useful error context:
async def choose_radio(page, selector, expected_text_selector=None, expected_text=None):
await page.waitForSelector(selector, {"visible": True})
await page.click(selector)
await page.waitForFunction(
"selector => document.querySelector(selector)?.checked === true",
{},
selector,
)
if expected_text_selector and expected_text is not None:
await page.waitForFunction(
"args => document.querySelector(args[0])?.textContent.includes(args[1])",
{},
[expected_text_selector, expected_text],
)
Keep the selector and expected result in the test name or failure message. When a test fails, you should immediately know which group, option, and application outcome were involved.
Troubleshooting common failures
“No node found for selector”
Cause: The component has not rendered, the selector does not match the current markup, or the radio is inside a different document context.
Fix: Inspect the live DOM, wait for the component’s stable container, and then wait for the exact input. Recheck spelling, quoting, name, and value.
The click succeeds but the wrong option is selected
Cause: The selector matched another radio group or a duplicate template node.
Fix: Scope it to the relevant form or container and include both name and value. Verify the exact selector matched the intended element before clicking.
The element exists but the click has no effect
Cause: The element is hidden, disabled, covered by an overlay, or the visible control is a label/custom component.
Fix: Wait with visible=True, inspect disabled state and overlays, and confirm the label-to-input association. If the product intentionally uses a custom control, test the user-facing control while still verifying the resulting native or Angular state.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
checked is true but Angular still has the old value
Cause: The test asserted too early, or the form uses updateOn: 'blur' or submit.
Fix: Wait for the application’s rendered result, perform the required blur or submit action, and assert the form outcome. Do not treat a timeout extension as a substitute for identifying the configured update mode.
The test hangs around navigation
Cause: The navigation wait was started after the click, or the click does not actually navigate.
Fix: Use asyncio.gather(page.waitForNavigation(), page.click(selector)) for navigation-causing actions. For same-page updates, remove the navigation wait and wait for the relevant DOM or model postcondition.
Free tools Windows power users keep installed
One-click scans. No signup required.
Best Value
Examples copied from JavaScript fail in Python
Cause: Pyppeteer is an unofficial Python port and its signatures and release history differ from current JavaScript Puppeteer.
Fix: Check the Pyppeteer version installed in the project and consult its Python API reference. Confirm the browser executable and Angular application versions used by your test environment.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Reliability, speed, and maintenance
- Use stable contracts: Treat
name,value, accessible labels, and test-specific attributes as part of the UI contract. Avoid positional selectors such as:nth-child(2). - Wait at the narrowest useful scope: Wait for the component or radio you need, not an unrelated page-wide delay.
- Assert user-visible consequences: Native state catches click failures; an Angular summary, validation message, or submit result catches model and change-detection failures.
- Keep diagnostics: On failure, capture the current URL, selector, and relevant text or HTML so markup changes are easy to diagnose.
- Separate navigation and same-page flows: The correct wait depends on the application behavior; adding both indiscriminately can create unnecessary timeouts.
No performance winner is established between selector styles or wait strategies. Choose the one that expresses the application’s actual contract and remains stable as templates change.
Or skip the browser setup
If your goal is a clean screenshot rather than an interaction test, ScreenshotNeo returns a screenshot or PDF from one request. It accepts the cookie or consent banner like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets before capture; each cleanup step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in headers.
Use the ScreenshotNeo API documentation for authentication and options. A minimal cURL request is:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
The same request in Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
And Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
ScreenshotNeo also provides an MCP server with take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account to try it.
FAQ
Should I click the label or the input?
Click the native input when it is visible and reliably identified. Click the associated label when the input is intentionally hidden and the label is the user-facing control, then verify the same checked and Angular outcomes.
Can I prove the form submitted by checking only checked?
No. checked proves the browser control state. Submission requires an application-level assertion such as a destination page, success message, or stored form result.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Fix the driver behind crashes, sound loss and screen glitches3Clear out junk files and repair common Windows errorsWhat if two options legitimately share a value?
Values should normally distinguish options within one radio group. If an application does not, scope the selector to the correct group container or use a stable label association so the test identifies one concrete control.
Frequently Asked Questions
Does Pyppeteer automatically wait for Angular change detection?
It waits only for the conditions you request. Add an explicit postcondition for the rendered result or form behavior that demonstrates Angular has processed the change.
How should I test a radio group that appears after an API response?
Wait for the group or its specific input to appear and become visible, then click and verify the resulting state; avoid a fixed delay based on an assumed response time.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.
Recommended Free Tools

