Using Codegen
Tags: playwright, testing, codegen, vscode
Published: 2023-11-20 03:51:52

**Start with confidence with autogenerated tests!** 🎭

---

Playwright allows you to generate test scripts based on your interactions with the UI. This feature is known as Playwright's code generation capability. When you interact with your app's UI elements during a testing session, Playwright records these interactions and generates corresponding test scripts. Quite straightforward! 
<br>

## Steps to Get Started with Codegen
<br>

<br>**Step 1**. **Open the Testing sidebar and click Record new**.
Playwright will open a new test file, as well as a blank browser page. 

```js
test('test', async ({ page }) => {
  // Recording...
});

```

**Step 2**. In the browser, **type the URL for the site** you want to test. 

**Step 3**. Once typed in, the **URL automatically gets recorded inside the testing file**:

```js
test('test', async ({ page }) => {
    await page.goto('https://corinamurg.netlify.app/');
})
```

**Step 4**. Now Playwright expects you to **manually perform actions** on that site, so it can translate these actions into test scripts. 
<br>

For more details, visit [Playwright's documentation on codegen](https://playwright.dev/docs/codegen#record-a-new-test)
<br>

## My Codegen Session
My goal was to test the links inside my navbar. As I was manually clicking every link in my navbar, Playwright translated these actions into test scripts. At the end, my `test` function looked like this:

```js
test('test navigation links', async ({ page }) => {
  await page.goto('https://corinamurg.netlify.app/');
  await page.getByRole('banner').getByRole('link', { name: 'Corina Murg' }).click();

  await page.getByRole('navigation').getByRole('link', { name: 'Home' }).click();

  await page.getByRole('navigation').getByRole('link', { name: 'Projects' }).click();

  await page.getByRole('navigation').getByRole('link', { name: 'Accessibility' }).click();

  await page.getByRole('navigation').getByRole('link', { name: 'About' }).click();

  const page2Promise = page.waitForEvent('popup');
  await page.getByRole('navigation').getByRole('link', { name: 'Blog' }).click();
  const page2 = await page2Promise;
});
```

My assumption was that the automated test would 
- check that each link is clickable, and 
- check that it opens the expected page. 

It just happens that when I learn, I like to break the code and notice how it reflects in the browser. So, I disabled the paths to the **Blog** and the **About** links and expected an error for each link. 

Well, this did not happen! At least not for both links, that is. I received the error for the **Blog** link (note: this link connects to an external site and has an target="_blank" attribute), but I did not receive the error for the **About** link. 🤔

Help came from the (awesome) Playwright documentation! Here's what it says about `.click()` method:
<br>
> Under the hood, this and other pointer-related methods:
> - wait for the element with the given selector to be in the DOM
> - wait for it to become displayed, i.e., not empty, no display: none, no visibility: hidden
> - wait for it to stop moving, for example, until the CSS transition finishes
> - scroll the element into view
> - wait for it to receive pointer events at the action point, for example, wait until the element becomes non-obscured by other elements
> - retry if the element is detached during any of the above checks
<br>

It turns out that in the case of my 'About' link, the `.click()` method checks if an element with the role of 'link' and the name 'About' exists within the navigation, and then it clicks on that element. BUT it does not check whether the link leads to a specific destination!
<br>

## Checking a Link to an External Page
There was no error for the **About** link, but then why did I get the error about the **Blog** link? The clue was in the last three lines of code:

```js
const page2Promise = page.waitForEvent('popup');

await page.getByRole('navigation').getByRole('link', { name: 'Blog' }).click();

const page2 = await page2Promise;

```

When a page is opened by a `target="_blank"` link, we get a reference to it by listening to the `popup` event on the page. The `waitForEvent()` method waits for the `popup` to happen within a given timeout. If the `popup` event doesn't occur, Playwright will throw an error. 

**One caveat**: the `waitForEvent()` method and the `popup` event help confirm the link's functionality by triggering its action, yet they don't verify if the link navigates to a specific URL. To make sure the link led to my blog page, I had to add additional lines of code. I also personalized the code by renaming the `popup` promise variable:

```js
const blogPromise = page.waitForEvent('popup');

await page.getByRole('navigation').getByRole('link', { name: 'Blog' }).click();

const blog = await blogPromise;

// WAIT FOR THE BLOG PAGE TO LOAD, THEN CHECK URL AND TITLE
await blog.waitForLoadState();
await expect(blog).toHaveURL('https://dev.to/corinamurg');
await expect(blog).toHaveTitle('Corina: Web is for Everyone - DEV Community');

```
<br>
<br>
## Checking the Link to an Internal Page
As I mentioned above, using `.click()` is a necessary start, but not sufficient on its own. We have to add extra checks or assertions to verify the full functionality of a link.

For my (internal) navbar links, I added assertions for all the URLs and a check for the 'About' page that a certain text was visible:

```js
await page.getByRole('navigation').getByRole('link', { name: 'Projects' }).click();
await expect(page).toHaveURL('https://corinamurg.netlify.app/projects');

await page.getByRole('navigation').getByRole('link', { name: 'Accessibility' }).click();
await expect(page).toHaveURL('https://corinamurg.netlify.app/accessibility');

await page.getByRole('navigation').getByRole('link', { name: 'About' }).click();
await expect(page).toHaveURL('https://corinamurg.netlify.app/about');
await expect(page.getByText('My Career in a Nutshell')).toBeVisible();
```

<br>
## Conclusion
What should you focus on while in codegen mode? 

First, decide on what tests you want to automate. Then, when using the codegen feature, interact with those elements of your web application that are relevant to the test you want to automate. 

The idea is to mimic the actions a real user would perform to achieve a certain task or navigate through a particular flow in your application. The code generated by Playwright will reflect these interactions, creating a baseline script. 

I ❤️ how the codegen feature significantly simplifies the initial setup of a test, especially for those who are new to writing test scripts (moi!). I don't have to write code from scratch and I can quickly get started. But of course, as I shared above, the auto-generated code has to be refined and expanded upon to create comprehensive automated tests. For that, don't forget to make use of the documentation!

So, what are you waiting for? Start playing! 🎭

Coming up: testing for accessibility!

**Note**: My site is still in construction, so expect to find UI and accessibility bugs/errors beyond those tested here.
<br>

## Resources

[Debbie O’Brien’s Playwright Series on dev.to](https://dev.to/debs_obrien/series/25202)

[Playwright Documentation](https://playwright.dev/docs/intro)
<br>

**Image credit**: Photo by [Alex Kondratiev](https://unsplash.com/@alexkondratiev)

Description: The image shows a hand pouring a blue liquid from a glass test tube into a flask with red liquid, and another hand pouring a green liquid into the flask. The background is plain white.