or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

api-testing.mdbrowser-management.mdelement-handling.mdindex.mdinput-simulation.mdnetwork-control.mdpage-interaction.md

api-testing.mddocs/

0

# API Testing

1

2

HTTP API testing capabilities with request/response handling, authentication, context management, and comprehensive testing utilities for REST and GraphQL APIs.

3

4

## Capabilities

5

6

### API Request Context

7

8

Create isolated contexts for API testing with session management, authentication, and configuration.

9

10

```typescript { .api }

11

/**

12

* HTTP API testing interface

13

*/

14

interface APIRequest {

15

/** Create new API request context */

16

newContext(options?: APIRequestNewContextOptions): Promise<APIRequestContext>;

17

/** Send GET request */

18

get(url: string, options?: APIRequestGetOptions): Promise<APIResponse>;

19

/** Send POST request */

20

post(url: string, options?: APIRequestPostOptions): Promise<APIResponse>;

21

/** Send PUT request */

22

put(url: string, options?: APIRequestPutOptions): Promise<APIResponse>;

23

/** Send PATCH request */

24

patch(url: string, options?: APIRequestPatchOptions): Promise<APIResponse>;

25

/** Send DELETE request */

26

delete(url: string, options?: APIRequestDeleteOptions): Promise<APIResponse>;

27

/** Send HEAD request */

28

head(url: string, options?: APIRequestHeadOptions): Promise<APIResponse>;

29

}

30

31

/**

32

* API request context with session management and authentication

33

*/

34

interface APIRequestContext {

35

/** Send GET request */

36

get(url: string, options?: APIRequestGetOptions): Promise<APIResponse>;

37

/** Send POST request */

38

post(url: string, options?: APIRequestPostOptions): Promise<APIResponse>;

39

/** Send PUT request */

40

put(url: string, options?: APIRequestPutOptions): Promise<APIResponse>;

41

/** Send PATCH request */

42

patch(url: string, options?: APIRequestPatchOptions): Promise<APIResponse>;

43

/** Send DELETE request */

44

delete(url: string, options?: APIRequestDeleteOptions): Promise<APIResponse>;

45

/** Send HEAD request */

46

head(url: string, options?: APIRequestHeadOptions): Promise<APIResponse>;

47

/** Send custom HTTP request */

48

fetch(urlOrRequest: string | APIRequest, options?: APIRequestFetchOptions): Promise<APIResponse>;

49

/** Get or set storage state for authentication */

50

storageState(options?: StorageStateOptions): Promise<StorageState>;

51

/** Clean up context resources */

52

dispose(): Promise<void>;

53

}

54

```

55

56

**Usage Examples:**

57

58

```typescript

59

// Create API context with authentication

60

const apiContext = await request.newContext({

61

baseURL: 'https://api.example.com',

62

extraHTTPHeaders: {

63

'Authorization': 'Bearer your-token-here',

64

'Content-Type': 'application/json'

65

}

66

});

67

68

// Simple API requests

69

const response = await apiContext.get('/users');

70

const users = await response.json();

71

72

const createResponse = await apiContext.post('/users', {

73

data: {

74

name: 'John Doe',

75

email: 'john@example.com'

76

}

77

});

78

79

// Context with session management

80

const authContext = await request.newContext();

81

82

// Login and save session

83

const loginResponse = await authContext.post('/auth/login', {

84

data: { username: 'user', password: 'pass' }

85

});

86

87

// Session is automatically maintained for subsequent requests

88

const profileResponse = await authContext.get('/profile');

89

90

// Save authentication state

91

const state = await authContext.storageState();

92

```

93

94

### API Response Handling

95

96

Comprehensive response handling with JSON parsing, status checking, and data extraction.

97

98

```typescript { .api }

99

/**

100

* API response interface with testing-focused methods

101

*/

102

interface APIResponse {

103

/** Get response URL */

104

url(): string;

105

/** Get HTTP status code */

106

status(): number;

107

/** Get status text */

108

statusText(): string;

109

/** Get response headers */

110

headers(): { [key: string]: string; };

111

/** Get all headers (including multiple values) */

112

allHeaders(): Promise<{ [key: string]: string; }>;

113

/** Get headers as array */

114

headersArray(): Promise<{ name: string; value: string; }[]>;

115

/** Get single header value */

116

headerValue(name: string): Promise<string | null>;

117

/** Get multiple header values */

118

headerValues(name: string): Promise<string[]>;

119

/** Parse response as JSON */

120

json(): Promise<any>;

121

/** Get response as text */

122

text(): Promise<string>;

123

/** Get response as buffer */

124

body(): Promise<Buffer>;

125

/** Check if status indicates success (200-299) */

126

ok(): boolean;

127

/** Get response sizes */

128

sizes(): Promise<ResponseSizes>;

129

}

130

```

131

132

**Usage Examples:**

133

134

```typescript

135

// Basic response handling

136

const response = await apiContext.get('/api/data');

137

138

console.log(`Status: ${response.status()}`);

139

console.log(`Success: ${response.ok()}`);

140

141

if (response.ok()) {

142

const data = await response.json();

143

console.log('Data:', data);

144

} else {

145

const error = await response.text();

146

console.log('Error:', error);

147

}

148

149

// Header inspection

150

const headers = response.headers();

151

const contentType = headers['content-type'];

152

const rateLimit = await response.headerValue('x-rate-limit-remaining');

153

154

// Response validation

155

const apiResponse = await apiContext.post('/api/users', {

156

data: { name: 'Test User', email: 'test@example.com' }

157

});

158

159

if (apiResponse.status() === 201) {

160

const createdUser = await apiResponse.json();

161

console.log('Created user:', createdUser);

162

} else {

163

console.log('Failed to create user:', apiResponse.statusText());

164

}

165

```

166

167

### Authentication Patterns

168

169

Common authentication patterns including basic auth, bearer tokens, and session management.

170

171

**Usage Examples:**

172

173

```typescript

174

// Basic Authentication

175

const basicAuthContext = await request.newContext({

176

httpCredentials: {

177

username: 'admin',

178

password: 'secret'

179

}

180

});

181

182

// Bearer Token Authentication

183

const tokenContext = await request.newContext({

184

extraHTTPHeaders: {

185

'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'

186

}

187

});

188

189

// API Key Authentication

190

const apiKeyContext = await request.newContext({

191

extraHTTPHeaders: {

192

'X-API-Key': 'your-api-key-here'

193

}

194

});

195

196

// Session-based Authentication

197

const sessionContext = await request.newContext();

198

199

// Login to get session cookie

200

await sessionContext.post('/auth/login', {

201

form: {

202

username: 'user@example.com',

203

password: 'password123'

204

}

205

});

206

207

// Session cookie is automatically included in subsequent requests

208

const userData = await sessionContext.get('/user/profile');

209

210

// OAuth2 Flow Simulation

211

const oauthContext = await request.newContext();

212

213

// Step 1: Get authorization code

214

const authResponse = await oauthContext.post('/oauth/authorize', {

215

form: {

216

client_id: 'your-client-id',

217

redirect_uri: 'http://localhost:3000/callback',

218

response_type: 'code',

219

scope: 'read write'

220

}

221

});

222

223

// Step 2: Exchange code for token

224

const tokenResponse = await oauthContext.post('/oauth/token', {

225

form: {

226

client_id: 'your-client-id',

227

client_secret: 'your-client-secret',

228

code: 'authorization-code',

229

grant_type: 'authorization_code'

230

}

231

});

232

233

const { access_token } = await tokenResponse.json();

234

235

// Step 3: Use token for API requests

236

const protectedContext = await request.newContext({

237

extraHTTPHeaders: {

238

'Authorization': `Bearer ${access_token}`

239

}

240

});

241

```

242

243

### Request Data Formats

244

245

Support for various request data formats including JSON, form data, and multipart uploads.

246

247

**Usage Examples:**

248

249

```typescript

250

// JSON Data

251

const jsonResponse = await apiContext.post('/api/users', {

252

data: {

253

name: 'John Doe',

254

email: 'john@example.com',

255

settings: {

256

theme: 'dark',

257

notifications: true

258

}

259

}

260

});

261

262

// Form Data (application/x-www-form-urlencoded)

263

const formResponse = await apiContext.post('/api/login', {

264

form: {

265

username: 'user@example.com',

266

password: 'secretpassword',

267

remember_me: 'true'

268

}

269

});

270

271

// Multipart Form Data (multipart/form-data)

272

const multipartResponse = await apiContext.post('/api/upload', {

273

multipart: {

274

title: 'Document Title',

275

description: 'File description',

276

file: fs.readFileSync('document.pdf')

277

}

278

});

279

280

// Raw Data

281

const rawResponse = await apiContext.post('/api/webhook', {

282

data: Buffer.from('raw binary data'),

283

headers: {

284

'Content-Type': 'application/octet-stream'

285

}

286

});

287

288

// GraphQL Queries

289

const graphqlResponse = await apiContext.post('/graphql', {

290

data: {

291

query: `

292

query GetUser($id: ID!) {

293

user(id: $id) {

294

id

295

name

296

email

297

posts {

298

title

299

content

300

}

301

}

302

}

303

`,

304

variables: {

305

id: '123'

306

}

307

}

308

});

309

310

const graphqlData = await graphqlResponse.json();

311

```

312

313

### Testing Utilities

314

315

Built-in utilities for common API testing scenarios including retries, timeouts, and validation.

316

317

**Usage Examples:**

318

319

```typescript

320

// Request with timeout

321

const response = await apiContext.get('/slow-endpoint', {

322

timeout: 5000 // 5 seconds

323

});

324

325

// Request with retries (custom implementation)

326

async function requestWithRetry(url: string, options: any, maxRetries = 3) {

327

for (let i = 0; i < maxRetries; i++) {

328

try {

329

const response = await apiContext.get(url, options);

330

if (response.ok()) return response;

331

332

if (i === maxRetries - 1) throw new Error(`Failed after ${maxRetries} retries`);

333

await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // Exponential backoff

334

} catch (error) {

335

if (i === maxRetries - 1) throw error;

336

}

337

}

338

}

339

340

// Rate limiting handling

341

async function handleRateLimit(response: APIResponse) {

342

if (response.status() === 429) {

343

const retryAfter = await response.headerValue('retry-after');

344

if (retryAfter) {

345

await new Promise(resolve => setTimeout(resolve, parseInt(retryAfter) * 1000));

346

}

347

}

348

}

349

350

// Response validation

351

async function validateApiResponse(response: APIResponse, expectedSchema: any) {

352

if (!response.ok()) {

353

throw new Error(`API error: ${response.status()} ${response.statusText()}`);

354

}

355

356

const data = await response.json();

357

358

// Validate response structure (pseudo-code)

359

if (!validateSchema(data, expectedSchema)) {

360

throw new Error('Response does not match expected schema');

361

}

362

363

return data;

364

}

365

```

366

367

## Configuration Types

368

369

```typescript { .api }

370

interface APIRequestNewContextOptions {

371

/** Base URL for relative requests. */

372

baseURL?: string;

373

/** Additional HTTP headers. */

374

extraHTTPHeaders?: { [key: string]: string; };

375

/** HTTP credentials. */

376

httpCredentials?: { username: string; password: string; };

377

/** Whether to ignore HTTPS errors. */

378

ignoreHTTPSErrors?: boolean;

379

/** Network proxy settings. */

380

proxy?: { server: string; bypass?: string; username?: string; password?: string; };

381

/** Storage state to populate context with. */

382

storageState?: string | StorageState;

383

/** Request timeout in milliseconds. */

384

timeout?: number;

385

/** User agent string. */

386

userAgent?: string;

387

}

388

389

interface APIRequestGetOptions {

390

/** Request data. */

391

data?: string | Buffer | Serializable;

392

/** Whether to throw on non-2xx/3xx status codes. */

393

failOnStatusCode?: boolean;

394

/** Form data. */

395

form?: { [key: string]: string | number | boolean; };

396

/** Request headers. */

397

headers?: { [key: string]: string; };

398

/** Whether to ignore HTTPS errors. */

399

ignoreHTTPSErrors?: boolean;

400

/** Maximum redirects to follow. */

401

maxRedirects?: number;

402

/** Maximum retries on network errors. */

403

maxRetries?: number;

404

/** Multipart form data. */

405

multipart?: { [key: string]: string | number | boolean; };

406

/** Query parameters. */

407

params?: { [key: string]: string | number | boolean; };

408

/** Request timeout. */

409

timeout?: number;

410

}

411

412

interface APIRequestPostOptions {

413

/** Request data. */

414

data?: string | Buffer | Serializable;

415

/** Whether to throw on non-2xx/3xx status codes. */

416

failOnStatusCode?: boolean;

417

/** Form data. */

418

form?: { [key: string]: string | number | boolean; };

419

/** Request headers. */

420

headers?: { [key: string]: string; };

421

/** Whether to ignore HTTPS errors. */

422

ignoreHTTPSErrors?: boolean;

423

/** Maximum redirects to follow. */

424

maxRedirects?: number;

425

/** Maximum retries on network errors. */

426

maxRetries?: number;

427

/** Multipart form data. */

428

multipart?: { [key: string]: string | number | boolean; };

429

/** Query parameters. */

430

params?: { [key: string]: string | number | boolean; };

431

/** Request timeout. */

432

timeout?: number;

433

}

434

435

interface APIRequestPutOptions {

436

/** Request data. */

437

data?: string | Buffer | Serializable;

438

/** Whether to throw on non-2xx/3xx status codes. */

439

failOnStatusCode?: boolean;

440

/** Form data. */

441

form?: { [key: string]: string | number | boolean; };

442

/** Request headers. */

443

headers?: { [key: string]: string; };

444

/** Whether to ignore HTTPS errors. */

445

ignoreHTTPSErrors?: boolean;

446

/** Maximum redirects to follow. */

447

maxRedirects?: number;

448

/** Maximum retries on network errors. */

449

maxRetries?: number;

450

/** Multipart form data. */

451

multipart?: { [key: string]: string | number | boolean; };

452

/** Query parameters. */

453

params?: { [key: string]: string | number | boolean; };

454

/** Request timeout. */

455

timeout?: number;

456

}

457

458

interface APIRequestDeleteOptions {

459

/** Request data. */

460

data?: string | Buffer | Serializable;

461

/** Whether to throw on non-2xx/3xx status codes. */

462

failOnStatusCode?: boolean;

463

/** Form data. */

464

form?: { [key: string]: string | number | boolean; };

465

/** Request headers. */

466

headers?: { [key: string]: string; };

467

/** Whether to ignore HTTPS errors. */

468

ignoreHTTPSErrors?: boolean;

469

/** Maximum redirects to follow. */

470

maxRedirects?: number;

471

/** Maximum retries on network errors. */

472

maxRetries?: number;

473

/** Multipart form data. */

474

multipart?: { [key: string]: string | number | boolean; };

475

/** Query parameters. */

476

params?: { [key: string]: string | number | boolean; };

477

/** Request timeout. */

478

timeout?: number;

479

}

480

481

interface StorageState {

482

/** Cookies to set. */

483

cookies: Array<{

484

name: string;

485

value: string;

486

domain: string;

487

path: string;

488

expires: number;

489

httpOnly: boolean;

490

secure: boolean;

491

sameSite: 'Strict' | 'Lax' | 'None';

492

}>;

493

/** Local storage entries to set. */

494

origins: Array<{

495

origin: string;

496

localStorage: Array<{

497

name: string;

498

value: string;

499

}>;

500

}>;

501

}

502

503

type Serializable = any;

504

```

505

506

## Integration with Browser Context

507

508

API testing can be integrated with browser automation for end-to-end testing scenarios.

509

510

**Usage Examples:**

511

512

```typescript

513

// Share authentication between browser and API contexts

514

const browser = await chromium.launch();

515

const context = await browser.newContext();

516

const page = await context.newPage();

517

518

// Login through browser

519

await page.goto('https://example.com/login');

520

await page.fill('[name="email"]', 'user@example.com');

521

await page.fill('[name="password"]', 'password123');

522

await page.click('button[type="submit"]');

523

524

// Extract authentication state

525

const storageState = await context.storageState();

526

527

// Use authentication state for API testing

528

const apiContext = await request.newContext({

529

baseURL: 'https://api.example.com',

530

storageState

531

});

532

533

// API requests now use the same authentication as browser

534

const apiResponse = await apiContext.get('/user/profile');

535

const userData = await apiResponse.json();

536

537

// Verify UI reflects API data

538

await page.goto('https://example.com/profile');

539

const displayedName = await page.textContent('.user-name');

540

console.assert(displayedName === userData.name);

541

```

542

543

## Error Handling

544

545

**Usage Examples:**

546

547

```typescript

548

try {

549

const response = await apiContext.get('/api/data');

550

551

if (!response.ok()) {

552

const errorBody = await response.text();

553

throw new Error(`API Error ${response.status()}: ${errorBody}`);

554

}

555

556

const data = await response.json();

557

return data;

558

} catch (error) {

559

if (error.message.includes('timeout')) {

560

console.log('Request timed out, retrying...');

561

// Implement retry logic

562

} else if (error.message.includes('ECONNREFUSED')) {

563

console.log('Server is not available');

564

} else {

565

console.log('API request failed:', error.message);

566

}

567

throw error;

568

}

569

```