Skip to main content
@qawolf/flows is the wrapper around QA Wolf test code. It tells the runner what to run, where to run it, and which runtime objects to inject. Most users should start with a launch-enabled flow.
import { expect, flow } from "@qawolf/flows/web";

export default flow(
  "Sign in",
  { target: "Web - Chrome", launch: true },
  async ({ page }) => {
    await page.goto("https://example.com/login");
    await page.getByLabel("Email").fill("qa@example.com");
    await page.getByRole("button", { name: "Continue" }).click();

    await expect(page.getByText("Check your email")).toBeVisible();
  },
);

Pick The Entry Point

Import from the package that matches the target you are authoring for.
import { expect, flow } from "@qawolf/flows/web";
Use the top-level package only for shared runtime helpers:
import { platform } from "@qawolf/flows";

if (platform.target === "Web - Chrome") {
  // Target-specific branch.
}

Start With launch: true

launch: true asks QA Wolf to start the browser or mobile app before your callback runs.
export default flow(
  "Open dashboard",
  { target: "Web - Chrome", launch: true },
  async ({ page }) => {
    await page.goto("https://example.com/dashboard");
  },
);
Every flow callback can receive input, setOutput(...), and test(...). When launch is enabled, the callback also receives platform runtime objects:
PlatformAdditional callback parameters
Webpage, context, optional browser
Androiddriver
iOSdriver

Pass Launch Options In The Definition

If startup needs options and those options are known up front, pass an object to launch.
import { flow } from "@qawolf/flows/web";

export default flow(
  "Open account with profile",
  {
    target: "Web - Chrome",
    launch: {
      browserContext: "persistent",
      userDataDir: "/tmp/qawolf-profile",
    },
  },
  async ({ page }) => {
    await page.goto("https://example.com/account");
  },
);
This keeps startup declarative while still giving the callback a ready-to-use page or driver.

Launch Manually For Advanced Flow Control

Pass the target directly and call launch(...) in the callback only when the flow must decide startup at runtime.
import { flow, launch } from "@qawolf/flows/web";

export default flow("Open account", "Web - Chrome", async () => {
  const useSavedProfile = process.env["USE_SAVED_PROFILE"] === "true";

  const launchResult = await launch(
    useSavedProfile
      ? {
          browserContext: "persistent",
          userDataDir: "/tmp/qawolf-profile",
        }
      : undefined,
  );
  if (!("context" in launchResult)) throw new Error("Expected browser launch");

  const page =
    "page" in launchResult
      ? launchResult.page
      : await launchResult.context.newPage();

  await page.goto("https://example.com/dashboard");
});
This is the advanced path for startup branches, delayed startup, or startup that depends on callback-only data.

Next Steps

Last modified on April 24, 2026