Shahathir (•◡•)

24 · Batu Caves, Selangor, Malaysia · 🇲🇾

professionally distracted

My Career Journey

  1. Financial Risk Group logo

    Financial Risk Group

    1yrs 3mos

    Assistant Software Developer

    Jun 2025 – Present

  2. Estee Lauder Companies logo

    Estee Lauder Companies

    6mos

    Software Engineer Intern

    Sep 2024 – Mar 2025

Tools & Platforms

TypeScript
JavaScript
Java
Python
PHP
Go
HTML5
CSS3
React
Next.js
Vite
Angular
Redux
React Router
Tailwind CSS
shadcn/ui
Material UI
Sass
Bootstrap
React Native
Node.js
TypeScript
JavaScript
Java
Python
PHP
Go
HTML5
CSS3
React
Next.js
Vite
Angular
Redux
React Router
Tailwind CSS
shadcn/ui
Material UI
Sass
Bootstrap
React Native
Node.js
Bun
Django
FastAPI
Spring Boot
Java Servlets
Express
PostgreSQL
MySQL
MS SQL Server
SQLite
Appwrite
Docker
AWS
Cloudflare
Nginx
Vercel
Netlify
DigitalOcean
Git
DBeaver
Postman
Bun
Django
FastAPI
Spring Boot
Java Servlets
Express
PostgreSQL
MySQL
MS SQL Server
SQLite
Appwrite
Docker
AWS
Cloudflare
Nginx
Vercel
Netlify
DigitalOcean
Git
DBeaver
Postman

Words I Live By

Shahathir is currently not listening to anything
Shahathir is currently not listening to anything

2026 © shahathir.me

Changelogs · Old site

  • AboutAbout
  • ThoughtsThoughts
  • TILTIL
  • BookmarksBookmarks
  • ExperienceExperience
  • ProjectsProjects
  • AccoladesAccolades
  • PhotographyPhotography
  • SongsSongs
  • StatsStats
  • UsesUses
  • ChatChat
  • Resume BuilderResume Builder
  • AboutAbout
  • ThoughtsThoughts
  • TILTIL
  • BookmarksBookmarks
  • ExperienceExperience
  • ProjectsProjects
  • AccoladesAccolades
  • PhotographyPhotography
  • SongsSongs
  • StatsStats
  • UsesUses
  • ChatChat
  • Resume BuilderResume Builder
March 10, 2026

The test suite that let us rewrite the UI underneath it

The app took a two-major framework upgrade and three library swaps without rewriting user-facing behavior. What made that safe was E2E tests that assert outcomes, plus auto-waiting instead of retries.

Over about a year, this app swapped its data-grid library, its code editor, and its unit-test runner, and took a two-major framework upgrade — all without rewriting the user-facing behavior. The thing that made those rewrites safe rather than terrifying was a Playwright suite that asserts what a user can see, so we could rip out the internals and still know the workflows worked.

The evidence I trust most isn't any single test — it's the git history. The grid, the editor, the runner, and the framework all changed underneath a stable set of behavior specs, and the only editor-swap-related test change in the whole arc was a three-line flakiness fix. That's a safety net doing its job: it stays quiet through an implementation change and speaks up only when behavior actually breaks.

Three pieces of infra make the suite dependable enough to lean on:

Authenticate once per worker, not once per test. A worker-scoped fixture logs in one browser context per parallel worker, saves the authenticated state to disk, and reuses it — across every test that worker runs, and across runs if the file's already there:

export const test = base.extend<{}, { workerStorageState: string }>({
  storageState: async ({ workerStorageState }, use) => use(workerStorageState),
  workerStorageState: [async ({ browser }, use) => {
    const id = test.info().parallelIndex;
    const file = path.resolve(test.info().project.outputDir, `.auth/${id}.json`);
    if (fs.existsSync(file)) return use(file);          // reuse across runs
    const ctx = await browser.newContext({ ignoreHTTPSErrors: true });
    const page = await ctx.newPage();
    const acct = accounts[id];                          // one account per worker
    await page.goto("/");
    await page.getByRole("textbox", { name: "Username" }).fill(acct.user);
    await page.getByRole("textbox", { name: "Password" }).fill(acct.pass);
    await page.getByRole("button", { name: "Sign in" }).click();
    await page.waitForURL("**/home");
    await ctx.storageState({ path: file });
    await page.close();
    await use(file);
  }, { scope: "worker" }],
});

Login isn't paid per test, and every test starts already authenticated — which matters when the thing you're rewriting sits behind login.

Run headless in Docker, pointed at the real app. CI brings the app up behind a reverse proxy in Docker, then runs the tests from the official Playwright image with baseURL pointed at it. There's deliberately no webServer block — Playwright doesn't own a multi-service app's lifecycle, and that's fine; ignoreHTTPSErrors lets it talk to the proxy's self-signed TLS.

Dependability comes from auto-waiting, not retries. This surprised me: retries are set to zero. The suite is dense with web-first assertions (expect(locator).toBeVisible(), toContainText()) that auto-retry until true or time out, and where an action depends on a backend round-trip, it synchronizes on the network rather than sleeping:

test("running a report shows results", async ({ page }) => {
  await page.goto("/reports");
  await Promise.all([
    page.waitForResponse((r) => r.url().includes("/reports/") && r.ok()),
    page.getByRole("button", { name: "Run" }).click(),
  ]);
  await expect(page.getByRole("row")).not.toHaveCount(0); // auto-waits; no sleep
});

A component that renders 50ms slower after a library swap doesn't fail a web-first assertion — but it would fail a fixed sleep. Auto-waiting made retries unnecessary enough that we left them off entirely, and failures keep a trace and video (retain-on-failure) so a red build opens straight into the Trace Viewer.

One honest constraint of the per-worker-login pattern: parallelism is bounded by the pool of seeded test accounts, because each worker needs its own session.

The reframe: the safety net wasn't clever tooling or retry magic. It was a decision to assert user-visible outcomes and to synchronize on real events — so the tests know only about the product, never the parts. That single decision is what turns "let's swap the grid library" from a rewrite you dread into a diff you can watch go green.