Skip to main content
Joel Lithgow / Blog
  • 01 Work portfolio
  • 02 Blog the log
  • 03 Art artwork
  • 04 About bio
June 11, 2025
June 11, 2025

Automating CMS Workflows with Playwright

How I used browser automation to fix hundreds of broken links in a fraction of the time it would have taken manually.

When our IT team updated the internal intranet search engine, they changed all the URL parameters and query structures. Suddenly, hundreds of links across our marketing site were going to break. The links were scattered throughout our Wagtail CMS—embedded in rich text fields, inline links, and various other content blocks.

The initial estimate for manual remediation? Several weeks of tedious copy-paste work.

I finished it in a day.

The Problem

URL migrations are deceptively complex. It's not just finding and replacing text in a database—links in a CMS live in:

  • Rich text fields where they're wrapped in HTML anchor tags

  • Inline links within body content that might have surrounding text

  • Structured content blocks with their own schemas

  • StreamField components (Wagtail's flexible content system)

A simple find-and-replace query wouldn't cut it. Each link needed to be found in context, the new URL structure applied, and the change saved back through the CMS interface to maintain proper revision history and trigger any associated workflows.

Why Playwright?

I chose Playwright for this automation task for several reasons:

  1. Real browser automation: Unlike HTTP-based approaches, Playwright actually drives a browser. This means JavaScript-rendered content, authentication flows, and dynamic CMS interfaces work correctly.

  2. Modern API: Playwright's async/await syntax and auto-waiting make tests reliable without explicit sleeps or waits.

  3. Cross-browser support: Though not critical for this task, knowing the same code works across Chromium, Firefox, and WebKit is reassuring.

  4. Debugging tools: Playwright's trace viewer and codegen features made development much faster.

The Approach

1. Audit First

Before writing any automation, I exported all pages with their content to identify exactly which links needed updating. This gave me:

  • A complete inventory of affected URLs

  • The specific pages where each link appeared

  • The context around each link (rich text, structured block, etc.)

2. Authentication Handling

Wagtail requires authentication, so the first step was handling login. Playwright's persistent context feature meant I could authenticate once and reuse the session:

const context = await browser.newContext({
  storageState: 'auth.json' // Saved session state
});

3. Navigating the CMS

Each page edit followed a predictable pattern:

  1. Navigate to the page's edit URL in Wagtail admin

  2. Locate the content blocks containing links

  3. Find links matching the old URL pattern

  4. Update to the new URL structure

  5. Save (or save as draft for review)

4. Rich Text Handling

Rich text fields were the trickiest part. Wagtail's Draftail editor stores content as structured data, but Playwright sees the rendered HTML. The solution was to:

  1. Click into the rich text field to activate it

  2. Use browser developer tools to understand the DOM structure

  3. Target link elements directly and update their href attributes

  4. Ensure the editor registered the changes

// Find links in rich text that match our pattern
const oldPattern = 'intranet.example.com/search?';
const links = await page.locator(`a[href*="${oldPattern}"]`);

for (const link of await links.all()) {
  const oldHref = await link.getAttribute('href');
  const newHref = transformUrl(oldHref); // Apply URL mapping
  await link.evaluate((el, newUrl) => {
    el.setAttribute('href', newUrl);
  }, newHref);
}

5. Verification

After each batch, I ran a verification pass to confirm:

  • Links were updated to the correct new format

  • No unintended changes were made

  • Page content remained otherwise intact

Results

What was estimated as weeks of manual work took about a day:

  • Setup and testing: ~4 hours

  • Running the automation: ~2 hours (with manual spot-checks)

  • Verification and cleanup: ~2 hours

Beyond the time savings, automation eliminated the human error factor. Manual copy-paste across hundreds of links would inevitably introduce typos or missed items. The automated approach was consistent and verifiable.

Lessons Learned

When to Automate

Not every repetitive task deserves automation. The decision factors:

  • Volume: 10-20 items? Maybe just do it manually. 100+? Automation pays off.

  • Complexity: Simple tasks compound quickly. Complex tasks might take longer to automate than to do by hand.

  • Error cost: If mistakes are costly to fix, automation's consistency becomes valuable.

  • Recurrence: One-time task vs. recurring need affects ROI significantly.

Test Your Transformations

Before running automation on production, I tested the URL transformation logic extensively on a subset of known URLs. Edge cases like query parameters with special characters, URLs with fragments, and already-updated URLs needed handling.

Keep Humans in the Loop

For this project, I ran the automation in batches with manual verification between runs. This caught a few edge cases the initial logic missed and gave stakeholders confidence in the process.

Document Everything

Automation scripts are technical debt if they're not documented. I created a README explaining:

  • What the script does

  • How to run it

  • What environment variables/config are needed

  • Known limitations

Beyond This Project

This experience reinforced my belief that QA thinking extends beyond testing into process improvement. The same mindset that asks "how do we verify this works correctly?" also asks "how do we do this efficiently and reliably?"

Playwright has since become a regular tool in my toolkit—not just for testing, but for any task where browser automation makes sense. The investment in learning it continues to pay dividends.


Interested in discussing automation strategies or Playwright specifically? Feel free to reach out.

#automation #testing #playwright #wagtail #qa
Continue reading →
June 1, 2025
June 1, 2025

What I've been listening to — June 2025

The songs I liked in June 2025.

Pleasure — Pure X · 2 songsZeroin — Dope Smoker · 2 songsFlies the Fields — Shipping News · 1 songSalt Sinks & Sugar Floats — 764-Hero · 1 songYou're In It Now — Pure X · 1 songBlue Moon Safari — Vegyn, Air · 1 songPoison Fruit — Ivan Conti · 1 songSecret — Jigsaw Youth · 1 songLegalize It — Dope Smoker · 1 songSplit — all under heaven · 1 songDrifting Deeper — 16 Underground · 1 songThe Suburbs (Deluxe) — Arcade Fire · 1 songAmerican Flyer — American Flyer · 1 songMammone — Rachel Kitchlew, SFJ, SHOLTO, David Bardon · 1 songMondo Tempo — Freak Heat Waves · 1 songPipistrella — Nicolini, Hanna Tembo · 1 songUnicorn — RIO KOSTA · 1 songWowee Zowee — Pavement · 1 songBoundary Road Snacks and Drinks (2024 Remaster) — Dry Cleaning · 1 songSofa Description 7" — Ulrika Spacek · 1 songTiny Music...Songs from the Vatican Gift Shop — Stone Temple Pilots · 1 song2001 — Carla Geneve · 1 songFuzz — Fuzz · 1 songMisty Woman — Aunt Cynthia's Cabin · 1 songTape 4 — Felbm · 1 songSweet Child — Pentangle · 1 songInstrumentals JID009 — Adrian Younge, Ali Shaheed Muhammad, Roy Ayers · 1 songThe Papas & The Mamas — The Mamas & The Papas · 1 songThe Life of Clutchy Hopkins — Clutchy Hopkins · 1 songRiver Road — Manic Focus · 1 songRomantic — Slow Hollows · 1 songBreakfast In America (Deluxe Edition) — Supertramp · 1 songAll Things Must Pass (Remastered 2014) — George Harrison · 1 songStuffed & Ready — Cherry Glazerr · 1 songDrugs In The Bathroom (Swordes Remix) — xJermsx, Swordes · 1 song

37 songs

#music
May 1, 2025
May 1, 2025

What I've been listening to — May 2025

The songs I liked in May 2025.

Modern English Decoration — Ulrika Spacek · 3 songsNew Plastic Ideas — Unwound · 3 songsDuress — Froth · 2 songsWowee Zowee (Sordid Sentinels Edition) — Pavement · 2 songsThe Album Paranoia — Ulrika Spacek · 1 songPleasure — Pure X · 1 songDeep in View — Cola · 1 songSun Coming Down — Ought · 1 song10 situazioni — Sestetto Dino Piana & Oscar Valdambrini · 1 songLord Luck — Ulrika Spacek · 1 songAnika — Anika · 1 songYou Are Arrived (But You've Been Cheated) — SPIRIT OF THE BEEHIVE · 1 songDay Catcher — Deliluh · 1 songLittle Dark Age — MGMT · 1 songVeckatimest — Grizzly Bear · 1 songChick on the Moon — Seth · 1 songHypnic Jerks — SPIRIT OF THE BEEHIVE · 1 songDripping — Pile · 1 songCrooked Rain, Crooked Rain — Pavement · 1 songThere's Nothing Wrong With Love — Built To Spill · 1 songFugue State — Vulfpeck · 1 songFrom the Corner To the Block — Galactic · 1 songCompilation — The Clean · 1 songHunger for a Way Out — Sweeping Promises · 1 songCold Hot Plumbs — Damaged Bug · 1 songSweet Princess EP — Dry Cleaning · 1 songCanned Music — Pearl & The Oysters · 1 songGuttering — MJ Lenderman, Wednesday, Advance Base · 1 songIt'll End In Tears (Remastered) — This Mortal Coil · 1 songLongwave — Bonny Doon · 1 songA Brief History of Amazing Letdowns — Lilys · 1 songHubba Bubba — Damaged Bug · 1 songDreamin' — Chuck Senrick · 1 songBody by Crystal — Spike Fuck · 1 songLateralus — TOOL · 1 songOld Ramon — Red House Painters · 1 songYou Forgot It In People — Broken Social Scene · 1 songThe Sophtware Slump — Grandaddy · 1 songLast Splash — The Breeders · 1 songA Catholic Education — Teenage Fanclub · 1 songDoolittle — Pixies · 1 songSlanted & Enchanted: Luxe & Reduxe — Pavement · 1 songCor-Crane Secret — Polvo · 1 songReading, Writing And Arithmetic — The Sundays · 1 song

50 songs

#music
April 1, 2025
April 1, 2025

What I've been listening to — April 2025

The songs I liked in April 2025.

Wowee Zowee (Sordid Sentinels Edition) — Pavement · 4 songsBurner — Breadwinner · 3 songsEngland Made Me — Black Box Recorder · 2 songsThe Three E.P.'s — The Beta Band · 2 songsWow2 — Boredoms · 2 songsAll Aboard — Washer · 1 songKrill — Krill · 1 songRoom On Fire — The Strokes · 1 songLucky Leaves — Krill · 1 songSleeping Beauty (Expanded Edition) — Sun Ra & His Arkestra · 1 songJoy Techniques — Nate Mercereau · 1 songRoman Candle — Elliott Smith · 1 songEast And West + Singles — Anna Domino · 1 songThe Head Hurts but the Heart Knows the Truth — Headache · 1 songManning Fireworks — MJ Lenderman · 1 songRat Saw God — Wednesday · 1 songBrighten the Corners — Pavement · 1 songChocolate and Cheese — Ween · 1 songCrooked Rain, Crooked Rain — Pavement · 1 songFloating Coffin — Thee Oh Sees · 1 songMic City Sons — Heatmiser · 1 songHeaven Adores You Soundtrack — Heatmiser, Elliott Smith · 1 songNew Plastic Ideas — Unwound · 1 songTrompe le Monde — Pixies · 1 songOutlaw — Eugene McDaniels · 1 songParanoid (Remaster) — Black Sabbath · 1 songPsychotic Reaction — Count Five · 1 songTerror Twilight — Pavement · 1 songA Northern Soul (Remastered 2016 / Deluxe) — The Verve · 1 songThe Fashion Focus — Starflyer 59 · 1 songEngland Made Me [25th Anniversary Edition] — Black Box Recorder · 1 songPermutation — Amon Tobin · 1 songI Can Hear The Heart Beating As One — Yo La Tengo · 1 songNight Walks — Hidden Orchestra · 1 songv2.0 (Deluxe Edition) — GoGo Penguin · 1 songTake My Drum to England — Jon Kennedy · 1 songMaps From the Wilderness — TM Juke · 1 songLost Sides — Doves · 1 songTír Na NÓg (2012 Remaster) — Tír na nÓg · 1 songThe Mystery Lights — The Mystery Lights · 1 songTainted Lunch — Warmduscher · 1 songJassbusters — Connan Mockasin · 1 songSalt — Angie McMahon · 1 songDance Yrself Clean — Soaked Oats · 1 songSafari — Minimal Man · 1 songSound In Time — Lungfish · 1 songBonsai Superstar — Brainiac · 1 songLost Souls — Doves · 1 songA Way of Life (35th Anniversary Edition; 2023 Remaster) — Suicide · 1 songFloating Into The Night — Julee Cruise · 1 songA Decade Deeper — Pill · 1 songLet It Bleed — The Rolling Stones · 1 songTweez — Slint · 1 songNecrophones — Lungfish · 1 songAnthology 1997-98 — Faraquet · 1 songRainbow — Boris · 1 songFake Train — Unwound · 1 songBuried Secrets — Painkiller · 1 songSuper Relax — Cibo Matto · 1 songDark Sunrise — Rikki Ililonga, Musi-O-Tunya · 1 songUltra Orange & Emmanuelle — Ultra Orange, Emmanuelle Seigner · 1 songFortune Goodies — Minami Deutsch · 1 songIV — BADBADNOTGOOD, Colin Stetson · 1 songHeavy Days Are Here Again — Frank Vandenkloot · 1 songAir Con Eden — Jerkcurb · 1 songSylvie — Sylvie · 1 songA New Place 2 Drown — Archy Marshall, King Krule · 1 song

75 songs

#music
2022 Q2
2024 Q3 Q4
2025 Q1 Q2 Q3 Q4
2026 Q1 Q2 Q3 Q4

Browse

All posts 102 → Links 17 → Music 22 → Birds 31 → Dictionary 15 →

Choose Your Theme

Keyboard shortcuts available: Ctrl+T to cycle themes, Escape to close modals, Enter/Space to activate buttons
FPS: --
Memory: --
This website uses themes. Press Ctrl+T to cycle through available themes.
Some elements on this page are interactive and can be dragged around.