Implementation:Microsoft Playwright GenerateCode
| Knowledge Sources | |
|---|---|
| Domains | Testing, Code_Generation, Multi_Language_Support |
| Last Updated | 2026-02-11 00:00 GMT |
Overview
Concrete tool for transforming recorded actions into idiomatic test code across multiple programming languages, provided by the Playwright library.
Description
The generateCode function is the core code generation engine that takes a list of recorded actions and produces syntactically correct, language-specific test code. It works in conjunction with the LanguageGenerator interface, which provides pluggable language backends for each supported output format.
The generation process follows these steps:
- Action collapsing: Before generation, the raw action list is preprocessed by
collapseActions()(defined atrecorderUtils.ts:L102-116). This step merges consecutive redundant actions, such as multiple navigations to the same URL, sequential fills on the same input element, or immediate openPage/closePage pairs. - Header generation: The language generator produces the file header, which includes imports, test function declarations, browser/context setup code, and any necessary boilerplate. The header is parameterized by
LanguageGeneratorOptionswhich carries the browser name, launch options, context options, and device name. - Action translation: Each collapsed action is passed through
generator.generateAction(), which produces one or more lines of code for that action. The translation handles action-specific concerns like selector quoting, value escaping, modifier keys, and signal handling (e.g., wrapping click-with-navigation inPromise.all). - Footer generation: The language generator produces the closing boilerplate (closing braces, teardown, etc.).
- Assembly: The header, action texts, and footer are joined into a complete source file.
The generated code is output through two channels simultaneously:
- Inspector UI: All 11 registered generators produce output in parallel, and each is displayed as a tab in the inspector window via
_pushAllSources(). - Output file: If
--outputwas specified, the selected language's output is written to disk viaThrottledFile, which batches writes to avoid excessive I/O during rapid recording.
The system ships with 11 language generators:
| Generator ID | Language | Framework | Group |
|---|---|---|---|
| playwright-test | TypeScript | Playwright Test | Test Runner |
| javascript | JavaScript | Playwright Library | Library |
| python-pytest | Python | Pytest | Test Runner |
| python | Python | Playwright Library (sync) | Library |
| python-async | Python | Playwright Library (async) | Library |
| csharp-mstest | C# | MSTest | Test Runner |
| csharp-nunit | C# | NUnit | Test Runner |
| csharp | C# | Playwright Library | Library |
| java-junit | Java | JUnit | Test Runner |
| java | Java | Playwright Library | Library |
| jsonl | JSON Lines | Machine-readable | Data |
Usage
Use this function when you need to:
- Generate test code from a list of recorded actions in any supported language.
- Build custom code generation pipelines that consume Playwright's action format.
- Extend the code generation system with new language backends.
- Implement language-specific post-processing or formatting on generated output.
Code Reference
Source Location
- Repository: playwright
- File:
packages/playwright-core/src/server/codegen/language.ts(L22-28 for generateCode) - File:
packages/playwright-core/src/server/codegen/types.ts(L31-39 for types) - Related:
packages/playwright-core/src/server/recorder/recorderUtils.ts(L102-116 for collapseActions)
Signature
function generateCode(
actions: ActionInContext[],
languageGenerator: LanguageGenerator,
options: LanguageGeneratorOptions
): {
header: string;
footer: string;
actionTexts: string[];
text: string;
}
LanguageGenerator Interface
interface LanguageGenerator {
id: string; // e.g., 'playwright-test'
groupName: string; // e.g., 'Test Runner'
name: string; // e.g., 'Playwright Test'
highlighter: string; // e.g., 'javascript'
generateHeader(options: LanguageGeneratorOptions): string;
generateAction(actionInContext: ActionInContext): string;
generateFooter(): string;
}
LanguageGeneratorOptions Type
interface LanguageGeneratorOptions {
browserName: string;
launchOptions: LaunchOptions;
contextOptions: BrowserContextOptions;
deviceName?: string;
saveStorage?: string;
generateAutoExpect?: boolean;
}
Import
// Internal function - not a public API import
// Defined in packages/playwright-core/src/server/codegen/language.ts
// Used by the Recorder class to generate code for all language tabs
I/O Contract
Inputs
| Name | Type | Required | Description |
|---|---|---|---|
| actions | ActionInContext[] | Yes | Array of recorded actions, typically pre-processed by collapseActions(). Each action contains the frame description, action details (name, selector, value, signals), and timing information.
|
| languageGenerator | LanguageGenerator | Yes | The language-specific generator that produces code strings. One of the 11 registered generators or a custom implementation. |
| options | LanguageGeneratorOptions | Yes | Generation options including browser name, launch options, context options, device name, and flags controlling output behavior. |
Outputs
| Name | Type | Description |
|---|---|---|
| header | string | Generated file header: imports, test function declaration, browser/context setup. |
| footer | string | Generated file footer: teardown, closing braces, end of test. |
| actionTexts | string[] | Array of generated code strings, one per action. Useful for incremental display in the inspector UI. |
| text | string | Complete generated source code: header + all action texts + footer, joined with newlines. Ready to write to a file or display. |
Usage Examples
Basic Example
import { generateCode } from './codegen/language';
import { PlaywrightTestGenerator } from './codegen/javascript';
const actions: ActionInContext[] = [
{
frame: { pageAlias: 'page', framePath: [] },
action: { name: 'navigate', url: 'https://example.com', signals: [] },
},
{
frame: { pageAlias: 'page', framePath: [] },
action: {
name: 'click',
selector: 'getByRole("link", { name: "About" })',
signals: [{ name: 'navigation', url: 'https://example.com/about' }],
button: 'left',
modifiers: 0,
clickCount: 1,
},
},
{
frame: { pageAlias: 'page', framePath: [] },
action: {
name: 'assertText',
selector: 'getByRole("heading", { name: "About Us" })',
text: 'About Us',
substring: false,
signals: [],
},
},
];
const result = generateCode(actions, new PlaywrightTestGenerator(), {
browserName: 'chromium',
launchOptions: {},
contextOptions: {},
});
console.log(result.text);
Generated Output (Playwright Test)
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://example.com');
await page.getByRole('link', { name: 'About' }).click();
await expect(page.getByRole('heading', { name: 'About Us' })).toHaveText('About Us');
});
Generated Output (Python Pytest)
from playwright.sync_api import Page, expect
def test_example(page: Page) -> None:
page.goto("https://example.com")
page.get_by_role("link", name="About").click()
expect(page.get_by_role("heading", name="About Us")).to_have_text("About Us")
Generated Output (C# NUnit)
using Microsoft.Playwright.NUnit;
using Microsoft.Playwright;
[TestFixture]
public class Tests : PageTest
{
[Test]
public async Task Test()
{
await Page.GotoAsync("https://example.com");
await Page.GetByRole(AriaRole.Link, new() { Name = "About" }).ClickAsync();
await Expect(Page.GetByRole(AriaRole.Heading, new() { Name = "About Us" })).ToHaveTextAsync("About Us");
}
}
Generated Output (Java JUnit)
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.*;
import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;
import org.junit.jupiter.api.*;
public class TestExample {
@Test
void test() {
page.navigate("https://example.com");
page.getByRole(AriaRole.LINK, new Page.GetByRoleOptions().setName("About")).click();
assertThat(page.getByRole(AriaRole.HEADING, new Page.GetByRoleOptions().setName("About Us"))).hasText("About Us");
}
}