Morrie Automation โ Interview Prep¶
One-liner: A Playwright + TypeScript test-automation framework for an AI-agent platform (questt.ai / demo.questt.ai) โ covering API, UI, and cross-layer E2E flows, with a Page Object Model, custom fixtures for dependency injection, storage-state auth reuse, and a parameterized Jenkins pipeline.
1. Elevator pitch¶
30-second version:
"Morrie is the Playwright + TypeScript automation framework for an AI-agent product. Users create AI agents with custom system prompts, chat with them in threads, and deploy hosted pages through a Page Builder. I built three layers of tests โ pure API tests with Axios, UI tests with the Page Object Model, and cross-layer E2E tests that, say, create an agent via the API, verify it in the UI, update it in the UI, and confirm the change back through the API. Auth is handled once in global setup โ both an API token and a real browser login saved as storage state โ so tests start already authenticated. It runs in Jenkins with a suite-selector parameter."
2-minute version adds: the framework uses custom Playwright fixtures to dependency-inject page objects and API clients into specs, a BaseApi Axios class with rate-limit handling (429 retry honoring Retry-After, plus a 700ms request spacing interceptor), centralized endpoints and test data, and Playwright's failure artifacts (screenshot on failure, video on retain-on-failure, trace on first retry). CI runs chromium headless with retries=1 and 2 workers.
2. The application under test¶
"Morrie" is the internal codename; the product is questt.ai (web demo.questt.ai, API api.demo.questt.ai). It lets users:
- create/configure AI agents (each with a name and a system prompt),
- have chat threads with them (the message API returns both a userMessage and an aiMessage),
- use a Page Builder that uploads React/TS component files, assigns routing URLs, and deploys them as hosted pages.
Testing is API + UI + hybrid E2E.
3. Tech stack & why¶
| Layer | Tech | Why |
|---|---|---|
| Language | TypeScript (^5.2.2, via ts-node) |
Type-safe page objects, IDE autocomplete on API models |
| Test runner | @playwright/test ^1.35.0 |
Built-in parallelism, auto-waiting, fixtures, tracing |
| API calls | Axios ^1.4.0 |
Interceptors for rate-limit/retry control (vs Playwright's APIRequestContext) |
| Config | dotenv ^17.2.3 |
.env-driven endpoints/creds |
| Reporters | list + html + json + junit | Human-readable + machine-readable for CI (JUnit for Jenkins) |
| Package mgr | npm (npm ci in CI) |
Lockfile-deterministic installs |
| CI | Jenkins (declarative pipeline) | Suite-selector parameter, artifact archiving |
Why Axios instead of Playwright's API testing? โ "I wanted request/response interceptors to enforce a minimum gap between calls and to retry HTTP 429 honoring Retry-After. That centralized rate-limit defense was cleaner in Axios than reimplementing it per request in Playwright's APIRequestContext."
4. Architecture / design patterns¶
src/
โโโ pages/ Page Object Model: LoginPage, DashboardPage, agentPage, PageBuilderPage
โโโ api/ API client classes: BaseApi (Axios + interceptors)
โ โโ AuthApi, UserApi, AgentsApi, ChatsApi
โโโ fixtures/ testFixture.ts โ extends Playwright base.test to inject
โ page objects + API clients as fixtures
โโโ data/ data-driven test data: agents.data.ts, chats.data.ts,
โ testUsers.json, payloads/samplePayload.json
โโโ config/ credentials.ts, env.dev.ts / env.stage.ts (env switching)
โโโ constants/ endpoints.ts (centralized URLs)
โโโ utils/ rateLimiter, apiLimiter, waitUtils, logger, apiUtils
global-setup.ts dual login โ auth.token.json (API) + auth.storage.json (browser)
tests/
โโโ api/ agents.api.spec, chats.api.spec, users.api.spec, auth.api.spec
โโโ web/ login.spec, dashboard.spec, agent.spec, page-builder/*
โโโ e2e/ e2e-lifecycle.spec (cross-layer APIโUI)
Patterns to name in interview:
1. Page Object Model โ each page's locators + actions live in a class; specs read as business steps.
2. API client layer โ a BaseApi superclass (Axios instance + interceptors) extended by feature clients (AuthApi, AgentsApi, ChatsApi, UserApi). Mirrors POM but for the API.
3. Custom fixtures (dependency injection) โ testFixture.ts extends Playwright's base.test so specs import test/expect from the fixture and receive page objects + API clients pre-wired. No manual instantiation in tests.
4. Data-driven โ all test data centralized in src/data/.
5. Two Playwright projects โ chromium (web + e2e, uses storageState: auth.storage.json) and api (no storage state). fullyParallel: true.
5. Auth handling (the centerpiece โ explain this well)¶
global-setup.ts does a dual login once, before any test:
1. API OTP login โ fetches access_token, saved to auth.token.json and injected into process.env.ACCESS_TOKEN for API specs.
2. Real UI OTP login via chromium โ saves cookies/localStorage to auth.storage.json.
Web/e2e tests load that storage state, so they start already logged in and skip the login UI every time. This is the standard Playwright performance pattern: authenticate once, reuse the session across all tests instead of logging in per test.
Talking point: "Logging in through the UI on every test is slow and flaky. Global setup authenticates once and persists the storage state; every web test reuses it. The login flow itself still has its own dedicated spec with storage state disabled, so I don't lose coverage of the login journey."
6. Key test scenarios¶
- API agents CRUD (
agents.api.spec.ts, serial): POST create โ GET all โ GET by id โ PATCH update โ DELETE; asserts 201/200/204 and_id. - API chats (
chats.api.spec.ts): create agent โ create thread โ list threads โ add message (asserts bothuserMessage+aiMessage) โ list messages โ delete. - API users (
users.api.spec.ts):GET /users/meshape validation,PATCHusername. - Web:
login.spec.ts(UI OTP login),dashboard.spec.ts(sidebar / Create Agent / user email visible),agent.spec.ts(create/edit/delete agent via UI). - E2E (
e2e-lifecycle.spec.ts): the showpiece โ Create via API โ Verify in UI โ Update via UI โ Verify via API โ Delete via UI โ confirm 404. This proves API and UI operate on the same backend state. - Page Builder (
tests/web/page-builder/): create/validation, config/edit, deploy/redeploy, deployed-page redirect-in-new-tab.
7. CI/CD (Jenkins)¶
Important honesty note: the root
Morrie_automation/Jenkinsfileis empty (0 bytes). The real pipeline isplaywright-automation/Jenkinsfile. Mention this if asked โ don't claim the root one does anything.
The real Jenkinsfile is a declarative pipeline:
- agent any; options timestamps() + ansiColor('xterm').
- A choice parameter TEST_SUITE (all | api | ui | e2e) selects which npm script runs.
- Stages: Checkout โ Install (npm ci, npx playwright install chromium) โ Run tests.
- Post-always: archives reports/**, test-results/**, auth.token.json; publishes JUnit and the HTML report.
CI-specific config (keyed off process.env.CI): retries: 1 (0 locally), workers: 2, forbidOnly: true, always headless, chromium only (no cross-browser), no sharding.
8. Reporting & utilities¶
- Failure artifacts:
screenshot: 'only-on-failure',video: 'retain-on-failure',trace: 'on-first-retry'โ the trace viewer is the key debugging tool. - Reporters:
list(console),html(reports/html-report),json(reports/results.json),junit(reports/junit/results.xml). No Allure. - Rate-limit defense (layered):
BaseApirequest interceptor enforces a 700ms gap between calls; response interceptor retries HTTP 429 up to 3ร honoringRetry-After.AgentPage.deleteAllAgentsadds UI-side exponential backoff on 429 viawaitForResponse. - Utilities:
rateLimiter.ts(queue-basedRateLimiter),apiLimiter.ts(singleton),waitUtils.ts,logger.ts,apiUtils.ts(buildHeaders).
9. Interview Q&A¶
Q1. Why Playwright over Selenium/Cypress?
Auto-waiting (it waits for elements to be actionable, cutting flaky explicit waits), built-in parallelism and test isolation via browser contexts, first-class tracing/video/screenshots, native API testing, and great TypeScript support. Versus Cypress, Playwright has true multi-tab/multi-context support and runs outside the browser process, which matters for the Page Builder's "deployed page opens in a new tab" scenario.
Q2. How is your framework structured?
Layered: Page Object Model for UI (
pages/), an Axios-based API client layer with aBaseApisuperclass (api/), custom fixtures that dependency-inject those into specs (fixtures/), centralized data (data/), endpoints (constants/), and utilities. Tests are split intoapi/,web/, and cross-layere2e/.
Q3. What are Playwright fixtures and how did you use them?
Fixtures are Playwright's dependency-injection mechanism. I extended
base.testso each spec automatically receives ready-to-use page objects and API clients โ e.g., a test signature getsagentPageandagentsApiinjected. Specs importtest/expectfrom my fixture file instead of@playwright/test. It removes boilerplate instantiation and centralizes setup/teardown.
Q4. How do you handle authentication efficiently?
Global setup logs in once โ both an API OTP login that saves an access token and a real browser login that saves storage state. Web tests reuse the storage state so they start authenticated and skip the login UI, which is the biggest speed and stability win. The login journey still has its own spec with storage state turned off so it stays covered.
Q5. Walk me through your E2E lifecycle test.
It creates an agent through the API, opens the UI and verifies it appears, edits it in the UI, then queries the API to confirm the update persisted, deletes it in the UI, and finally asserts the API returns 404. It's the strongest test because it proves the API and UI are consistent views of the same backend state โ a pure UI test or pure API test can't catch a sync bug between them.
Q6. How do you deal with flakiness?
Rely on Playwright's auto-waiting and web-first assertions (
expect(locator).toBeVisible()retries) instead of fixed sleeps; prefer role/label/text locators; usetrace: 'on-first-retry'to debug intermittent failures; retries=1 in CI. Honest caveat: this codebase still has somewaitForTimeouthard waits and a deprecatedwaitForNavigationI'd refactor to web-first assertions โ that's exactly the kind of flakiness source I'd remove.
Q7. What's your locator strategy?
Prefer accessible, user-facing locators โ
getByRole,getByLabel,getByTextโ because they're resilient to DOM churn and mirror how users find elements. Honest caveat: a few page objects (Page Builder, Dashboard) use brittle CSS attribute/class-substring selectors like[class*="error"]; I'd replace those withdata-testidhooks coordinated with devs.
Q8. How do you handle API rate limits in tests?
Two layers. In
BaseApi, a request interceptor enforces a minimum 700ms gap between calls, and a response interceptor retries 429s up to three times honoring theRetry-Afterheader. On the UI side, bulk operations like deleting all agents use exponential backoff watching for 429 responses. So the suite is a well-behaved client rather than hammering the backend.
Q9. Why Axios for API tests instead of Playwright's request context?
I needed interceptors for the centralized rate-limit/retry logic. Axios interceptors made that a single cross-cutting concern; doing it in Playwright's APIRequestContext would have meant wrapping every call. The tradeoff is I don't get Playwright's trace integration on API calls, which I accept for the cleaner rate-limit handling.
Q10. How does CI run and what does it produce?
A Jenkins declarative pipeline with a
TEST_SUITEchoice parameter (all/api/ui/e2e). It checks out, runsnpm ci+ installs chromium, runs the selected suite headless with retries=1 and 2 workers, then always archives reports/test-results/auth token and publishes the JUnit results and HTML report.
Q11. Do you run cross-browser?
Currently chromium only โ that's a known gap. Playwright makes adding firefox/webkit projects trivial (just add them to
playwright.config.ts), so I'd add them where the product's browser support matrix requires it. I'd also consider sharding across CI agents for speed once the suite grows.
Q12. How do you make tests data-driven?
Test data lives in
src/data/(typed.data.tsfiles and JSON), so specs reference named data rather than inlining literals. For parametrized runs Playwright lets me loop over a data array generating a test per entry. Centralizing data also means updating a payload in one place.
Q13. What's the weakest part of this framework and how would you fix it?
A few things, honestly: the root Jenkinsfile is empty (the real one is in the subfolder); some Page Builder specs reference fixtures/files that don't exist yet and a method (
isDeployButtonVisible) that isn't implemented, so those would fail; credentials and storage-state files are committed. I'd fix the missing implementations, move secrets out of the repo, replace hard waits and brittle CSS selectors, and add cross-browser coverage. I'd frame these as known tech-debt with a prioritized plan.
10. STAR story (memorize)¶
Situation: The questt.ai AI-agent product needed automated coverage across its API and a React UI, and login was slow and flaky when done per-test.
Task: Build a maintainable Playwright + TypeScript framework with fast, reliable auth and cross-layer confidence.
Action: I built a layered framework โ POM for UI, an Axios BaseApi client layer with rate-limit interceptors, and custom fixtures for dependency injection โ plus a global-setup dual login that persists both an API token and browser storage state. I added a cross-layer E2E test that creates via API and verifies via UI and back.
Result: Tests start pre-authenticated and run in parallel; the E2E lifecycle test catches API/UI sync bugs neither layer alone would; CI runs any suite on demand via a Jenkins parameter and publishes HTML/JUnit reports.
11. Honest caveats (be ready)¶
- Root
Jenkinsfileis empty; real pipeline is inplaywright-automation/. - Some Page Builder specs reference non-existent fixture files / an unimplemented method โ would fail as-is.
- Hardcoded credentials (
rohan@questt.com, OTP111111) and committedauth.storage.json/auth.token.jsonโ security smell; move to secrets. - Several
waitForTimeouthard waits + deprecatedwaitForNavigationโ flakiness risk; refactor to web-first assertions. - chromium-only, no sharding โ known scaling gaps.
.github/agents/contains Playwright test-planner/generator/healer agent definitions and*.plan.mdplans โ the suite is partly aspirational (plans describe more than is implemented). Frame as "AI-assisted authoring workflow; some plans not yet realized."