@neoma/fixtures 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Shipdventures
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # @neoma/fixtures
2
+
3
+ Test fixtures for @neoma/* NestJS packages. Provides mock Express and NestJS objects, custom Jest matchers, and a mock logger for unit-testing NestJS building blocks (guards, interceptors, filters, pipes, middleware).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install --save-dev @neoma/fixtures
9
+ ```
10
+
11
+ ### Peer dependencies
12
+
13
+ - `@nestjs/common` 11.x
14
+ - `jest` >= 29.0.0
15
+ - `@types/jest` >= 29.0.0
16
+
17
+ ## Usage
18
+
19
+ ### Express mocks
20
+
21
+ ```typescript
22
+ import { express } from '@neoma/fixtures'
23
+ import type { MockRequest, MockResponse } from '@neoma/fixtures'
24
+
25
+ // Create a request with randomized defaults
26
+ const req = express.request()
27
+
28
+ // Override specific properties
29
+ const customReq = express.request({
30
+ method: 'POST',
31
+ headers: { authorization: 'Bearer token' },
32
+ body: { name: 'test' },
33
+ })
34
+
35
+ // Case-insensitive header access
36
+ req.get('Authorization') // => 'Bearer token'
37
+ req.get('authorization') // => 'Bearer token'
38
+ req.header('Authorization') // => 'Bearer token'
39
+
40
+ // Create a response with locals and headers
41
+ const res = express.response({
42
+ locals: { user: { id: 'abc' } },
43
+ headers: { 'Content-Type': 'text/html' },
44
+ })
45
+
46
+ // status() sets statusCode AND returns this for chaining
47
+ res.status(404).json({ error: 'Not found' })
48
+ expect(res.statusCode).toBe(404)
49
+ expect(res.json).toHaveBeenCalledWith({ error: 'Not found' })
50
+
51
+ // Mutable headers
52
+ res.setHeader('X-Custom', 'value')
53
+ res.getHeader('x-custom') // => 'value'
54
+
55
+ // Create a signed cookie (HMAC-SHA256, cookie-parser format)
56
+ const cookie = express.cookie('user123', 'my-secret')
57
+ const jsonCookie = express.cookie({ userId: 'abc' }, 'my-secret')
58
+ ```
59
+
60
+ ### NestJS ExecutionContext
61
+
62
+ ```typescript
63
+ import { executionContext, express } from '@neoma/fixtures'
64
+
65
+ // Minimal context (just switchToHttp)
66
+ const ctx = executionContext(req, res)
67
+ ctx.switchToHttp().getRequest() // => req
68
+ ctx.switchToHttp().getResponse() // => res
69
+
70
+ // With bare handler function (for isolated guard testing)
71
+ const handler = (): void => {}
72
+ Reflect.defineMetadata('roles', ['admin'], handler)
73
+ const ctx = executionContext(req, res, handler)
74
+ ctx.getHandler() // => handler
75
+ ctx.getClass() // => Object
76
+
77
+ // With custom class
78
+ const ctx = executionContext(req, res, handler, MyController)
79
+ ctx.getClass() // => MyController
80
+
81
+ // With typed route object (for integration-style testing)
82
+ const ctx = executionContext(req, res, {
83
+ controller: UserController,
84
+ method: 'findAll',
85
+ })
86
+ ctx.getHandler() // => UserController.prototype.findAll
87
+ ctx.getClass() // => UserController
88
+ ```
89
+
90
+ ### MockLoggerService
91
+
92
+ ```typescript
93
+ import { MockLoggerService } from '@neoma/fixtures'
94
+
95
+ const logger = new MockLoggerService()
96
+ // Use anywhere NestJS expects a LoggerService
97
+ // All methods (log, error, warn, debug, verbose, trace, fatal, setLogLevels) are jest.fn()
98
+
99
+ logger.error('something failed')
100
+ expect(logger.error).toHaveBeenCalledWith('something failed')
101
+ ```
102
+
103
+ ### Custom Jest matchers
104
+
105
+ Add to your Jest config's `setupFilesAfterEnv`:
106
+
107
+ ```json
108
+ {
109
+ "setupFilesAfterEnv": ["@neoma/fixtures/matchers"]
110
+ }
111
+ ```
112
+
113
+ Then use in tests:
114
+
115
+ ```typescript
116
+ // Check that a function throws a specific error class
117
+ expect(() => service.register(email)).toThrowMatching(
118
+ EmailAlreadyExistsException,
119
+ )
120
+
121
+ // Check class + properties
122
+ expect(() => service.register(email)).toThrowMatching(
123
+ EmailAlreadyExistsException,
124
+ { email: 'test@example.com' },
125
+ )
126
+
127
+ // Check an already-caught error
128
+ expect(caughtError).toMatchError(NotFoundException, { message: 'Not found' })
129
+ ```
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,80 @@
1
+ import { type IncomingHttpHeaders, type OutgoingHttpHeaders } from "http";
2
+ import { type Socket } from "net";
3
+ /**
4
+ * Mirrors the Express Response interface — method signatures match
5
+ * express.Response, not the underlying Node http.ServerResponse.
6
+ */
7
+ export interface MockResponse {
8
+ statusCode?: number;
9
+ getHeaders(): OutgoingHttpHeaders;
10
+ get(name: string): string | undefined;
11
+ header(field: string, value?: string | Array<string>): MockResponse;
12
+ getHeader(name: string): string | number | string[] | undefined;
13
+ setHeader(name: string, value: string | string[]): MockResponse;
14
+ removeHeader(name: string): void;
15
+ cookie: jest.Mock;
16
+ clearCookie: jest.Mock;
17
+ end: jest.Mock;
18
+ status(code: number): MockResponse;
19
+ json: jest.Mock;
20
+ render: jest.Mock;
21
+ redirect: jest.Mock;
22
+ send: jest.Mock;
23
+ locals: Record<string, any>;
24
+ }
25
+ export interface MockRequest {
26
+ get(name: string): any;
27
+ header(name: string): any;
28
+ body: any;
29
+ headers: IncomingHttpHeaders;
30
+ method: string;
31
+ url: string;
32
+ res: MockResponse;
33
+ path: string;
34
+ params: Record<string, string>;
35
+ signedCookies: Record<string, string>;
36
+ connection: Socket;
37
+ [key: string]: any;
38
+ }
39
+ type ExpressFixtures = {
40
+ /**
41
+ * Creates a signed cookie string using the provided value and secret according
42
+ * to how the cookie-parser library would sign a cookie, i.e. HMAC-SHA256.
43
+ *
44
+ * @param val The cookie value to sign. If an object, it will be JSON.stringified
45
+ * with the `s:j:` prefix before signing.
46
+ * @param secret The secret to use to sign the cookie. If not provided, an unsigned
47
+ * cookie will be returned.
48
+ *
49
+ * @returns The signed value string only — no cookie name prefix, no flags.
50
+ * Format: `${encodedPrefix}${value}.${encodedSignature}`
51
+ */
52
+ cookie(val: string | object, secret?: string): string;
53
+ /**
54
+ * Creates a MockResponse with status, json, and header functions that
55
+ * are instances of a jest.Mock and with a locals property.
56
+ *
57
+ * @param options.locals Any locals to populate the response's locals property.
58
+ * @param options.headers Any headers to set on the response. They will be accessible through
59
+ * both the getHeaders and get functions.
60
+ *
61
+ * @returns A MockResponse with status, get, getHeaders, getHeader, setHeader,
62
+ * removeHeader, json, header, render, redirect, send, cookie, clearCookie,
63
+ * and end functions, and a locals property.
64
+ */
65
+ response: (options?: {
66
+ locals?: Record<string, any>;
67
+ headers?: OutgoingHttpHeaders;
68
+ }) => MockResponse;
69
+ /**
70
+ * Creates a MockRequest with body, and headers properties, and a mock response
71
+ * object. Also adds convenience methods get and header to provide case insensitive
72
+ * access to the request headers.
73
+ *
74
+ * @param req A Partial MockRequest to provide values for body, headers, and res objects,
75
+ * and get, and headers functions. Any properties not provided will use sensible defaults.
76
+ */
77
+ request: (options?: Partial<MockRequest>) => MockRequest;
78
+ };
79
+ export declare const express: ExpressFixtures;
80
+ export {};
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.express = void 0;
7
+ const crypto_1 = __importDefault(require("crypto"));
8
+ const faker_1 = require("@faker-js/faker");
9
+ const { helpers, internet, system } = faker_1.faker;
10
+ const caseInsensitiveSearch = (obj, key) => {
11
+ return obj[key.toLowerCase()];
12
+ };
13
+ const convertHeadersToLowerCase = (headers = {}) => {
14
+ const result = {};
15
+ for (const key of Object.keys(headers)) {
16
+ result[key.toLowerCase()] = headers[key];
17
+ }
18
+ return result;
19
+ };
20
+ exports.express = {
21
+ cookie(val, secret) {
22
+ const cookieValue = typeof val === "string" ? val : `j:${JSON.stringify(val)}`;
23
+ const prefix = "s:";
24
+ if (!secret) {
25
+ return `${encodeURIComponent(prefix)}${cookieValue}`;
26
+ }
27
+ const signature = crypto_1.default
28
+ .createHmac("sha256", secret)
29
+ .update(cookieValue)
30
+ .digest("base64")
31
+ .replace(/=+$/, "");
32
+ return `${encodeURIComponent(prefix)}${cookieValue}.${encodeURIComponent(signature)}`;
33
+ },
34
+ response({ locals = {}, headers = {}, } = {
35
+ locals: {},
36
+ headers: {},
37
+ }) {
38
+ const clonedHeaders = convertHeadersToLowerCase(headers);
39
+ return {
40
+ getHeaders() {
41
+ return clonedHeaders;
42
+ },
43
+ get(name) {
44
+ return caseInsensitiveSearch(clonedHeaders, name);
45
+ },
46
+ header(field, value) {
47
+ clonedHeaders[field.toLowerCase()] = value;
48
+ return this;
49
+ },
50
+ getHeader(name) {
51
+ return clonedHeaders[name.toLowerCase()];
52
+ },
53
+ setHeader(name, value) {
54
+ clonedHeaders[name.toLowerCase()] = value;
55
+ return this;
56
+ },
57
+ removeHeader(name) {
58
+ delete clonedHeaders[name];
59
+ delete clonedHeaders[name.toLowerCase()];
60
+ },
61
+ cookie: jest.fn().mockReturnThis(),
62
+ clearCookie: jest.fn().mockReturnThis(),
63
+ end: jest.fn().mockReturnThis(),
64
+ status(code) {
65
+ this.statusCode = code;
66
+ return this;
67
+ },
68
+ json: jest.fn().mockReturnThis(),
69
+ render: jest.fn(),
70
+ redirect: jest.fn().mockReturnThis(),
71
+ send: jest.fn().mockReturnThis(),
72
+ locals,
73
+ };
74
+ },
75
+ request({ body = {}, headers = {}, method = helpers.arrayElement(["GET", "POST", "PUT", "DELETE", "PATCH"]), url = internet.url(), res = exports.express.response(), path = system.filePath(), params = {}, signedCookies = {}, } = {
76
+ body: {},
77
+ headers: {},
78
+ method: helpers.arrayElement(["GET", "POST", "PUT", "DELETE", "PATCH"]),
79
+ url: internet.url(),
80
+ res: exports.express.response(),
81
+ path: system.filePath(),
82
+ params: {},
83
+ signedCookies: {},
84
+ }) {
85
+ // Build the base object with the arguments spread, then normalize headers
86
+ // afterward so that the spread cannot re-add un-normalized keys.
87
+ const base = {
88
+ body,
89
+ method,
90
+ url,
91
+ res,
92
+ path,
93
+ params,
94
+ signedCookies,
95
+ // eslint-disable-next-line prefer-rest-params
96
+ ...arguments[0],
97
+ };
98
+ const normalizedHeaders = convertHeadersToLowerCase(base.headers ?? headers);
99
+ return {
100
+ get(name) {
101
+ return normalizedHeaders[name.toLowerCase()];
102
+ },
103
+ header(name) {
104
+ return normalizedHeaders[name.toLowerCase()];
105
+ },
106
+ ...base,
107
+ headers: normalizedHeaders,
108
+ // Must include the connection so that the Bunyan req serializer treats it as a real request.
109
+ connection: {},
110
+ };
111
+ },
112
+ };
113
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/express/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export { express } from "./express";
2
+ export type { MockRequest, MockResponse } from "./express";
3
+ export { executionContext } from "./nestjs";
4
+ export { MockLoggerService } from "./loggers";
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MockLoggerService = exports.executionContext = exports.express = void 0;
4
+ // Express mocks — mock request, response, and signed cookie helpers
5
+ var express_1 = require("./express");
6
+ Object.defineProperty(exports, "express", { enumerable: true, get: function () { return express_1.express; } });
7
+ // NestJS mocks — partial ExecutionContext for guard/interceptor/filter tests
8
+ var nestjs_1 = require("./nestjs");
9
+ Object.defineProperty(exports, "executionContext", { enumerable: true, get: function () { return nestjs_1.executionContext; } });
10
+ // Logger mock — implements LoggerService with jest.fn() methods
11
+ var loggers_1 = require("./loggers");
12
+ Object.defineProperty(exports, "MockLoggerService", { enumerable: true, get: function () { return loggers_1.MockLoggerService; } });
13
+ // Custom matchers are loaded separately via Jest setupFilesAfterEnv:
14
+ // import '@neoma/fixtures/matchers'
15
+ // They are NOT re-exported here — they self-register via expect.extend().
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,oEAAoE;AACpE,qCAAmC;AAA1B,kGAAA,OAAO,OAAA;AAGhB,6EAA6E;AAC7E,mCAA2C;AAAlC,0GAAA,gBAAgB,OAAA;AAEzB,gEAAgE;AAChE,qCAA6C;AAApC,4GAAA,iBAAiB,OAAA;AAE1B,qEAAqE;AACrE,sCAAsC;AACtC,0EAA0E"}
@@ -0,0 +1,23 @@
1
+ import { type LoggerService } from "@nestjs/common";
2
+ /**
3
+ * A mock service for representing the LoggerService in tests.
4
+ *
5
+ * @property debug A jest function that mocks the debug method from LoggerService.
6
+ * @property error A jest function that mocks the error method from LoggerService.
7
+ * @property fatal A jest function that mocks the fatal method from LoggerService.
8
+ * @property log A jest function that mocks the log method from LoggerService.
9
+ * @property trace A jest function that mocks the trace method from LoggerService.
10
+ * @property verbose A jest function that mocks the optional verbose method from LoggerService.
11
+ * @property warn A jest function that mocks the warn method from LoggerService.
12
+ * @property setLogLevels A jest function that mocks the optional setLogLevels method from LoggerService.
13
+ */
14
+ export declare class MockLoggerService implements LoggerService {
15
+ debug: jest.Mock<any, any, any>;
16
+ error: jest.Mock<any, any, any>;
17
+ fatal: jest.Mock<any, any, any>;
18
+ log: jest.Mock<any, any, any>;
19
+ trace: jest.Mock<any, any, any>;
20
+ verbose: jest.Mock<any, any, any>;
21
+ warn: jest.Mock<any, any, any>;
22
+ setLogLevels: jest.Mock<any, any, any>;
23
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MockLoggerService = void 0;
4
+ /**
5
+ * A mock service for representing the LoggerService in tests.
6
+ *
7
+ * @property debug A jest function that mocks the debug method from LoggerService.
8
+ * @property error A jest function that mocks the error method from LoggerService.
9
+ * @property fatal A jest function that mocks the fatal method from LoggerService.
10
+ * @property log A jest function that mocks the log method from LoggerService.
11
+ * @property trace A jest function that mocks the trace method from LoggerService.
12
+ * @property verbose A jest function that mocks the optional verbose method from LoggerService.
13
+ * @property warn A jest function that mocks the warn method from LoggerService.
14
+ * @property setLogLevels A jest function that mocks the optional setLogLevels method from LoggerService.
15
+ */
16
+ class MockLoggerService {
17
+ debug = jest.fn();
18
+ error = jest.fn();
19
+ fatal = jest.fn();
20
+ log = jest.fn();
21
+ trace = jest.fn();
22
+ verbose = jest.fn();
23
+ warn = jest.fn();
24
+ setLogLevels = jest.fn();
25
+ }
26
+ exports.MockLoggerService = MockLoggerService;
27
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/loggers/index.ts"],"names":[],"mappings":";;;AAEA;;;;;;;;;;;GAWG;AACH,MAAa,iBAAiB;IACrB,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACjB,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACjB,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACjB,GAAG,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACf,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACjB,OAAO,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IACnB,IAAI,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;IAChB,YAAY,GAAG,IAAI,CAAC,EAAE,EAAE,CAAA;CAChC;AATD,8CASC"}
@@ -0,0 +1,23 @@
1
+ declare global {
2
+ namespace jest {
3
+ interface Matchers<R> {
4
+ /**
5
+ * Checks if a function throws an instance of an error class, optionally
6
+ * with specific properties.
7
+ *
8
+ * @param ErrorClass The expected error class/constructor
9
+ * @param expectedProps Optional object of properties to match
10
+ */
11
+ toThrowMatching<T>(ErrorClass: new (...args: any[]) => T, expectedProps?: Partial<T>): R;
12
+ /**
13
+ * Checks if a value is an instance of an error class, optionally
14
+ * with specific properties.
15
+ *
16
+ * @param ErrorClass The expected error class/constructor
17
+ * @param expectedProps Optional object of properties to match
18
+ */
19
+ toMatchError<T>(ErrorClass: new (...args: any[]) => T, expectedProps?: Partial<T>): R;
20
+ }
21
+ }
22
+ }
23
+ export {};
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_util_1 = require("node:util");
4
+ const jest_matcher_utils_1 = require("jest-matcher-utils");
5
+ const checkErrorInstance = (subject, ErrorClass, expectedProps) => {
6
+ // Check type
7
+ if (!(subject instanceof ErrorClass)) {
8
+ return {
9
+ pass: false,
10
+ message: () =>
11
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- falsy check is intentional for constructor name fallback
12
+ `Expected ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(ErrorClass.name)} but got ${(0, jest_matcher_utils_1.RECEIVED_COLOR)(subject?.constructor?.name || typeof subject)}`,
13
+ };
14
+ }
15
+ // If no props specified, just type check passes
16
+ if (!expectedProps) {
17
+ return {
18
+ pass: true,
19
+ message: () => `Expected not to be ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(ErrorClass.name)}`,
20
+ };
21
+ }
22
+ // Check properties
23
+ for (const [key, value] of Object.entries(expectedProps)) {
24
+ if (!(0, node_util_1.isDeepStrictEqual)(subject[key], value)) {
25
+ return {
26
+ pass: false,
27
+ message: () => `Expected ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(ErrorClass.name)} with ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(`${key}: ${JSON.stringify(value)}`)}, got ${(0, jest_matcher_utils_1.RECEIVED_COLOR)(`${key}: ${JSON.stringify(subject[key])}`)}`,
28
+ };
29
+ }
30
+ }
31
+ return {
32
+ pass: true,
33
+ message: () => `Expected not to be ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(ErrorClass.name)} with given properties`,
34
+ };
35
+ };
36
+ const toThrowMatching = (subject, ErrorClass, expectedProps) => {
37
+ if (!(subject instanceof Function)) {
38
+ return {
39
+ pass: false,
40
+ message: () => `toThrowMatching requires a function, but received ${(0, jest_matcher_utils_1.RECEIVED_COLOR)(typeof subject)}. Use toMatchError to check an already-caught error.`,
41
+ };
42
+ }
43
+ try {
44
+ subject();
45
+ return {
46
+ pass: false,
47
+ message: () => `Expected function to throw ${(0, jest_matcher_utils_1.EXPECTED_COLOR)(ErrorClass.name)}, but it did not throw`,
48
+ };
49
+ }
50
+ catch (e) {
51
+ return checkErrorInstance(e, ErrorClass, expectedProps);
52
+ }
53
+ };
54
+ const toMatchError = (subject, ErrorClass, expectedProps) => {
55
+ if (subject instanceof Function) {
56
+ return {
57
+ pass: false,
58
+ message: () => `toMatchError requires an error instance, but received a function. Use toThrowMatching to assert on a throwing function.`,
59
+ };
60
+ }
61
+ return checkErrorInstance(subject, ErrorClass, expectedProps);
62
+ };
63
+ expect.extend({
64
+ toThrowMatching,
65
+ toMatchError,
66
+ });
67
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/matchers/index.ts"],"names":[],"mappings":";;AAAA,yCAA6C;AAE7C,2DAAmE;AAEnE,MAAM,kBAAkB,GAAG,CACzB,OAAgB,EAChB,UAAyC,EACzC,aAAuC,EACb,EAAE;IAC5B,aAAa;IACb,IAAI,CAAC,CAAC,OAAO,YAAY,UAAU,CAAC,EAAE,CAAC;QACrC,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE;YACZ,oIAAoI;YACpI,YAAY,IAAA,mCAAc,EAAC,UAAU,CAAC,IAAI,CAAC,YAAY,IAAA,mCAAc,EAAE,OAAe,EAAE,WAAW,EAAE,IAAI,IAAI,OAAO,OAAO,CAAC,EAAE;SACjI,CAAA;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO;YACL,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,GAAG,EAAE,CAAC,sBAAsB,IAAA,mCAAc,EAAC,UAAU,CAAC,IAAI,CAAC,EAAE;SACvE,CAAA;IACH,CAAC;IAED,mBAAmB;IACnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC;QACzD,IAAI,CAAC,IAAA,6BAAiB,EAAE,OAAe,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;YACrD,OAAO;gBACL,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,GAAG,EAAE,CACZ,YAAY,IAAA,mCAAc,EAAC,UAAU,CAAC,IAAI,CAAC,SAAS,IAAA,mCAAc,EAAC,GAAG,GAAG,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,IAAA,mCAAc,EAAC,GAAG,GAAG,KAAK,IAAI,CAAC,SAAS,CAAE,OAAe,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE;aACtL,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,IAAI;QACV,OAAO,EAAE,GAAG,EAAE,CACZ,sBAAsB,IAAA,mCAAc,EAAC,UAAU,CAAC,IAAI,CAAC,wBAAwB;KAChF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,eAAe,GAAG,CACtB,OAAgB,EAChB,UAAyC,EACzC,aAAuC,EACb,EAAE;IAC5B,IAAI,CAAC,CAAC,OAAO,YAAY,QAAQ,CAAC,EAAE,CAAC;QACnC,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CACZ,qDAAqD,IAAA,mCAAc,EAAC,OAAO,OAAO,CAAC,sDAAsD;SAC5I,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,OAAO,EAAE,CAAA;QACT,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CACZ,8BAA8B,IAAA,mCAAc,EAAC,UAAU,CAAC,IAAI,CAAC,wBAAwB;SACxF,CAAA;IACH,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,kBAAkB,CAAC,CAAC,EAAE,UAAU,EAAE,aAAa,CAAC,CAAA;IACzD,CAAC;AACH,CAAC,CAAA;AAED,MAAM,YAAY,GAAG,CACnB,OAAgB,EAChB,UAAyC,EACzC,aAAuC,EACb,EAAE;IAC5B,IAAI,OAAO,YAAY,QAAQ,EAAE,CAAC;QAChC,OAAO;YACL,IAAI,EAAE,KAAK;YACX,OAAO,EAAE,GAAG,EAAE,CACZ,yHAAyH;SAC5H,CAAA;IACH,CAAC;IAED,OAAO,kBAAkB,CAAC,OAAO,EAAE,UAAU,EAAE,aAAa,CAAC,CAAA;AAC/D,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,CAAC;IACZ,eAAe;IACf,YAAY;CACb,CAAC,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { type ExecutionContext } from "@nestjs/common";
2
+ import { type MockRequest, type MockResponse } from "../express";
3
+ /**
4
+ * Creates a partial ExecutionContext with a switchToHttp method that then allows
5
+ * access to req and res through getRequest and getResponse methods respectively.
6
+ *
7
+ * ExecutionContext extends ArgumentsHost so use this function to create
8
+ * ArgumentsHosts too.
9
+ *
10
+ * @param req A MockRequest that is returned when switchToHttp().getRequest is called.
11
+ * @param res A MockResponse that is returned when switchToHttp().getResponse is called.
12
+ * @param handler Either a bare handler function (for isolated guard testing where
13
+ * metadata is applied with Reflect.defineMetadata) or a typed route object (for
14
+ * integration-style testing where metadata lives on a real controller). When omitted,
15
+ * getHandler and getClass are not included on the returned context.
16
+ * @param cls An optional class returned by getClass(). Only meaningful when handler
17
+ * is a bare function; ignored when handler is a route object. Defaults to Object.
18
+ * @returns A partial ExecutionContext that supports switchToHttp and, when a handler
19
+ * is supplied, getHandler/getClass.
20
+ */
21
+ export declare const executionContext: (req?: MockRequest, res?: MockResponse, handler?: (() => void) | {
22
+ controller: new (...args: any[]) => any;
23
+ method: string;
24
+ }, cls?: new (...args: any[]) => any) => Partial<ExecutionContext>;
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.executionContext = void 0;
4
+ const express_1 = require("../express");
5
+ /**
6
+ * Creates a partial ExecutionContext with a switchToHttp method that then allows
7
+ * access to req and res through getRequest and getResponse methods respectively.
8
+ *
9
+ * ExecutionContext extends ArgumentsHost so use this function to create
10
+ * ArgumentsHosts too.
11
+ *
12
+ * @param req A MockRequest that is returned when switchToHttp().getRequest is called.
13
+ * @param res A MockResponse that is returned when switchToHttp().getResponse is called.
14
+ * @param handler Either a bare handler function (for isolated guard testing where
15
+ * metadata is applied with Reflect.defineMetadata) or a typed route object (for
16
+ * integration-style testing where metadata lives on a real controller). When omitted,
17
+ * getHandler and getClass are not included on the returned context.
18
+ * @param cls An optional class returned by getClass(). Only meaningful when handler
19
+ * is a bare function; ignored when handler is a route object. Defaults to Object.
20
+ * @returns A partial ExecutionContext that supports switchToHttp and, when a handler
21
+ * is supplied, getHandler/getClass.
22
+ */
23
+ const executionContext = (req = express_1.express.request(), res = req.res, handler, cls) => {
24
+ req.res = res;
25
+ let resolvedHandler;
26
+ let resolvedClass;
27
+ if (typeof handler === "function") {
28
+ // Features pattern: bare handler + optional class
29
+ resolvedHandler = handler;
30
+ resolvedClass = cls ?? Object;
31
+ }
32
+ else if (handler) {
33
+ // Garmr pattern: typed route object
34
+ resolvedHandler = handler.controller.prototype[handler.method];
35
+ resolvedClass = handler.controller;
36
+ }
37
+ return {
38
+ switchToHttp: jest.fn().mockReturnValue({
39
+ getResponse: jest.fn().mockReturnValue(res),
40
+ getRequest: jest.fn().mockReturnValue(req),
41
+ }),
42
+ ...(resolvedHandler && {
43
+ getHandler: jest.fn().mockReturnValue(resolvedHandler),
44
+ getClass: jest.fn().mockReturnValue(resolvedClass),
45
+ }),
46
+ };
47
+ };
48
+ exports.executionContext = executionContext;
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/nestjs/index.ts"],"names":[],"mappings":";;;AAEA,wCAAyE;AAEzE;;;;;;;;;;;;;;;;;GAiBG;AACI,MAAM,gBAAgB,GAAG,CAC9B,MAAmB,iBAAO,CAAC,OAAO,EAAE,EACpC,MAAoB,GAAG,CAAC,GAAG,EAC3B,OAE+D,EAC/D,GAAiC,EACN,EAAE;IAC7B,GAAG,CAAC,GAAG,GAAG,GAAG,CAAA;IAEb,IAAI,eAAyC,CAAA;IAC7C,IAAI,aAAwD,CAAA;IAE5D,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,kDAAkD;QAClD,eAAe,GAAG,OAAO,CAAA;QACzB,aAAa,GAAG,GAAG,IAAI,MAAM,CAAA;IAC/B,CAAC;SAAM,IAAI,OAAO,EAAE,CAAC;QACnB,oCAAoC;QACpC,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAC9D,aAAa,GAAG,OAAO,CAAC,UAAU,CAAA;IACpC,CAAC;IAED,OAAO;QACL,YAAY,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YACtC,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;YAC3C,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC;SAC3C,CAAC;QACF,GAAG,CAAC,eAAe,IAAI;YACrB,UAAU,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,eAAe,CAAC;YACtD,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,aAAa,CAAC;SACnD,CAAC;KACH,CAAA;AACH,CAAC,CAAA;AAjCY,QAAA,gBAAgB,oBAiC5B"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@neoma/fixtures",
3
+ "version": "0.1.0",
4
+ "description": "Test fixtures for @neoma/* NestJS packages",
5
+ "author": "Shipdventures",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/shipdventures/neoma-fixtures"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/shipdventures/neoma-fixtures/issues"
12
+ },
13
+ "homepage": "https://github.com/shipdventures/neoma-fixtures#readme",
14
+ "keywords": [
15
+ "nestjs",
16
+ "fixtures",
17
+ "testing",
18
+ "neoma"
19
+ ],
20
+ "private": false,
21
+ "main": "dist/index.js",
22
+ "types": "dist/index.d.ts",
23
+ "_comment": "This package uses an exports map instead of main+types only because the ./matchers sub-path must be loadable independently via Jest setupFilesAfterEnv without importing the main entry point.",
24
+ "exports": {
25
+ ".": {
26
+ "require": "./dist/index.js",
27
+ "types": "./dist/index.d.ts"
28
+ },
29
+ "./matchers": {
30
+ "require": "./dist/matchers/index.js",
31
+ "types": "./dist/matchers/index.d.ts"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "prepublishOnly": "cp ../../README.md ../../LICENSE ."
36
+ },
37
+ "files": [
38
+ "dist",
39
+ "README.md",
40
+ "LICENSE",
41
+ "!**/*.json",
42
+ "!**/*.tsbuildinfo"
43
+ ],
44
+ "license": "MIT",
45
+ "peerDependencies": {
46
+ "@nestjs/common": "11.x",
47
+ "@types/jest": ">=29.0.0",
48
+ "jest": ">=29.0.0"
49
+ },
50
+ "dependencies": {
51
+ "@faker-js/faker": "^9.0.0"
52
+ }
53
+ }