From 6da6de76e713f9c29fc21bb9db25a61f77c8d786 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 14:50:59 -0300
Subject: [PATCH 01/10] refactor(examples-next-js-pages): simplify setup
---
.../src/__tests__/HomePage.e2e.test.ts | 2 +-
.../src/pages/_app.page.tsx | 23 +++++++---
.../interceptors/InterceptorProvider.tsx | 15 -------
.../with-next-js-pages/src/services/github.ts | 7 +--
.../tests/interceptors/github.ts | 43 +++++++++++++++++++
.../tests/interceptors/github/fixtures.ts | 26 -----------
.../tests/interceptors/github/interceptor.ts | 21 ---------
.../tests/interceptors/index.ts | 39 -----------------
.../tests/interceptors/utils.ts | 6 +++
9 files changed, 68 insertions(+), 114 deletions(-)
delete mode 100644 examples/with-next-js-pages/src/providers/interceptors/InterceptorProvider.tsx
create mode 100644 examples/with-next-js-pages/tests/interceptors/github.ts
delete mode 100644 examples/with-next-js-pages/tests/interceptors/github/fixtures.ts
delete mode 100644 examples/with-next-js-pages/tests/interceptors/github/interceptor.ts
delete mode 100644 examples/with-next-js-pages/tests/interceptors/index.ts
create mode 100644 examples/with-next-js-pages/tests/interceptors/utils.ts
diff --git a/examples/with-next-js-pages/src/__tests__/HomePage.e2e.test.ts b/examples/with-next-js-pages/src/__tests__/HomePage.e2e.test.ts
index c71e1a47..4e731636 100644
--- a/examples/with-next-js-pages/src/__tests__/HomePage.e2e.test.ts
+++ b/examples/with-next-js-pages/src/__tests__/HomePage.e2e.test.ts
@@ -1,6 +1,6 @@
import test, { expect } from '@playwright/test';
-import { githubFixtures } from '../../tests/interceptors/github/fixtures';
+import { githubFixtures } from '../../tests/interceptors/github';
test.describe('Home page', () => {
const { repository } = githubFixtures;
diff --git a/examples/with-next-js-pages/src/pages/_app.page.tsx b/examples/with-next-js-pages/src/pages/_app.page.tsx
index a51fa8ce..7dc2d8a9 100644
--- a/examples/with-next-js-pages/src/pages/_app.page.tsx
+++ b/examples/with-next-js-pages/src/pages/_app.page.tsx
@@ -2,24 +2,33 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import clsx from 'clsx';
import type { AppProps } from 'next/app';
import { Inter } from 'next/font/google';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
-import InterceptorProvider from '../providers/interceptors/InterceptorProvider';
+import { loadInterceptors } from '../../tests/interceptors/utils';
import '../styles/global.css';
const inter = Inter({ subsets: ['latin'] });
+const SHOULD_LOAD_INTERCEPTORS = process.env.NODE_ENV !== 'production';
function App({ Component, pageProps }: AppProps) {
const [queryClient] = useState(() => new QueryClient());
+ const [isLoading, setIsLoading] = useState(SHOULD_LOAD_INTERCEPTORS);
+
+ useEffect(() => {
+ if (SHOULD_LOAD_INTERCEPTORS) {
+ void (async () => {
+ await loadInterceptors();
+ setIsLoading(false);
+ })();
+ }
+ }, []);
return (
-
-
-
-
-
+
+ {!isLoading && }
+
);
}
diff --git a/examples/with-next-js-pages/src/providers/interceptors/InterceptorProvider.tsx b/examples/with-next-js-pages/src/providers/interceptors/InterceptorProvider.tsx
deleted file mode 100644
index 82cbebda..00000000
--- a/examples/with-next-js-pages/src/providers/interceptors/InterceptorProvider.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import { PropsWithChildren, useEffect } from 'react';
-
-import { loadInterceptors } from '../../../tests/interceptors';
-
-type Props = PropsWithChildren;
-
-function InterceptorProvider({ children }: Props) {
- useEffect(() => {
- void loadInterceptors();
- }, []);
-
- return children;
-}
-
-export default InterceptorProvider;
diff --git a/examples/with-next-js-pages/src/services/github.ts b/examples/with-next-js-pages/src/services/github.ts
index ea1d5b24..31a5ea89 100644
--- a/examples/with-next-js-pages/src/services/github.ts
+++ b/examples/with-next-js-pages/src/services/github.ts
@@ -1,9 +1,8 @@
import type { JSONValue } from 'zimic';
-import { waitForLoadedInterceptors } from '../../tests/interceptors';
import environment from '../config/environment';
-const BASE_URL = environment.GITHUB_API_BASE_URL;
+const GITHUB_API_BASE_URL = environment.GITHUB_API_BASE_URL;
const CACHE_STRATEGY = process.env.NODE_ENV === 'production' ? 'default' : 'no-store';
export type GitHubRepository = JSONValue<{
@@ -15,13 +14,11 @@ export type GitHubRepository = JSONValue<{
}>;
export async function fetchGitHubRepository(ownerName: string, repositoryName: string) {
- await waitForLoadedInterceptors();
-
try {
const sanitizedOwnerName = encodeURIComponent(ownerName);
const sanitizedRepositoryName = encodeURIComponent(repositoryName);
- const response = await fetch(`${BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
+ const response = await fetch(`${GITHUB_API_BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
cache: CACHE_STRATEGY,
});
diff --git a/examples/with-next-js-pages/tests/interceptors/github.ts b/examples/with-next-js-pages/tests/interceptors/github.ts
new file mode 100644
index 00000000..16da1a90
--- /dev/null
+++ b/examples/with-next-js-pages/tests/interceptors/github.ts
@@ -0,0 +1,43 @@
+import { http } from 'zimic/interceptor';
+
+import environment from '../../src/config/environment';
+import { GitHubRepository } from '../../src/services/github';
+
+const githubInterceptor = http.createInterceptor<{
+ '/repos/:owner/:name': {
+ GET: {
+ response: {
+ 200: { body: GitHubRepository };
+ 404: { body: { message: string } };
+ 500: { body: { message: string } };
+ };
+ };
+ };
+}>({
+ type: 'local',
+ baseURL: environment.GITHUB_API_BASE_URL,
+});
+
+export const githubFixtures = {
+ repository: {
+ id: 1,
+ name: 'example',
+ full_name: 'owner/example',
+ html_url: 'https://github.com/owner/example',
+ owner: { login: 'owner' },
+ } satisfies GitHubRepository,
+
+ apply() {
+ githubInterceptor.get('/repos/:owner/:name').respond({
+ status: 404,
+ body: { message: 'Not Found' },
+ });
+
+ githubInterceptor.get(`/repos/${this.repository.owner.login}/${this.repository.name}`).respond({
+ status: 200,
+ body: this.repository,
+ });
+ },
+};
+
+export default githubInterceptor;
diff --git a/examples/with-next-js-pages/tests/interceptors/github/fixtures.ts b/examples/with-next-js-pages/tests/interceptors/github/fixtures.ts
deleted file mode 100644
index 6332e3ec..00000000
--- a/examples/with-next-js-pages/tests/interceptors/github/fixtures.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { GitHubRepository } from '../../../src/services/github';
-import githubInterceptor from './interceptor';
-
-export const githubFixtures = {
- repository: {
- id: 1,
- name: 'example',
- full_name: 'owner/example',
- html_url: 'https://github.com/owner/example',
- owner: { login: 'owner' },
- },
-} satisfies Record;
-
-export function applyGitHubFixtures() {
- const { repository } = githubFixtures;
-
- githubInterceptor.get('/repos/:owner/:name').respond({
- status: 404,
- body: { message: 'Not Found' },
- });
-
- githubInterceptor.get(`/repos/${repository.owner.login}/${repository.name}`).respond({
- status: 200,
- body: repository,
- });
-}
diff --git a/examples/with-next-js-pages/tests/interceptors/github/interceptor.ts b/examples/with-next-js-pages/tests/interceptors/github/interceptor.ts
deleted file mode 100644
index a7c6ebce..00000000
--- a/examples/with-next-js-pages/tests/interceptors/github/interceptor.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { http } from 'zimic/interceptor';
-
-import environment from '../../../src/config/environment';
-import { GitHubRepository } from '../../../src/services/github';
-
-const githubInterceptor = http.createInterceptor<{
- '/repos/:owner/:name': {
- GET: {
- response: {
- 200: { body: GitHubRepository };
- 404: { body: { message: string } };
- 500: { body: { message: string } };
- };
- };
- };
-}>({
- type: 'local',
- baseURL: environment.GITHUB_API_BASE_URL,
-});
-
-export default githubInterceptor;
diff --git a/examples/with-next-js-pages/tests/interceptors/index.ts b/examples/with-next-js-pages/tests/interceptors/index.ts
deleted file mode 100644
index ab60ec74..00000000
--- a/examples/with-next-js-pages/tests/interceptors/index.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { applyGitHubFixtures } from './github/fixtures';
-import githubInterceptor from './github/interceptor';
-
-export let markInterceptorsAsLoaded: (() => void) | undefined;
-let areInterceptorsLoaded = false;
-
-const loadInterceptorsPromise = new Promise((resolve) => {
- markInterceptorsAsLoaded = () => {
- areInterceptorsLoaded = true;
- resolve();
- };
-});
-
-export async function waitForLoadedInterceptors() {
- if (process.env.NODE_ENV === 'production' || areInterceptorsLoaded) {
- return;
- }
-
- await loadInterceptorsPromise;
-}
-
-export async function loadInterceptors() {
- if (process.env.NODE_ENV === 'production' || areInterceptorsLoaded) {
- return;
- }
-
- await githubInterceptor.start();
- applyGitHubFixtures();
-
- markInterceptorsAsLoaded?.();
-}
-
-export async function stopInterceptors() {
- if (process.env.NODE_ENV === 'production') {
- return;
- }
-
- await githubInterceptor.stop();
-}
diff --git a/examples/with-next-js-pages/tests/interceptors/utils.ts b/examples/with-next-js-pages/tests/interceptors/utils.ts
new file mode 100644
index 00000000..5d6d2d5d
--- /dev/null
+++ b/examples/with-next-js-pages/tests/interceptors/utils.ts
@@ -0,0 +1,6 @@
+import githubInterceptor, { githubFixtures } from './github';
+
+export async function loadInterceptors() {
+ await githubInterceptor.start();
+ githubFixtures.apply();
+}
From d08f164a4052b6fb5f37582b1c555294afabadf4 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 15:00:47 -0300
Subject: [PATCH 02/10] refactor(examples-next-js-app): improve setup
---
examples/with-next-js-app/.env.development | 3 +-
examples/with-next-js-app/.env.test | 3 ++
examples/with-next-js-app/README.md | 2 +-
examples/with-next-js-app/package.json | 5 ++-
.../with-next-js-app/playwright.config.ts | 2 +-
.../src/__tests__/HomePage.e2e.test.ts | 2 +-
examples/with-next-js-app/src/app/layout.tsx | 9 +---
examples/with-next-js-app/src/app/page.tsx | 2 +-
.../src/config/environment.ts | 2 +-
.../interceptors/InterceptorProvider.tsx | 17 --------
.../with-next-js-app/src/services/github.ts | 7 +--
.../tests/interceptors/github.ts | 43 +++++++++++++++++++
.../tests/interceptors/github/fixtures.ts | 26 -----------
.../tests/interceptors/github/interceptor.ts | 21 ---------
.../tests/interceptors/index.ts | 39 -----------------
.../tests/interceptors/scripts/load.ts | 23 ++++++++++
16 files changed, 83 insertions(+), 123 deletions(-)
delete mode 100644 examples/with-next-js-app/src/providers/interceptors/InterceptorProvider.tsx
create mode 100644 examples/with-next-js-app/tests/interceptors/github.ts
delete mode 100644 examples/with-next-js-app/tests/interceptors/github/fixtures.ts
delete mode 100644 examples/with-next-js-app/tests/interceptors/github/interceptor.ts
delete mode 100644 examples/with-next-js-app/tests/interceptors/index.ts
create mode 100644 examples/with-next-js-app/tests/interceptors/scripts/load.ts
diff --git a/examples/with-next-js-app/.env.development b/examples/with-next-js-app/.env.development
index 937666de..a0b79abb 100644
--- a/examples/with-next-js-app/.env.development
+++ b/examples/with-next-js-app/.env.development
@@ -1,4 +1,3 @@
NODE_ENV=development
-ZIMIC_SERVER_URL=http://localhost:3005
-NEXT_PUBLIC_GITHUB_API_BASE_URL=$ZIMIC_SERVER_URL/github
+GITHUB_API_BASE_URL=https://api.github.com
diff --git a/examples/with-next-js-app/.env.test b/examples/with-next-js-app/.env.test
index 2c87534c..062bada0 100644
--- a/examples/with-next-js-app/.env.test
+++ b/examples/with-next-js-app/.env.test
@@ -1 +1,4 @@
NODE_ENV=test
+
+ZIMIC_SERVER_URL=http://localhost:3005
+GITHUB_API_BASE_URL=$ZIMIC_SERVER_URL/github
diff --git a/examples/with-next-js-app/README.md b/examples/with-next-js-app/README.md
index 00c16bbe..39de5406 100644
--- a/examples/with-next-js-app/README.md
+++ b/examples/with-next-js-app/README.md
@@ -62,7 +62,7 @@ GitHub API and simulate a test case where the repository is found and another wh
1. Start the application:
```bash
- pnpm run dev
+ pnpm run dev:mock
```
After started, the application will be available at [http://localhost:3004](http://localhost:3004).
diff --git a/examples/with-next-js-app/package.json b/examples/with-next-js-app/package.json
index cdff4192..e7f63288 100644
--- a/examples/with-next-js-app/package.json
+++ b/examples/with-next-js-app/package.json
@@ -3,7 +3,9 @@
"version": "0.0.0",
"private": false,
"scripts": {
- "dev": "dotenv -c development -- zimic server start --port 3005 --ephemeral -- next dev --turbo --port 3004",
+ "dev": "dotenv -c development -- next dev --turbo --port 3004",
+ "dev:mock": "dotenv -c test -- zimic server start --port 3005 --ephemeral -- pnpm dev:load-interceptors -- pnpm dev",
+ "dev:load-interceptors": "tsx ./tests/interceptors/scripts/load.ts",
"test": "dotenv -c test -- dotenv -c development -- playwright test",
"test:turbo": "pnpm run test",
"types:check": "tsc --noEmit",
@@ -25,6 +27,7 @@
"@types/react-dom": "^18.3.0",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.1",
+ "tsx": "^4.7.0",
"typescript": "^5.4.3",
"zimic": "latest"
}
diff --git a/examples/with-next-js-app/playwright.config.ts b/examples/with-next-js-app/playwright.config.ts
index 0b20d779..4d1022bc 100644
--- a/examples/with-next-js-app/playwright.config.ts
+++ b/examples/with-next-js-app/playwright.config.ts
@@ -33,7 +33,7 @@ export default defineConfig({
],
webServer: {
- command: 'pnpm run dev',
+ command: 'pnpm run dev:mock',
port: 3004,
stdout: 'pipe',
stderr: 'pipe',
diff --git a/examples/with-next-js-app/src/__tests__/HomePage.e2e.test.ts b/examples/with-next-js-app/src/__tests__/HomePage.e2e.test.ts
index c71e1a47..4e731636 100644
--- a/examples/with-next-js-app/src/__tests__/HomePage.e2e.test.ts
+++ b/examples/with-next-js-app/src/__tests__/HomePage.e2e.test.ts
@@ -1,6 +1,6 @@
import test, { expect } from '@playwright/test';
-import { githubFixtures } from '../../tests/interceptors/github/fixtures';
+import { githubFixtures } from '../../tests/interceptors/github';
test.describe('Home page', () => {
const { repository } = githubFixtures;
diff --git a/examples/with-next-js-app/src/app/layout.tsx b/examples/with-next-js-app/src/app/layout.tsx
index 58f9a5c1..1e6a44d1 100644
--- a/examples/with-next-js-app/src/app/layout.tsx
+++ b/examples/with-next-js-app/src/app/layout.tsx
@@ -3,9 +3,6 @@ import { Metadata } from 'next';
import { Inter } from 'next/font/google';
import { PropsWithChildren } from 'react';
-import { loadInterceptors } from '../../tests/interceptors';
-import InterceptorProvider from '../providers/interceptors/InterceptorProvider';
-
import '../styles/global.css';
const inter = Inter({ subsets: ['latin'] });
@@ -15,13 +12,11 @@ export const metadata: Metadata = {
description: 'Generated by create-next-app',
};
-async function RootLayout({ children }: PropsWithChildren) {
- await loadInterceptors();
-
+function RootLayout({ children }: PropsWithChildren) {
return (
- {children}
+ {children}
);
diff --git a/examples/with-next-js-app/src/app/page.tsx b/examples/with-next-js-app/src/app/page.tsx
index f92e8c85..8d4ed0d7 100644
--- a/examples/with-next-js-app/src/app/page.tsx
+++ b/examples/with-next-js-app/src/app/page.tsx
@@ -22,7 +22,7 @@ function HomePage({ searchParams }: Props) {
{shouldFetchRepository && (
- Loading...
}>
+ Loading...}>
)}
diff --git a/examples/with-next-js-app/src/config/environment.ts b/examples/with-next-js-app/src/config/environment.ts
index 329f426c..a1dfc20b 100644
--- a/examples/with-next-js-app/src/config/environment.ts
+++ b/examples/with-next-js-app/src/config/environment.ts
@@ -1,5 +1,5 @@
const environment = {
- GITHUB_API_BASE_URL: process.env.NEXT_PUBLIC_GITHUB_API_BASE_URL ?? '',
+ GITHUB_API_BASE_URL: process.env.GITHUB_API_BASE_URL ?? '',
};
export default environment;
diff --git a/examples/with-next-js-app/src/providers/interceptors/InterceptorProvider.tsx b/examples/with-next-js-app/src/providers/interceptors/InterceptorProvider.tsx
deleted file mode 100644
index 257f4df8..00000000
--- a/examples/with-next-js-app/src/providers/interceptors/InterceptorProvider.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-'use client';
-
-import { PropsWithChildren, useEffect } from 'react';
-
-import { loadInterceptors } from '../../../tests/interceptors';
-
-type Props = PropsWithChildren;
-
-function InterceptorProvider({ children }: Props) {
- useEffect(() => {
- void loadInterceptors();
- }, []);
-
- return children;
-}
-
-export default InterceptorProvider;
diff --git a/examples/with-next-js-app/src/services/github.ts b/examples/with-next-js-app/src/services/github.ts
index ea1d5b24..31a5ea89 100644
--- a/examples/with-next-js-app/src/services/github.ts
+++ b/examples/with-next-js-app/src/services/github.ts
@@ -1,9 +1,8 @@
import type { JSONValue } from 'zimic';
-import { waitForLoadedInterceptors } from '../../tests/interceptors';
import environment from '../config/environment';
-const BASE_URL = environment.GITHUB_API_BASE_URL;
+const GITHUB_API_BASE_URL = environment.GITHUB_API_BASE_URL;
const CACHE_STRATEGY = process.env.NODE_ENV === 'production' ? 'default' : 'no-store';
export type GitHubRepository = JSONValue<{
@@ -15,13 +14,11 @@ export type GitHubRepository = JSONValue<{
}>;
export async function fetchGitHubRepository(ownerName: string, repositoryName: string) {
- await waitForLoadedInterceptors();
-
try {
const sanitizedOwnerName = encodeURIComponent(ownerName);
const sanitizedRepositoryName = encodeURIComponent(repositoryName);
- const response = await fetch(`${BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
+ const response = await fetch(`${GITHUB_API_BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
cache: CACHE_STRATEGY,
});
diff --git a/examples/with-next-js-app/tests/interceptors/github.ts b/examples/with-next-js-app/tests/interceptors/github.ts
new file mode 100644
index 00000000..aa554a87
--- /dev/null
+++ b/examples/with-next-js-app/tests/interceptors/github.ts
@@ -0,0 +1,43 @@
+import { http } from 'zimic/interceptor';
+
+import environment from '../../src/config/environment';
+import { GitHubRepository } from '../../src/services/github';
+
+const githubInterceptor = http.createInterceptor<{
+ '/repos/:owner/:name': {
+ GET: {
+ response: {
+ 200: { body: GitHubRepository };
+ 404: { body: { message: string } };
+ 500: { body: { message: string } };
+ };
+ };
+ };
+}>({
+ type: 'remote',
+ baseURL: environment.GITHUB_API_BASE_URL,
+});
+
+export const githubFixtures = {
+ repository: {
+ id: 1,
+ name: 'example',
+ full_name: 'owner/example',
+ html_url: 'https://github.com/owner/example',
+ owner: { login: 'owner' },
+ } satisfies GitHubRepository,
+
+ async apply() {
+ await githubInterceptor.get('/repos/:owner/:name').respond({
+ status: 404,
+ body: { message: 'Not Found' },
+ });
+
+ await githubInterceptor.get(`/repos/${this.repository.owner.login}/${this.repository.name}`).respond({
+ status: 200,
+ body: this.repository,
+ });
+ },
+};
+
+export default githubInterceptor;
diff --git a/examples/with-next-js-app/tests/interceptors/github/fixtures.ts b/examples/with-next-js-app/tests/interceptors/github/fixtures.ts
deleted file mode 100644
index 66893fb7..00000000
--- a/examples/with-next-js-app/tests/interceptors/github/fixtures.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { GitHubRepository } from '../../../src/services/github';
-import githubInterceptor from './interceptor';
-
-export const githubFixtures = {
- repository: {
- id: 1,
- name: 'example',
- full_name: 'owner/example',
- html_url: 'https://github.com/owner/example',
- owner: { login: 'owner' },
- },
-} satisfies Record;
-
-export async function applyGitHubFixtures() {
- const { repository } = githubFixtures;
-
- await githubInterceptor.get('/repos/:owner/:name').respond({
- status: 404,
- body: { message: 'Not Found' },
- });
-
- await githubInterceptor.get(`/repos/${repository.owner.login}/${repository.name}`).respond({
- status: 200,
- body: repository,
- });
-}
diff --git a/examples/with-next-js-app/tests/interceptors/github/interceptor.ts b/examples/with-next-js-app/tests/interceptors/github/interceptor.ts
deleted file mode 100644
index 7e7cc41f..00000000
--- a/examples/with-next-js-app/tests/interceptors/github/interceptor.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { http } from 'zimic/interceptor';
-
-import environment from '../../../src/config/environment';
-import { GitHubRepository } from '../../../src/services/github';
-
-const githubInterceptor = http.createInterceptor<{
- '/repos/:owner/:name': {
- GET: {
- response: {
- 200: { body: GitHubRepository };
- 404: { body: { message: string } };
- 500: { body: { message: string } };
- };
- };
- };
-}>({
- type: 'remote',
- baseURL: environment.GITHUB_API_BASE_URL,
-});
-
-export default githubInterceptor;
diff --git a/examples/with-next-js-app/tests/interceptors/index.ts b/examples/with-next-js-app/tests/interceptors/index.ts
deleted file mode 100644
index 5a4d3d31..00000000
--- a/examples/with-next-js-app/tests/interceptors/index.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { applyGitHubFixtures } from './github/fixtures';
-import githubInterceptor from './github/interceptor';
-
-export let markInterceptorsAsLoaded: (() => void) | undefined;
-let areInterceptorsLoaded = false;
-
-const loadInterceptorsPromise = new Promise((resolve) => {
- markInterceptorsAsLoaded = () => {
- areInterceptorsLoaded = true;
- resolve();
- };
-});
-
-export async function waitForLoadedInterceptors() {
- if (process.env.NODE_ENV === 'production' || areInterceptorsLoaded) {
- return;
- }
-
- await loadInterceptorsPromise;
-}
-
-export async function loadInterceptors() {
- if (process.env.NODE_ENV === 'production' || areInterceptorsLoaded) {
- return;
- }
-
- await githubInterceptor.start();
- await applyGitHubFixtures();
-
- markInterceptorsAsLoaded?.();
-}
-
-export async function stopInterceptors() {
- if (process.env.NODE_ENV === 'production') {
- return;
- }
-
- await githubInterceptor.stop();
-}
diff --git a/examples/with-next-js-app/tests/interceptors/scripts/load.ts b/examples/with-next-js-app/tests/interceptors/scripts/load.ts
new file mode 100644
index 00000000..519ca364
--- /dev/null
+++ b/examples/with-next-js-app/tests/interceptors/scripts/load.ts
@@ -0,0 +1,23 @@
+import { runCommand } from 'zimic/server';
+
+import githubInterceptor, { githubFixtures } from '../github';
+
+async function runOnReadyCommand() {
+ const commandDivisorIndex = process.argv.indexOf('--');
+ if (commandDivisorIndex !== -1) {
+ const [command, ...commandArguments] = process.argv.slice(commandDivisorIndex + 1);
+ await runCommand(command, commandArguments);
+ }
+}
+
+async function loadInterceptors() {
+ await githubInterceptor.start();
+ await githubFixtures.apply();
+ console.log('Interceptors loaded.');
+
+ await runOnReadyCommand();
+
+ await githubInterceptor.stop();
+}
+
+void loadInterceptors();
From 5031c51dc182d9c5abc062ea44fa6378fb55f9db Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 15:03:00 -0300
Subject: [PATCH 03/10] refactor(examples-playwright): simplify setup
---
examples/with-playwright/.env.development | 2 +-
examples/with-playwright/.env.test | 2 +-
examples/with-playwright/README.md | 2 +-
examples/with-playwright/package.json | 8 ++--
examples/with-playwright/playwright.config.ts | 2 +-
.../src/app/__tests__/HomePage.e2e.test.ts | 4 +-
examples/with-playwright/src/app/page.tsx | 2 +-
.../with-playwright/src/config/environment.ts | 2 +-
.../with-playwright/src/services/github.ts | 4 +-
.../tests/interceptors/github.ts | 43 +++++++++++++++++++
.../tests/interceptors/github/fixtures.ts | 26 -----------
.../tests/interceptors/github/interceptor.ts | 21 ---------
.../tests/interceptors/scripts/load.ts | 23 ++++++++++
examples/with-playwright/tests/setup.ts | 13 ------
pnpm-lock.yaml | 15 +++----
15 files changed, 87 insertions(+), 82 deletions(-)
create mode 100644 examples/with-playwright/tests/interceptors/github.ts
delete mode 100644 examples/with-playwright/tests/interceptors/github/fixtures.ts
delete mode 100644 examples/with-playwright/tests/interceptors/github/interceptor.ts
create mode 100644 examples/with-playwright/tests/interceptors/scripts/load.ts
delete mode 100644 examples/with-playwright/tests/setup.ts
diff --git a/examples/with-playwright/.env.development b/examples/with-playwright/.env.development
index 5973dabb..a0b79abb 100644
--- a/examples/with-playwright/.env.development
+++ b/examples/with-playwright/.env.development
@@ -1,3 +1,3 @@
NODE_ENV=development
-NEXT_PUBLIC_GITHUB_API_BASE_URL=https://api.github.com
+GITHUB_API_BASE_URL=https://api.github.com
diff --git a/examples/with-playwright/.env.test b/examples/with-playwright/.env.test
index 1031b27b..fa1d900b 100644
--- a/examples/with-playwright/.env.test
+++ b/examples/with-playwright/.env.test
@@ -1,4 +1,4 @@
NODE_ENV=test
ZIMIC_SERVER_URL=http://localhost:3003
-NEXT_PUBLIC_GITHUB_API_BASE_URL=$ZIMIC_SERVER_URL/github
+GITHUB_API_BASE_URL=$ZIMIC_SERVER_URL/github
diff --git a/examples/with-playwright/README.md b/examples/with-playwright/README.md
index 2c2b5e3c..7054229b 100644
--- a/examples/with-playwright/README.md
+++ b/examples/with-playwright/README.md
@@ -62,7 +62,7 @@ GitHub API and simulate a test case where the repository is found and another wh
1. Start the application:
```bash
- pnpm run dev:test
+ pnpm run dev:mock
```
After started, it will be available at [http://localhost:3002](http://localhost:3002).
diff --git a/examples/with-playwright/package.json b/examples/with-playwright/package.json
index 83af907d..3f7e1df9 100644
--- a/examples/with-playwright/package.json
+++ b/examples/with-playwright/package.json
@@ -3,9 +3,10 @@
"version": "0.0.0",
"private": false,
"scripts": {
- "dev": "dotenv -c development -- next dev --port 3002",
- "dev:test": "dotenv -c test -- pnpm dev",
- "test": "dotenv -c test -- dotenv -c development -- zimic server start --port 3003 --ephemeral -- playwright test",
+ "dev": "dotenv -c development -- next dev --turbo --port 3002",
+ "dev:mock": "dotenv -c test -- zimic server start --port 3003 --ephemeral -- pnpm dev:load-interceptors -- pnpm dev",
+ "dev:load-interceptors": "tsx ./tests/interceptors/scripts/load.ts",
+ "test": "dotenv -c test -- dotenv -c development -- playwright test",
"test:turbo": "pnpm run test",
"types:check": "tsc --noEmit",
"deps:install-playwright": "pnpm playwright install chromium",
@@ -25,6 +26,7 @@
"@types/react-dom": "^18.3.0",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.1",
+ "tsx": "^4.7.0",
"typescript": "^5.4.3",
"zimic": "latest"
}
diff --git a/examples/with-playwright/playwright.config.ts b/examples/with-playwright/playwright.config.ts
index 6975de5c..43814990 100644
--- a/examples/with-playwright/playwright.config.ts
+++ b/examples/with-playwright/playwright.config.ts
@@ -34,7 +34,7 @@ export default defineConfig({
],
webServer: {
- command: 'pnpm run dev:test',
+ command: 'pnpm run dev:mock',
url: 'http://localhost:3002',
stdout: 'pipe',
stderr: 'pipe',
diff --git a/examples/with-playwright/src/app/__tests__/HomePage.e2e.test.ts b/examples/with-playwright/src/app/__tests__/HomePage.e2e.test.ts
index 65b7e273..4d2b2b51 100644
--- a/examples/with-playwright/src/app/__tests__/HomePage.e2e.test.ts
+++ b/examples/with-playwright/src/app/__tests__/HomePage.e2e.test.ts
@@ -1,8 +1,6 @@
import test, { expect } from '@playwright/test';
-import { githubFixtures } from '../../../tests/interceptors/github/fixtures';
-
-import '../../../tests/setup';
+import { githubFixtures } from '../../../tests/interceptors/github';
test.describe('Home page', () => {
const { repository } = githubFixtures;
diff --git a/examples/with-playwright/src/app/page.tsx b/examples/with-playwright/src/app/page.tsx
index 1fa7b3d8..610adc15 100644
--- a/examples/with-playwright/src/app/page.tsx
+++ b/examples/with-playwright/src/app/page.tsx
@@ -19,7 +19,7 @@ function HomePage({ searchParams }: Props) {
{shouldFetchRepository && (
- Loading...}>
+ Loading...}>
)}
diff --git a/examples/with-playwright/src/config/environment.ts b/examples/with-playwright/src/config/environment.ts
index 329f426c..a1dfc20b 100644
--- a/examples/with-playwright/src/config/environment.ts
+++ b/examples/with-playwright/src/config/environment.ts
@@ -1,5 +1,5 @@
const environment = {
- GITHUB_API_BASE_URL: process.env.NEXT_PUBLIC_GITHUB_API_BASE_URL ?? '',
+ GITHUB_API_BASE_URL: process.env.GITHUB_API_BASE_URL ?? '',
};
export default environment;
diff --git a/examples/with-playwright/src/services/github.ts b/examples/with-playwright/src/services/github.ts
index 025f7af9..f8a77170 100644
--- a/examples/with-playwright/src/services/github.ts
+++ b/examples/with-playwright/src/services/github.ts
@@ -3,7 +3,7 @@ import type { JSONValue } from 'zimic';
import environment from '../config/environment';
-const BASE_URL = environment.GITHUB_API_BASE_URL;
+const GITHUB_API_BASE_URL = environment.GITHUB_API_BASE_URL;
const CACHE_STRATEGY = process.env.NODE_ENV === 'production' ? 'default' : 'no-store';
export type GitHubRepository = JSONValue<{
@@ -19,7 +19,7 @@ export const fetchGitHubRepository = cache(async (ownerName: string, repositoryN
const sanitizedOwnerName = encodeURIComponent(ownerName);
const sanitizedRepositoryName = encodeURIComponent(repositoryName);
- const response = await fetch(`${BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
+ const response = await fetch(`${GITHUB_API_BASE_URL}/repos/${sanitizedOwnerName}/${sanitizedRepositoryName}`, {
cache: CACHE_STRATEGY,
});
diff --git a/examples/with-playwright/tests/interceptors/github.ts b/examples/with-playwright/tests/interceptors/github.ts
new file mode 100644
index 00000000..aa554a87
--- /dev/null
+++ b/examples/with-playwright/tests/interceptors/github.ts
@@ -0,0 +1,43 @@
+import { http } from 'zimic/interceptor';
+
+import environment from '../../src/config/environment';
+import { GitHubRepository } from '../../src/services/github';
+
+const githubInterceptor = http.createInterceptor<{
+ '/repos/:owner/:name': {
+ GET: {
+ response: {
+ 200: { body: GitHubRepository };
+ 404: { body: { message: string } };
+ 500: { body: { message: string } };
+ };
+ };
+ };
+}>({
+ type: 'remote',
+ baseURL: environment.GITHUB_API_BASE_URL,
+});
+
+export const githubFixtures = {
+ repository: {
+ id: 1,
+ name: 'example',
+ full_name: 'owner/example',
+ html_url: 'https://github.com/owner/example',
+ owner: { login: 'owner' },
+ } satisfies GitHubRepository,
+
+ async apply() {
+ await githubInterceptor.get('/repos/:owner/:name').respond({
+ status: 404,
+ body: { message: 'Not Found' },
+ });
+
+ await githubInterceptor.get(`/repos/${this.repository.owner.login}/${this.repository.name}`).respond({
+ status: 200,
+ body: this.repository,
+ });
+ },
+};
+
+export default githubInterceptor;
diff --git a/examples/with-playwright/tests/interceptors/github/fixtures.ts b/examples/with-playwright/tests/interceptors/github/fixtures.ts
deleted file mode 100644
index 66893fb7..00000000
--- a/examples/with-playwright/tests/interceptors/github/fixtures.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import { GitHubRepository } from '../../../src/services/github';
-import githubInterceptor from './interceptor';
-
-export const githubFixtures = {
- repository: {
- id: 1,
- name: 'example',
- full_name: 'owner/example',
- html_url: 'https://github.com/owner/example',
- owner: { login: 'owner' },
- },
-} satisfies Record;
-
-export async function applyGitHubFixtures() {
- const { repository } = githubFixtures;
-
- await githubInterceptor.get('/repos/:owner/:name').respond({
- status: 404,
- body: { message: 'Not Found' },
- });
-
- await githubInterceptor.get(`/repos/${repository.owner.login}/${repository.name}`).respond({
- status: 200,
- body: repository,
- });
-}
diff --git a/examples/with-playwright/tests/interceptors/github/interceptor.ts b/examples/with-playwright/tests/interceptors/github/interceptor.ts
deleted file mode 100644
index 7e7cc41f..00000000
--- a/examples/with-playwright/tests/interceptors/github/interceptor.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { http } from 'zimic/interceptor';
-
-import environment from '../../../src/config/environment';
-import { GitHubRepository } from '../../../src/services/github';
-
-const githubInterceptor = http.createInterceptor<{
- '/repos/:owner/:name': {
- GET: {
- response: {
- 200: { body: GitHubRepository };
- 404: { body: { message: string } };
- 500: { body: { message: string } };
- };
- };
- };
-}>({
- type: 'remote',
- baseURL: environment.GITHUB_API_BASE_URL,
-});
-
-export default githubInterceptor;
diff --git a/examples/with-playwright/tests/interceptors/scripts/load.ts b/examples/with-playwright/tests/interceptors/scripts/load.ts
new file mode 100644
index 00000000..519ca364
--- /dev/null
+++ b/examples/with-playwright/tests/interceptors/scripts/load.ts
@@ -0,0 +1,23 @@
+import { runCommand } from 'zimic/server';
+
+import githubInterceptor, { githubFixtures } from '../github';
+
+async function runOnReadyCommand() {
+ const commandDivisorIndex = process.argv.indexOf('--');
+ if (commandDivisorIndex !== -1) {
+ const [command, ...commandArguments] = process.argv.slice(commandDivisorIndex + 1);
+ await runCommand(command, commandArguments);
+ }
+}
+
+async function loadInterceptors() {
+ await githubInterceptor.start();
+ await githubFixtures.apply();
+ console.log('Interceptors loaded.');
+
+ await runOnReadyCommand();
+
+ await githubInterceptor.stop();
+}
+
+void loadInterceptors();
diff --git a/examples/with-playwright/tests/setup.ts b/examples/with-playwright/tests/setup.ts
deleted file mode 100644
index b85441e1..00000000
--- a/examples/with-playwright/tests/setup.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import test from '@playwright/test';
-
-import { applyGitHubFixtures } from './interceptors/github/fixtures';
-import githubInterceptor from './interceptors/github/interceptor';
-
-test.beforeAll(async () => {
- await githubInterceptor.start();
- await applyGitHubFixtures();
-});
-
-test.afterAll(async () => {
- await githubInterceptor.stop();
-});
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 647133ce..3fffd33a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -207,6 +207,9 @@ importers:
tailwindcss:
specifier: ^3.4.1
version: 3.4.3
+ tsx:
+ specifier: ^4.7.0
+ version: 4.7.0
typescript:
specifier: ^5.4.3
version: 5.4.5
@@ -296,6 +299,9 @@ importers:
tailwindcss:
specifier: ^3.4.1
version: 3.4.3
+ tsx:
+ specifier: ^4.7.0
+ version: 4.7.0
typescript:
specifier: ^5.4.3
version: 5.4.3
@@ -3071,9 +3077,6 @@ packages:
resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==}
engines: {node: '>= 0.4'}
- get-tsconfig@4.7.2:
- resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
-
get-tsconfig@4.7.3:
resolution: {integrity: sha512-ZvkrzoUA0PQZM6fy6+/Hce561s+faD1rsNwhnO5FelNjyy7EMGJ3Rz1AQ8GYDWjhRs/7dBLOEJvhK8MiEJOAFg==}
@@ -8086,10 +8089,6 @@ snapshots:
es-errors: 1.3.0
get-intrinsic: 1.2.4
- get-tsconfig@4.7.2:
- dependencies:
- resolve-pkg-maps: 1.0.0
-
get-tsconfig@4.7.3:
dependencies:
resolve-pkg-maps: 1.0.0
@@ -10271,7 +10270,7 @@ snapshots:
tsx@4.7.0:
dependencies:
esbuild: 0.19.11
- get-tsconfig: 4.7.2
+ get-tsconfig: 4.7.3
optionalDependencies:
fsevents: 2.3.3
From 9b558a65352158efc73b1c1c3241b3272530370e Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 15:36:51 -0300
Subject: [PATCH 04/10] docs(#zimic): sync server start help message
---
README.md | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
index 3d31c8ef..811f811e 100644
--- a/README.md
+++ b/README.md
@@ -1807,13 +1807,21 @@ Positionals:
[string]
Options:
- -h, --hostname The hostname to start the server on.
+ --help Show help [boolean]
+ --version Show version number [boolean]
+ -h, --hostname The hostname to start the server on.
[string] [default: "localhost"]
- -p, --port The port to start the server on. [number]
- -e, --ephemeral Whether the server should stop automatically after the
- on-ready command finishes. If no on-ready command is provided
- and ephemeral is true, the server will stop immediately after
- starting. [boolean] [default: false]
+ -p, --port The port to start the server on. [number]
+ -e, --ephemeral Whether the server should stop automatically
+ after the on-ready command finishes. If no
+ on-ready command is provided and ephemeral is
+ true, the server will stop immediately after
+ starting. [boolean] [default: false]
+ -l, --log-unhandled-requests Whether to log a warning when no interceptors
+ were found for the base URL of a request. If an
+ interceptor was matched, the logging behavior
+ for that base URL is configured in the
+ interceptor itself. [boolean]
```
You can use this command to start an independent server:
From e4789406e59154979457e98a711894e0d591b8bb Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 15:49:42 -0300
Subject: [PATCH 05/10] feat(#zimic): provide server exports
---
README.md | 24 +++++++++++
.../v0/interceptor/exports/exports.test.ts | 22 ++++++++++
packages/zimic/package.json | 6 +++
packages/zimic/server.d.ts | 1 +
.../src/cli/__tests__/server.cli.node.test.ts | 4 +-
packages/zimic/src/cli/cli.ts | 1 -
packages/zimic/src/cli/server/start.ts | 19 +++------
.../__tests__/HttpInterceptor.node.test.ts | 4 +-
.../errors/NotStartedHttpInterceptorError.ts | 2 +-
.../errors/UnknownHttpInterceptorPlatform.ts | 2 +-
.../errors/UnknownHttpInterceptorTypeError.ts | 2 +-
.../HttpinterceptorWorker.node.test.ts | 4 +-
.../errors/UnregisteredServiceWorkerError.ts | 2 +-
.../__tests__/HttpRequestHandler.node.test.ts | 4 +-
.../errors/NoResponseDefinitionError.ts | 2 +-
.../interceptor/server/InterceptorServer.ts | 20 ++++++----
.../__tests__/InterceptorServer.node.test.ts | 10 +++--
.../zimic/src/interceptor/server/constants.ts | 2 +
.../NotStartedInterceptorServerError.ts | 8 ++--
.../zimic/src/interceptor/server/factory.ts | 15 +++++++
.../zimic/src/interceptor/server/index.ts | 11 +++++
.../src/interceptor/server/types/options.ts | 26 ++++++++++++
.../src/interceptor/server/types/public.ts | 40 +++++++++++++++++++
packages/zimic/src/utils/http.ts | 4 +-
packages/zimic/src/utils/processes.ts | 18 ++++++---
packages/zimic/src/utils/urls.ts | 6 +--
packages/zimic/src/utils/webSocket.ts | 6 +--
.../errors/InvalidWebSocketMessage.ts | 2 +-
.../errors/NotStartedWebSocketHandlerError.ts | 2 +-
.../zimic/tests/utils/interceptorServers.ts | 7 ++++
packages/zimic/tsup.config.ts | 1 +
31 files changed, 217 insertions(+), 60 deletions(-)
create mode 100644 packages/zimic/server.d.ts
create mode 100644 packages/zimic/src/interceptor/server/factory.ts
create mode 100644 packages/zimic/src/interceptor/server/index.ts
create mode 100644 packages/zimic/src/interceptor/server/types/options.ts
create mode 100644 packages/zimic/tests/utils/interceptorServers.ts
diff --git a/README.md b/README.md
index 811f811e..af289aae 100644
--- a/README.md
+++ b/README.md
@@ -108,6 +108,7 @@ Zimic provides a flexible and type-safe way to mock HTTP requests.
- [`zimic browser init`](#zimic-browser-init)
- [`zimic server`](#zimic-server)
- [`zimic server start`](#zimic-server-start)
+ - [Programmatic usage](#programmatic-usage)
- [Changelog](#changelog)
## Getting started
@@ -1839,6 +1840,29 @@ zimic server start --port 4000 --ephemeral -- npm run test
The command after `--` will be executed when the server is ready. The flag `--ephemeral` indicates that the server
should automatically stop after the command finishes.
+#### Programmatic usage
+
+The module `zimic/server` exports resources for managing interceptor servers programmatically. Even though we recommend
+using the CLI, this module is also an option.
+
+```ts
+import { createInterceptorServer, runCommand } from 'zimic/server';
+
+const server = createInterceptorServer({ hostname: 'localhost', port: 3000 });
+
+await server.start();
+console.log(server.isRunning()); // true
+
+const [command, ...commandArguments] = process.argv.slice(3);
+await runCommand(command, commandArguments);
+
+await server.stop();
+```
+
+The export `runCommand` is a helper function that runs a command with its arguments. The
+[Next.js App Router](./examples/README.md#nextjs) and the [Playwright](./examples/README.md#playwright) examples use
+this function to run the application after loading the interceptors and fixtures.
+
---
## Changelog
diff --git a/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
index 6563204c..8e78a991 100644
--- a/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
+++ b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
@@ -64,6 +64,16 @@ import {
NotStartedHttpInterceptorError,
UnregisteredServiceWorkerError,
} from 'zimic0/interceptor';
+import {
+ createInterceptorServer,
+ InterceptorServer,
+ InterceptorServerOptions,
+ NotStartedInterceptorServerError,
+ runCommand,
+ CommandError,
+ DEFAULT_ACCESS_CONTROL_HEADERS,
+ DEFAULT_PREFLIGHT_STATUS_CODE,
+} from 'zimic0/server';
describe('Exports', () => {
it('should export all expected resources', () => {
@@ -162,5 +172,17 @@ describe('Exports', () => {
expect(typeof NotStartedHttpInterceptorError).toBe('function');
expectTypeOf().not.toBeAny();
expect(typeof UnregisteredServiceWorkerError).toBe('function');
+
+ expect(typeof createInterceptorServer).toBe('function');
+ expectTypeOf().not.toBeAny();
+ expectTypeOf().not.toBeAny();
+ expectTypeOf().not.toBeAny();
+ expect(typeof NotStartedInterceptorServerError).toBe('function');
+ expect(typeof runCommand).toBe('function');
+ expectTypeOf().not.toBeAny();
+ expect(typeof CommandError).toBe('function');
+
+ expect(DEFAULT_ACCESS_CONTROL_HEADERS).toEqual(expect.any(Object));
+ expect(DEFAULT_PREFLIGHT_STATUS_CODE).toEqual(expect.any(Number));
});
});
diff --git a/packages/zimic/package.json b/packages/zimic/package.json
index b14dceb4..5b58ca36 100644
--- a/packages/zimic/package.json
+++ b/packages/zimic/package.json
@@ -52,6 +52,12 @@
"default": "./dist/interceptor.js",
"types": "./dist/interceptor.d.ts"
},
+ "./server": {
+ "import": "./dist/server.mjs",
+ "require": "./dist/server.js",
+ "default": "./dist/server.js",
+ "types": "./dist/server.d.ts"
+ },
"./package.json": "./package.json"
},
"scripts": {
diff --git a/packages/zimic/server.d.ts b/packages/zimic/server.d.ts
new file mode 100644
index 00000000..699fd532
--- /dev/null
+++ b/packages/zimic/server.d.ts
@@ -0,0 +1 @@
+export * from './dist/server';
diff --git a/packages/zimic/src/cli/__tests__/server.cli.node.test.ts b/packages/zimic/src/cli/__tests__/server.cli.node.test.ts
index 890cc255..4e607b82 100644
--- a/packages/zimic/src/cli/__tests__/server.cli.node.test.ts
+++ b/packages/zimic/src/cli/__tests__/server.cli.node.test.ts
@@ -373,7 +373,7 @@ describe('CLI (server)', async () => {
await usingIgnoredConsole(['error', 'log'], async (spies) => {
const error = new CommandError('node', exitCode, null);
await expect(runCLI()).rejects.toThrowError(error);
- expect(error.message).toBe(`[zimic] Command 'node' exited with code ${exitCode}.`);
+ expect(error.message).toBe(`Command 'node' exited with code ${exitCode}.`);
expect(spies.error).toHaveBeenCalledTimes(1);
expect(spies.error).toHaveBeenCalledWith(error);
@@ -398,7 +398,7 @@ describe('CLI (server)', async () => {
await usingIgnoredConsole(['error', 'log'], async (spies) => {
const error = new CommandError('node', null, signal);
await expect(runCLI()).rejects.toThrowError(error);
- expect(error.message).toBe(`[zimic] Command 'node' exited after signal ${signal}.`);
+ expect(error.message).toBe(`Command 'node' exited after signal ${signal}.`);
expect(spies.error).toHaveBeenCalledTimes(1);
expect(spies.error).toHaveBeenCalledWith(error);
diff --git a/packages/zimic/src/cli/cli.ts b/packages/zimic/src/cli/cli.ts
index be9e5c82..7ab2f1e1 100644
--- a/packages/zimic/src/cli/cli.ts
+++ b/packages/zimic/src/cli/cli.ts
@@ -39,7 +39,6 @@ async function runCLI() {
.positional('onReady', {
description: 'A command to run when the server is ready to accept connections.',
type: 'string',
- array: true,
})
.option('hostname', {
type: 'string',
diff --git a/packages/zimic/src/cli/server/start.ts b/packages/zimic/src/cli/server/start.ts
index d6cf0e7b..584057e2 100644
--- a/packages/zimic/src/cli/server/start.ts
+++ b/packages/zimic/src/cli/server/start.ts
@@ -1,7 +1,8 @@
+import { createInterceptorServer } from '@/interceptor/server';
+import { InterceptorServerOptions } from '@/interceptor/server/types/options';
+import { InterceptorServer } from '@/interceptor/server/types/public';
import { logWithPrefix } from '@/utils/console';
-import { runCommand, PROCESS_EXIT_EVENTS } from '@/utils/processes';
-
-import InterceptorServer, { InterceptorServerOptions } from '../../interceptor/server/InterceptorServer';
+import { runCommand } from '@/utils/processes';
interface InterceptorServerStartOptions extends InterceptorServerOptions {
ephemeral: boolean;
@@ -20,7 +21,7 @@ async function startInterceptorServer({
onUnhandledRequest,
onReady,
}: InterceptorServerStartOptions) {
- const server = new InterceptorServer({
+ const server = createInterceptorServer({
hostname,
port,
onUnhandledRequest,
@@ -28,12 +29,6 @@ async function startInterceptorServer({
singletonServer = server;
- for (const exitEvent of PROCESS_EXIT_EVENTS) {
- process.on(exitEvent, async () => {
- await server.stop();
- });
- }
-
await server.start();
logWithPrefix(`${ephemeral ? 'Ephemeral s' : 'S'}erver is running on ${server.httpURL()}`);
@@ -45,10 +40,6 @@ async function startInterceptorServer({
if (ephemeral) {
await server.stop();
}
-
- for (const exitEvent of PROCESS_EXIT_EVENTS) {
- process.removeAllListeners(exitEvent);
- }
}
export default startInterceptorServer;
diff --git a/packages/zimic/src/interceptor/http/interceptor/__tests__/HttpInterceptor.node.test.ts b/packages/zimic/src/interceptor/http/interceptor/__tests__/HttpInterceptor.node.test.ts
index a49980a5..a4e5e827 100644
--- a/packages/zimic/src/interceptor/http/interceptor/__tests__/HttpInterceptor.node.test.ts
+++ b/packages/zimic/src/interceptor/http/interceptor/__tests__/HttpInterceptor.node.test.ts
@@ -1,12 +1,12 @@
import { describe } from 'vitest';
-import InterceptorServer from '@/interceptor/server/InterceptorServer';
import { getNodeBaseURL } from '@tests/utils/interceptors';
+import { createInternalInterceptorServer } from '@tests/utils/interceptorServers';
import { declareSharedHttpInterceptorTests } from './shared/interceptorTests';
describe('HttpInterceptor (Node.js)', () => {
- const server = new InterceptorServer();
+ const server = createInternalInterceptorServer();
declareSharedHttpInterceptorTests({
platform: 'node',
diff --git a/packages/zimic/src/interceptor/http/interceptor/errors/NotStartedHttpInterceptorError.ts b/packages/zimic/src/interceptor/http/interceptor/errors/NotStartedHttpInterceptorError.ts
index c11b9316..08aa8972 100644
--- a/packages/zimic/src/interceptor/http/interceptor/errors/NotStartedHttpInterceptorError.ts
+++ b/packages/zimic/src/interceptor/http/interceptor/errors/NotStartedHttpInterceptorError.ts
@@ -7,7 +7,7 @@
*/
class NotStartedHttpInterceptorError extends Error {
constructor() {
- super('[zimic] Interceptor is not running. Did you forget to call `await interceptor.start()`?');
+ super('Interceptor is not running. Did you forget to call `await interceptor.start()`?');
this.name = 'NotStartedHttpInterceptorError';
}
}
diff --git a/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorPlatform.ts b/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorPlatform.ts
index fa4c6eb6..2901cfc9 100644
--- a/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorPlatform.ts
+++ b/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorPlatform.ts
@@ -8,7 +8,7 @@ class UnknownHttpInterceptorPlatform extends Error {
/* istanbul ignore next -- @preserve
* Ignoring because checking unknown platforms is currently not possible in our Vitest setup */
constructor() {
- super('[zimic] Unknown interceptor platform.');
+ super('Unknown interceptor platform.');
this.name = 'UnknownHttpInterceptorPlatform';
}
}
diff --git a/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorTypeError.ts b/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorTypeError.ts
index 9e3a3ae6..2aa73a9d 100644
--- a/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorTypeError.ts
+++ b/packages/zimic/src/interceptor/http/interceptor/errors/UnknownHttpInterceptorTypeError.ts
@@ -3,7 +3,7 @@ import { HttpInterceptorType } from '../types/options';
class UnknownHttpInterceptorTypeError extends TypeError {
constructor(unknownType: unknown) {
super(
- `[zimic] Unknown HTTP interceptor type: ${unknownType}. The available options are ` +
+ `Unknown HTTP interceptor type: ${unknownType}. The available options are ` +
`'${'local' satisfies HttpInterceptorType}' and ` +
`'${'remote' satisfies HttpInterceptorType}'.`,
);
diff --git a/packages/zimic/src/interceptor/http/interceptorWorker/__tests__/HttpinterceptorWorker.node.test.ts b/packages/zimic/src/interceptor/http/interceptorWorker/__tests__/HttpinterceptorWorker.node.test.ts
index 66084cb1..4ed19c74 100644
--- a/packages/zimic/src/interceptor/http/interceptorWorker/__tests__/HttpinterceptorWorker.node.test.ts
+++ b/packages/zimic/src/interceptor/http/interceptorWorker/__tests__/HttpinterceptorWorker.node.test.ts
@@ -1,12 +1,12 @@
import { describe } from 'vitest';
-import InterceptorServer from '@/interceptor/server/InterceptorServer';
import { getNodeBaseURL } from '@tests/utils/interceptors';
+import { createInternalInterceptorServer } from '@tests/utils/interceptorServers';
import { declareSharedHttpInterceptorWorkerTests } from './shared/workerTests';
describe('HttpInterceptorWorker (Node.js)', () => {
- const server = new InterceptorServer();
+ const server = createInternalInterceptorServer();
declareSharedHttpInterceptorWorkerTests({
platform: 'node',
diff --git a/packages/zimic/src/interceptor/http/interceptorWorker/errors/UnregisteredServiceWorkerError.ts b/packages/zimic/src/interceptor/http/interceptorWorker/errors/UnregisteredServiceWorkerError.ts
index 1d98f1e9..052bd96a 100644
--- a/packages/zimic/src/interceptor/http/interceptorWorker/errors/UnregisteredServiceWorkerError.ts
+++ b/packages/zimic/src/interceptor/http/interceptorWorker/errors/UnregisteredServiceWorkerError.ts
@@ -8,7 +8,7 @@ import { SERVICE_WORKER_FILE_NAME } from '@/cli/browser/shared/constants';
class UnregisteredServiceWorkerError extends Error {
constructor() {
super(
- `[zimic] Failed to register the browser service worker: ` +
+ `Failed to register the browser service worker: ` +
`script '${window.location.origin}/${SERVICE_WORKER_FILE_NAME}' not found.\n\n` +
'Did you forget to run "npx zimic browser init "?\n\n' +
'Learn more at https://github.com/zimicjs/zimic#browser-post-install.',
diff --git a/packages/zimic/src/interceptor/http/requestHandler/__tests__/HttpRequestHandler.node.test.ts b/packages/zimic/src/interceptor/http/requestHandler/__tests__/HttpRequestHandler.node.test.ts
index c14d457a..d9676feb 100644
--- a/packages/zimic/src/interceptor/http/requestHandler/__tests__/HttpRequestHandler.node.test.ts
+++ b/packages/zimic/src/interceptor/http/requestHandler/__tests__/HttpRequestHandler.node.test.ts
@@ -1,12 +1,12 @@
import { describe } from 'vitest';
-import InterceptorServer from '@/interceptor/server/InterceptorServer';
import { getNodeBaseURL } from '@tests/utils/interceptors';
+import { createInternalInterceptorServer } from '@tests/utils/interceptorServers';
import { declareSharedHttpRequestHandlerTests } from './shared/requestHandlerTests';
describe('HttpRequestHandler (Node.js)', () => {
- const server = new InterceptorServer();
+ const server = createInternalInterceptorServer();
declareSharedHttpRequestHandlerTests({
platform: 'node',
diff --git a/packages/zimic/src/interceptor/http/requestHandler/errors/NoResponseDefinitionError.ts b/packages/zimic/src/interceptor/http/requestHandler/errors/NoResponseDefinitionError.ts
index 53f20c53..74aedf4b 100644
--- a/packages/zimic/src/interceptor/http/requestHandler/errors/NoResponseDefinitionError.ts
+++ b/packages/zimic/src/interceptor/http/requestHandler/errors/NoResponseDefinitionError.ts
@@ -1,6 +1,6 @@
class NoResponseDefinitionError extends TypeError {
constructor() {
- super('[zimic] Cannot generate a response without a definition. Use .respond() to set a response.');
+ super('Cannot generate a response without a definition. Use .respond() to set a response.');
this.name = 'NoResponseDefinitionError';
}
}
diff --git a/packages/zimic/src/interceptor/server/InterceptorServer.ts b/packages/zimic/src/interceptor/server/InterceptorServer.ts
index d2f06db2..87340412 100644
--- a/packages/zimic/src/interceptor/server/InterceptorServer.ts
+++ b/packages/zimic/src/interceptor/server/InterceptorServer.ts
@@ -8,23 +8,17 @@ import HttpInterceptorWorker from '@/interceptor/http/interceptorWorker/HttpInte
import HttpInterceptorWorkerStore from '@/interceptor/http/interceptorWorker/HttpInterceptorWorkerStore';
import { deserializeResponse, serializeRequest } from '@/utils/fetch';
import { getHttpServerPort, startHttpServer, stopHttpServer } from '@/utils/http';
+import { PROCESS_EXIT_EVENTS } from '@/utils/processes';
import { createRegexFromURL, createURL, excludeNonPathParams } from '@/utils/urls';
import { WebSocket } from '@/webSocket/types';
import WebSocketServer from '@/webSocket/WebSocketServer';
import { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE } from './constants';
import NotStartedInterceptorServerError from './errors/NotStartedInterceptorServerError';
+import { InterceptorServerOptions } from './types/options';
import { InterceptorServer as PublicInterceptorServer } from './types/public';
import { HttpHandlerCommit, InterceptorServerWebSocketSchema } from './types/schema';
-export interface InterceptorServerOptions {
- hostname?: string;
- port?: number;
- onUnhandledRequest?: {
- log?: boolean;
- };
-}
-
interface HttpHandler {
id: string;
url: { regex: RegExp };
@@ -103,6 +97,12 @@ class InterceptorServer implements PublicInterceptorServer {
return;
}
+ for (const exitEvent of PROCESS_EXIT_EVENTS) {
+ process.on(exitEvent, async () => {
+ await this.stop();
+ });
+ }
+
this._httpServer = createServer({
keepAlive: true,
joinDuplicateHeaders: true,
@@ -199,6 +199,10 @@ class InterceptorServer implements PublicInterceptorServer {
await this.stopWebSocketServer();
await this.stopHttpServer();
+
+ for (const exitEvent of PROCESS_EXIT_EVENTS) {
+ process.removeAllListeners(exitEvent);
+ }
}
private async stopHttpServer() {
diff --git a/packages/zimic/src/interceptor/server/__tests__/InterceptorServer.node.test.ts b/packages/zimic/src/interceptor/server/__tests__/InterceptorServer.node.test.ts
index 01f092c1..b15184e9 100644
--- a/packages/zimic/src/interceptor/server/__tests__/InterceptorServer.node.test.ts
+++ b/packages/zimic/src/interceptor/server/__tests__/InterceptorServer.node.test.ts
@@ -1,5 +1,7 @@
import { afterEach, describe, expect, it } from 'vitest';
+import { createInternalInterceptorServer } from '@tests/utils/interceptorServers';
+
import InterceptorServer from '../InterceptorServer';
// These are integration tests for the server. Only features not easily reproducible by the CLI and the remote
@@ -13,7 +15,7 @@ describe('Interceptor server', () => {
});
it('should start correctly with a defined port', async () => {
- server = new InterceptorServer({ hostname: 'localhost', port: 8080 });
+ server = createInternalInterceptorServer({ hostname: 'localhost', port: 8080 });
expect(server.isRunning()).toBe(false);
expect(server.hostname()).toBe('localhost');
@@ -29,7 +31,7 @@ describe('Interceptor server', () => {
});
it('should start correctly with an undefined port', async () => {
- server = new InterceptorServer({ hostname: 'localhost' });
+ server = createInternalInterceptorServer({ hostname: 'localhost' });
expect(server.isRunning()).toBe(false);
expect(server.hostname()).toBe('localhost');
@@ -45,7 +47,7 @@ describe('Interceptor server', () => {
});
it('should not throw an error is started multiple times', async () => {
- server = new InterceptorServer({ hostname: 'localhost' });
+ server = createInternalInterceptorServer({ hostname: 'localhost' });
expect(server.isRunning()).toBe(false);
@@ -60,7 +62,7 @@ describe('Interceptor server', () => {
});
it('should not throw an error if stopped multiple times', async () => {
- server = new InterceptorServer({ hostname: 'localhost' });
+ server = createInternalInterceptorServer({ hostname: 'localhost' });
expect(server.isRunning()).toBe(false);
diff --git a/packages/zimic/src/interceptor/server/constants.ts b/packages/zimic/src/interceptor/server/constants.ts
index 1e00c52d..99b77375 100644
--- a/packages/zimic/src/interceptor/server/constants.ts
+++ b/packages/zimic/src/interceptor/server/constants.ts
@@ -15,6 +15,7 @@ export type AccessControlHeaders = HttpSchema.Headers<{
'access-control-max-age'?: string;
}>;
+/** The default access control headers for the server. */
export const DEFAULT_ACCESS_CONTROL_HEADERS = Object.freeze({
'access-control-allow-origin': '*',
'access-control-allow-methods': ALLOWED_ACCESS_CONTROL_HTTP_METHODS,
@@ -23,4 +24,5 @@ export const DEFAULT_ACCESS_CONTROL_HEADERS = Object.freeze({
'access-control-max-age': process.env.SERVER_ACCESS_CONTROL_MAX_AGE,
}) satisfies AccessControlHeaders;
+/** The default status code for the preflight request. */
export const DEFAULT_PREFLIGHT_STATUS_CODE = 204;
diff --git a/packages/zimic/src/interceptor/server/errors/NotStartedInterceptorServerError.ts b/packages/zimic/src/interceptor/server/errors/NotStartedInterceptorServerError.ts
index d3a3e7c8..578939f8 100644
--- a/packages/zimic/src/interceptor/server/errors/NotStartedInterceptorServerError.ts
+++ b/packages/zimic/src/interceptor/server/errors/NotStartedInterceptorServerError.ts
@@ -1,10 +1,8 @@
-/* istanbul ignore next -- @preserve
- * This error is a fallback to prevent doing operations without a started server. It should not happen in normal
- * conditions.
- */
+/* istanbul ignore next -- @preserve */
+/** An error thrown when the interceptor server is not running. */
class NotStartedInterceptorServerError extends Error {
constructor() {
- super('[zimic] The interceptor server is not running.');
+ super('The interceptor server is not running.');
this.name = 'NotStartedInterceptorServerError';
}
}
diff --git a/packages/zimic/src/interceptor/server/factory.ts b/packages/zimic/src/interceptor/server/factory.ts
new file mode 100644
index 00000000..190dc7dd
--- /dev/null
+++ b/packages/zimic/src/interceptor/server/factory.ts
@@ -0,0 +1,15 @@
+import InterceptorServer from './InterceptorServer';
+import { InterceptorServerOptions } from './types/options';
+import { InterceptorServer as PublicInterceptorServer } from './types/public';
+
+/**
+ * Creates an {@link https://github.com/zimicjs/zimic#zimic-server interceptor server}.
+ *
+ * @param options The options to create the server.
+ * @returns The created server.
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ * @see {@link https://github.com/zimicjs/zimic#remote-http-interceptors Remote HTTP Interceptors} .
+ */
+export function createInterceptorServer(options?: InterceptorServerOptions): PublicInterceptorServer {
+ return new InterceptorServer(options);
+}
diff --git a/packages/zimic/src/interceptor/server/index.ts b/packages/zimic/src/interceptor/server/index.ts
new file mode 100644
index 00000000..e4ee9834
--- /dev/null
+++ b/packages/zimic/src/interceptor/server/index.ts
@@ -0,0 +1,11 @@
+import NotStartedInterceptorServerError from './errors/NotStartedInterceptorServerError';
+
+export type { InterceptorServerOptions } from './types/options';
+export type { InterceptorServer } from './types/public';
+
+export { DEFAULT_ACCESS_CONTROL_HEADERS, DEFAULT_PREFLIGHT_STATUS_CODE } from './constants';
+
+export { createInterceptorServer } from './factory';
+export { NotStartedInterceptorServerError };
+
+export { runCommand, CommandError } from '@/utils/processes';
diff --git a/packages/zimic/src/interceptor/server/types/options.ts b/packages/zimic/src/interceptor/server/types/options.ts
new file mode 100644
index 00000000..f87eea0c
--- /dev/null
+++ b/packages/zimic/src/interceptor/server/types/options.ts
@@ -0,0 +1,26 @@
+/**
+ * The options to create an {@link https://github.com/zimicjs/zimic#zimic-server interceptor server}.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
+export interface InterceptorServerOptions {
+ /**
+ * The hostname to start the server on.
+ *
+ * @default localhost
+ */
+ hostname?: string;
+
+ /** The port to start the server on. If no port is provided, a random one is chosen. */
+ port?: number;
+
+ /** The strategy to handle unhandled requests. */
+ onUnhandledRequest?: {
+ /**
+ * Whether to log unhandled requests.
+ *
+ * @default true
+ */
+ log?: boolean;
+ };
+}
diff --git a/packages/zimic/src/interceptor/server/types/public.ts b/packages/zimic/src/interceptor/server/types/public.ts
index 59fe529d..203bba61 100644
--- a/packages/zimic/src/interceptor/server/types/public.ts
+++ b/packages/zimic/src/interceptor/server/types/public.ts
@@ -1,9 +1,49 @@
+/**
+ * A server to intercept and handle requests. It is used in combination with
+ * {@link https://github.com/zimicjs/zimic#remote-http-interceptors remote interceptors}.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
export interface InterceptorServer {
+ /**
+ * The hostname of the server.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
hostname: () => string;
+
+ /**
+ * The port of the server.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
port: () => number | undefined;
+
+ /**
+ * The HTTP URL of the server.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
httpURL: () => string | undefined;
+
+ /**
+ * Whether the server is running.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
isRunning: () => boolean;
+ /**
+ * Start the server.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
start: () => Promise;
+
+ /**
+ * Stop the server.
+ *
+ * @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
+ */
stop: () => Promise;
}
diff --git a/packages/zimic/src/utils/http.ts b/packages/zimic/src/utils/http.ts
index 07670e89..6fe21cd6 100644
--- a/packages/zimic/src/utils/http.ts
+++ b/packages/zimic/src/utils/http.ts
@@ -4,14 +4,14 @@ class HttpServerTimeoutError extends Error {}
export class HttpServerStartTimeoutError extends HttpServerTimeoutError {
constructor(reachedTimeout: number) {
- super(`[zimic] HTTP server start timed out after ${reachedTimeout}ms.`);
+ super(`HTTP server start timed out after ${reachedTimeout}ms.`);
this.name = 'HttpServerStartTimeout';
}
}
export class HttpServerStopTimeoutError extends HttpServerTimeoutError {
constructor(reachedTimeout: number) {
- super(`[zimic] HTTP server stop timed out after ${reachedTimeout}ms.`);
+ super(`HTTP server stop timed out after ${reachedTimeout}ms.`);
this.name = 'HttpServerStopTimeout';
}
}
diff --git a/packages/zimic/src/utils/processes.ts b/packages/zimic/src/utils/processes.ts
index c2e190af..960edc27 100644
--- a/packages/zimic/src/utils/processes.ts
+++ b/packages/zimic/src/utils/processes.ts
@@ -1,3 +1,4 @@
+import { SpawnOptions } from 'child_process';
import { spawn } from 'cross-spawn';
export const PROCESS_EXIT_EVENTS = Object.freeze([
@@ -9,20 +10,27 @@ export const PROCESS_EXIT_EVENTS = Object.freeze([
'SIGBREAK',
] as const);
+/** An error thrown when a command exits with a non-zero code. */
export class CommandError extends Error {
constructor(command: string, exitCode: number | null, signal: NodeJS.Signals | null) {
- super(
- `[zimic] Command '${command}' exited ` +
- `${exitCode === null ? `after signal ${signal}` : `with code ${exitCode}`}.`,
- );
+ super(`Command '${command}' exited ${exitCode === null ? `after signal ${signal}` : `with code ${exitCode}`}.`);
this.name = 'CommandError';
}
}
-export async function runCommand(command: string, commandArguments: string[]) {
+/**
+ * Runs a command with the given arguments.
+ *
+ * @param command The command to run.
+ * @param commandArguments The arguments to pass to the command.
+ * @param options The options to pass to the spawn function. By default, stdio is set to 'inherit'.
+ * @throws {CommandError} When the command exits with a non-zero code.
+ */
+export async function runCommand(command: string, commandArguments: string[], options: SpawnOptions = {}) {
await new Promise((resolve, reject) => {
const childProcess = spawn(command, commandArguments, {
stdio: 'inherit',
+ ...options,
});
childProcess.once('error', (error) => {
diff --git a/packages/zimic/src/utils/urls.ts b/packages/zimic/src/utils/urls.ts
index b981794a..00e978e1 100644
--- a/packages/zimic/src/utils/urls.ts
+++ b/packages/zimic/src/utils/urls.ts
@@ -1,6 +1,6 @@
export class InvalidURLError extends TypeError {
constructor(url: unknown) {
- super(`[zimic] Invalid URL: '${url}'`);
+ super(`Invalid URL: '${url}'`);
this.name = 'InvalidURL';
}
}
@@ -8,7 +8,7 @@ export class InvalidURLError extends TypeError {
export class UnsupportedURLProtocolError extends TypeError {
constructor(protocol: string, availableProtocols: string[] | readonly string[]) {
super(
- `[zimic] Unsupported URL protocol: '${protocol}'. ` +
+ `Unsupported URL protocol: '${protocol}'. ` +
`The available options are ${availableProtocols.map((protocol) => `'${protocol}'`).join(', ')}`,
);
this.name = 'UnsupportedURLProtocolError';
@@ -62,7 +62,7 @@ export function excludeNonPathParams(url: URL) {
export class DuplicatedPathParamError extends Error {
constructor(url: string, paramName: string) {
super(
- `[zimic] The path parameter '${paramName}' appears more than once in the URL '${url}'. This is not supported. ` +
+ `The path parameter '${paramName}' appears more than once in the URL '${url}'. This is not supported. ` +
'Please make sure that each parameter is unique.',
);
this.name = 'DuplicatedPathParamError';
diff --git a/packages/zimic/src/utils/webSocket.ts b/packages/zimic/src/utils/webSocket.ts
index a9ed4069..8e9a4476 100644
--- a/packages/zimic/src/utils/webSocket.ts
+++ b/packages/zimic/src/utils/webSocket.ts
@@ -6,21 +6,21 @@ class WebSocketTimeoutError extends Error {}
export class WebSocketOpenTimeoutError extends WebSocketTimeoutError {
constructor(reachedTimeout: number) {
- super(`[zimic] Web socket open timed out after ${reachedTimeout}ms.`);
+ super(`Web socket open timed out after ${reachedTimeout}ms.`);
this.name = 'WebSocketOpenTimeout';
}
}
export class WebSocketMessageTimeoutError extends WebSocketTimeoutError {
constructor(reachedTimeout: number) {
- super(`[zimic] Web socket message timed out after ${reachedTimeout}ms.`);
+ super(`Web socket message timed out after ${reachedTimeout}ms.`);
this.name = 'WebSocketMessageTimeout';
}
}
export class WebSocketCloseTimeoutError extends WebSocketTimeoutError {
constructor(reachedTimeout: number) {
- super(`[zimic] Web socket close timed out after ${reachedTimeout}ms.`);
+ super(`Web socket close timed out after ${reachedTimeout}ms.`);
this.name = 'WebSocketCloseTimeout';
}
}
diff --git a/packages/zimic/src/webSocket/errors/InvalidWebSocketMessage.ts b/packages/zimic/src/webSocket/errors/InvalidWebSocketMessage.ts
index 57b01222..07837ac0 100644
--- a/packages/zimic/src/webSocket/errors/InvalidWebSocketMessage.ts
+++ b/packages/zimic/src/webSocket/errors/InvalidWebSocketMessage.ts
@@ -1,6 +1,6 @@
class InvalidWebSocketMessage extends Error {
constructor(message: unknown) {
- super(`[zimic] Web socket message is invalid and could not be parsed: ${message}`);
+ super(`Web socket message is invalid and could not be parsed: ${message}`);
this.name = 'InvalidWebSocketMessage';
}
}
diff --git a/packages/zimic/src/webSocket/errors/NotStartedWebSocketHandlerError.ts b/packages/zimic/src/webSocket/errors/NotStartedWebSocketHandlerError.ts
index d8b31a69..fd346228 100644
--- a/packages/zimic/src/webSocket/errors/NotStartedWebSocketHandlerError.ts
+++ b/packages/zimic/src/webSocket/errors/NotStartedWebSocketHandlerError.ts
@@ -1,6 +1,6 @@
class NotStartedWebSocketHandlerError extends Error {
constructor() {
- super('[zimic] Web socket handler is not running.');
+ super('Web socket handler is not running.');
this.name = 'NotStartedWebSocketHandlerError';
}
}
diff --git a/packages/zimic/tests/utils/interceptorServers.ts b/packages/zimic/tests/utils/interceptorServers.ts
new file mode 100644
index 00000000..15156fc3
--- /dev/null
+++ b/packages/zimic/tests/utils/interceptorServers.ts
@@ -0,0 +1,7 @@
+import { InterceptorServerOptions, createInterceptorServer } from '@/interceptor/server';
+import InterceptorServer from '@/interceptor/server/InterceptorServer';
+import { InterceptorServer as PublicInterceptorServer } from '@/interceptor/server/types/public';
+
+export function createInternalInterceptorServer(options?: InterceptorServerOptions) {
+ return createInterceptorServer(options) satisfies PublicInterceptorServer as InterceptorServer;
+}
diff --git a/packages/zimic/tsup.config.ts b/packages/zimic/tsup.config.ts
index aafba563..b969fad9 100644
--- a/packages/zimic/tsup.config.ts
+++ b/packages/zimic/tsup.config.ts
@@ -22,6 +22,7 @@ export default defineConfig([
entry: {
index: 'src/index.ts',
interceptor: 'src/interceptor/index.ts',
+ server: 'src/interceptor/server/index.ts',
},
},
{
From b424744c55f9b2b6bf1a827449e005c6cb8469e2 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 15:55:04 -0300
Subject: [PATCH 06/10] test(zimic-test-client): split shared and server-only
exports
---
.../interceptor/exports/exports.node.test.ts | 27 +++++++++++++++++++
.../v0/interceptor/exports/exports.test.ts | 22 ---------------
2 files changed, 27 insertions(+), 22 deletions(-)
create mode 100644 apps/zimic-test-client/tests/v0/interceptor/exports/exports.node.test.ts
diff --git a/apps/zimic-test-client/tests/v0/interceptor/exports/exports.node.test.ts b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.node.test.ts
new file mode 100644
index 00000000..36c30e93
--- /dev/null
+++ b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.node.test.ts
@@ -0,0 +1,27 @@
+import { describe, expect, expectTypeOf, it } from 'vitest';
+import {
+ createInterceptorServer,
+ InterceptorServer,
+ InterceptorServerOptions,
+ NotStartedInterceptorServerError,
+ runCommand,
+ CommandError,
+ DEFAULT_ACCESS_CONTROL_HEADERS,
+ DEFAULT_PREFLIGHT_STATUS_CODE,
+} from 'zimic0/server';
+
+describe('Exports (Node.js)', () => {
+ it('should export all expected resources', () => {
+ expect(typeof createInterceptorServer).toBe('function');
+ expectTypeOf().not.toBeAny();
+ expectTypeOf().not.toBeAny();
+ expectTypeOf().not.toBeAny();
+ expect(typeof NotStartedInterceptorServerError).toBe('function');
+ expect(typeof runCommand).toBe('function');
+ expectTypeOf().not.toBeAny();
+ expect(typeof CommandError).toBe('function');
+
+ expect(DEFAULT_ACCESS_CONTROL_HEADERS).toEqual(expect.any(Object));
+ expect(DEFAULT_PREFLIGHT_STATUS_CODE).toEqual(expect.any(Number));
+ });
+});
diff --git a/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
index 8e78a991..6563204c 100644
--- a/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
+++ b/apps/zimic-test-client/tests/v0/interceptor/exports/exports.test.ts
@@ -64,16 +64,6 @@ import {
NotStartedHttpInterceptorError,
UnregisteredServiceWorkerError,
} from 'zimic0/interceptor';
-import {
- createInterceptorServer,
- InterceptorServer,
- InterceptorServerOptions,
- NotStartedInterceptorServerError,
- runCommand,
- CommandError,
- DEFAULT_ACCESS_CONTROL_HEADERS,
- DEFAULT_PREFLIGHT_STATUS_CODE,
-} from 'zimic0/server';
describe('Exports', () => {
it('should export all expected resources', () => {
@@ -172,17 +162,5 @@ describe('Exports', () => {
expect(typeof NotStartedHttpInterceptorError).toBe('function');
expectTypeOf().not.toBeAny();
expect(typeof UnregisteredServiceWorkerError).toBe('function');
-
- expect(typeof createInterceptorServer).toBe('function');
- expectTypeOf().not.toBeAny();
- expectTypeOf().not.toBeAny();
- expectTypeOf().not.toBeAny();
- expect(typeof NotStartedInterceptorServerError).toBe('function');
- expect(typeof runCommand).toBe('function');
- expectTypeOf().not.toBeAny();
- expect(typeof CommandError).toBe('function');
-
- expect(DEFAULT_ACCESS_CONTROL_HEADERS).toEqual(expect.any(Object));
- expect(DEFAULT_PREFLIGHT_STATUS_CODE).toEqual(expect.any(Number));
});
});
From 46cdfc54aad0ccd8fb1bc7d95a25d731a8583763 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 17:00:27 -0300
Subject: [PATCH 07/10] docs(examples): update `README.md` with simplified
setup
---
examples/with-next-js-app/README.md | 24 ++++++++++--------------
examples/with-next-js-pages/README.md | 18 +++++-------------
examples/with-playwright/README.md | 18 +++++++-----------
examples/with-vitest-browser/README.md | 2 +-
4 files changed, 23 insertions(+), 39 deletions(-)
diff --git a/examples/with-next-js-app/README.md b/examples/with-next-js-app/README.md
index 39de5406..f78cd00c 100644
--- a/examples/with-next-js-app/README.md
+++ b/examples/with-next-js-app/README.md
@@ -2,24 +2,21 @@
Zimic + Next.js App Router
-This example uses Zimic with [Next.js](https://nextjs.org). The application is verified with end-to-end tests using
-[Playwright](https://playwright.dev).
+This example uses Zimic with [Next.js](https://nextjs.org).
## Application
-The application is a simple [Next.js](https://nextjs.org) application using the
-[App Router](https://nextjs.org/docs/app). It fetches repositories from the
-[GitHub API](https://docs.github.com/en/rest).
+The application is a simple [Next.js](https://nextjs.org) project using the [App Router](https://nextjs.org/docs/app).
+It fetches repositories from the [GitHub API](https://docs.github.com/en/rest).
-- Application: [`src/app/app/page.page.tsx`](./src/app/page.tsx)
-- Interceptor provider:
- [`src/providers/interceptors/InterceptorProvider.tsx`](./src/providers/interceptors/InterceptorProvider.tsx)
- - This provider is used to apply Zimic mocks when the application is started in development.
+- Application: [`src/app/page.tsx`](./src/app/page.tsx)
- GitHub fetch: [`src/services/github.ts`](./src/services/github.ts)
- - Before fetching resources, it is necessary to wait for the interceptors and fixtures to be loaded. This is done via
- `await waitForLoadedInterceptors();`.
-A `postinstall` in [`package.json`](./package.json) script is used to install Playwright's browsers.
+A `postinstall` script in [`package.json`](./package.json) is used to install Playwright's browsers.
+
+The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and
+fixtures before the application is started in development. It is included in the command `dev:mock` in
+[`package.json`](./package.json).
## Testing
@@ -28,8 +25,7 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor: [`tests/interceptors/github/interceptor.ts`](./tests/interceptors/github/interceptor.ts)
- - Fixtures: [`tests/interceptors/github/fixtures.ts`](./tests/interceptors/github/fixtures.ts)
+- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
diff --git a/examples/with-next-js-pages/README.md b/examples/with-next-js-pages/README.md
index fa59aa8d..c0070add 100644
--- a/examples/with-next-js-pages/README.md
+++ b/examples/with-next-js-pages/README.md
@@ -2,26 +2,19 @@
Zimic + Next.js Pages Router
-This example uses Zimic with [Next.js](https://nextjs.org). The application is verified with end-to-end tests using
-[Playwright](https://playwright.dev).
+This example uses Zimic with [Next.js](https://nextjs.org).
## Application
-The application is a simple [Next.js](https://nextjs.org) application using the
+The application is a simple [Next.js](https://nextjs.org) project using the
[Pages Router](https://nextjs.org/docs/pages). It fetches repositories from the
[GitHub API](https://docs.github.com/en/rest).
- Application: [`src/pages/index.page.tsx`](./src/pages/index.page.tsx)
-- Interceptor provider:
- [`src/providers/interceptors/InterceptorProvider.tsx`](./src/providers/interceptors/InterceptorProvider.tsx)
- - This provider is used to apply Zimic mocks when the application is started in development.
- GitHub fetch: [`src/services/github.ts`](./src/services/github.ts)
- - Before fetching resources, it is necessary to wait for the interceptors and fixtures to be loaded. This is done via
- `await waitForLoadedInterceptors();`.
-A `postinstall` in [`package.json`](./package.json) script is used to install Playwright's browsers and initialize
-Zimic's mock service worker to the `./public` directory. The mock service worker at `./public/mockServiceWorker.js` is
-ignored in the [`.gitignore`](./.gitignore) file.
+The file [`_app.page.tsx`](./src/pages/_app.page.tsx) loads the interceptors and fixtures before the rest of the
+application is rendered in development.
## Testing
@@ -30,8 +23,7 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor: [`tests/interceptors/github/interceptor.ts`](./tests/interceptors/github/interceptor.ts)
- - Fixtures: [`tests/interceptors/github/fixtures.ts`](./tests/interceptors/github/fixtures.ts)
+- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
diff --git a/examples/with-playwright/README.md b/examples/with-playwright/README.md
index 7054229b..3ead339c 100644
--- a/examples/with-playwright/README.md
+++ b/examples/with-playwright/README.md
@@ -6,13 +6,17 @@ This example uses Zimic with [Playwright](https://playwright.dev) in end-to-end
## Application
-The tested application is a simple [Next.js](https://nextjs.org) application, fetching repositories from the
+The tested application is a simple [Next.js](https://nextjs.org) project, fetching repositories from the
[GitHub API](https://docs.github.com/en/rest).
- Application: [`src/app/page.tsx`](./src/app/page.tsx)
- GitHub fetch: [`src/services/github.ts`](./src/services/github.ts)
-A `postinstall` in [`package.json`](./package.json#L12) script is used to install Playwright's browsers.
+A `postinstall` script in [`package.json`](./package.json) is used to install Playwright's browsers.
+
+The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and
+fixtures before the application is started in development. It is included in the command `dev:mock` in
+[`package.json`](./package.json).
## Testing
@@ -21,21 +25,13 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor: [`tests/interceptors/github/interceptor.ts`](./tests/interceptors/github/interceptor.ts)
- - Fixtures: [`tests/interceptors/github/fixtures.ts`](./tests/interceptors/github/fixtures.ts)
+- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
- Test suite: [`src/app/__tests__/HomePage.e2e.test.ts`](./src/app/__tests__/HomePage.e2e.test.ts)
-- Test setup file: [`tests/setup.ts`](./tests/setup.ts)
- - This file is responsible for starting the Zimic interceptors before each test. It also applies default mock
- responses based on the [fixtures](./tests/interceptors/github/interceptor.ts).
- Playwright configuration: [`playwright.config.ts`](./playwright.config.ts)
-> [!IMPORTANT]
->
-> The setup file must be imported from each test file to apply the global `test.beforeAll` and `test.afterAll`.
-
### Running
1. Clone this example:
diff --git a/examples/with-vitest-browser/README.md b/examples/with-vitest-browser/README.md
index 60571b13..4c35012d 100644
--- a/examples/with-vitest-browser/README.md
+++ b/examples/with-vitest-browser/README.md
@@ -13,7 +13,7 @@ The application is a simple HTML layout rendered by vanilla JavaScript, fetching
- Application: [`src/app.ts`](./src/app.ts)
-A `postinstall` in [`package.json`](./package.json) script is used to install Playwright's browsers and initialize
+A `postinstall` script in [`package.json`](./package.json) is used to install Playwright's browsers and initialize
Zimic's mock service worker to the `./public` directory. The mock service worker at `./public/mockServiceWorker.js` is
ignored in the [`.gitignore`](./.gitignore) file.
From 74523c51910e79f036f7ac4297b75d172c5300d2 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 18:11:17 -0300
Subject: [PATCH 08/10] docs(examples): add explanation about parallelism in
playwright
---
examples/with-next-js-app/README.md | 6 +++---
examples/with-next-js-pages/README.md | 4 ++--
examples/with-playwright/README.md | 21 ++++++++++++++++++---
3 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/examples/with-next-js-app/README.md b/examples/with-next-js-app/README.md
index f78cd00c..7d16a676 100644
--- a/examples/with-next-js-app/README.md
+++ b/examples/with-next-js-app/README.md
@@ -14,8 +14,8 @@ It fetches repositories from the [GitHub API](https://docs.github.com/en/rest).
A `postinstall` script in [`package.json`](./package.json) is used to install Playwright's browsers.
-The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and
-fixtures before the application is started in development. It is included in the command `dev:mock` in
+The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and mocks
+before the application is started in development. It is used by the command `dev:mock` in
[`package.json`](./package.json).
## Testing
@@ -25,7 +25,7 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
+- GitHub interceptor and mocks: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
diff --git a/examples/with-next-js-pages/README.md b/examples/with-next-js-pages/README.md
index c0070add..51123109 100644
--- a/examples/with-next-js-pages/README.md
+++ b/examples/with-next-js-pages/README.md
@@ -13,7 +13,7 @@ The application is a simple [Next.js](https://nextjs.org) project using the
- Application: [`src/pages/index.page.tsx`](./src/pages/index.page.tsx)
- GitHub fetch: [`src/services/github.ts`](./src/services/github.ts)
-The file [`_app.page.tsx`](./src/pages/_app.page.tsx) loads the interceptors and fixtures before the rest of the
+The file [`_app.page.tsx`](./src/pages/_app.page.tsx) loads the interceptors and mocks before the rest of the
application is rendered in development.
## Testing
@@ -23,7 +23,7 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
+- GitHub interceptor and mocks: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
diff --git a/examples/with-playwright/README.md b/examples/with-playwright/README.md
index 3ead339c..aff92ef1 100644
--- a/examples/with-playwright/README.md
+++ b/examples/with-playwright/README.md
@@ -14,10 +14,25 @@ The tested application is a simple [Next.js](https://nextjs.org) project, fetchi
A `postinstall` script in [`package.json`](./package.json) is used to install Playwright's browsers.
-The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and
-fixtures before the application is started in development. It is included in the command `dev:mock` in
+The script [`tests/interceptors/scripts/load.ts`](./tests/interceptors/scripts/load.ts) loads the interceptors and mocks
+before the application is started in development. It is used by the command `dev:mock` in
[`package.json`](./package.json).
+> [!NOTE]
+>
+> **Preventing racing conditions**
+>
+> The mocks are loaded before starting the application to prevent racing conditions in tests. This example uses a single
+> interceptor server, so we would need to reduce the number of workers to 1 if the mocks were applied inside the tests
+> or `beforeEach`/`beforeAll`/`afterEach`/`afterAll` hooks. That would make the tests significantly slower in large
+> applications, which is a trade-off to consider.
+>
+> If using a single test worker is not a problem for your project, applying the mocks inside your tests or hooks is
+> perfectly possible. On the other hand, if you need parallelism, you can still simulate dynamic behavior by creating
+> all of the mocks you need beforehand in a load script like in this example. Using
+> [restrictions](https://github.com/zimicjs/zimic#http-handlerwithrestriction) is a good way to narrow down the scope of
+> those mocks.
+
## Testing
An example test suite uses [Playwright](https://playwright.dev) to test the application. Zimic is used to mock the
@@ -25,7 +40,7 @@ GitHub API and simulate a test case where the repository is found and another wh
### Zimic
-- GitHub interceptor and fixtures: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
+- GitHub interceptor and mocks: [`tests/interceptors/github.ts`](./tests/interceptors/github.ts)
### Test
From 79ef6ddf418cb728414b24f12ffa02519c927749 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 18:27:37 -0300
Subject: [PATCH 09/10] refactor(#zimic): clear only own server process
listeners
---
.../interceptor/server/InterceptorServer.ts | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)
diff --git a/packages/zimic/src/interceptor/server/InterceptorServer.ts b/packages/zimic/src/interceptor/server/InterceptorServer.ts
index 87340412..0c9b7840 100644
--- a/packages/zimic/src/interceptor/server/InterceptorServer.ts
+++ b/packages/zimic/src/interceptor/server/InterceptorServer.ts
@@ -49,7 +49,7 @@ class InterceptorServer implements PublicInterceptorServer {
private knownWorkerSockets = new Set();
- constructor(options: InterceptorServerOptions = {}) {
+ constructor(options: InterceptorServerOptions) {
this._hostname = options.hostname ?? 'localhost';
this._port = options.port;
this.onUnhandledRequest = options.onUnhandledRequest;
@@ -98,9 +98,7 @@ class InterceptorServer implements PublicInterceptorServer {
}
for (const exitEvent of PROCESS_EXIT_EVENTS) {
- process.on(exitEvent, async () => {
- await this.stop();
- });
+ process.on(exitEvent, this.stop);
}
this._httpServer = createServer({
@@ -192,18 +190,18 @@ class InterceptorServer implements PublicInterceptorServer {
}
}
- async stop() {
+ stop = async () => {
if (!this.isRunning()) {
return;
}
- await this.stopWebSocketServer();
- await this.stopHttpServer();
-
for (const exitEvent of PROCESS_EXIT_EVENTS) {
- process.removeAllListeners(exitEvent);
+ process.removeListener(exitEvent, this.stop);
}
- }
+
+ await this.stopWebSocketServer();
+ await this.stopHttpServer();
+ };
private async stopHttpServer() {
const httpServer = this.httpServer();
From e3ebe11e747b196ae9c7ac4ee2016441e28c7a98 Mon Sep 17 00:00:00 2001
From: Diego Aquino
Date: Sun, 26 May 2024 18:27:54 -0300
Subject: [PATCH 10/10] docs(#zimic): improve documentation
---
README.md | 9 ++++-----
.../zimic/src/interceptor/server/factory.ts | 2 +-
.../src/interceptor/server/types/public.ts | 19 +++++++++----------
3 files changed, 14 insertions(+), 16 deletions(-)
diff --git a/README.md b/README.md
index af289aae..990a3234 100644
--- a/README.md
+++ b/README.md
@@ -1843,25 +1843,24 @@ should automatically stop after the command finishes.
#### Programmatic usage
The module `zimic/server` exports resources for managing interceptor servers programmatically. Even though we recommend
-using the CLI, this module is also an option.
+using the CLI, this module is a valid alternative for more advanced use cases.
```ts
import { createInterceptorServer, runCommand } from 'zimic/server';
const server = createInterceptorServer({ hostname: 'localhost', port: 3000 });
-
await server.start();
-console.log(server.isRunning()); // true
+// Run a command when the server is ready
const [command, ...commandArguments] = process.argv.slice(3);
await runCommand(command, commandArguments);
await server.stop();
```
-The export `runCommand` is a helper function that runs a command with its arguments. The
+The helper function `runCommand` is useful to run a shell command in server scripts. The
[Next.js App Router](./examples/README.md#nextjs) and the [Playwright](./examples/README.md#playwright) examples use
-this function to run the application after loading the interceptors and fixtures.
+this function to run the application after the interceptor server is ready and all mocks are set up.
---
diff --git a/packages/zimic/src/interceptor/server/factory.ts b/packages/zimic/src/interceptor/server/factory.ts
index 190dc7dd..9196e11e 100644
--- a/packages/zimic/src/interceptor/server/factory.ts
+++ b/packages/zimic/src/interceptor/server/factory.ts
@@ -10,6 +10,6 @@ import { InterceptorServer as PublicInterceptorServer } from './types/public';
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
* @see {@link https://github.com/zimicjs/zimic#remote-http-interceptors Remote HTTP Interceptors} .
*/
-export function createInterceptorServer(options?: InterceptorServerOptions): PublicInterceptorServer {
+export function createInterceptorServer(options: InterceptorServerOptions = {}): PublicInterceptorServer {
return new InterceptorServer(options);
}
diff --git a/packages/zimic/src/interceptor/server/types/public.ts b/packages/zimic/src/interceptor/server/types/public.ts
index 203bba61..cc9ee36b 100644
--- a/packages/zimic/src/interceptor/server/types/public.ts
+++ b/packages/zimic/src/interceptor/server/types/public.ts
@@ -6,42 +6,41 @@
*/
export interface InterceptorServer {
/**
- * The hostname of the server.
- *
+ * @returns The hostname of the server.
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/
hostname: () => string;
/**
- * The port of the server.
- *
+ * @returns The port of the server.
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/
port: () => number | undefined;
/**
- * The HTTP URL of the server.
- *
+ * @returns The HTTP URL of the server.
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/
httpURL: () => string | undefined;
/**
- * Whether the server is running.
- *
+ * @returns Whether the server is running.
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/
isRunning: () => boolean;
/**
- * Start the server.
+ * Starts the server.
+ *
+ * The server is automatically stopped if a process exit event is detected, such as SIGINT, SIGTERM, or an uncaught
+ * exception.
*
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/
start: () => Promise;
/**
- * Stop the server.
+ * Stops the server.
*
* @see {@link https://github.com/zimicjs/zimic#zimic-server `zimic server` API reference}
*/