DEV Community

autonekoqa
autonekoqa

Posted on

Playwright Basics: Your First Test with Page, Test Structure, and Codegen

If you are starting with Playwright, the first real hurdle is not syntax.

It is understanding three things clearly:

  1. what the page object actually represents
  2. how a test file should be structured so it stays readable
  3. how to use Codegen without blindly trusting generated code

I wrote a longer version on Auto Neko here:

This DEV version is the shorter, practical walkthrough.

What page really is

In Playwright, page is the browser tab you control in code.

That means when you write:

await page.goto("https://coffee.autoneko.com/");
await page.getByRole("link", { name: /login/i }).click();
Enter fullscreen mode Exit fullscreen mode

you are telling Playwright:

  • open a real page
  • wait until it is actionable
  • perform the interaction like a user would

That mental model matters because a lot of beginner confusion comes from treating Playwright as "just a list of commands" instead of "a model of browser interaction".

A clean first test

Here is a simple first example:

import { test, expect } from "@playwright/test";

test("open homepage and verify title", async ({ page }) => {
  await page.goto("https://coffee.autoneko.com/");
  await expect(page).toHaveTitle(/Neko Coffee/i);
});
Enter fullscreen mode Exit fullscreen mode

This already teaches four important habits:

  • keep one clear scenario per test
  • always await browser actions
  • prefer Playwright assertions like toHaveTitle()
  • verify behavior, not just "the page opened"

A better structure than one giant script

Beginners often write long, flat test scripts.

A better pattern is:

import { test, expect } from "@playwright/test";

test("user can open login page", async ({ page }) => {
  await test.step("Go to homepage", async () => {
    await page.goto("https://coffee.autoneko.com/");
  });

  await test.step("Open login page", async () => {
    await page.getByRole("link", { name: /login/i }).click();
  });

  await test.step("Verify login form is visible", async () => {
    await expect(page.getByRole("heading", { name: /login/i })).toBeVisible();
  });
});
Enter fullscreen mode Exit fullscreen mode

Why this helps:

  • reports are easier to read
  • failures tell you which step broke
  • future you will hate the test less

Codegen is useful, but not sacred

Playwright Codegen is great for:

  • discovering selectors
  • recording rough flows
  • learning the API quickly

Run it with:

npx playwright codegen https://coffee.autoneko.com/
Enter fullscreen mode Exit fullscreen mode

But after recording, clean the output.

Typical cleanup:

  • replace fragile selectors with getByRole() or getByLabel()
  • remove extra clicks
  • add assertions
  • rename the test so it describes user intent

Codegen should give you a draft, not the final version.

Common beginner mistakes

These are the ones I see most often:

  1. putting everything into one test
  2. using weak selectors when better accessible locators exist
  3. copying Codegen output without refactoring
  4. verifying too little
  5. treating failures as "Playwright is flaky" when the real issue is poor test design

A good first practice path

If you want a sensible order for learning Playwright, I would go like this:

  1. first test with page
  2. locator basics
  3. auto-waiting and actionability
  4. web-first assertions
  5. cleaner test data and reusable structure

The full step-by-step article, with examples and a more detailed breakdown, is here:

If this helps, the next article to read is:

Top comments (0)