TypeScript for QA โ
Why I automate in TypeScript rather than JavaScript, and the code standards I apply in the test repository.
Why TypeScript โ
- Static typing: errors show up as you type, not at minute 12 of the CI run.
- Maintainability: in a large test repo, the compiler is your first test suite.
- A real POM: abstract classes, interfaces, and strict inheritance (SOLID in the POM).
- Tooling: autocompletion and first-class VS Code integration.
- And an organizational reason that doesn't show up in tutorials: using the same language as your frontend team. If the product is React+TS, automating in TS gives you shared standards and support from their experts. Ask your team what they use before deciding your stack.
A strategy anecdote: our Playwright PoC was done in JavaScript on purpose (minimizing variables while evaluating the framework), and the final project in TypeScript. PoC in the language you know; product in the right language.
Naming conventions โ
| Element | Convention | Example |
|---|---|---|
| Classes | PascalCase | LoginPage, CustomFieldList |
| Methods and variables | camelCase | openMenu(), navigateToProjects() |
| Files | kebab-case | custom-field.list.ts |
Typing you can feel โ
Explicit signatures with typed objects, defaults, and optionals:
async find(component: { referenceId: string; name: string }, exist: boolean = true)Autocompletion, enforced correct usage, and errors at development time. The medium-term goal: zero any โ every domain object with its own interface. (Confession: after a migration there are always stray anys left. They get removed iteratively, but you have to put it in the plan or it never happens.)
ESLint with the Playwright rules โ
Flat config combining three layers: recommended JS + TypeScript + eslint-plugin-playwright (specific rules: await misuse, misused assertions, Playwright globals):
// eslint.config.mjs
import pluginJs from "@eslint/js";
import pluginTs from "@typescript-eslint/eslint-plugin";
import parserTs from "@typescript-eslint/parser";
import pluginPlaywright from "eslint-plugin-playwright";
export default [
pluginJs.configs.recommended,
{
files: ["**/*.ts"],
languageOptions: { parser: parserTs },
plugins: { "@typescript-eslint": pluginTs },
rules: { ...pluginTs.configs.recommended.rules },
},
{
files: ["tests/**/*.ts"],
plugins: { playwright: pluginPlaywright },
rules: { ...pluginPlaywright.configs.recommended.rules },
},
];A tip learned the hard way: type-aware rules (no-floating-promises and friends) require wiring up the tsconfig (project service). It's tempting to disable them to get started quickly โ but no-floating-promises is precisely the rule that catches Playwright's number one bug: the forgotten await. Wire them up as soon as you can.
In CI, ESLint emits checkstyle format so the pipeline renders the findings as a build report:
npx eslint --format=checkstyle -o checkstyle-result.xml srcThe test repository also goes through SonarCloud like any other repo โ test code is code too.
The automation engineer's checklist โ
My checklist before calling an automation done:
- [ ] Native locators (
getByRole,getByTestIdโฆ), no fragile selectors - [ ] Reused what exists: POM, API services, data factories, helpers
- [ ] Test in its domain folder, with its full metadata (tags + test management tool ID)
- [ ] No
waitForTimeoutโ web-first assertions with auto-retry - [ ] Pipeline green: execution + ESLint + Sonar, no regressions in stable tests
- [ ] Automation status updated in the test management tool