Why test?
This blog has been a place I want to share what I’ve learned while writing code. It’s not a terribly important part of my life
but that does not mean I want it broken. The biggest thing I was worried about was having links to nowhere, especially when referencing
other posts. This is a perfect use case to put Playwright to the test!
What is Playwright?
It’s just my favorite from the increasingly packed category of browser automation tools. It’s way more state of the art than selenium and it’s got a great API.
I can highly recommend it to anyone who wants to test any scale of web application.
The problem, and the solution
Simply, there’s a number of blog posts (9) and
I want to assert that every <a...>...
tag has a valid href attribute.
Check out the code below. I promise it’s not much. You just need to scan it.
import { test, expect } from '@playwright/test';
test.describe('Blog', () => {
test('All links on the list page should work, and there should be no broken images', async ({ page, }) => {
await page.goto('/microblog/');
const microblogLinks = page.locator('[href^="/microblog/"]');
for (const link of await microblogLinks.all()) {
const href = await link.getAttribute('href');
const header = link.getByRole('heading');
const text = await header.innerText();
expect(text.length).toBeGreaterThan(5);
// Register the request listener
page.on('response', response => {
expect(response.status(), `Request for ${response.url()} failed on ${href}}`).toBeLessThan(400);
})
if (href && !href.startsWith('#')) {
await page.goto(href);
await page.waitForLoadState('networkidle')
// Check the heading
const header = page.locator('.acontent h1');
await expect(header).toHaveText(text);
expect(page.url()).toContain(href);
// Check for broken links
// const sublinks = await page.getByRole('link');
// for (const sublink of await sublinks.all()) {
for (const sublink of await page.$$('.acontent a')) {
const href = await sublink.getAttribute('href');
console.log(`Testing ${href} on ${page.url()}`);
// Just go `fetch` it
if (href && !href.startsWith('#') && href !== '/') {
let responseStatus;
if (!href.startsWith('/')) {
// An external link, all we need to do is go fetch it
responseStatus = (await fetch(href)).status;
} else {
// An internal link, we want to use the browser to fetch it relative to the current page
responseStatus = await page.evaluate(async (href) => {
try {
const response = await fetch(href);
return response.status;
} catch (e) {
return 600;
}
}, href);
}
expect(responseStatus, `Request for ${href}`).toBeLessThan(400);
}
}
await page.goBack();
}
}
})
});
This all took about an hour of playing around. Just took adding an npm test
in the cloudflare pages build
and I’m good to go. Now my build fails before it ever gets to the site.
The lesson
I’m a huge fan of testing. This was a little reminder that it can be fun,
and the warm fuzzies of knowing that the most updated part of my site doesn’t have a
embarassingly obvious mistake to anyone who visits is really nice.
Take some time, node is easy to get a project going with and playwright makes it dead simple to test.