From d77bafaeea3355350a1360fcb8150aa516ea3f97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Zaninotto?= Date: Fri, 7 Jun 2024 00:30:59 +0200 Subject: [PATCH] Make sinon work --- example/sinon.ts | 49 +++++++++---------------------- src/BaseServer.ts | 6 +--- src/adapters/FetchMockServer.ts | 4 +-- src/adapters/MswServer.ts | 4 +-- src/adapters/SinonServer.ts | 51 +++++++++++++++++---------------- src/types.ts | 5 ++++ 6 files changed, 49 insertions(+), 70 deletions(-) diff --git a/example/sinon.ts b/example/sinon.ts index abf95a8..941eb8d 100644 --- a/example/sinon.ts +++ b/example/sinon.ts @@ -9,42 +9,19 @@ export const initializeSinon = () => { baseUrl: 'http://localhost:3000', data, loggingEnabled: true, - }); - - restServer.addMiddleware(withDelay(300)); - restServer.addMiddleware(async (request, context, next) => { - if (request.requestHeaders.Authorization === undefined) { - return { - status: 401, - headers: {}, - }; - } - - return next(request, context); - }); - - restServer.addMiddleware(async (request, context, next) => { - if (context.collection === 'books' && request.method === 'POST') { - if ( - restServer.database.getCount(context.collection, { - filter: { - title: context.requestBody?.title, - }, - }) > 0 - ) { - return { - status: 400, - headers: {}, - body: { - errors: { - title: 'An article with this title already exists. The title must be unique.', - }, - }, - }; - } - } - - return next(request, context); + middlewares: [ + withDelay(300), + async (context, next) => { + if (!context.headers?.get('Authorization')) { + return { + status: 401, + headers: {}, + }; + } + return next(context); + }, + // FIXME: add validation middleware + ], }); // use sinon.js to monkey-patch XmlHttpRequest diff --git a/src/BaseServer.ts b/src/BaseServer.ts index fa9fa3a..5a79288 100644 --- a/src/BaseServer.ts +++ b/src/BaseServer.ts @@ -6,6 +6,7 @@ import type { FakeRestContext, CollectionItem, QueryFunction, + NormalizedRequest, } from './types.js'; export class BaseServer { @@ -396,8 +397,3 @@ export type BaseServerOptions = DatabaseOptions & { defaultQuery?: QueryFunction; middlewares?: Array; }; - -export type NormalizedRequest = Pick< - FakeRestContext, - 'url' | 'method' | 'params' | 'requestBody' | 'headers' ->; diff --git a/src/adapters/FetchMockServer.ts b/src/adapters/FetchMockServer.ts index 0fd4703..afcf813 100644 --- a/src/adapters/FetchMockServer.ts +++ b/src/adapters/FetchMockServer.ts @@ -1,7 +1,7 @@ import { BaseServer } from '../BaseServer.js'; import { parseQueryString } from '../parseQueryString.js'; -import type { BaseServerOptions, NormalizedRequest } from '../BaseServer.js'; -import type { BaseResponse, APIServer } from '../types.js'; +import type { BaseServerOptions } from '../BaseServer.js'; +import type { BaseResponse, APIServer, NormalizedRequest } from '../types.js'; import type { MockResponseObject } from 'fetch-mock'; export class FetchMockServer { diff --git a/src/adapters/MswServer.ts b/src/adapters/MswServer.ts index 6bc7b26..ee8f134 100644 --- a/src/adapters/MswServer.ts +++ b/src/adapters/MswServer.ts @@ -1,7 +1,7 @@ import { http, HttpResponse } from 'msw'; import { BaseServer } from '../BaseServer.js'; -import type { BaseServerOptions, NormalizedRequest } from '../BaseServer.js'; -import type { APIServer } from '../types.js'; +import type { BaseServerOptions } from '../BaseServer.js'; +import type { APIServer, NormalizedRequest } from '../types.js'; export class MswServer { server: APIServer; diff --git a/src/adapters/SinonServer.ts b/src/adapters/SinonServer.ts index 1e38b5b..4d4f226 100644 --- a/src/adapters/SinonServer.ts +++ b/src/adapters/SinonServer.ts @@ -1,30 +1,39 @@ import type { SinonFakeXMLHttpRequest } from 'sinon'; -import { - type BaseResponse, - BaseServer, - type BaseServerOptions, -} from '../BaseServer.js'; +import { BaseServer, type BaseServerOptions } from '../BaseServer.js'; import { parseQueryString } from '../parseQueryString.js'; +import type { BaseResponse, APIServer, NormalizedRequest } from '../types.js'; -export class SinonServer extends BaseServer< - SinonFakeXMLHttpRequest, - SinonFakeRestResponse -> { +export class SinonServer { loggingEnabled = false; + server: APIServer; constructor({ loggingEnabled = false, + server, ...options }: SinonServerOptions = {}) { - super(options); + this.server = server || new BaseServer(options); this.loggingEnabled = loggingEnabled; } - toggleLogging() { - this.loggingEnabled = !this.loggingEnabled; + getHandler() { + return (request: SinonFakeXMLHttpRequest) => { + // This is an internal property of SinonFakeXMLHttpRequest but we have to set it to 4 to + // suppress sinon's synchronous processing (which would result in HTTP 404). This allows us + // to handle the request asynchronously. + // See https://github.com/sinonjs/sinon/issues/637 + // @ts-expect-error + request.readyState = 4; + const normalizedRequest = this.getNormalizedRequest(request); + this.server + .handle(normalizedRequest) + .then((response) => this.respond(response, request)); + // Let Sinon know we've handled the request + return true; + }; } - async getNormalizedRequest(request: SinonFakeXMLHttpRequest) { + getNormalizedRequest(request: SinonFakeXMLHttpRequest): NormalizedRequest { const req: Request | SinonFakeXMLHttpRequest = typeof request === 'string' ? new Request(request) : request; @@ -45,6 +54,7 @@ export class SinonServer extends BaseServer< return { url: req.url, + headers: new Headers(request.requestHeaders), params, requestBody, method: req.method, @@ -135,18 +145,8 @@ export class SinonServer extends BaseServer< } } - getHandler() { - return (request: SinonFakeXMLHttpRequest) => { - // This is an internal property of SinonFakeXMLHttpRequest but we have to set it to 4 to - // suppress sinon's synchronous processing (which would result in HTTP 404). This allows us - // to handle the request asynchronously. - // See https://github.com/sinonjs/sinon/issues/637 - // @ts-expect-error - request.readyState = 4; - this.handle(request); - // Let Sinon know we've handled the request - return true; - }; + toggleLogging() { + this.loggingEnabled = !this.loggingEnabled; } } @@ -167,5 +167,6 @@ export type SinonFakeRestResponse = { }; export type SinonServerOptions = BaseServerOptions & { + server?: APIServer; loggingEnabled?: boolean; }; diff --git a/src/types.ts b/src/types.ts index d27aefb..465df28 100644 --- a/src/types.ts +++ b/src/types.ts @@ -48,6 +48,11 @@ export type FakeRestContext = { params: { [key: string]: any }; }; +export type NormalizedRequest = Pick< + FakeRestContext, + 'url' | 'method' | 'params' | 'requestBody' | 'headers' +>; + export type APIServer = { baseUrl?: string; handle: (context: FakeRestContext) => Promise;