Skip to content

Commit

Permalink
refactor(interceptor): change getter methods to getter properties (#490
Browse files Browse the repository at this point in the history
…) (#636)

### Refactoring
- [interceptor] Changed getter methods to getter properties, as a way to
simplify the Zimic API.

#### `HttpInterceptor`

| Previously                | Now                     |
| ------------------------- | ----------------------- |
| `interceptor.baseURL()`   | `interceptor.baseURL`   |
| `interceptor.platform()`  | `interceptor.platform`  |
| `interceptor.isRunning()` | `interceptor.isRunning` |

#### `HttpRequestHandler`

| Previously           | Now                |
| -------------------- | ------------------ |
| `handler.method()`   | `handler.method`   |
| `handler.path()`     | `handler.path`     |
| `handler.requests()` | `handler.requests` |

#### `InterceptorServer`

| Previously                      | Now                           |
| ------------------------------- | ----------------------------- |
| `server.hostname()`             | `server.hostname`             |
| `server.port()`                 | `server.port`                 |
| `server.logUnhandledRequests()` | `server.logUnhandledRequests` |
| `server.httpURL()`              | `server.httpURL`              |
| `server.isRunning()`            | `server.isRunning`            |

> [!IMPORTANT]
>
> For remote interceptors, `await handler.requests()` was the way to
access the intercepted requests. Now, you can access the requests in
both local and remote interceptors by using `handler.requests`, without
needing to await.

Part of #490.
  • Loading branch information
diego-aquino authored Mar 8, 2025
1 parent 6f915b2 commit 8c02f7f
Show file tree
Hide file tree
Showing 60 changed files with 1,548 additions and 1,936 deletions.
491 changes: 246 additions & 245 deletions apps/zimic-test-client/tests/fetch/FetchClient.test.ts

Large diffs are not rendered by default.

Large diffs are not rendered by default.

8 changes: 8 additions & 0 deletions apps/zimic-test-client/tests/utils/requests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { expect } from 'vitest';

export function expectResponseStatus<Response extends globalThis.Response, Status extends Response['status']>(
response: Response,
status: Status,
): asserts response is Extract<Response, { status: Status }> {
expect(response.status).toBe(status);
}
7 changes: 1 addition & 6 deletions apps/zimic-test-client/vitest.config.mts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
/// <reference types="vitest" />

import os from 'os';
import path from 'path';
import { defineConfig } from 'vitest/config';

const numberOfCPUs = os.cpus().length;
const maxWorkers = process.env.CI === 'true' ? numberOfCPUs : Math.ceil(numberOfCPUs / 2);

export default defineConfig({
publicDir: './public',
test: {
globals: false,
testTimeout: 5000,
minWorkers: 1,
maxWorkers,
maxConcurrency: maxWorkers,
maxWorkers: process.env.CI === 'true' ? '50%' : '25%',
clearMocks: true,
coverage: {
provider: 'istanbul',
Expand Down
102 changes: 48 additions & 54 deletions docs/wiki/api‐zimic‐interceptor‐http.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,18 @@
- [Saving requests](#saving-requests)
- [HTTP `interceptor.start()`](#http-interceptorstart)
- [HTTP `interceptor.stop()`](#http-interceptorstop)
- [HTTP `interceptor.isRunning()`](#http-interceptorisrunning)
- [HTTP `interceptor.baseURL()`](#http-interceptorbaseurl)
- [HTTP `interceptor.platform()`](#http-interceptorplatform)
- [HTTP `interceptor.isRunning`](#http-interceptorisrunning)
- [HTTP `interceptor.baseURL`](#http-interceptorbaseurl)
- [HTTP `interceptor.platform`](#http-interceptorplatform)
- [HTTP `interceptor.<method>(path)`](#http-interceptormethodpath)
- [Path parameters](#path-parameters)
- [HTTP `interceptor.checkTimes()`](#http-interceptorchecktimes)
- [HTTP `interceptor.clear()`](#http-interceptorclear)
- [`HttpInterceptor` utility types](#httpinterceptor-utility-types)
- [`InferHttpInterceptorSchema`](#inferhttpinterceptorschema)
- [`HttpRequestHandler`](#httprequesthandler)
- [HTTP `handler.method()`](#http-handlermethod)
- [HTTP `handler.path()`](#http-handlerpath)
- [HTTP `handler.method`](#http-handlermethod)
- [HTTP `handler.path`](#http-handlerpath)
- [HTTP `handler.with(restriction)`](#http-handlerwithrestriction)
- [Static restrictions](#static-restrictions)
- [Computed restrictions](#computed-restrictions)
Expand All @@ -32,7 +32,7 @@
- [HTTP `handler.times()`](#http-handlertimes)
- [HTTP `handler.checkTimes()`](#http-handlerchecktimes)
- [HTTP `handler.clear()`](#http-handlerclear)
- [HTTP `handler.requests()`](#http-handlerrequests)
- [HTTP `handler.requests`](#http-handlerrequests)
- [Intercepted HTTP resources](#intercepted-http-resources)
- [Guides](#guides)

Expand Down Expand Up @@ -132,7 +132,7 @@ const interceptor = httpInterceptor.create<{
});

// Your application should use this base URL when making requests
const baseURL = interceptor.baseURL();
console.log(interceptor.baseURL);
```

#### Unhandled requests
Expand Down Expand Up @@ -333,7 +333,7 @@ httpInterceptor.default.remote.onUnhandledRequest = (request) => {
#### Saving requests

The option `saveRequests` indicates whether [request handlers](#httprequesthandler) should save their intercepted
requests in memory and make them accessible through [`handler.requests()`](#http-handlerrequests).
requests in memory and make them accessible through [`handler.requests`](#http-handlerrequests).

This setting is configured per interceptor and is `false` by default. If set to `true`, each handler will keep track of
their intercepted requests in memory.
Expand Down Expand Up @@ -426,28 +426,28 @@ called.
await interceptor.stop();
```

### HTTP `interceptor.isRunning()`
### HTTP `interceptor.isRunning`

Returns whether the interceptor is currently running and ready to use.
Whether the interceptor is currently running and ready to use.

```ts
const isRunning = interceptor.isRunning();
const isRunning = interceptor.isRunning;
```

### HTTP `interceptor.baseURL()`
### HTTP `interceptor.baseURL`

Returns the base URL of the interceptor.
The base URL of the interceptor.

```ts
const baseURL = interceptor.baseURL();
console.log(interceptor.baseURL);
```

### HTTP `interceptor.platform()`
### HTTP `interceptor.platform`

Returns the platform used by the interceptor (`browser` or `node`).
The platform used by the interceptor (`browser` or `node`).

```ts
const platform = interceptor.platform();
console.log(interceptor.platform);
```

### HTTP `interceptor.<method>(path)`
Expand Down Expand Up @@ -694,47 +694,43 @@ correct.
When multiple handlers match the same method and path, the _last_ created with
[`interceptor.<method>(path)`](#http-interceptormethodpath) will be used.

### HTTP `handler.method()`
### HTTP `handler.method`

Returns the method that matches a handler.

<table><tr><td width="900px" valign="top"><details open><summary><b>Using a local interceptor</b></summary>

```ts
const creationHandler = interceptor.post('/users');
const method = creationHandler.method();
console.log(method); // 'POST'
const handler = interceptor.post('/users');
console.log(handler.method); // 'POST'
```

</details></td><td width="900px" valign="top"><details open><summary><b>Using a remote interceptor</b></summary>

```ts
const handler = await interceptor.post('/users');
const method = handler.method();
console.log(method); // 'POST'
console.log(handler.method); // 'POST'
```

</details></td></tr></table>

### HTTP `handler.path()`
### HTTP `handler.path`

Returns the path that matches a handler. The base URL of the interceptor is not included, but it is used when matching
requests.

<table><tr><td width="900px" valign="top"><details open><summary><b>Using a local interceptor</b></summary>

```ts
const listHandler = interceptor.get('/users');
const path = listHandler.path();
console.log(path); // '/users'
const handler = interceptor.get('/users');
console.log(handler.path); // '/users'
```

</details></td><td width="900px" valign="top"><details open><summary><b>Using a remote interceptor</b></summary>

```ts
const handler = await interceptor.get('/users');
const path = handler.path();
console.log(path); // '/users'
console.log(handler.path); // '/users'
```

</details></td></tr></table>
Expand Down Expand Up @@ -1547,10 +1543,10 @@ const rangeListHandler = await interceptor
> [!TIP]
>
> Prior to v0.12.0, a common strategy to check the number of requests was to assert the length of `handler.requests()`.
> Prior to v0.12.0, a common strategy to check the number of requests was to assert the length of `handler.requests`.
> [`handler.times()`](#http-handlertimes), combined with [`handler.checkTimes()`](#http-handlerchecktimes) or
> [`interceptor.checkTimes()`](#http-interceptorchecktimes), archives the same purpose in a shorter and more declarative
> way. In most cases, these methods are preferred over manually checking the length of `handler.requests()`.
> way. In most cases, these methods are preferred over manually checking the length of `handler.requests`.
### HTTP `handler.checkTimes()`

Expand Down Expand Up @@ -1607,44 +1603,44 @@ To make the handler match requests again, register a new response with `handler.
<table><tr><td width="900px" valign="top"><details open><summary><b>Using a local interceptor</b></summary>

```ts
const genericListHandler = interceptor.get('/users').respond({
const genericHandler = interceptor.get('/users').respond({
status: 200,
body: [],
});

const specificListHandler = interceptor.get('/users').respond({
const specificHandler = interceptor.get('/users').respond({
status: 200,
body: [{ username: 'me' }],
});

specificListHandler.clear();
// Now, requests GET /users will match `genericListHandler` and receive an empty array
specificHandler.clear();
// Now, requests GET /users will match `genericHandler` and receive an empty array

specificListHandler.requests(); // Now empty
console.log(specificHandler.requests); // []
```

</details></td><td width="900px" valign="top"><details open><summary><b>Using a remote interceptor</b></summary>

```ts
const genericListHandler = await interceptor.get('/users').respond({
const genericHandler = await interceptor.get('/users').respond({
status: 200,
body: [],
});

const specificListHandler = await interceptor.get('/users').respond({
const specificHandler = await interceptor.get('/users').respond({
status: 200,
body: [{ username: 'me' }],
});

await specificListHandler.clear();
// Now, requests GET /users will match `genericListHandler` and receive an empty array
await specificHandler.clear();
// Now, requests GET /users will match `genericHandler` and receive an empty array

await specificListHandler.requests(); // Now empty
console.log(specificHandler.requests); // []
```

</details></td></tr></table>

### HTTP `handler.requests()`
### HTTP `handler.requests`

Returns the intercepted requests that matched this handler, along with the responses returned to each of them. This is
useful for testing that the correct requests were made by your application. Learn more about the `request` and
Expand Down Expand Up @@ -1672,12 +1668,11 @@ await fetch(`http://localhost:3000/users/${1}`, {
body: JSON.stringify({ username: 'new' }),
});

const updateRequests = await updateHandler.requests();
expect(updateRequests).toHaveLength(1);
expect(updateRequests[0].pathParams).toEqual({ id: '1' });
expect(updateRequests[0].body).toEqual({ username: 'new' });
expect(updateRequests[0].response.status).toBe(200);
expect(updateRequests[0].response.body).toEqual([{ username: 'new' }]);
expect(updateHandler.requests).toHaveLength(1);
expect(updateHandler.requests[0].pathParams).toEqual({ id: '1' });
expect(updateHandler.requests[0].body).toEqual({ username: 'new' });
expect(updateHandler.requests[0].response.status).toBe(200);
expect(updateHandler.requests[0].response.body).toEqual([{ username: 'new' }]);
```

</details></td><td width="900px" valign="top"><details open><summary><b>Using a remote interceptor</b></summary>
Expand All @@ -1697,12 +1692,11 @@ await fetch(`http://localhost:3000/users/${1}`, {
body: JSON.stringify({ username: 'new' }),
});

const updateRequests = await updateHandler.requests();
expect(updateRequests).toHaveLength(1);
expect(updateRequests[0].pathParams).toEqual({ id: '1' });
expect(updateRequests[0].body).toEqual({ username: 'new' });
expect(updateRequests[0].response.status).toBe(200);
expect(updateRequests[0].response.body).toEqual([{ username: 'new' }]);
expect(updateHandler.requests).toHaveLength(1);
expect(updateHandler.requests[0].pathParams).toEqual({ id: '1' });
expect(updateHandler.requests[0].body).toEqual({ username: 'new' });
expect(updateHandler.requests[0].response.status).toBe(200);
expect(updateHandler.requests[0].response.body).toEqual([{ username: 'new' }]);
```

</details></td></tr></table>
Expand Down
35 changes: 7 additions & 28 deletions docs/wiki/getting‐started‐interceptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ use remote interceptors.
const interceptor = httpInterceptor.create<Schema>({
type: 'local',
baseURL: 'http://localhost:3000',
saveRequests: true, // Allow access to `handler.requests()`
saveRequests: true, // Allow access to `handler.requests`
});
```

Expand All @@ -220,7 +220,7 @@ use remote interceptors.
type: 'remote',
// The interceptor server is at http://localhost:4000
baseURL: 'http://localhost:4000/my-service',
saveRequests: true, // Allow access to `handler.requests()`
saveRequests: true, // Allow access to `handler.requests`
});
```

Expand Down Expand Up @@ -349,40 +349,19 @@ use remote interceptors.

5.2. After your application made requests, check if they are as you expect:

<table><tr><td width="900px" valign="top"><details open><summary><b>Using a local interceptor</b></summary>

```ts
// Your application makes requests...
const requests = handler.requests();
expect(requests).toHaveLength(1);
expect(requests[0].headers.get('authorization')).toBe('Bearer my-token');
expect(requests[0].searchParams.size).toBe(1);
expect(requests[0].searchParams.get('username')).toBe('my');
expect(requests[0].body).toBe(null);
```

</details></td><td width="900px" valign="top"><details open><summary><b>Using a remote interceptor</b></summary>

```ts
// Your application makes requests...
const requests = await handler.requests();
expect(requests).toHaveLength(1);
expect(handler.requests).toHaveLength(1);
expect(requests[0].headers.get('authorization')).toBe('Bearer my-token');
expect(handler.requests[0].headers.get('authorization')).toBe('Bearer my-token');
expect(requests[0].searchParams.size).toBe(1);
expect(requests[0].searchParams.get('username')).toBe('my');
expect(handler.requests[0].searchParams.size).toBe(1);
expect(handler.requests[0].searchParams.get('username')).toBe('my');
expect(requests[0].body).toBe(null);
expect(handler.requests[0].body).toBe(null);
```

</details></td></tr></table>

NOTE: The code above checks the requests manually. This is optional in this example because the
[`with`](apizimicinterceptorhttp#http-handlerwithrestriction) and
[`times`](apizimicinterceptorhttp#http-handlertimes) calls act as a declarative validation, meaning that exactly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ describe('FetchClient > Redirects', () => {

if (isClientSide()) {
// In the browser, the response is opaque and the status is 0. No headers are available.
// @ts-expect-error The status 0 is not in the schema.
expectResponseStatus(firstResponse, 0);
redirectPath = '/redirected';
} else {
Expand Down
2 changes: 1 addition & 1 deletion packages/zimic-fetch/tests/utils/requests.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { expect } from 'vitest';

export function expectResponseStatus<Response extends globalThis.Response, Status extends number>(
export function expectResponseStatus<Response extends globalThis.Response, Status extends Response['status']>(
response: Response,
status: Status,
): asserts response is Extract<Response, { status: Status }> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ describe('Type generation (OpenAPI)', () => {
const inputFilePath = path.join(inputDirectory, `${inputFileNameWithoutExtension}.${fileType}`);

const inputFilePathOrURL = fixtureCase.shouldUseURLAsInput
? joinURL(schemaInterceptor.baseURL(), 'spec', fixtureName)
? joinURL(schemaInterceptor.baseURL, 'spec', fixtureName)
: inputFilePath;

const bufferedInputFileContent = await filesystem.readFile(inputFilePath);
Expand Down Expand Up @@ -315,8 +315,7 @@ describe('Type generation (OpenAPI)', () => {

expect(generatedOutputContent).toBe(expectedOutputContent);

const schemaRequests = getSchemaHandler.requests();
expect(schemaRequests).toHaveLength(fixtureCase.shouldUseURLAsInput ? 1 : 0);
expect(getSchemaHandler.requests).toHaveLength(fixtureCase.shouldUseURLAsInput ? 1 : 0);
});
}
});
Expand Down
Loading

0 comments on commit 8c02f7f

Please sign in to comment.