A Playwright Alternative for Tests That Leave the Page

· TestDriver Team

A Playwright alternative that drives the real file picker with plain-language steps instead of selectors.

Automate and scale manual testing with AI ->

Above: the test below running against a public upload form, sped up with idle time removed.

You can replace Playwright scripts with tests that describe each step in plain language, including the steps that happen outside the page. TestDriver matches each step against a screenshot, so:

  • you test the file picker, dialogs and other windows your users see, not only the DOM
  • you don’t write or maintain selectors
  • your test reads like the steps you’d give a teammate

TestDriver generates and runs black-box end-to-end tests. It starts your app in a fresh virtual machine, looks at the screen and uses a real mouse and keyboard, so it never needs selectors or access to your code. Each test is a normal Vitest file, and every run is recorded as a replay.

The example uploads a file on the-internet.herokuapp.com/upload, a public practice page, through Chrome’s real Open File dialog. The full project is on GitHub: testdriverai/example-playwright-alternative.

Playwright vs. TestDriver

Playwright uploads files with setInputFiles(), which attaches the file to the <input type="file"> element directly. The native dialog never opens:

// Playwright (illustrative, not run)
await page.goto("https://the-internet.herokuapp.com/upload");
await page.locator("#file-upload").setInputFiles("/tmp/testdriver-upload.txt");
await page.locator("#file-submit").click();
await expect(page.locator("#uploaded-files")).toHaveText("testdriver-upload.txt");

The same steps from the TestDriver test that ran:

await testdriver.find("the Choose File button in the File Uploader form").click();
await testdriver.find("the Open File dialog title at the top of the file picker window", { timeout: 15000 });
await testdriver.pressKeys(["ctrl", "l"]);
await testdriver.type("/tmp/testdriver-upload.txt");
await testdriver.pressKeys(["enter"]);
await testdriver.find("the Upload button below the Choose File button").click();

Playwright’s approach is fast and stable, and it’s the right call when you only care about what the server receives. It depends on element IDs, and it skips the dialog your users actually click through. TestDriver depends on what’s on screen, so it covers the picker too. For a broader comparison, see Playwright alternatives.

TestDriver tests are JavaScript, like Playwright Test, so the language stays the same.

Watch the full replay

The replay shows every step with its screenshot and log, including the Open File dialog.

The test

/**
 * Uploads a file through Chrome's real file picker dialog on a public practice page
 * and checks the page confirms the upload.
 */
import { describe, expect, it } from "vitest";
import { TestDriver } from "testdriverai/vitest/hooks";

describe("File upload (Playwright alternative)", () => {
  it("uploads a file through the browser's file picker", async (context) => {
    const testdriver = TestDriver(context);

    // Create the file to upload inside the sandbox.
    await testdriver.exec("sh", 'echo "Uploaded by a TestDriver test" > /tmp/testdriver-upload.txt', 30000);

    // Open the practice upload page in Chrome.
    await testdriver.provision.chrome({ url: "https://the-internet.herokuapp.com/upload" });

    // Open the file picker the user would see.
    await testdriver.find("the Choose File button in the File Uploader form", { timeout: 30000 }).click();

    // Wait for the dialog, then type the file's path and confirm.
    await testdriver.find("the Open File dialog title at the top of the file picker window", { timeout: 15000 });
    await testdriver.pressKeys(["ctrl", "l"]);
    await testdriver.type("/tmp/testdriver-upload.txt");
    await testdriver.pressKeys(["enter"]);

    // Submit the form.
    await testdriver.find("the Upload button below the Choose File button").click();

    // Wait for the result page, then check the file name.
    await testdriver.find("the File Uploaded! heading", { timeout: 15000 });
    const result = await testdriver.assert(
      "the page shows the File Uploaded! heading with testdriver-upload.txt listed as the uploaded file",
    );
    expect(result).toBeTruthy();

    // Hold the final screen so the end of the replay shows the result.
    await testdriver.wait(3000);
  });
});

How it works

Create the file and open the page

exec() runs a shell command in the Linux sandbox, so the test writes its own upload file first. provision.chrome() then opens the page in a real Chrome window.

Describe elements by what users see

Each find() names the label and where it sits, like “the Upload button below the Choose File button”. The Open File dialog is a separate window, not part of the page, and TestDriver finds it the same way. See locating elements.

Wait for results, then assert

find() with a timeout keeps looking until the dialog or the result page appears, so there are no fixed sleeps. assert() checks a plain-language statement against the screen and returns true or false. See making assertions.

Run it yourself

Clone the example repo, or set up TestDriver in your own project and run the test:

npx testdriverai init
npx vitest run playwright-alternative.test.mjs

To point it at your own app:

  • Change the url in provision.chrome() to your upload page.
  • Write the file you need with exec(), or download a fixture with curl.
  • Rewrite the find() descriptions to match your labels, then add an assert() for what your app shows after the upload.

FAQ

Do I need selectors or test IDs? No. Steps are matched on screenshots, so there are no IDs or data-testid attributes to add or keep in sync.

Can I keep my Playwright tests? Yes. Both are JavaScript test files, so they can live in the same repo. Start with the flows Playwright can’t reach, like native dialogs, and keep the rest.

Can I run it in CI? Yes. It’s a normal Vitest file and the browser runs in TestDriver’s cloud sandbox. See CI/CD.

Automate and scale manual testing with AI

TestDriver uses computer-use AI to test any app - write tests in plain English and run them anywhere.