Migrate from Puppeteer to TesterArmyYour Puppeteer scripts,
without the selector upkeep
TesterArmy replaces scripted automation with an AI agent that executes natural-language steps using vision. Paste one prompt into Claude Code, Cursor, or Codex and the agent reads your Puppeteer code, converts it, and verifies every test.
Takes less than 2 minutes. No credit card required.
Scripts that work
until the DOM changes
Puppeteer scripts are imperative browser automation: page.waitForSelector, page.$eval, manual retries, and assertions bolted on through Jest or plain Node scripts. They work until the DOM changes.
Every DOM change costs an afternoon
Your scripts drive the page through CSS and XPath selectors, so any markup change can turn them red. The scripts work until the DOM changes, and then someone spends an afternoon updating selectors.
waitForSelector and retries hold the suite together
Every page.click() needs a waitForSelector or waitForNavigation in front of it, and flaky runs get patched with sleeps and manual retry loops. The waiting logic ends up being as much code as the test itself.
Assertions are bolted on
Puppeteer is an automation library, not a test framework, so checks arrive through Jest expect() calls on page.$eval and page.content(). Reading what a test actually verifies means untangling DOM plumbing first.
Debugging means headless: false and screenshots
When a script fails you relaunch it with puppeteer.launch({ headless: false }) and scatter page.screenshot() calls to see what happened. You reconstruct the failure one still frame at a time instead of watching it.
What your suite reads like
after the conversion
The intent behind each page.* call becomes an act step and each assertion becomes an assert step, while the selectors and waits are dropped. This is the same checkout flow in both languages.
it("checkout with saved card", async () => {
await page.goto(`${BASE_URL}/products/desk-lamp`);
await page.waitForSelector('[data-testid="add-to-cart"]');
await page.click('[data-testid="add-to-cart"]');
await page.waitForSelector('[data-testid="cart-count"]');
await page.click('[data-testid="cart-link"]');
await page.waitForNavigation({ waitUntil: "networkidle0" });
await page.click("#checkout-button");
await page.waitForSelector("#payment-form", { timeout: 15000 });
await page.click('input[name="saved-card"]');
await page.click('[data-testid="place-order"]');
await page.waitForSelector('[data-testid="order-confirmation"]');
const total = await page.$eval('[data-testid="order-total"]',
(el) => el.textContent);
expect(total).toBe("$49.00");
});The CSS selectors, waitForSelector calls, and the page.$eval plumbing are dropped in conversion. The agent finds elements visually and waits on its own, so the next DOM change does not cost anyone an afternoon.
The whole migration
is one prompt
Paste this into your coding agent in the repository that contains your Puppeteer tests. It finds every file importing puppeteer, skips the scrapers and PDF generators, converts each test into plain-English steps, and verifies every one locally with real exit codes.
Migrate this repository's Puppeteer tests to TesterArmy using the
TesterArmy CLI (`ta`). Verify auth first with `ta status --json`, and use
`ta --help` plus subcommand help to discover commands. Prefer --json output.
1. Discover: find all files importing puppeteer or puppeteer-core
(Jest-Puppeteer suites, *.e2e.js scripts, jest-puppeteer.config.js).
Identify the base URL from config, env vars, or page.goto() calls.
Only migrate files that TEST user flows - skip scrapers, PDF/screenshot
generators, and crawling utilities.
2. Create a TesterArmy project (skip if one exists in `ta projects list`):
echo '{"name":"<repo name>","url":"<base URL>","projectType":"web"}' | ta projects create --json
3. Save durable context as project memories (category site_structure,
importance high): key routes, the login URL, setup steps from
beforeAll/globalSetup that describe the app.
echo '{"category":"site_structure","title":"...","content":"...","importance":"high"}' | ta memories create --project <projectId> --json
4. Convert each test/script into a TesterArmy test:
- Express the USER INTENT of each action in plain English. Drop all
selectors, waitForSelector/waitForNavigation calls, sleeps, and retry
loops - the TesterArmy agent finds elements visually and waits on its
own.
- page.goto/click/type/select -> steps with type "act"
- expect() assertions on page content -> steps with type "assert"
- manual login sequences or cookie injection -> one step with type "login"
- page.screenshot() evidence points -> steps with type "screenshot"
- Keep tests focused: 3-10 steps. Split long scripts that cover
multiple flows into separate tests.
Create each test:
echo '{"title":"<test name>","description":"Migrated from <file>","steps":[{"title":"Navigate to /login","type":"act"},{"title":"Dashboard loads","type":"assert"}]}' | ta tests create --project <projectId> --json
Step types: act, assert, login, screenshot.
5. Never hardcode passwords in test steps. If scripts read credentials from
env vars or inject cookies, tell me which credentials to add and I will run:
echo '{"kind":"login","label":"...","username":"...","password":"..."}' | ta projects credentials-create <projectId> --json
6. Verify: run each migrated test locally with
`ta tests run <testId> --local --url <base URL> --json` and report results.
Exit code 0 = pass, 1 = fail.
7. Report a summary table: source file -> TesterArmy test ID -> run result,
plus a list of anything you intentionally skipped and why.Everything you built
has a place to land
Every Puppeteer concept maps to a TesterArmy equivalent - and the two things that break most often, selectors and waits, map to nothing at all.
Puppeteer and TesterArmy,
row by row
Puppeteer is free, built by the Chrome team, and for scraping, PDF generation, and precise Chrome automation it stays the right tool. This table is about what owning a test suite on top of it costs, and where each side wins.
- Who maintains the testsTesterArmyThe agent finds elements visually and waits on its own, so nobody owns a selector layer.AdvantagePuppeteerYour team owns the scripts, CSS selectors, waitForSelector calls, and retry loops - maintenance scales with DOM change.
- What breaks on a UI changeTesterArmyUsually nothing - a renamed data-testid or a restructured page is invisible to a vision agent.AdvantagePuppeteerEvery script whose selector touched the changed markup, and updating them is the selector afternoon.
- Writing a new testTesterArmy3-10 plain-English steps that anyone on the team can write and review.PuppeteerAn imperative page.* script with selectors and waits, plus Jest or similar bolted on for the assertions.
- Flaky failuresTesterArmyThe agent waits like a human, and every failure comes with a recording and a step trace.PuppeteerHandled with waitForSelector, waitForNavigation, sleeps, and retry loops that accumulate inside the scripts.
- Run verdictsTesterArmyThree verdicts - passed, failed, or blocked. Environment and setup stops land in blocked with the reason attached, so the failure count only holds real product failures.AdvantagePuppeteerTwo verdicts. A crashed Chrome instance and a product bug both arrive as the same Jest failure until you read the trace.
- License and infrastructureTesterArmyA paid service - the agent, browsers, and infrastructure are included.PuppeteerFree and open source from the Chrome team; you run the browsers, CI workers, and waiting logic yourself.Advantage
- Scraping and Chrome automationTesterArmyOut of scope - TesterArmy tests user flows rather than automating browsers, so scrapers and generators stay in Puppeteer.PuppeteerPuppeteer's home turf: scraping, PDF generation, crawling, and precise CDP control are what it is built for.Advantage
- Pull request workflowTesterArmyPR testing is built in - runs trigger from GitHub and report back on the pull request.PuppeteerYou wire your Puppeteer job into CI yourself and maintain the workflow and its retries.
- Native mobile appsTesterArmyThe same plain-English tests run on iOS and Android via managed cloud simulators.AdvantagePuppeteerChrome and web focused - native apps need a separate stack like Appium, Maestro, or Detox.
- Entry priceTesterArmyFree trial without a credit card; Hobby is $99 per month with 250 test runs included.Puppeteer$0 forever - Puppeteer has no paid tier, and the cost is the engineering time in the rows above.
Facts checked August 2026. Spotted something out of date? Tell us and we will fix it.
From imperative scripts to green runs in four steps
Install the CLI, hand the prompt to your coding agent, and review what comes back. Your Puppeteer scripts keep running until every migrated test is green.
Point the agent at your repo
Install the TesterArmy CLI and authenticate, then paste the migration prompt into Claude Code, Cursor, or Codex. The agent finds every file importing puppeteer or puppeteer-core, identifies the base URL, and creates a matching web project.
The agent converts every test
Each script becomes a test of 3-10 plain-English steps: page.* calls turn into act steps, expect() assertions into assert steps, cookie injection into a single login step, and page.screenshot() evidence points into screenshot steps.
Every migrated test runs locally
The agent verifies each test against your app with ta tests run --local and reports pass or fail through real exit codes, plus a summary of the scrapers and generators it intentionally skipped and why.
Wire the suite into CI
Replace your Puppeteer CI job with ta tests run --group or a group webhook. Keep Puppeteer for scraping or PDF work, and delete the test scripts once the TesterArmy runs are green.
What comes with you,
what stays behind
End-to-end user flows translate cleanly, and the guide is explicit about what to leave behind. Puppeteer stays in your repo for the jobs it is the right tool for.
When Puppeteer is still the right call
- Most of your Puppeteer code is scrapers, PDF generators, or crawling utilities rather than user-flow tests - that is Puppeteer's home turf, and the migration prompt itself says to skip those files.
- Your tests depend on request interception with page.setRequestInterception or on low-level CDP control - TesterArmy runs against a real environment instead of an instrumented browser.
- Your flows must run air-gapped or fully offline - TesterArmy tests run against a real, reachable environment.
- You have engineers who genuinely enjoy owning the scripts and your DOM rarely churns.
Nothing forces the choice on day one. Migrated tests run alongside your Puppeteer scripts, Puppeteer keeps the scraping and PDF work permanently, and the guide's own advice is to delete the test scripts only once the TesterArmy runs are green.
Frequently Asked Questions
The next DOM change
should not cost an afternoon
Your Puppeteer scripts encode real knowledge about how your product is supposed to work. The migration keeps that knowledge and retires the machinery around it - the selectors, the waitForSelector calls, the retry loops - so the tests describe intent instead of DOM structure.