How do you make `npx playwright codegen` emit pytest code instead of TypeScript?
answer
- One flag, many bindings
- The dropdown does the same thing
- Default emits a test block
- Runner targets versus library targets
- Language, not browser engine
basics
~10 sPass --target python-pytest, or switch the language dropdown in the recorder window. The default target is playwright-test, and --target only changes the binding the same recorded actions are printed in.
solid answer
~30 s`npx playwright codegen --target python-pytest -o test_track_order.py https://tracker.example.com` records the same session but prints it as pytest code; the recorder window's language dropdown does the same thing live. As of Playwright 1.63 the values include `playwright-test` (the default), `javascript`, `python`, `python-async`, `python-pytest`, `java`, `java-junit`, `csharp`, `csharp-mstest` and `csharp-nunit`. The split that matters is runner targets versus library targets: `playwright-test`, `python-pytest`, `java-junit` and `csharp-nunit` emit a test function that receives a ready `page`, while `javascript`, `python`, `java` and `csharp` emit a standalone script that launches a browser, does the work and closes it. The recorded actions and locators are identical either way.
code
bash · 5 lines# pytest output, saved to a file
npx playwright codegen --target python-pytest -o tests/test_track_order.py https://tracker.example.com
# a standalone Node script that launches and closes its own browser
npx playwright codegen --target javascript -o scripts/track-order.js https://tracker.example.comgo deeper
Know that the output language is a choice, set with --target or the recorder's dropdown, and that the default gives you a Playwright test block rather than a standalone script.
Explain the runner-versus-library split: one target family produces a test function handed a ready page, the other a program that launches and closes its own browser.
Recognise that only the binding changes. If a recording is wrong, switching targets will not fix it, because the actions and locators were decided during recording.
Consider the polyglot case: teams on Python, Java or .NET get the same recorder, so codegen is a shared onboarding tool rather than something only the Node-based suite benefits from.
`--target` chooses which language and which harness the recorded actions are printed in. It changes nothing about the recording itself. ## Setting it Two equivalent routes: 1. on the command line — `npx playwright codegen --target python-pytest https://tracker.example.com`; 2. in the recorder window — the language dropdown at the top, switchable mid-session, which re-renders everything recorded so far in the new binding. Combine with `-o` to write straight to a file: `-o tests/test_track_order.py`. Pick an extension that matches the target; the flag does not rename the file for you. ## The values, and the split that matters As of Playwright 1.63 the accepted values are `playwright-test` (the default), `javascript`, `python`, `python-async`, `python-pytest`, `java`, `java-junit`, `csharp`, `csharp-mstest` and `csharp-nunit`. They fall into two families: | family | example values | what comes out | | --- | --- | --- | | runner targets | `playwright-test`, `python-pytest`, `java-junit`, `csharp-nunit`, `csharp-mstest` | a test function that receives a ready-made `page`, with no launch or teardown code | | library targets | `javascript`, `python`, `python-async`, `java`, `csharp` | a standalone program that launches a browser, opens a context and page, runs the steps, then closes both | That is the distinction interviewers are usually probing. A library target's output is runnable on its own — useful for a scraping script or a one-off reproduction of an order-tracker bug — while a runner target's output drops into an existing suite, because setup and teardown are the runner's job. ## What does not change - the actions recorded and their order; - which element each step targets — the same locator preferences apply in every language, so a click recorded as `getByRole('button', { name: 'Refresh status' })` appears as `get_by_role("button", name="Refresh status")` in Python and the equivalent in Java or C#; - the assertions you inserted from the toolbar, rendered in the target's assertion style. ## Practical notes - The default is `playwright-test`, so a Node user who never passes the flag gets a `test('test', async ({ page }) => { ... })` block using the `page` fixture. - Switching the dropdown mid-recording is the quickest way to see the same flow in two bindings side by side — handy when a team runs its suite in Python but you are more fluent in TypeScript. - The Python and Java CLIs expose the same recorder through their own entry points, so a team that does not have Node in the loop still gets codegen. - `--target` is unrelated to `-b` / `--browser`: one picks the output language, the other the engine doing the recording. Mixing them up is a common slip.
- What is different about the code produced by `--target javascript` versus the default?`--target javascript` emits a standalone program: it launches a browser, creates a context and page, runs the steps and closes everything, so you can execute the file directly. The default `playwright-test` emits a `test()` block that takes the `page` fixture and leaves lifecycle to the runner, which is what you want when the code is joining an existing suite.
saying these in an interview costs you the question
- Assumes codegen can only emit JavaScript or TypeScript
- Thinks --target selects which browser records
- Believes the library targets emit a test function
- Cannot name the default target
- Expects -o to rename the file for the target