Frontend Testing (Playwright)
We use Playwright for Python, run through pytest, so frontend and backend tests share one runner and one Kiwi sync.
4.1 Manual first: inspect in the browser
Run the test case by hand first, with the browser’s developer tools open, and record it in the Excel run sheet (section 2.6).
Open developer tools with F12, or right-click an element and choose Inspect.
What to look at
| Tab | Use it to check |
|---|---|
| Elements | The element really is what it looks like: <button> vs a styled <div>, type="password" on the password field, disabled, and the visible label |
| Console | No JavaScript errors while you use the page. Red errors are findings, even when the screen looks fine |
| Network | The request behind a button: its status (4xx/5xx show in red), how long it took, and whether clicking twice sends it twice |
Also switch on the device toolbar (Ctrl+Shift+M) to check the page on a
narrow screen.
Note the locator while you inspect
In the run sheet’s Notes column, write how the element can be identified:
its role and visible label, its name, or a data-testid. That note becomes the
locator when you automate the case later (section 4.4), and it’s the moment to
ask developers for a data-testid if an element has nothing stable to grab.
What goes in the run sheet
| Expected result (before) | Actual result (after) |
|---|---|
| Login button visible and clickable | Visible, enabled, label “Log in” |
| Password is masked | input[name="password"] has type="password" |
| Wrong password keeps the user on the login page | Stayed on /accounts/login/, error message shown |
| No console errors | Console clean |
Evidence: a screenshot (Win+Shift+S), plus a screenshot of the Console or
Network tab if something failed there.
4.2 Setup
py -3.11 -m venv .venv
.venv\Scripts\activate.bat
python -m pip install pytest pytest-playwright requestsOn company laptops, use the Edge already installed instead of downloading a
browser. Put this in pytest.ini:
[pytest]
testpaths = tests
addopts = -v -rA --browser-channel msedge --screenshot only-on-failure --tracing retain-on-failure --output test-results4.3 Finding elements: use codegen first
playwright codegen --channel msedge https://<your app>/Hover over any element to see the locator Playwright suggests, and click around to generate code. Start every new UI test from here, using the locator you noted during the manual run (section 4.1).
Pick locators in this order:
- By role and name:
page.get_by_role("button", name="Log in") - By label:
page.get_by_label("Email") - By form field name or test ID:
page.locator('input[name="username"]'),page.get_by_test_id("submit")
Never use CSS classes or positions (div > div:nth-child(3) > button). They
break on the next redesign.
4.4 How a test looks
@pytest.mark.kiwi_case(4)
def test_WEB_KIWILOGIN_003_unknown_user_stays_on_login_page(login_page):
login_page.locator('input[name="username"]').fill("qa-user-that-does-not-exist")
login_page.locator('input[name="password"]').fill("wrong-password-123")
login_page.get_by_role("button", name="Log in").click()
expect(login_page).to_have_url(re.compile(r"/accounts/login/"))- Steps are Playwright actions:
.fill(),.click(). - Expected results are
expect(...)checks.expectwaits automatically until the page is ready, so never usesleep().
4.5 What to check on every screen
- The main action works (happy path).
- Wrong input shows a clear error message.
- Buttons are disabled or hidden for users who can’t use them.
- Empty state: what shows when there’s no data.
- Error state: what shows when the backend fails.
4.6 Running and debugging
| What you want | Command |
|---|---|
| Run everything | pytest |
| Watch it in the browser | pytest --headed --slowmo 1000 |
| Step through one test | set PWDEBUG=1 then pytest -k 003 |
| Look at a failed test | playwright show-trace test-results\<test folder>\trace.zip |
A failed test leaves a screenshot and a trace in test-results\. Attach the
screenshot to the failed execution in Kiwi.