November 27, 2023
4 minutes read
Playwright, coupled with Cucumber and TypeScript, offers a robust framework for end-to-end testing of web applications. This blog post guides you through the setup process, ensuring a seamless integration of these technologies.
Before diving into the installation, make sure you have the following prerequisites installed in place:
Create a Playwright project
Install playwright by running the below command:
npm init playwright@latest
Select the settings as below:
You can also create a playwright project using the VS Code Extension. Search for the Playwright Test for VSCode
plugin in VS Code and install it.
Setup Cucumber
Install below dependencies:
npm i @cucumber/cucumber -D
npm i ts-node -D
We will be installing ts-node
as we will be using TypeScript in our project. This will allow us to run TypeScript files directly without compiling then to JavaScript first.
Project Cleanup
After installing playwright, remove the below files and folders:
tests/example.spec.ts
tests-examples/demo-todo-app.spec.ts
playwright.config.ts
Once we have setup playwright and installed related dependencies, we will setup the basic project structure.
Create the directory as following:
project-root
|-- src
| `-- test
| `-- features
| `-- steps
We will put all out feature files inside the features
directory and all the step definitions inside the steps
directory.
Lets create a simple feature file for login scenario. Create a file login.feature
inside the features
directory.
#login.feature
Feature: User Authentication Tests
Background:
Given User navigates to the application
When User click on the login link
Scenario: Login should be successful
When User enters the username as "janedoee"
And User enters the password as "Password@123"
And User clicks on the login button
Then User is logged in successfully
We can define all the configuration for cucumber in a file. Create a file cucumber.json
in the root of your project.
//cucumber.json
{
"default": {
"formatOptions": {
"snippetInterface": "async-await"
},
"paths": ["src/test/features/"],
"require": ["src/test/steps/*.ts"],
"dryRun": true
}
}
Here we have created a default
profile and mentioned the path
where the feature files are present.
We have set dryRun
as true which will prepare a test run but don’t run the test.
In the formatOptions
, we have set snippetInterface
as async-await
. This will generate the functions in the code snippet as async-await.
require
is set to path where the step definitions files are present.
Add the below scripts in the package.json
file to execute the cucumber tests.
//package.json
...
"scripts": {
"test": "cucumber-js test"
}
...
Lets run the test with the following command: npm run test
As we have not created out step definition file, this will generate failures in the terminal with code snippets for all the undefined steps in the feature file.
> playwright-ts-setup@1.0.0 test
> cucumber-js test
UUUUUU
Failures:
1) Scenario: Login should be successful # src/test/features/login.feature:7
? Given User navigates to the application
Undefined. Implement with the following snippet:
Given('User navigates to the application', async function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
...
Create loginSteps.ts
inside the steps
directory. Copy all the code snippets generated for the undefined steps in the terminal to loginSteps.ts
file.
//loginSteps.ts
import { Given, When, Then } from '@cucumber/cucumber';
Given('User navigates to the application', async function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
When('User click on the login link', async function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
When('User enters the username as {string}', async function (string) {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
When('User enters the password as {string}', async function (string) {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
When('User clicks on the login button', async function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Then('User is logged in successfully', async function () {
// Write code here that turns the phrase above into concrete actions
return 'pending';
});
Before running the test, make sure dryRun
is set to false
in cucumber.json
configuation file.
Lets run the test again with npm run test
.
We will encounter the below error:
SyntaxError: Cannot use import statement outside a module
To resolve this we need to add another property in the cucumber.js
configuration file
//cucumber.json
{
"default": {
...
"requireModule": ["ts-node/register"],
...
}
}
Now rerun the test npm run test
.
You will see the below output in the terminal. This means that the test has been successfully ran. As we have not yet added any code in the step definition function, its showing as pending.
> playwright-ts-setup@1.0.0 test
> cucumber-js test
P-----
Warnings:
1) Scenario: Login should be successful # src/test/features/login.feature:7
? Given User navigates to the application # src/test/steps/loginSteps.ts:3
Pending
- When User click on the login link # src/test/steps/loginSteps.ts:8
- When User enters the username as "janedoee" # src/test/steps/loginSteps.ts:13
- And User enters the password as "Password@123" # src/test/steps/loginSteps.ts:18
- And User clicks on the login button # src/test/steps/loginSteps.ts:23
- Then User is logged in successfully # src/test/steps/loginSteps.ts:28
1 scenario (1 pending)
6 steps (1 pending, 5 skipped)
0m00.008s (executing steps: 0m00.000s)
If you see that even though you have implemented the step definitions for all the steps in the feature file but they are not mapped to the step definition, you can update the cucumber settings in the VSCode.
Navigate to settings in VSCode and open the settings.json
file. Update the below settings:
//settings.json of VSCode
...
"cucumber.features": [
"src/test/features/*.feature",
...
],
"cucumber.glue": [
"src/test/steps/*.ts",
...
]
...
Lets write the step definition for the below step:
Given User navigates to the application
Here we will launch the web browser and then navigate to application. Add the below code in the loginSteps.ts
file
import { Given, When, Then } from '@cucumber/cucumber';
import { Browser, Page, chromium } from '@playwright/test';
let browser: Browser;
let page: Page;
Given('User navigates to the application', async function () {
browser = await chromium.launch({ headless: false });
page = await browser.newPage();
await page.goto('https://bookcart.azurewebsites.net/');
});
We will write step definitions for the other steps as well
When('User click on the login link', async function () {
await page.locator("//button//span[text()='Login']").click();
});
When('User enters the username as {string}', async function (username) {
await page.locator("//input[@formcontrolname='username']").fill(username);
});
When('User enters the password as {string}', async function (password) {
await page.locator("//input[@formcontrolname='password']").fill(password);
});
When('User clicks on the login button', async function () {
await page
.locator("//button[@color='primary']//span[text()='Login']")
.click();
});
Then('User is logged in successfully', async function () {
await expect(
page.locator("//button//span[contains(text(), 'janedoee')]")
).toBeVisible();
await browser.close();
});
As we have defined all the steps, let run our e2e test: npm run test
. You will see the below output in the terminal and the test have been passed.
> playwright-ts-setup@1.0.0 test
> cucumber-js test
......
1 scenario (1 passed)
6 steps (6 passed)
0m05.281s (executing steps: 0m05.270s)
Yay! 🥳 🎉
By following these steps, you’ve successfully set up Playwright with Cucumber and TypeScript for effective end-to-end testing of your web applications.