Phase 1 Design Handoff
Objective
Build the shared Playwright API testing support layer before writing test cases.
Deliverables
support/
client.ts
assertions.ts
cleanup.ts
fixtures.ts
playwright.config.ts
tests/
01-auth.spec.ts
02-discovery.spec.ts
Phase Gate
✅ All 10 tests passing
✅ Envelope assertions used everywhere
✅ Cleanup registry implemented
✅ Test tags correctly applied
✅ Jennifer reviews framework before Phase 2
Overall Architecture
Tests
│
▼
Fixtures
│
▼
Typed Client
│
▼
Assertions
│
▼
API
Cleanup runs independently.
Tests
│
register(resource)
│
▼
Cleanup Registry
│
afterAll()
│
DELETE resources
Envelope Contract
Every endpoint returns the same response shape.
interface ApiEnvelope {
status: 'Success' | 'Failure';
message?: string;
data: T;
}
No test should inspect raw JSON.
Instead:
const env = await client.get<VM[]>('/virtual-machines');
assertEnvelope(env);
assertArray(env);
The engineer should assume every endpoint returns this contract.
support/client.ts
Purpose
Hide HTTP plumbing.
Tests should never manually:
build URLs
set headers
parse JSON
check response status
Instead:
const env = await client.get<User[]>('/users');
instead of
const response = await request.get(...);
expect(response.status()).toBe(200);
const env = await response.json();
expect(env.status).toBe('Success');
Responsibilities
The client should
prepend BASE_URL
attach auth token
call Playwright request context
verify HTTP status
deserialize JSON
return
ApiEnvelope
Example interface
class Client {
get<T>(path: string)
post<T>(path: string, body: unknown)
put<T>(path: string, body: unknown)
delete(path: string)
}
support/assertions.ts
Purpose
Centralize common assertions.
Instead of repeating
expect(env.status).toBe("Success");
100+ times,
tests call
assertEnvelope(env);
Helper 1
assertEnvelope(env)
Checks
Success status exists
data property exists
Helper 2
assertArray(env)
Checks
Array.isArray(env.data)
Helper 3
Optional
assertObject(env)
Verifies
typeof env.data === "object"
Useful later.
support/cleanup.ts
Purpose
Automatically delete anything created during testing.
Tests never delete directly.
Instead
cleanup.register(/virtual-machines/${slug});
Later
afterAll(async () => {
await cleanup.run(client);
});
Responsibilities
Maintain
private resources: string[]
Methods
register(path)
run(client)
run()
should
iterate all paths
DELETE each
ignore already-deleted resources
clear registry
The engineer should ensure cleanup executes even if earlier tests fail.
Phase 1 Design Handoff
Objective
Build the shared Playwright API testing support layer before writing test cases.
Deliverables
support/
client.ts
assertions.ts
cleanup.ts
fixtures.ts
playwright.config.ts
tests/
01-auth.spec.ts
02-discovery.spec.ts
Phase Gate
✅ All 10 tests passing
✅ Envelope assertions used everywhere
✅ Cleanup registry implemented
✅ Test tags correctly applied
✅ Jennifer reviews framework before Phase 2
Overall Architecture
Tests
│
▼
Fixtures
│
▼
Typed Client
│
▼
Assertions
│
▼
API
Cleanup runs independently.
Tests
│
register(resource)
│
▼
Cleanup Registry
│
afterAll()
│
DELETE resources
Envelope Contract
Every endpoint returns the same response shape.
interface ApiEnvelope {
status: 'Success' | 'Failure';
message?: string;
data: T;
}
No test should inspect raw JSON.
Instead:
const env = await client.get<VM[]>('/virtual-machines');
assertEnvelope(env);
assertArray(env);
The engineer should assume every endpoint returns this contract.
support/client.ts
Purpose
Hide HTTP plumbing.
Tests should never manually:
build URLs
set headers
parse JSON
check response status
Instead:
const env = await client.get<User[]>('/users');
instead of
const response = await request.get(...);
expect(response.status()).toBe(200);
const env = await response.json();
expect(env.status).toBe('Success');
Responsibilities
The client should
prepend BASE_URL
attach auth token
call Playwright request context
verify HTTP status
deserialize JSON
return
ApiEnvelope
Example interface
class Client {
}
support/assertions.ts
Purpose
Centralize common assertions.
Instead of repeating
expect(env.status).toBe("Success");
100+ times,
tests call
assertEnvelope(env);
Helper 1
assertEnvelope(env)
Checks
Success status exists
data property exists
Helper 2
assertArray(env)
Checks
Array.isArray(env.data)
Helper 3
Optional
assertObject(env)
Verifies
typeof env.data === "object"
Useful later.
support/cleanup.ts
Purpose
Automatically delete anything created during testing.
Tests never delete directly.
Instead
cleanup.register(
/virtual-machines/${slug});Later
afterAll(async () => {
await cleanup.run(client);
});
Responsibilities
Maintain
private resources: string[]
Methods
register(path)
run(client)
run()
should
iterate all paths
DELETE each
ignore already-deleted resources
clear registry
The engineer should ensure cleanup executes even if earlier tests fail.