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 2025
↗

What I've been listening to — June 2025

Pleasure — Pure X · 2 songs Zeroin — Dope Smoker · 2 songs Flies the Fields — Shipping News · 1 song Salt Sinks & Sugar Floats — 764-Hero · 1 song You're In It Now — Pure X · 1 song Blue Moon Safari — Vegyn, Air · 1 song Poison Fruit — Ivan Conti · 1 song Secret — Jigsaw Youth · 1 song Legalize It — Dope Smoker · 1 song Split — all under heaven · 1 song Drifting Deeper — 16 Underground · 1 song The Suburbs (Deluxe) — Arcade Fire · 1 song American Flyer — American Flyer · 1 song Mammone — Rachel Kitchlew, SFJ, SHOLTO, David Bardon · 1 song Mondo Tempo — Freak Heat Waves · 1 song Pipistrella — Nicolini, Hanna Tembo · 1 song Unicorn — RIO KOSTA · 1 song Wowee Zowee — Pavement · 1 song Boundary Road Snacks and Drinks (2024 Remaster) — Dry Cleaning · 1 song Sofa Description 7" — Ulrika Spacek · 1 song Tiny Music...Songs from the Vatican Gift Shop — Stone Temple Pilots · 1 song 2001 — Carla Geneve · 1 song Fuzz — Fuzz · 1 song Misty Woman — Aunt Cynthia's Cabin · 1 song Tape 4 — Felbm · 1 song Sweet Child — Pentangle · 1 song Instrumentals JID009 — Adrian Younge, Ali Shaheed Muhammad, Roy Ayers · 1 song The Papas & The Mamas — The Mamas & The Papas · 1 song The Life of Clutchy Hopkins — Clutchy Hopkins · 1 song River Road — Manic Focus · 1 song Romantic — Slow Hollows · 1 song Breakfast In America (Deluxe Edition) — Supertramp · 1 song All Things Must Pass (Remastered 2014) — George Harrison · 1 song Stuffed & Ready — Cherry Glazerr · 1 song Drugs In The Bathroom (Swordes Remix) — xJermsx, Swordes · 1 song
#music
May 1, 2025
May 2025
↗

What I've been listening to — May 2025

Modern English Decoration — Ulrika Spacek · 3 songs New Plastic Ideas — Unwound · 3 songs Duress — Froth · 2 songs Wowee Zowee (Sordid Sentinels Edition) — Pavement · 2 songs The Album Paranoia — Ulrika Spacek · 1 song Pleasure — Pure X · 1 song Deep in View — Cola · 1 song Sun Coming Down — Ought · 1 song 10 situazioni — Sestetto Dino Piana & Oscar Valdambrini · 1 song Lord Luck — Ulrika Spacek · 1 song Anika — Anika · 1 song You Are Arrived (But You've Been Cheated) — SPIRIT OF THE BEEHIVE · 1 song Day Catcher — Deliluh · 1 song Little Dark Age — MGMT · 1 song Veckatimest — Grizzly Bear · 1 song Chick on the Moon — Seth · 1 song Hypnic Jerks — SPIRIT OF THE BEEHIVE · 1 song Dripping — Pile · 1 song Crooked Rain, Crooked Rain — Pavement · 1 song There's Nothing Wrong With Love — Built To Spill · 1 song Fugue State — Vulfpeck · 1 song From the Corner To the Block — Galactic · 1 song Compilation — The Clean · 1 song Hunger for a Way Out — Sweeping Promises · 1 song Cold Hot Plumbs — Damaged Bug · 1 song Sweet Princess EP — Dry Cleaning · 1 song Canned Music — Pearl & The Oysters · 1 song Guttering — MJ Lenderman, Wednesday, Advance Base · 1 song It'll End In Tears (Remastered) — This Mortal Coil · 1 song Longwave — Bonny Doon · 1 song A Brief History of Amazing Letdowns — Lilys · 1 song Hubba Bubba — Damaged Bug · 1 song Dreamin' — Chuck Senrick · 1 song Body by Crystal — Spike Fuck · 1 song Lateralus — TOOL · 1 song Old Ramon — Red House Painters · 1 song You Forgot It In People — Broken Social Scene · 1 song The Sophtware Slump — Grandaddy · 1 song Last Splash — The Breeders · 1 song A Catholic Education — Teenage Fanclub · 1 song Doolittle — Pixies · 1 song Slanted & Enchanted: Luxe & Reduxe — Pavement · 1 song Cor-Crane Secret — Polvo · 1 song Reading, Writing And Arithmetic — The Sundays · 1 song
#music
April 1, 2025
April 2025
↗

What I've been listening to — April 2025

Wowee Zowee (Sordid Sentinels Edition) — Pavement · 4 songs Burner — Breadwinner · 3 songs England Made Me — Black Box Recorder · 2 songs The Three E.P.'s — The Beta Band · 2 songs Wow2 — Boredoms · 2 songs All Aboard — Washer · 1 song Krill — Krill · 1 song Room On Fire — The Strokes · 1 song Lucky Leaves — Krill · 1 song Sleeping Beauty (Expanded Edition) — Sun Ra & His Arkestra · 1 song Joy Techniques — Nate Mercereau · 1 song Roman Candle — Elliott Smith · 1 song East And West + Singles — Anna Domino · 1 song The Head Hurts but the Heart Knows the Truth — Headache · 1 song Manning Fireworks — MJ Lenderman · 1 song Rat Saw God — Wednesday · 1 song Brighten the Corners — Pavement · 1 song Chocolate and Cheese — Ween · 1 song Crooked Rain, Crooked Rain — Pavement · 1 song Floating Coffin — Thee Oh Sees · 1 song Mic City Sons — Heatmiser · 1 song Heaven Adores You Soundtrack — Heatmiser, Elliott Smith · 1 song New Plastic Ideas — Unwound · 1 song Trompe le Monde — Pixies · 1 song Outlaw — Eugene McDaniels · 1 song Paranoid (Remaster) — Black Sabbath · 1 song Psychotic Reaction — Count Five · 1 song Terror Twilight — Pavement · 1 song A Northern Soul (Remastered 2016 / Deluxe) — The Verve · 1 song The Fashion Focus — Starflyer 59 · 1 song England Made Me [25th Anniversary Edition] — Black Box Recorder · 1 song Permutation — Amon Tobin · 1 song I Can Hear The Heart Beating As One — Yo La Tengo · 1 song Night Walks — Hidden Orchestra · 1 song v2.0 (Deluxe Edition) — GoGo Penguin · 1 song Take My Drum to England — Jon Kennedy · 1 song Maps From the Wilderness — TM Juke · 1 song Lost Sides — Doves · 1 song Tír Na NÓg (2012 Remaster) — Tír na nÓg · 1 song The Mystery Lights — The Mystery Lights · 1 song Tainted Lunch — Warmduscher · 1 song Jassbusters — Connan Mockasin · 1 song Salt — Angie McMahon · 1 song Dance Yrself Clean — Soaked Oats · 1 song Safari — Minimal Man · 1 song Sound In Time — Lungfish · 1 song Bonsai Superstar — Brainiac · 1 song Lost Souls — Doves · 1 song A Way of Life (35th Anniversary Edition; 2023 Remaster) — Suicide · 1 song Floating Into The Night — Julee Cruise · 1 song A Decade Deeper — Pill · 1 song Let It Bleed — The Rolling Stones · 1 song Tweez — Slint · 1 song Necrophones — Lungfish · 1 song Anthology 1997-98 — Faraquet · 1 song Rainbow — Boris · 1 song Fake Train — Unwound · 1 song Buried Secrets — Painkiller · 1 song Super Relax — Cibo Matto · 1 song Dark Sunrise — Rikki Ililonga, Musi-O-Tunya · 1 song Ultra Orange & Emmanuelle — Ultra Orange, Emmanuelle Seigner · 1 song Fortune Goodies — Minami Deutsch · 1 song IV — BADBADNOTGOOD, Colin Stetson · 1 song Heavy Days Are Here Again — Frank Vandenkloot · 1 song Air Con Eden — Jerkcurb · 1 song Sylvie — Sylvie · 1 song A New Place 2 Drown — Archy Marshall, King Krule · 1 song
#music
2022 Q2 Q3 Q4
2023 Q1 Q2 Q3 Q4
2024 Q1 Q2 Q3 Q4
2025 Q1 Q2 Q3 Q4
2026 Q1 Q2 Q3

Browse

All posts 107 → Links 18 → Music 51 → Birds 28 → Dictionary 5 →

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.