Free tools Windows power users keep installed
One-click scans. No signup required.
To select an Angular radio button reliably with Pyppeteer, wait for the exact input to be visible, click it, then wait until its native checked property is true. If the test is about Angular behavior, also assert the associated form control, model value, or rendered result. For a click that submits or navigates, start waitForNavigation() at the same time as the click so the navigation event cannot be missed.
The reliable sequence
A radio click has several different meanings in a test: the element may exist in the DOM, it may be visible, the browser may have checked it, and Angular may or may not have finished updating its form state. Treat those as separate postconditions.
- Identify one radio with a selector that includes its group and value.
- Wait for that selector with
visible=True. - Click the selector.
- Wait for
checked === true. - Assert the Angular form/model outcome when that is what the user journey depends on.
Pyppeteer 0.0.25 exposes Page.waitForSelector, Page.click and Page.waitForFunction. Its selector click finds a matching element and clicks its center. The API documentation is old, so verify the signatures against the version installed in your project.
A complete Pyppeteer example
This example assumes markup containing input type="radio" name="delivery" value="express". Replace the selector with attributes that are stable in your application.
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →#1 Best Overall
import asyncio
from pyppeteer import launch
RADIO = 'input[type="radio"][name="delivery"][value="express"]'
async def main():
browser = await launch(headless=True)
page = await browser.newPage()
try:
await page.goto('http://localhost:4200/checkout',
{'waitUntil': 'networkidle0'})
await page.waitForSelector(RADIO, {'visible': True})
await page.click(RADIO)
await page.waitForFunction(
'selector => document.querySelector(selector)?.checked === true',
{}, RADIO
)
checked = await page.evaluate(
'selector => document.querySelector(selector)?.checked === true',
RADIO
)
assert checked, 'Express radio is not checked'
finally:
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
waitForSelector with visible=True avoids clicking an input that merely exists but is hidden. The browser-side function wait then waits for a meaningful state instead of guessing with a sleep.
Build a selector that cannot hit the wrong option
Prefer group and value attributes
Radio groups are defined by their name. Include both name and value whenever possible:
input[type="radio"][name="delivery"][value="express"]
A selector such as input[type="radio"] can match several controls, possibly in another group. An unqualified value selector can also match a different part of a page.
Use a label when the input is visually hidden
Design systems often hide the native input and make the label or a styled wrapper clickable. If the input itself is not visible, wait for and click the associated label instead, then verify the input:
Rank #2
LABEL = 'label[for="delivery-express"]'
RADIO = '#delivery-express'
await page.waitForSelector(LABEL, {'visible': True})
await page.click(LABEL)
await page.waitForFunction(
'selector => document.querySelector(selector)?.checked === true',
{}, RADIO
)
Use an accessible name or a stable test attribute when your component library provides one. Do not rely on generated Angular classes or positional selectors that can change when the template is rearranged.
Check how many elements match
Before clicking, inspect the match count during test development:
count = await page.evaluate(
'selector => document.querySelectorAll(selector).length', RADIO
)
assert count == 1, f'Expected one radio, found {count}'
Wait for presence, visibility and readiness separately
DOM presence is not visibility
Pyppeteer’s selector wait can finish when an element is present in the DOM. Request visibility when the user must be able to interact with it:
await page.waitForSelector(RADIO, {'visible': True})
If the radio is rendered only after an API response, wait for the radio or its container rather than sleeping for an arbitrary number of seconds.
Wait for an enabled or application-specific condition
A visible control can still be disabled or covered by an overlay. Add a browser-side condition that matches your page:
await page.waitForFunction(
"""selector => {
const el = document.querySelector(selector);
return el && !el.disabled && el.offsetParent !== null;
}""",
{}, RADIO
)
If a consent dialog, loading mask or modal blocks the control, remove or close that application element through the same user-visible path your test is intended to exercise.
Prove the Angular state, not only the DOM state
Angular’s RadioControlValueAccessor writes radio values and listens for radio changes. It is used by FormControlDirective, FormControlName and NgModel. Angular’s value-accessor layer bridges a FormControl and a native form element, with model/view synchronization and change detection involved.
Native assertion
This confirms the browser control state:
await page.waitForFunction(
'selector => document.querySelector(selector)?.checked === true',
{}, RADIO
)
It does not prove that the intended Angular value has propagated or that a submit handler has accepted it.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Assert a rendered result
The most robust black-box assertion is often a visible consequence of the model, such as a delivery-price element or an enabled continue button:
await page.waitForFunction(
"""() => {
const price = document.querySelector('[data-testid="delivery-price"]');
return price && price.textContent.includes('$12');
}"""
)
Use an application-specific selector and expected value. Avoid reaching into Angular’s private component internals; those are implementation details rather than a stable contract.
Account for NgModel update timing
NgModel binds a FormControl to a form element and emits ngModelChange. Its updateOn setting can be change, blur or submit; change is the default. If the application deliberately uses blur or submit, a checked radio may not be the final model assertion. Trigger the required blur or submit action and wait for the corresponding rendered outcome.
Clicks that submit or navigate
Register the navigation wait concurrently with the click. Waiting for the click to finish before starting the navigation wait creates a race in which the navigation event can already have occurred.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Repair Windows errors before they cause bigger problems3Fix the driver behind crashes, sound loss and screen glitchesBest Value
import asyncio
await page.waitForSelector(RADIO, {'visible': True})
await asyncio.gather(
page.waitForNavigation({'waitUntil': 'networkidle0'}),
page.click(RADIO),
)
If selecting the radio changes the page without a full navigation, wait for a URL change, a route-specific element, or a rendered result instead of waitForNavigation. For a form that submits only after a separate button click, verify the radio first, then coordinate the button click with navigation.
Reusable helper for a test suite
async def select_radio(page, selector, *, expected_selector=None):
await page.waitForSelector(selector, {'visible': True})
await page.waitForFunction(
"""selector => {
const el = document.querySelector(selector);
return el && !el.disabled && el.offsetParent !== null;
}""",
{}, selector
)
await page.click(selector)
await page.waitForFunction(
'selector => document.querySelector(selector)?.checked === true',
{}, selector
)
if expected_selector:
await page.waitForSelector(expected_selector, {'visible': True})
# await select_radio(page, RADIO,
# expected_selector='[data-testid="delivery-price"]')
Keep the helper’s final assertion focused on a contract that is meaningful for the page. A generic helper should not pretend to know every form’s Angular model semantics.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Troubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
waitForSelector times out |
The selector is wrong, the route has not rendered the form, or the radio is inside a frame. | Inspect the final HTML, wait for the route-specific container, and switch to the correct frame before querying. |
| Element exists but click fails | The input is hidden, disabled, covered, or outside the current viewport. | Wait with visible=True, wait for enabled/readiness conditions, close overlays, or click the visible label. |
| The wrong radio becomes checked | The selector matches multiple groups or options. | Qualify it with stable name, value, an ID, or a test attribute; assert the match count is one. |
checked is true but the Angular result is unchanged |
The application updates on blur/submit, change detection has not completed, or the click hit a non-associated custom element. | Trigger the configured event, wait for a rendered model consequence, and verify the label’s association with the input. |
| Navigation wait hangs | The click does not navigate, or the wait was attached to the wrong action. | Use a route/result wait for client-side transitions; use concurrent gather only when navigation is expected. |
| Code works in JavaScript examples but not Python | Pyppeteer is an unofficial Python port and its signatures are not identical to current Puppeteer. | Use the installed Pyppeteer API reference and pin the package/browser combination used by the project. |
Reliability, speed and maintenance choices
- Prefer conditions over fixed delays. A delay can be too short on a slow run and waste time on a fast run. A selector, property or rendered-result wait expresses what the test actually needs.
- Keep selectors semantic and local. Group/value attributes and associated labels survive layout changes better than
:nth-childselectors. - Use one final business assertion. Checking
checkedcatches browser interaction problems; checking the form result catches Angular integration problems. - Capture diagnostics on failure. Save the URL, HTML, console messages and a screenshot so hidden overlays and late rendering are visible in CI logs.
- Pin and review versions. The cited Pyppeteer documentation is for 0.0.25, while Angular’s current documentation does not certify a particular Pyppeteer/Angular pair. Confirm your installed versions and browser revision before relying on version-specific behavior.
Or skip the browser setup
For a diagnostic screenshot of the page or the post-selection result, ScreenshotNeo can return an image or PDF through one request. It is useful when CI needs visual evidence without installing and managing a browser. Before capture, it accepts the cookie/consent banner like a visitor and removes more than 60 known consent platforms, newsletter popups and chat widgets. Bot checks, blank pages, timeouts and failed loads are not billed, and response headers identify the page verdict and billing status. Its MCP server provides take_screenshot, get_page_info and capture_pdf tools for AI agents.
See the ScreenshotNeo API documentation for authentication and options.
Recommended Free Tools
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
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 includes full-page capture with lazy images loaded, element capture by CSS selector, custom JavaScript and CSS, waits for a selector, delay or network idle, device and viewport controls, cookies and headers, blocking rules, caching, signed links, asynchronous jobs and bulk capture. Every feature is on every plan: 1,000 shots per month are free with no card; paid plans start at $5 for 3,000 shots. Sign up for the free ScreenshotNeo plan.
Frequently Asked Questions
Should I assert the radio’s value or the selected label’s text?
Assert the application outcome that users depend on. A value assertion is precise when the value is the contract; a rendered label or summary is better when the test represents what a user sees.
What if the radio is inside an iframe?
Select the frame first and run the same selector, visibility, click and postcondition sequence against that frame’s page context.
Can a screenshot prove that Angular updated its form model?
A screenshot proves only what was rendered. Pair it with a DOM or application-level assertion when model state is part of the test contract.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →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.




