playwright-bdd
playwright-bdd copied to clipboard
Reverse mode: auto-generate BDD scenarios
In Playwright there is a codegen feature that generates test-code while you perform actions in browser. What I'm thinking about is to generate BDD code in some similar way.
Imagine the following scenario:
- open todo app
- create two todos
- mark the first todo as completed
- filter only completed todos
In codegen mode Playwright will generate the test:
import { test, expect } from '@playwright/test';
test('test', async ({ page }) => {
await page.goto('https://demo.playwright.dev/todomvc/#/');
await page.getByPlaceholder('What needs to be done?').fill('todo 1');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await page.getByPlaceholder('What needs to be done?').fill('todo 2');
await page.getByPlaceholder('What needs to be done?').press('Enter');
await expect(page.getByTestId('todo-title')).toHaveCount(2); // <- added manually
await page.getByRole('listitem').filter({ hasText: 'todo 1' }).getByRole('checkbox', { name: 'Toggle Todo' }).check();
await page.getByRole('link', { name: 'Completed' }).click();
await expect(page.getByTestId('todo-title')).toHaveCount(1); // <- added manually
});
The code looks rather structured and it would be possible to convert it into BDD scenario:
Scenario: Scenario 1
Given I am on page "https://demo.playwright.dev/todomvc/#/"
When I fill "todo 1" and press Enter
And I fill "todo 2" and press Enter
Then Count of "todo-title" equals to 2
When I enable checkbox for "todo 1"
And I click on "Completed"
Then Count of "todo-title" equals to 1
Necessary step definitions can be generated as well:
Given('I am on page {string}', async ({ page }, url: string) => {
await page.goto(url);
});
When('I fill {string} and press Enter', async ({ page }, text: string) => {
await page.getByPlaceholder('What needs to be done?').fill(text);
await page.getByPlaceholder('What needs to be done?').press('Enter');
});
Then('Count of {string} equals to {int}', async ({ page }, testId: string, count: number) => {
await expect(page.getByTestId(testId)).toHaveCount(count);
});
When('I enable checkbox for {string}', async ({ page }, hasText: string) => {
await page.getByRole('listitem').filter({ hasText }).getByRole('checkbox', { name: 'Toggle Todo' }).check();
});
When('I click on {string}', async ({ page }, name: string) => {
await page.getByRole('link', { name }).click();
});
If I record second scenario existing step definitions can be reused.
I feel that there can be edge cases, but developer can always fix it manually. In general having such easy way to generate BDD scenarios looks useful to define project requirements and behavior.
Would appreciate your thoughts on that :)
I've also found that there is jsonl output for Playwright's codegen, that can be turned into BDD steps with less effort. for the scenario above jsonl output is the following:
{"browserName":"chromium","launchOptions":{"headless":false},"contextOptions":{}}
{"name":"openPage","url":"about:blank","signals":[],"pageAlias":"page"}
{"name":"navigate","url":"https://demo.playwright.dev/todomvc/","signals":[],"pageAlias":"page"}
{"name":"navigate","url":"https://demo.playwright.dev/todomvc/#/","signals":[],"pageAlias":"page"}
{"name":"fill","selector":"internal:attr=[placeholder=\"What needs to be done?\"i]","signals":[],"text":"todo 1","pageAlias":"page","locator":{"kind":"placeholder","body":"What needs to be done?","options":{"exact":false}}}
{"name":"press","selector":"internal:attr=[placeholder=\"What needs to be done?\"i]","signals":[],"key":"Enter","modifiers":0,"pageAlias":"page","locator":{"kind":"placeholder","body":"What needs to be done?","options":{"exact":false}}}
{"name":"fill","selector":"internal:attr=[placeholder=\"What needs to be done?\"i]","signals":[],"text":"todo 2","pageAlias":"page","locator":{"kind":"placeholder","body":"What needs to be done?","options":{"exact":false}}}
{"name":"press","selector":"internal:attr=[placeholder=\"What needs to be done?\"i]","signals":[],"key":"Enter","modifiers":0,"pageAlias":"page","locator":{"kind":"placeholder","body":"What needs to be done?","options":{"exact":false}}}
{"name":"check","selector":"internal:role=listitem >> internal:has-text=\"todo 1\"i >> internal:role=checkbox[name=\"Toggle Todo\"i]","signals":[],"pageAlias":"page","locator":{"kind":"role","body":"listitem","options":{"attrs":[]},"next":{"kind":"has-text","body":"todo 1","options":{"exact":false},"next":{"kind":"role","body":"checkbox","options":{"attrs":[],"exact":false,"name":"Toggle Todo"}}}}}
{"name":"click","selector":"internal:role=link[name=\"Completed\"i]","signals":[{"name":"navigation","url":"https://demo.playwright.dev/todomvc/#/completed"}],"button":"left","modifiers":0,"clickCount":1,"pageAlias":"page","locator":{"kind":"role","body":"link","options":{"attrs":[],"exact":false,"name":"Completed"}}}
This feature looks really nice, unfortunatelly not every project/webpage have good locators, so could be tricky for implementation.
Really awesome idea but could be tough to develop