Skip to main content
A flow has three parts:
flow("Name shown in QA Wolf", target, async (runtime) => {
  // test steps
});
  • Name appears in runs, bug reports, and logs.
  • Target selects the browser, device, or CLI environment.
  • Callback contains the workflow steps.

Callback Parameters

Every flow callback can receive:
  • input for values passed into the flow
  • setOutput(...) for values a later flow can read
  • test(...) for named sub-steps
import { flow } from "@qawolf/flows/cli";

export default flow(
  "Prepare account fixture",
  "Basic",
  async ({ input, setOutput, test }) => {
    await test("publish account fixture", async () => {
      setOutput("account", {
        input,
        role: "admin",
      });
    });
  },
);
Launch-enabled flows receive platform objects in addition to these parameters.

Launch In The Flow Definition

Use { target, launch: true } when the flow should start the session before the callback runs.
import { flow } from "@qawolf/flows/web";

export default flow(
  "Checkout",
  { target: "Web - Chrome", launch: true },
  async ({ page }) => {
    await page.goto("https://example.com/cart");
  },
);
The callback receives the launched object for that platform:
Entry pointLaunch object
@qawolf/flows/webpage, context, optional browser
@qawolf/flows/androiddriver
@qawolf/flows/iosdriver
When startup needs options, pass an options object to launch instead of calling launch(...) manually.
import { flow } from "@qawolf/flows/web";

export default flow(
  "Checkout with profile",
  {
    target: "Web - Chrome",
    launch: {
      browserContext: "persistent",
      userDataDir: "/tmp/qawolf-profile",
    },
  },
  async ({ page }) => {
    await page.goto("https://example.com/cart");
  },
);
Use this shape when the startup options are known before the callback runs.

Manual Launch Is For Advanced Flows

Pass the target directly and call launch(...) inside the callback only when startup depends on callback logic.
import { flow, launch } from "@qawolf/flows/web";

export default flow("Checkout", "Web - Chrome", async () => {
  const usePersistentProfile = process.env["USE_PROFILE"] === "true";

  const launchResult = await launch(
    usePersistentProfile
      ? {
          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/cart");
});
Use manual launch for advanced flows where startup is part of the test logic:
  • branch on runtime state before launching
  • delay launch until after setup steps
  • combine startup with callback-only data

CLI Flows Are Simpler

CLI flows do not launch a browser or mobile app.
import { flow } from "@qawolf/flows/cli";
import { readFile } from "node:fs/promises";

export default flow("Read build metadata", "Basic", async () => {
  const metadata = await readFile("build-metadata.json", "utf8");

  console.log(JSON.parse(metadata));
});
Last modified on April 24, 2026