taqwright

Configuration

Project config lives in taqwright.config.ts. This is exactly what taqwright init generates for an Android project with the demo app (the default npm init taqwright path) — every knob is listed, essentials uncommented, the rest commented placeholders you enable by deleting the leading // . The bundled demo app and its managed taqwright_api34 AVD are already wired, so npx taqwright test works out of the box. Choosing --platform both adds an ios project; declining the demo prompt leaves buildPath / appBundleId as commented placeholders instead.

taqwright.config.ts

taqwright.config.ts
import { defineConfig, Platform } from 'taqwright';

// Every config knob is listed here. Essentials are uncommented; everything
// else is a commented placeholder you can enable by removing the leading
// "// ". Hover any field in your editor for the full type docs.
export default defineConfig({
  testDir: './tests',
  timeout: 60_000,
  expectTimeout: 30_000,
  // 'html' writes playwright-report/ — view it with: npx taqwright show-report
  reporter: [['list'], ['html', { open: 'never' }]],

  // ─── Optional top-level overrides ─────────────────────────────────
  // retries: 1,
  // outputDir: './test-results',
  // fullyParallel: false,
  // forbidOnly: !!process.env.CI,
  // testMatch: ['**/*.spec.ts'],
  // testIgnore: ['**/wip/**'],
  // globalSetup: './setup.ts',
  // globalTeardown: './teardown.ts',

  projects: [
    {
      name: 'android',
      use: {
        platform: Platform.ANDROID,
        device: {
          provider: 'emulator',
          name: 'taqwright_api34',          // AVD from `taqwright install --with-avd`
          // osVersion: '14',
          // udid: 'emulator-5554',
          // orientation: 'portrait',
          //
          // ─── Parallel runs (optional) ────────────────────────────
          // Declare a pool of devices to fan tests out across, then
          // bump `workers` at the top of this config to match. Worker
          // N picks pool[N]; `workers > pool.length` fails fast. Each
          // worker gets its own Appium + driver ports auto-staggered.
          // pool: [
          //   { udid: 'emulator-5554', name: 'Pixel_7_API_34' },
          //   { udid: 'emulator-5556', name: 'Pixel_7_API_34_2' },
          //   { udid: 'emulator-5558', name: 'Pixel_7_API_34_3' },
          // ],
        },
        // Spawn `npx appium` automatically when nothing is listening on
        // the configured host:port. Set `autoStart: false` to manage
        // Appium yourself (e.g. when Appium runs in Docker — see the
        // `Run in Docker` guide).
        appium: {
          autoStart: true,
          // Boot an offline Android emulator automatically. Needs a
          // string device.name equal to the AVD id (e.g. 'Pixel_7_API_34',
          // see 'emulator -list-avds'); a RegExp name is rejected at
          // config load. iOS simulators boot via XCUITest regardless.
          autoStartDevice: true,   // cold-boots the taqwright_api34 AVD
          host: 'localhost',
          port: 4723,           // Appium 3 default
          path: '/',            // Appium 3 default (Appium 1.x used '/wd/hub')
          // newCommandTimeout: 240,
          // logLevel: 'warn',
        },

        // ─── Reset between tests ────────────────────────────────────
        // Bound to the bundled demo app (app/DemoApp-v1.0.0.apk).
        // resetBetweenTests reinstalls + relaunches it fresh before every
        // test, so each starts from a known state. All three are
        // type-required together.
        resetBetweenTests: true,
        buildPath: './app/DemoApp-v1.0.0.apk',
        appBundleId: 'com.taqelah.demo_app',

        // ─── Extra capabilities (escape hatch) ──────────────────────
        // Anything Appium accepts; merged on top of the auto-built caps.
        // capabilities: {
        //   'appium:autoGrantPermissions': true,
        //   'appium:autoAcceptAlerts': true,
        // },

        // ─── Per-project locator-action timeout (ms) ────────────────
        // Overrides the top-level `expectTimeout` for this project only.
        // expectTimeout: 30_000,

        // ─── Trace artifact ─────────────────────────────────────────
        // Captures a per-action screenshot + page-source timeline as a
        // self-contained `trace.html` under the test's output dir, also
        // attached to the Playwright HTML report. Adds one screenshot +
        // page-source round-trip per action (~100–300ms local, more
        // over USB) — recommended for CI: 'on-failure'.
        //   'off'                — no overhead (default)
        //   'on'                 — every test
        //   'on-failure'         — only failed tests
        //   'retain-on-failure'  — alias of 'on-failure' on mobile
        // trace: 'on-failure',

        // ─── Screen recording (video) ───────────────────────────────
        // Records the device screen via Appium for the whole run and
        // attaches a screen.mp4 to the Playwright HTML report (as
        // 'taqwright-video'). No per-action cost like trace, but every
        // run pays the device recorder + an mp4 transfer at teardown —
        // recommended for CI: 'on-failure'. iOS-simulator support varies.
        //   'off'                — no recording (default)
        //   'on'                 — every test
        //   'on-failure'         — only failed tests
        //   'retain-on-failure'  — alias of 'on-failure' on mobile
        // video: 'on-failure',
      },

      // ─── Per-project test-runner overrides ────────────────────────
      // timeout: 90_000,
      // retries: 2,
      // grep: /smoke/,
      // grepInvert: /flaky/,
      // dependencies: ['setup'],
      // testMatch: ['**/android/*.spec.ts'],
    },

    // ─── Cloud examples (BrowserStack / LambdaTest) ─────────────────
    // Uncomment a block below to add a cloud project. Set the matching
    // env vars before launching:
    //   BROWSERSTACK_USERNAME / BROWSERSTACK_ACCESS_KEY
    //   LAMBDATEST_USERNAME    / LAMBDATEST_ACCESS_KEY
    // For now, cloud devices are wired through the inspector
    // ('taqwright inspect'); cloud test-runner support lands separately.
    //
    // {
    //   name: 'browserstack',
    //   use: {
    //     platform: Platform.ANDROID,
    //     device: {
    //       provider: 'browserstack',
    //       name: 'Google Pixel 8',
    //       osVersion: '14.0',
    //       orientation: 'portrait',
    //     },
    //     resetBetweenTests: true,
    //     buildPath: 'bs://<app-id-from-app-upload>',
    //     appBundleId: 'com.example.app',
    //   },
    // },
    // {
    //   name: 'lambdatest',
    //   use: {
    //     platform: Platform.IOS,
    //     device: {
    //       provider: 'lambdatest',
    //       name: 'iPhone 15',
    //       osVersion: '17',
    //     },
    //     resetBetweenTests: true,
    //     buildPath: 'lt://<app-id-from-app-upload>',
    //     appBundleId: 'com.example.MyApp',
    //   },
    // },
  ],
});
What this config does:
  • reporter — console list plus an HTML report under playwright-report/; open it with npx taqwright show-report.
  • android projectappium.autoStart spawns Appium and autoStartDevice cold-boots the managed taqwright_api34 AVD (from npx taqwright install --with-avd) — no manual emulator launch.
  • resetBetweenTests + buildPath + appBundleId — bound to the bundled demo app (app/DemoApp-v1.0.0.apk); reinstalled + relaunched fresh before every test.
  • Everything else — parallel pool, trace, video, extra capabilities, and the BrowserStack / LambdaTest cloud projects — is a commented placeholder you enable as needed.

To run your own app instead of the demo, swap buildPath and appBundleId (the Android package id from your AndroidManifest.xml), and point device.name at one of your own AVDs (emulator -list-avds) — or drop autoStartDevice and boot an emulator yourself. The iosParallelCaps() helper below is for iOS projects you add.

iosParallelCaps(slot, opts?)

iOS-specific helper that returns a capabilities block tuned for parallel runs. Two parallel iOS simulators would otherwise collide on the default WDA port (8100), the MJPEG screenshot port (9100), and the Xcode DerivedData directory. iosParallelCaps(N) generates per-slot offsets so each session is isolated:

SlotwdaLocalPortmjpegServerPortderivedDataPath
081009100/tmp/wda-0
181019101/tmp/wda-1
N8100 + N9100 + N/tmp/wda-N

One slot per separate iOS project, not per device. The slot argument exists so two iOS projects in the same config don't fight (project A → iosParallelCaps(0), project B → iosParallelCaps(1)). Parallelism within one iOS project comes from device.pool + workers, and the worker fixture auto-staggers these same ports per worker — so a single iOS project (even with a 3-simulator pool) just uses iosParallelCaps(0). Only wdaLocalPort / mjpegServerPort / derivedDataPath change with the slot; the WDA tunings below are identical for every slot.

It returns a plain object (Record<string, unknown>), so use the ... spread to merge it with your own extra capabilities — later keys win:

capabilities: {
  ...iosParallelCaps(0),                              // WDA ports + tunings (slot 0)
  'appium:forceSimulatorSoftwareKeyboardPresence': true, // your extra cap, merged on top
},

Plus baked-in WDA tunings that work for most setups: useNewWDA: true (fresh build each session, prevents stale-WDA failures), wdaStartupRetries: 4, and generous launch/connection timeouts (120s / 360s) that cover Xcode's slow first-build phase. Override any of these tuning fields via the second argument (the spread above is for adding arbitrary caps; the second arg only accepts the WDA knobs):

capabilities: iosParallelCaps(0, {
  useNewWDA: false,           // reuse WDA across sessions
  wdaLaunchTimeout: 180_000,  // 3 min for slower CI machines
}),
When you don't need this helper:
  • You're using device.pool within a single iOS project — the worker fixture already auto-staggers these per-worker (since v0.x).
  • You're running iOS through a cloud provider (BrowserStack / LambdaTest) — the cloud manages WDA itself; don't set these caps.
  • You only ever run one iOS simulator at a time — Appium's defaults work fine without staggering.

Common use options

OptionDescription
platformPlatform.ANDROID or Platform.IOS.
deviceWhere the test runs. { provider: 'emulator' | 'local-device' | 'browserstack' | 'lambdatest', name, udid?, osVersion? }. See Parallel runs for device.pool.
buildPathPath to the APK / IPA / .app to install.
appBundleIdApp bundle id (Android package name / iOS bundle id).
resetBetweenTestsIf true, terminate → uninstall → install → activate the app between every test. Requires buildPath and appBundleId.
expectTimeoutDefault timeout for locator actions, in ms. Default 30000.
appiumAppium server connection: { host, port, path, autoStart?, autoStartDevice? }. autoStart: true spawns npx appium if nothing is listening on the port. autoStartDevice: true cold-boots an offline Android emulator via appium:avd (requires a string device.name = the AVD id; a RegExp is rejected at config load); iOS simulators are auto-booted by XCUITest regardless; real devices / cloud: no-op.
capabilitiesEscape hatch for extra Appium capabilities, merged on top of the defaults — e.g. appium:autoGrantPermissions, appium:wdaLocalPort. For iOS, taqwright sets appium:forceSimulatorSoftwareKeyboardPresence: true by default (the simulator hides the software keyboard otherwise); pass it here as false to opt out.
trace'off' | 'on' | 'on-failure' | 'retain-on-failure'. Captures per-action screenshots + page-source as a self-contained trace.html under the test's output dir. Details in Tracing.
video'off' | 'on' | 'on-failure' | 'retain-on-failure'. Records the device screen via Appium for the whole run and attaches a screen.mp4. Details in Screen recording.