I am writing my first e2e tests in Playwright. Before I was using Cypress. This is a Next.js app.
I have an app that:
This is controlled in the _app.tsx
file with Authguard:
const AuthGuard = ({ children }: { children: React.ReactNode }) => {
const { isLoading, isValidating, error } = useMe();
if (isLoading || isValidating) return <Spinner belowText="Loading..." />;
if (error) {
if (error.response && error.response.status === 401)
return <Spinner belowText="Unauthorized" />;
if (!error.response && error.code === "ERR_NETWORK")
return (
<Spinner belowText="Network error. Check if you are connected to the VPN" />
);
return <p>Unknown error, redirecting to login...</p>;
}
return <>{children}</>;
};
...
const isAuthRequired = Component.auth ?? true;
...
<EmotionThemeProvider theme={theme}>
<CssBaseline />
{isAuthRequired ? (
<AuthGuard>
<Component {...pageProps} />
</AuthGuard>
) : (
<Component {...pageProps} />
)}
</EmotionThemeProvider>
...
And this is my test:
import { test } from "@playwright/test";
test("unauthorized users should be redirected to /login", async ({ page }) => {
// Go to the homepage
await page.goto("/");
// Wait for the Unauthorized spinner
const spinner = await page.waitForSelector('text="Unauthorized"', { timeout: 10000 });
// Verify that the spinner was found
if (spinner) {
// Check that the page has been redirected to /login
const url = page.url();
expect(url).toContain("/login");
} else {
throw new Error('Unauthorized spinner not found');
}
});
The problem I am facing is:
When I run npx playwright test --ui
I see that:
page.goto /
works finepage.waitForSelector locator('text="Unauthorized"')
also works fine (the log says locator resolved to visible <p>Unauthorized</p>
)page.waitForSelector locator('text="Unauthorized"')
if I go to the Console tab, I see Failed to load resource: the server responded with a status of 401 (Unauthorized)
which is normal, because the user is not logged in yet.The test should not fail. Should redirect to /login
and PASS. But the test fails because of that network error.
The hook useMe
is the one that makes the redirect to /login
:
const useMe = () => {
const router = useRouter();
return useSWR(
"/users/me",
(url: string) => axios.get<MeResponse>(url).then((res) => res.data),
{
revalidateOnFocus: false,
onError: () => {
router.push("/login");
},
}
);
};
export { useMe };
How can I solve this?
How to wait for url to redirect and then verify?
Use toHaveURL assertion.
await expect(page).toHaveURL(/.*login/)
;
So that it will wait for url to redirect and does not verify the url immediately before the change.