Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use Horseman’s .select(selector, value), but call it only after the AngularJS page has rendered the specific <option> you need. Then verify both the browser’s selected value and the application state that depends on AngularJS’s model. This matters because ngOptions can create options asynchronously, and a select’s visible label is not necessarily its submitted value.
What you are actually selecting
Horseman drives PhantomJS from Node with a chainable, promise-based API. Its .select() method changes a native HTML <select> element by CSS selector and option value:
.select('select#plan', 'pro')
AngularJS adds a separate layer. Its ngModel directive binds the control to a scope property, while ngOptions can generate the option elements from an array or object expression. On an initial page load, the select may exist before the option you want does. Selecting too early can therefore fail, select nothing, or leave a dependent widget unchanged.
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 →Think of the operation as three checks:
- DOM readiness: the intended
<select>and option exist. - Selection: Horseman sets the option’s actual
value. - Application response: the selected state and any dependent result are correct.
Horseman and AngularJS document the API and binding behavior, but no generic API guarantee covers every custom directive, watcher, or third-party widget. The final verification must observe the page you are automating.
#1 Best Overall
Prerequisites and a minimal Horseman script
Install and expose PhantomJS
Horseman requires a working PhantomJS executable. It must be on your system path, installed through a supported PhantomJS package, or supplied with Horseman’s phantomPath option. If Horseman starts but cannot create a session, fix this runtime requirement before debugging AngularJS.
Basic selection
const Horseman = require('node-horseman');
const horseman = new Horseman();
horseman
.open('https://example.com/form')
.waitForSelector('select#plan')
.select('select#plan', 'pro')
.evaluate(function () {
var select = document.querySelector('select#plan');
return {
value: select ? select.value : null,
text: select && select.selectedIndex >= 0
? select.options[select.selectedIndex].text
: null
};
})
.then(function (result) {
console.log(result);
})
.close()
.catch(function (error) {
console.error(error);
return horseman.close();
});
Replace the URL, selector, and value with those from the target page. The label shown to a user might be “Professional,” while the HTML value is pro; .select() needs the latter.
Reliable workflow for AngularJS selects
1. Choose a stable select selector
Prefer an ID or a deliberate form-control class over a positional selector such as select:nth-of-type(2). Inspect the rendered markup in a browser or with an evaluation step. The selector must identify the actual native <select>, not a decorative wrapper used by a custom control.
2. Wait for the select and its intended option
.waitForSelector(selector) waits for a matching element and has a documented default timeout of 5,000 milliseconds. Waiting only for the select is insufficient when ngOptions fills it later. Wait for the option itself:
const targetOption = 'select#plan option[value="pro"]';
horseman
.waitForSelector('select#plan')
.waitForSelector(targetOption)
.select('select#plan', 'pro');
This condition-based approach observes the thing you need. A fixed sleep can be useful for diagnosing a page, but it does not prove that AngularJS has completed its data request or digest cycle.
3. Select by the option’s value
Use the exact value attribute:
.select('select#plan', 'pro')
Do not infer the value from the visible text. With ngOptions, AngularJS may generate values that represent model items rather than human-readable labels. Read the rendered option values, or use a page-side evaluation to enumerate them:
.evaluate(function () {
return Array.prototype.map.call(
document.querySelectorAll('select#plan option'),
function (option) {
return { value: option.value, text: option.text };
}
);
})
4. Verify the native selection
After .select(), read select.value, the selected option’s text, and (when relevant) the selected index:
Quick wins for a faster PC:
Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →.evaluate(function () {
var select = document.querySelector('select#plan');
if (!select) return null;
var option = select.options[select.selectedIndex];
return {
value: select.value,
text: option ? option.text : null,
index: select.selectedIndex
};
})
Fail the run if the returned value is not the one requested. A screenshot or log of this object makes CI failures easier to diagnose.
5. Verify the AngularJS result
A native selection is not the same as proving that the application model and dependent UI are correct. Check a visible result such as a price, a second select, an error message, or a results panel. If the page updates after a watcher or asynchronous request, wait for that observable result rather than assuming the update is immediate.
horseman
.select('select#plan', 'pro')
.waitForSelector('#plan-summary')
.evaluate(function () {
return document.querySelector('#plan-summary').textContent.trim();
})
.then(function (summary) {
console.log('Updated summary:', summary);
});
Use a page-specific condition: an element appearing, text changing, a loading marker disappearing, or a second control receiving options. The correct signal depends on the application.
A complete reusable example
The following script waits for an asynchronously populated option, selects it, verifies the native value, and checks a dependent result. It also closes PhantomJS on success or failure.
const Horseman = require('node-horseman');
const url = 'https://example.com/checkout';
const selectCss = 'select#country';
const wantedValue = 'us';
const optionCss = selectCss + ' option[value="' + wantedValue + '"]';
const horseman = new Horseman({
// Set this when PhantomJS is not on PATH:
// phantomPath: '/absolute/path/to/phantomjs'
});
async function run() {
try {
await horseman.open(url);
await horseman.waitForSelector(selectCss);
await horseman.waitForSelector(optionCss);
await horseman.select(selectCss, wantedValue);
const selected = await horseman.evaluate(function (selector) {
var el = document.querySelector(selector);
if (!el) return null;
var option = el.options[el.selectedIndex];
return {
value: el.value,
text: option ? option.text : null
};
}, selectCss);
if (!selected || selected.value !== wantedValue) {
throw new Error('Expected ' + wantedValue + ', got ' + JSON.stringify(selected));
}
// Replace this with an application-specific result on your page.
await horseman.waitForSelector('#country-result');
console.log(selected);
} finally {
await horseman.close();
}
}
run().catch(function (error) {
console.error(error);
process.exitCode = 1;
});
evaluate executes in the page context. Pass only JSON-serializable arguments and return plain data. DOM nodes, closures, and functions cannot be returned as ordinary results.
Rank #4
- Used Book in Good Condition
Handling dependent and custom controls
Dependent selects
For a country/state or category/product chain, select the parent, then wait for the child’s new option. Reusing an old child value can produce a false positive if the browser leaves a stale selection visible.
await horseman.select('select#country', 'us');
await horseman.waitForSelector('select#state option[value="ca"]');
await horseman.select('select#state', 'ca');
await horseman.waitForSelector('#address-validation-success');
Model objects and ngOptions
An ngOptions expression can display one property while using another as the model value. Inspect the resulting DOM rather than guessing how the expression maps objects. If the page uses a custom directive around the select, confirm that it still exposes a native select and that the directive reacts to a programmatic selection.
Events and digest timing
Verify the application’s visible outcome, not an internal scope variable that may be inaccessible or implementation-specific. If a watcher triggers an HTTP request, wait for the request’s rendered result or for its loading indicator to finish. A short delay may help isolate timing problems, but it should not replace an observable readiness condition.
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 matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallTroubleshooting common failures
| Symptom | Likely cause | Fix |
|---|---|---|
| Horseman cannot start | PhantomJS is missing or not executable. | Put PhantomJS on PATH, install it through a supported package, or set phantomPath. |
waitForSelector times out for the option |
ngOptions has not populated it, the value is wrong, or the page request failed. |
Inspect rendered option values, verify the page’s data request, and wait for the exact option rather than sleeping. |
| Select exists but selection is unchanged | The selector targets a wrapper, or the supplied value does not match an option’s value. | Target the native <select> and enumerate its options with evaluate. |
| Value changes but dependent UI does not | A custom directive or watcher has different event/digest behavior. | Wait for and assert the page’s visible dependent result; investigate the directive’s required interaction if it is not native. |
| Visible label is correct but assertion fails | The label and value attributes differ. | Assert both separately and pass the exact value to .select(). |
| Evaluation returns unusable data | A DOM node, function, or non-serializable object was returned. | Return strings, numbers, booleans, arrays, or plain JSON objects. |
| Run hangs or leaks processes | The Horseman instance was not closed on every path. | Put .close() in a finally block or both success and error handlers. |
Timing, reliability, and maintenance
- Prefer state over elapsed time: wait for the target option or result. This reduces both premature selections and unnecessary idle time.
- Keep selectors resilient: IDs and semantic attributes generally survive layout changes better than positional selectors; re-check them when the AngularJS template changes.
- Assert the contract: record the requested value, returned value, selected label, and a business-visible result.
- Control timeout expectations: the documented generic selector wait defaults to 5,000 ms. Set a strategy appropriate to your page’s normal network and rendering time, and distinguish a slow response from a missing option.
- Clean up every run: closing shuts down PhantomJS and prevents orphaned browser processes.
AngularJS support officially ended in January 2022. Existing applications can still be automated, but their templates, directives, and data endpoints are application-specific. Treat framework age as a maintenance consideration when deciding how much diagnostic logging and regression coverage to keep.
Or skip the browser setup
If you only need a rendered image or PDF rather than an interaction with AngularJS state, ScreenshotNeo provides a single HTTP request. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Only clean shots are billed: bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and billing status.
Use the API documentation at https://screenshotneo.com/docs/ for the full option set, including waits, custom JavaScript, selectors, device emulation, PDFs, and asynchronous jobs.
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 also has an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every plan includes all features. The Free plan provides 1,000 shots per month without a card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
When this method is the right fit
Use Horseman when your test must change an AngularJS model and inspect the resulting application behavior. Wait for the actual option, select by its value, and assert the visible consequence. If your requirement is simply a clean capture of a rendered page, an API call avoids maintaining a PhantomJS runtime and browser interaction script.
Frequently Asked Questions
Can I pass the visible option text to Horseman’s select method?
Only when it is also the option’s value. Inspect the rendered value attribute and pass that exact string to .select(selector, value).
Why does waiting for the select element alone fail on an AngularJS page?
The <select> can be present while ngOptions is still creating its options. Wait for the specific option you intend to select.
What should I assert after selecting an option?
Assert the native selected value and a page-specific visible result, such as updated text, a dependent option, or a completed validation state.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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.

