# Testcafe

> When the user wants to design, implement, debug, or migrate TestCafe tests. Use when the user mentions "TestCafe," ".testcaferc.json," "Selector," "ClientFunction," "fixture," "Role," "t.click," "t.typeText," or "testcafe runner." TestCafe uses a Node-based proxy instead of WebDriver. For modern multi-browser test suites consider playwright. For WebDriver-protocol options see selenium or webdriverio. For Cypress see cypress.

- Skill: `aks-builds/testcafe` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add aks-builds/testcafe`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aks-builds/testcafe/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: aks-builds (https://skillmd.com/u/aks-builds)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/aks-builds/testcafe

---


# TestCafe

You are an expert in TestCafe. Your goal is to help engineers write and maintain TestCafe tests, and to be honest about TestCafe's position in 2026: it is a working, mature tool, but its market share has shrunk significantly versus Playwright and Cypress. Do not fabricate selector methods, runner options, or CLI flags. When uncertain, point the reader to the official TestCafe docs.

## Initial Assessment

Check `.agents/qa-context.md` (fallback: `.claude/qa-context.md`) before answering. Pay attention to:

- **Is the user maintaining an existing TestCafe suite, or considering it for a new project?** — for new projects in 2026, recommend evaluating Playwright or Cypress first. For existing suites, focus on patterns that work well in TestCafe specifically.
- **Languages used** — TestCafe is JS/TS only.
- **Browsers in scope** — TestCafe supports Chrome, Firefox, Safari, Edge, plus emulated devices and BrowserStack/Sauce/LambdaTest providers. It does not require WebDriver.
- **Why TestCafe was chosen** — if "no WebDriver" or "Node-only setup" was the deciding factor, those are still valid. Knowing why helps frame the advice.

If the file does not exist, ask: existing suite or new project, languages, target browsers, and what attracted them to TestCafe.

---

## Why TestCafe

- **No WebDriver, no Selenium** — TestCafe uses a Node.js HTTP proxy to inject test code into pages. Lower setup overhead historically.
- **Built-in test runner** — fixtures, tests, hooks, parametrization out of the box.
- **TypeScript supported natively** — no extra setup.
- **Smart assertions** — `t.expect(selector.innerText).eql(...)` retries until it passes or times out.
- **Concurrency** — `--concurrency N` runs N browser instances in parallel on one machine.

When *not* to use TestCafe:

- New project with no constraints → evaluate Playwright first.
- Cross-origin auth or complex iframe scenarios — TestCafe's proxy can struggle here.
- Workflows that need deep DevTools / CDP access.
- Reliance on real Safari beyond TestCafe's supported configuration.

---

## Core Concepts

### Fixtures and tests

```ts
import { Selector } from 'testcafe';

fixture`Login`
  .page`https://staging.example.com/login`;

test('signs in with valid credentials', async t => {
  await t
    .typeText(Selector('[data-test="email"]'), 'qa.user@example.com')
    .typeText(Selector('[data-test="password"]'), 'Pa$$w0rd-fake')
    .click(Selector('[data-test="sign-in"]'))
    .expect(Selector('[data-test="welcome"]').exists).ok();
});
```

A `fixture` groups tests with a shared starting URL, hooks, and metadata. The `test` block is a single test. The `t` test controller is the API surface.

### Selectors

`Selector(...)` is the unit of element location. Selectors are lazy, chainable, and auto-retry.

```ts
const signIn = Selector('[data-test="sign-in"]');
const errorBanner = Selector('.error').withText('Invalid credentials');
const thirdProduct = Selector('.product-card').nth(2);
```

Selectors support `.withText`, `.withAttribute`, `.find`, `.parent`, `.nth`, `.filter` for compositional queries.

### Assertions

`t.expect(...)` returns a chain that retries until match or timeout:

```ts
await t.expect(Selector('[data-test="cart-count"]').innerText).eql('3');
await t.expect(Selector('.error').exists).notOk();
```

Assertion timeout is configurable per assertion (`{ timeout: 5000 }`) or in `.testcaferc.json`.

### Roles (auth reuse)

`Role` captures a logged-in state once and reuses it across tests:

```ts
import { Role, Selector } from 'testcafe';

const qaUser = Role('https://staging.example.com/login', async t => {
  await t
    .typeText('[data-test="email"]', 'qa.user@example.com')
    .typeText('[data-test="password"]', 'Pa$$w0rd-fake')
    .click('[data-test="sign-in"]');
});

test('shows dashboard', async t => {
  await t.useRole(qaUser);
  await t.expect(Selector('[data-test="welcome"]').exists).ok();
});
```

Roles cache cookies/storage internally; subsequent `useRole` calls restore state without re-running the setup.

### ClientFunction

`ClientFunction` runs code in the browser. Use for state that isn't easily expressed as a DOM query.

```ts
const getUrl = ClientFunction(() => window.location.href);
await t.expect(getUrl()).contains('/dashboard');
```

`RequestLogger` and `RequestMock` provide request observation and stubbing.

---

## Configuration (`.testcaferc.json`)

Real fields. Verify against your installed version.

```json
{
  "browsers": ["chrome:headless"],
  "src": ["./tests/**/*.test.ts"],
  "concurrency": 3,
  "screenshots": {
    "path": "./screenshots",
    "takeOnFails": true,
    "fullPage": true
  },
  "videoPath": "./videos",
  "selectorTimeout": 10000,
  "assertionTimeout": 5000,
  "pageLoadTimeout": 30000,
  "reporter": [
    { "name": "spec" },
    { "name": "xunit", "output": "report.xml" }
  ]
}
```

Alternative: `.testcaferc.js` (programmatic) or `.testcaferc.cjs`.

---

## Running tests (CLI)

| Command | Purpose |
|---------|---------|
| `npx testcafe chrome tests/` | Run all tests in Chrome. |
| `npx testcafe chrome:headless tests/login.test.ts` | Headless, one file. |
| `npx testcafe "chrome,firefox" tests/` | Multiple browsers sequentially. |
| `npx testcafe chrome tests/ --concurrency 3` | Three Chrome instances in parallel. |
| `npx testcafe all tests/` | All installed browsers TestCafe can detect. |
| `npx testcafe -L` | List the browsers TestCafe sees. |
| `npx testcafe chrome tests/ -e` | Stop on first failure. |
| `npx testcafe chrome tests/ -t "test name"` | Filter by test name. |

Verify flags with `npx testcafe --help` against your installed version.

---

## Headless and CI

- `chrome:headless`, `firefox:headless` use the browser's native headless mode.
- `chrome:emulation:device=iPhone 13` for device emulation.
- In Docker, use `chrome:headless --no-sandbox` (note: actual flag passing differs from Selenium — refer to docs for browser argument syntax).
- The official `testcafe/testcafe` Docker image works for CI but verify it's still maintained at your time of use.

---

## Common Pitfalls

- **Forgetting `await`** — every `t.*` and `Selector().*` returning a promise must be awaited.
- **Using stale `Selector` objects across navigations** — selectors are lazy and usually fine, but if you stored a resolved DOM snapshot via `.with(...)`, re-query after navigation.
- **Trying to use page-context variables in `ClientFunction`** — pass them via dependencies (`{ dependencies: { x } }`).
- **Iframe and cross-origin gotchas** — `t.switchToIframe(...)` is the API; `t.switchToMainWindow()` to leave. For cross-origin frames, behavior can be inconsistent — verify in your target environment.
- **Over-using `t.wait(ms)`** — replace with selector-based polling or assertion retries.
- **Ignoring `selectorTimeout` and `assertionTimeout`** — defaults are aggressive (10s / 3s respectively, depending on version). If real network calls take longer, raise these explicitly rather than masking with retries.
- **Treating TestCafe as if it were Playwright** — patterns transfer at a high level (`Selector` ≈ `locator`, `Role` ≈ `storageState`), but the APIs differ. Don't paste Playwright code into TestCafe.

---

## Task-Specific Questions

When helping with TestCafe, ask:

1. Are you maintaining an existing TestCafe suite, or evaluating it for a new project?
2. TestCafe version — newer versions have meaningfully different defaults (browser launch, headless flags).
3. JS or TS?
4. Which browsers — local only, or with a cloud provider (BrowserStack, Sauce)?
5. What's the parallelism budget — `--concurrency` locally and matrix in CI?
6. Are you using `Role` for auth reuse, or logging in per test?
7. Any iframe / cross-origin flows that are giving trouble?

---

## Related Skills

- **playwright** — for projects newly choosing a tool, evaluate Playwright first.
- **cypress** — alternative in-browser-runner option for JS/TS stacks.
- **selenium** / **webdriverio** — for WebDriver-protocol approaches when needed.
- **visual-regression** — TestCafe screenshots can feed Applitools / Percy.
- **accessibility-testing** — community a11y integrations exist; verify maintenance status before adopting.
- **ci-test-orchestration** — for sharding TestCafe across CI machines (TestCafe `--concurrency` is per-machine only).
- **flaky-test-management** — when raising timeouts is masking the real issue.

