Dealing With Navigation Using Playwright Automated Login

FossFrontendTesting

Introduction

While adding tests to minv using SvelteKit’s Playwright integration, I ran into an extremely confusing error. Here, I try to explain how to fix it (atleast for now 😉).

The Error

I was using Playwright’s automated login feature like such:

test.beforeEach(async ({ page }) => {
        await page.goto('/auth');
        await page.locator('[placeholder="channelname"]').fill('playwright-user-1');
        await page.locator('[placeholder="password"]').fill('playwright-secret-1');
        await page.locator('button:has-text("Authenticate")').click();
});


test('Delete a video', async ({ page }) => {
        // Goto /video/playwright-title-1
        await page.locator('ul >> text=Videos').click()
        await page.locator('text=playwright-title-1').click();

        // Click text=Delete Video
        await page.locator('text=Delete Video').click();
        await expect(page).toHaveURL('/video/playwright-title-1/delete');

        // Click text=Yes
        await page.locator('text=Yes').click();
        await expect(page).toHaveURL('/videos');
});

However, my tests were failing for a weird reason: The ul >> text=Videos element wasn’t being clicked. This seemed very strange and unexpected.

The Attempted Solution

After lots of RTFM and trial/error, I pinned it down to a(n incomplete) navigation.

I added this code to wait for navigation:

await Promise.all([
        page.waitForNavigation({ url: '/videos' }),
        await page.locator('ul >> text=Videos').click()
]);

However, I was still having the same error.

The Solution

I decided to try the same test without the autologin. This time, it worked!

I put the same code in the authentication function like so:

await Promise.all([
        page.waitForNavigation({ url: '/' }),
        await page.locator('button:has-text("Authenticate")').click()
]);

This time, it worked with autologin!

Though I’m not sure, it seems that the Authenticate button click from the function had not completed navigation when the test’s Videos link was clicked, therefore rendering the link click useless.

Also, I found a more concise way of waiting for navigation. The final code looks like this:

test.beforeEach(async ({ page }) => {
        await page.goto('/auth');
        await page.locator('[placeholder="channelname"]').fill('playwright-user-3');
        await page.locator('[placeholder="password"]').fill('playwright-secret-3');

        await page.locator('button:has-text("Authenticate")').click();
        await page.waitForURL('/');
});

test('Delete a video', async ({ page }) => {
        // Goto /video/playwright-title-1
        await page.locator('ul >> text=Videos').click()
        await page.waitForURL('/videos')
        await page.locator('text=playwright-title-1').click();

        // Click text=Delete Video
        await page.locator('text=Delete Video').click();
        await expect(page).toHaveURL('/video/playwright-title-1/delete');

        // Click text=Yes
        await page.locator('text=Yes').click();
        await expect(page).toHaveURL('/videos');
});

References