mirror of
https://github.com/nestjs/nest.git
synced 2026-02-21 23:11:44 +00:00
Merge pull request #16369 from nestjs/build/esm-migration
build: migrate from cjs to esm
This commit is contained in:
@@ -1,12 +1,6 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
check-legacy-node-version:
|
||||
type: boolean
|
||||
default: false
|
||||
legacy-node-version:
|
||||
type: string
|
||||
default: '18.20'
|
||||
maintenance-node-version:
|
||||
type: string
|
||||
default: '20.18'
|
||||
@@ -15,7 +9,7 @@ parameters:
|
||||
default: '22.11'
|
||||
current-node-version:
|
||||
type: string
|
||||
default: '23.3'
|
||||
default: '24.1'
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
@@ -62,61 +56,36 @@ jobs:
|
||||
docker:
|
||||
- image: cimg/node:<< parameters.node-version >>
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
- equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
]
|
||||
- not: << pipeline.parameters.check-legacy-node-version >>
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- run:
|
||||
name: Skip
|
||||
command: |
|
||||
echo Skipping
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- not:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
]
|
||||
- << pipeline.parameters.check-legacy-node-version >>
|
||||
not:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- when:
|
||||
condition:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- run:
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
- when:
|
||||
condition:
|
||||
not:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- *run-unit-tests
|
||||
- *run-unit-tests
|
||||
|
||||
lint:
|
||||
working_directory: ~/nest
|
||||
@@ -183,7 +152,6 @@ jobs:
|
||||
name: Build all samples
|
||||
command: npm run build:samples
|
||||
|
||||
|
||||
workflows:
|
||||
build-and-test:
|
||||
jobs:
|
||||
@@ -195,7 +163,6 @@ workflows:
|
||||
parameters:
|
||||
node-version:
|
||||
[
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
'<< pipeline.parameters.active-node-version >>',
|
||||
'<< pipeline.parameters.current-node-version >>',
|
||||
@@ -209,4 +176,3 @@ workflows:
|
||||
- samples:
|
||||
requires:
|
||||
- build
|
||||
|
||||
|
||||
@@ -116,7 +116,6 @@ We cannot accept code without this.
|
||||
1. In GitHub, send a pull request to `nestjs:master`.
|
||||
|
||||
- If we suggest changes then:
|
||||
|
||||
- Make the required updates.
|
||||
- Re-run the Nest test suites to ensure tests are still passing.
|
||||
- Rebase your branch and force push to your GitHub repository (this will update your Pull Request):
|
||||
@@ -159,7 +158,7 @@ from the main (upstream) repository:
|
||||
|
||||
## <a name="development"></a> Development Setup
|
||||
|
||||
You will need [Node.js](https://nodejs.org) version >= 10.13.0 (except for v13).
|
||||
You will need [Node.js](https://nodejs.org) version >= 20.
|
||||
|
||||
1. After cloning the repo, run:
|
||||
|
||||
@@ -322,8 +321,10 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
<!-- [coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md -->
|
||||
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
|
||||
<!-- [individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html -->
|
||||
<!-- [corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html -->
|
||||
|
||||
[dev-doc]: https://github.com/nestjs/nest/blob/master/docs/DEVELOPER.md
|
||||
[github]: https://github.com/nestjs/nest
|
||||
[stackoverflow]: https://stackoverflow.com/questions/tagged/nestjs
|
||||
|
||||
16
gulpfile.js
16
gulpfile.js
@@ -1,16 +0,0 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Load the TypeScript compiler, then load the TypeScript gulpfile which simply loads all
|
||||
* the tasks. The tasks are really inside tools/gulp/tasks.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
|
||||
const projectDir = __dirname;
|
||||
const tsconfigPath = path.join(projectDir, 'tools/gulp/tsconfig.json');
|
||||
|
||||
require('ts-node').register({
|
||||
project: tsconfigPath
|
||||
});
|
||||
|
||||
require('./tools/gulp/gulpfile');
|
||||
13
gulpfile.mjs
Normal file
13
gulpfile.mjs
Normal file
@@ -0,0 +1,13 @@
|
||||
/**
|
||||
* Load the TypeScript compiler, then load the TypeScript gulpfile which simply loads all
|
||||
* the tasks. The tasks are really inside tools/gulp/tasks.
|
||||
*/
|
||||
|
||||
import { register } from 'node:module';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
register('ts-node/esm', pathToFileURL('./'), {
|
||||
data: { project: './tools/gulp/tsconfig.json' },
|
||||
});
|
||||
|
||||
await import('./tools/gulp/gulpfile.ts');
|
||||
@@ -1,7 +0,0 @@
|
||||
export const mochaHooks = (): Mocha.RootHookObject => {
|
||||
return {
|
||||
async beforeAll(this: Mocha.Context) {
|
||||
await import('reflect-metadata');
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { FooService } from './foo.service';
|
||||
import { FooService } from './foo.service.js';
|
||||
|
||||
@Injectable()
|
||||
export class BarService {
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as chai from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import * as sinon from 'sinon';
|
||||
import { BarService } from '../src/bar.service';
|
||||
import { FooService } from '../src/foo.service';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const { expect } = chai;
|
||||
import { BarService } from '../src/bar.service.js';
|
||||
import { FooService } from '../src/foo.service.js';
|
||||
|
||||
describe('Auto-Mocking Bar Deps', () => {
|
||||
let service: BarService;
|
||||
let fooService: FooService;
|
||||
const stub = sinon.stub();
|
||||
const stub = vi.fn();
|
||||
beforeEach(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
providers: [BarService],
|
||||
@@ -23,12 +17,12 @@ describe('Auto-Mocking Bar Deps', () => {
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).not.to.be.undefined;
|
||||
expect(fooService).not.to.be.undefined;
|
||||
expect(service).not.toBeUndefined();
|
||||
expect(fooService).not.toBeUndefined();
|
||||
});
|
||||
it('should call bar.bar', () => {
|
||||
service.bar();
|
||||
expect(stub.called);
|
||||
expect(stub).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,23 +33,25 @@ describe('Auto-Mocking with token in factory', () => {
|
||||
})
|
||||
.useMocker(token => {
|
||||
if (token === FooService) {
|
||||
return { foo: sinon.stub };
|
||||
return { foo: vi.fn() };
|
||||
}
|
||||
})
|
||||
.compile();
|
||||
const service = moduleRef.get(BarService);
|
||||
const fooServ = moduleRef.get<{ foo: sinon.SinonStub }>(FooService as any);
|
||||
const fooServ = moduleRef.get<{ foo: ReturnType<typeof vi.fn> }>(
|
||||
FooService as any,
|
||||
);
|
||||
service.bar();
|
||||
expect(fooServ.foo.called);
|
||||
expect(fooServ.foo).toHaveBeenCalled();
|
||||
});
|
||||
it('cannot mock the dependencies', async () => {
|
||||
const moduleRef = Test.createTestingModule({
|
||||
providers: [BarService],
|
||||
}).useMocker(token => {
|
||||
if (token === FooService.name + 'something that fails the token') {
|
||||
return { foo: sinon.stub };
|
||||
return { foo: vi.fn() };
|
||||
}
|
||||
}).compile;
|
||||
expect(moduleRef()).to.eventually.throw();
|
||||
await expect(moduleRef()).rejects.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Express Cors', () => {
|
||||
let app: NestExpressApplication;
|
||||
@@ -25,7 +25,7 @@ describe('Express Cors', () => {
|
||||
];
|
||||
describe('Dynamic config', () => {
|
||||
describe('enableCors', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -66,13 +66,13 @@ describe('Express Cors', () => {
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Application Options', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -114,14 +114,14 @@ describe('Express Cors', () => {
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('Static config', () => {
|
||||
describe('enableCors', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -141,14 +141,14 @@ describe('Express Cors', () => {
|
||||
.expect('access-control-expose-headers', 'foo,bar')
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Application Options', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -169,7 +169,7 @@ describe('Express Cors', () => {
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,8 +3,8 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe.skip('Fastify Cors', () => {
|
||||
let app: NestFastifyApplication;
|
||||
@@ -28,7 +28,7 @@ describe.skip('Fastify Cors', () => {
|
||||
];
|
||||
describe('Dynamic config', () => {
|
||||
describe('enableCors', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -73,13 +73,13 @@ describe.skip('Fastify Cors', () => {
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Application Options', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -124,7 +124,7 @@ describe.skip('Fastify Cors', () => {
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -132,7 +132,7 @@ describe.skip('Fastify Cors', () => {
|
||||
|
||||
describe('Static config', () => {
|
||||
describe('enableCors', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -154,14 +154,14 @@ describe.skip('Fastify Cors', () => {
|
||||
.expect('access-control-expose-headers', 'foo,bar')
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Application Options', () => {
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
@@ -184,10 +184,10 @@ describe.skip('Fastify Cors', () => {
|
||||
.expect('access-control-expose-headers', 'foo,bar')
|
||||
.expect('content-length', '0');
|
||||
});
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await app.close();
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppController } from './app.controller.js';
|
||||
|
||||
@Module({
|
||||
controllers: [AppController],
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { WebhooksExplorer } from '../src/webhooks.explorer';
|
||||
import { NonAppliedDecorator } from '../src/decorators/non-applied.decorator';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
import { NonAppliedDecorator } from '../src/decorators/non-applied.decorator.js';
|
||||
import { WebhooksExplorer } from '../src/webhooks.explorer.js';
|
||||
|
||||
describe('DiscoveryModule', () => {
|
||||
let moduleRef: TestingModule;
|
||||
@@ -14,10 +13,14 @@ describe('DiscoveryModule', () => {
|
||||
}).compile();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await moduleRef.close();
|
||||
});
|
||||
|
||||
it('should discover all providers & handlers with corresponding annotations', async () => {
|
||||
const webhooksExplorer = moduleRef.get(WebhooksExplorer);
|
||||
|
||||
expect(webhooksExplorer.getWebhooks()).to.be.eql([
|
||||
expect(webhooksExplorer.getWebhooks()).toEqual([
|
||||
{
|
||||
handlers: [
|
||||
{
|
||||
@@ -45,7 +48,7 @@ describe('DiscoveryModule', () => {
|
||||
const providers = discoveryService.getProviders({
|
||||
metadataKey: NonAppliedDecorator.KEY,
|
||||
});
|
||||
expect(providers).to.be.eql([]);
|
||||
expect(providers).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if no controllers were found for a given discoverable decorator', () => {
|
||||
@@ -54,6 +57,6 @@ describe('DiscoveryModule', () => {
|
||||
const controllers = discoveryService.getControllers({
|
||||
metadataKey: NonAppliedDecorator.KEY,
|
||||
});
|
||||
expect(controllers).to.be.eql([]);
|
||||
expect(controllers).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
import { MyWebhookModule } from './my-webhook/my-webhook.module';
|
||||
import { WebhooksExplorer } from './webhooks.explorer';
|
||||
import { MyWebhookModule } from './my-webhook/my-webhook.module.js';
|
||||
import { WebhooksExplorer } from './webhooks.explorer.js';
|
||||
|
||||
@Module({
|
||||
imports: [MyWebhookModule, DiscoveryModule],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators.js';
|
||||
|
||||
@Webhook({ name: 'cleanup' })
|
||||
export class CleanupWebhook {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators.js';
|
||||
|
||||
@Webhook({ name: 'flush' })
|
||||
export class FlushWebhook {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CleanupWebhook } from './cleanup.webhook';
|
||||
import { FlushWebhook } from './flush.webhook';
|
||||
import { CleanupWebhook } from './cleanup.webhook.js';
|
||||
import { FlushWebhook } from './flush.webhook.js';
|
||||
|
||||
@Module({ providers: [CleanupWebhook, FlushWebhook] })
|
||||
export class MyWebhookModule {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
||||
import { Webhook, WebhookHandler } from './decorators/webhook.decorators';
|
||||
import { Webhook, WebhookHandler } from './decorators/webhook.decorators.js';
|
||||
|
||||
@Injectable()
|
||||
export class WebhooksExplorer {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('GraphQL - Code-first', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('GraphQL - Guards', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication, ValidationPipe } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('GraphQL Pipes', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { join } from 'path';
|
||||
import { RecipesModule } from './recipes/recipes.module';
|
||||
import { RecipesModule } from './recipes/recipes.module.js';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { AppModule } from './app.module.js';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { APP_FILTER } from '@nestjs/core';
|
||||
import { UnauthorizedFilter } from '../common/filters/unauthorized.filter';
|
||||
import { DateScalar } from '../common/scalars/date.scalar';
|
||||
import { RecipesResolver } from './recipes.resolver';
|
||||
import { RecipesService } from './recipes.service';
|
||||
import { UnauthorizedFilter } from '../common/filters/unauthorized.filter.js';
|
||||
import { DateScalar } from '../common/scalars/date.scalar.js';
|
||||
import { RecipesResolver } from './recipes.resolver.js';
|
||||
import { RecipesService } from './recipes.service.js';
|
||||
|
||||
@Module({
|
||||
providers: [
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { NotFoundException, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql';
|
||||
import { PubSub } from 'graphql-subscriptions';
|
||||
import { AuthGuard } from '../common/guards/auth.guard';
|
||||
import { DataInterceptor } from '../common/interceptors/data.interceptor';
|
||||
import { NewRecipeInput } from './dto/new-recipe.input';
|
||||
import { RecipesArgs } from './dto/recipes.args';
|
||||
import { Recipe } from './models/recipe';
|
||||
import { RecipesService } from './recipes.service';
|
||||
import { AuthGuard } from '../common/guards/auth.guard.js';
|
||||
import { DataInterceptor } from '../common/interceptors/data.interceptor.js';
|
||||
import { NewRecipeInput } from './dto/new-recipe.input.js';
|
||||
import { RecipesArgs } from './dto/recipes.args.js';
|
||||
import { Recipe } from './models/recipe.js';
|
||||
import { RecipesService } from './recipes.service.js';
|
||||
|
||||
const pubSub = new PubSub();
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { NewRecipeInput } from './dto/new-recipe.input';
|
||||
import { RecipesArgs } from './dto/recipes.args';
|
||||
import { Recipe } from './models/recipe';
|
||||
import { NewRecipeInput } from './dto/new-recipe.input.js';
|
||||
import { RecipesArgs } from './dto/recipes.args.js';
|
||||
import { Recipe } from './models/recipe.js';
|
||||
|
||||
@Injectable()
|
||||
export class RecipesService {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncClassApplicationModule } from '../src/async-options-class.module';
|
||||
import request from 'supertest';
|
||||
import { AsyncClassApplicationModule } from '../src/async-options-class.module.js';
|
||||
|
||||
describe('GraphQL (async class)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncExistingApplicationModule } from '../src/async-options-existing.module';
|
||||
import request from 'supertest';
|
||||
import { AsyncExistingApplicationModule } from '../src/async-options-existing.module.js';
|
||||
|
||||
describe('GraphQL (async existing)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import * as request from 'supertest';
|
||||
import { AsyncApplicationModule } from '../src/async-options.module';
|
||||
import request from 'supertest';
|
||||
import { AsyncApplicationModule } from '../src/async-options.module.js';
|
||||
|
||||
describe('GraphQL (async configuration)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -2,11 +2,10 @@ import { ApolloDriver } from '@nestjs/apollo';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { join } from 'path';
|
||||
import * as request from 'supertest';
|
||||
import { CatsRequestScopedService } from '../src/cats/cats-request-scoped.service';
|
||||
import { CatsModule } from '../src/cats/cats.module';
|
||||
import request from 'supertest';
|
||||
import { CatsRequestScopedService } from '../src/cats/cats-request-scoped.service.js';
|
||||
import { CatsModule } from '../src/cats/cats.module.js';
|
||||
|
||||
describe('GraphQL request scoped', () => {
|
||||
let app: INestApplication;
|
||||
@@ -17,7 +16,9 @@ describe('GraphQL request scoped', () => {
|
||||
CatsModule.enableRequestScope(),
|
||||
GraphQLModule.forRoot({
|
||||
driver: ApolloDriver,
|
||||
typePaths: [join(__dirname, '..', 'src', '**', '*.graphql')],
|
||||
typePaths: [
|
||||
join(import.meta.dirname, '..', 'src', '**', '*.graphql'),
|
||||
],
|
||||
}),
|
||||
],
|
||||
}).compile();
|
||||
@@ -53,7 +54,7 @@ describe('GraphQL request scoped', () => {
|
||||
});
|
||||
|
||||
it(`should create resolver for each incoming request`, () => {
|
||||
expect(CatsRequestScopedService.COUNTER).to.be.eql(3);
|
||||
expect(CatsRequestScopedService.COUNTER).toEqual(3);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('GraphQL', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { join } from 'path';
|
||||
import { CatsModule } from './cats/cats.module';
|
||||
import { CatsModule } from './cats/cats.module.js';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -10,7 +10,7 @@ import { CatsModule } from './cats/cats.module';
|
||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||
driver: ApolloDriver,
|
||||
includeStacktraceInErrorResponses: true,
|
||||
typePaths: [join(__dirname, '**', '*.graphql')],
|
||||
typePaths: [join(import.meta.dirname, '**', '*.graphql')],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
@@ -2,12 +2,12 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GqlOptionsFactory, GraphQLModule } from '@nestjs/graphql';
|
||||
import { join } from 'path';
|
||||
import { CatsModule } from './cats/cats.module';
|
||||
import { CatsModule } from './cats/cats.module.js';
|
||||
|
||||
class ConfigService implements GqlOptionsFactory {
|
||||
createGqlOptions(): ApolloDriverConfig {
|
||||
return {
|
||||
typePaths: [join(__dirname, '**', '*.graphql')],
|
||||
typePaths: [join(import.meta.dirname, '**', '*.graphql')],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { CatsModule } from './cats/cats.module';
|
||||
import { ConfigModule } from './config.module';
|
||||
import { ConfigService } from './config.service';
|
||||
import { CatsModule } from './cats/cats.module.js';
|
||||
import { ConfigModule } from './config.module.js';
|
||||
import { ConfigService } from './config.service.js';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GraphQLModule } from '@nestjs/graphql';
|
||||
import { join } from 'path';
|
||||
import { CatsModule } from './cats/cats.module';
|
||||
import { CatsModule } from './cats/cats.module.js';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -10,7 +10,7 @@ import { CatsModule } from './cats/cats.module';
|
||||
GraphQLModule.forRootAsync<ApolloDriverConfig>({
|
||||
driver: ApolloDriver,
|
||||
useFactory: async () => ({
|
||||
typePaths: [join(__dirname, '**', '*.graphql')],
|
||||
typePaths: [join(import.meta.dirname, '**', '*.graphql')],
|
||||
}),
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { Cat } from './interfaces/cat.interface';
|
||||
import { Cat } from './interfaces/cat.interface.js';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class CatsRequestScopedService {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { DynamicModule, Module, Scope } from '@nestjs/common';
|
||||
import { CatsRequestScopedService } from './cats-request-scoped.service';
|
||||
import { CatsResolvers } from './cats.resolvers';
|
||||
import { CatsService } from './cats.service';
|
||||
import { CatsRequestScopedService } from './cats-request-scoped.service.js';
|
||||
import { CatsResolvers } from './cats.resolvers.js';
|
||||
import { CatsService } from './cats.service.js';
|
||||
|
||||
@Module({
|
||||
providers: [CatsService, CatsResolvers],
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ParseIntPipe, UseGuards } from '@nestjs/common';
|
||||
import { Args, Mutation, Query, Resolver, Subscription } from '@nestjs/graphql';
|
||||
import { PubSub } from 'graphql-subscriptions';
|
||||
import { CatsGuard } from './cats.guard';
|
||||
import { CatsService } from './cats.service';
|
||||
import { Cat } from './interfaces/cat.interface';
|
||||
import { CatsGuard } from './cats.guard.js';
|
||||
import { CatsService } from './cats.service.js';
|
||||
import { Cat } from './interfaces/cat.interface.js';
|
||||
|
||||
const pubSub = new PubSub();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cat } from './interfaces/cat.interface';
|
||||
import { Cat } from './interfaces/cat.interface.js';
|
||||
|
||||
@Injectable()
|
||||
export class CatsService {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigService } from './config.service';
|
||||
import { ConfigService } from './config.service.js';
|
||||
|
||||
@Module({
|
||||
providers: [ConfigService],
|
||||
|
||||
@@ -6,7 +6,7 @@ import { join } from 'path';
|
||||
export class ConfigService implements GqlOptionsFactory {
|
||||
createGqlOptions(): GqlModuleOptions {
|
||||
return {
|
||||
typePaths: [join(__dirname, '**', '*.graphql')],
|
||||
typePaths: [join(import.meta.dirname, '**', '*.graphql')],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { AppModule } from './app.module.js';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -4,10 +4,9 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { RawServerDefault } from 'fastify';
|
||||
import * as request from 'supertest';
|
||||
import { ErrorsController } from '../src/errors/errors.controller';
|
||||
import request from 'supertest';
|
||||
import { ErrorsController } from '../src/errors/errors.controller.js';
|
||||
|
||||
describe('Error messages', () => {
|
||||
let server: RawServerDefault;
|
||||
@@ -82,14 +81,12 @@ describe('Error messages', () => {
|
||||
url: '/sync',
|
||||
})
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).to.equal(HttpStatus.BAD_REQUEST);
|
||||
expect(payload).to.equal(
|
||||
JSON.stringify({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'Integration test',
|
||||
}),
|
||||
);
|
||||
expect(statusCode).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(JSON.parse(payload)).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'Integration test',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,17 +94,15 @@ describe('Error messages', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/sync',
|
||||
url: '/async',
|
||||
})
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).to.equal(HttpStatus.BAD_REQUEST);
|
||||
expect(payload).to.equal(
|
||||
JSON.stringify({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'Integration test',
|
||||
}),
|
||||
);
|
||||
expect(statusCode).toBe(HttpStatus.BAD_REQUEST);
|
||||
expect(JSON.parse(payload)).toEqual({
|
||||
statusCode: 400,
|
||||
error: 'Bad Request',
|
||||
message: 'Integration test',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -118,13 +113,11 @@ describe('Error messages', () => {
|
||||
url: '/unexpected-error',
|
||||
})
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).to.equal(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(payload).to.equal(
|
||||
JSON.stringify({
|
||||
statusCode: 500,
|
||||
message: 'Internal server error',
|
||||
}),
|
||||
);
|
||||
expect(statusCode).toBe(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
expect(JSON.parse(payload)).toEqual({
|
||||
statusCode: 500,
|
||||
message: 'Internal server error',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
const MIDDLEWARE_VALUE = 'middleware';
|
||||
|
||||
@@ -8,8 +8,8 @@ import {
|
||||
RequestMethod,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
const MIDDLEWARE_VALUE = 'middleware';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as express from 'express';
|
||||
import * as request from 'supertest';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Hello world (express instance)', () => {
|
||||
let server: App;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as express from 'express';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Hello world (express instance with multiple applications)', () => {
|
||||
let server;
|
||||
|
||||
@@ -3,8 +3,7 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Hello world (fastify adapter)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
@@ -26,7 +25,10 @@ describe('Hello world (fastify adapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).toBe(200);
|
||||
expect(payload).toEqual('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
it(`/GET (Promise/async)`, () => {
|
||||
@@ -35,7 +37,10 @@ describe('Hello world (fastify adapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/hello/async',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).toBe(200);
|
||||
expect(payload).toEqual('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
it(`/GET (Observable stream)`, () => {
|
||||
@@ -44,7 +49,10 @@ describe('Hello world (fastify adapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/hello/stream',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).toBe(200);
|
||||
expect(payload).toEqual('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
it(`/GET { host: ":tenant.example.com" } not matched`, () => {
|
||||
@@ -54,7 +62,7 @@ describe('Hello world (fastify adapter)', () => {
|
||||
url: '/host',
|
||||
})
|
||||
.then(({ payload }) => {
|
||||
expect(JSON.parse(payload)).to.be.eql({
|
||||
expect(JSON.parse(payload)).toEqual({
|
||||
error: 'Internal Server Error',
|
||||
message:
|
||||
'HTTP adapter does not support filtering on host: ":tenant.example.com"',
|
||||
@@ -70,7 +78,7 @@ describe('Hello world (fastify adapter)', () => {
|
||||
url: '/host-array',
|
||||
})
|
||||
.then(({ payload }) => {
|
||||
expect(JSON.parse(payload)).to.be.eql({
|
||||
expect(JSON.parse(payload)).toEqual({
|
||||
error: 'Internal Server Error',
|
||||
message:
|
||||
'HTTP adapter does not support filtering on hosts: [":tenant.example1.com", ":tenant.example2.com"]',
|
||||
@@ -84,7 +92,10 @@ describe('Hello world (fastify adapter)', () => {
|
||||
.inject()
|
||||
.get('/hello')
|
||||
.end()
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
.then(({ payload, statusCode }) => {
|
||||
expect(statusCode).toBe(200);
|
||||
expect(payload).toEqual('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
it('/HEAD should respond to with a 200', () => {
|
||||
@@ -93,7 +104,7 @@ describe('Hello world (fastify adapter)', () => {
|
||||
method: 'HEAD',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ statusCode }) => expect(statusCode).to.be.eq(200));
|
||||
.then(({ statusCode }) => expect(statusCode).toBe(200));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Middleware before init (FastifyAdapter)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
@@ -81,11 +80,11 @@ describe('Middleware before init (FastifyAdapter)', () => {
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload, headers }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
expect(statusCode).toBe(200);
|
||||
expect(JSON.parse(payload)).toEqual({ data: 'test_data' });
|
||||
// Verify both module-level and global middleware were applied
|
||||
expect(headers['x-middleware']).to.equal('applied');
|
||||
expect(headers['x-global-middleware']).to.equal('applied');
|
||||
expect(headers['x-middleware']).toBe('applied');
|
||||
expect(headers['x-global-middleware']).toBe('applied');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -123,8 +122,8 @@ describe('Middleware before init (FastifyAdapter)', () => {
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
expect(statusCode).toBe(200);
|
||||
expect(JSON.parse(payload)).toEqual({ data: 'test_data' });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,91 +1,4 @@
|
||||
/* Temporarily disabled due to various regressions
|
||||
|
||||
import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ApplicationModule } from '../src/app.module';
|
||||
|
||||
describe('Hello world (fastify adapter with multiple applications)', () => {
|
||||
let adapter: FastifyAdapter;
|
||||
let apps: NestFastifyApplication[];
|
||||
|
||||
beforeEach(async () => {
|
||||
const module1 = await Test.createTestingModule({
|
||||
imports: [ApplicationModule],
|
||||
}).compile();
|
||||
const module2 = await Test.createTestingModule({
|
||||
imports: [ApplicationModule],
|
||||
}).compile();
|
||||
|
||||
adapter = new FastifyAdapter();
|
||||
|
||||
apps = [
|
||||
module1.createNestApplication<NestFastifyApplication>(adapter),
|
||||
module2
|
||||
.createNestApplication<NestFastifyApplication>(adapter, {
|
||||
bodyParser: false,
|
||||
})
|
||||
.setGlobalPrefix('/app2'),
|
||||
];
|
||||
await Promise.all(apps.map(app => app.init()));
|
||||
});
|
||||
|
||||
it(`/GET`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it(`/GET (app2)`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/app2/hello',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it(`/GET (Promise/async)`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello/async',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it(`/GET (app2 Promise/async)`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/app2/hello/async',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it(`/GET (Observable stream)`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/hello/stream',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it(`/GET (app2 Observable stream)`, () => {
|
||||
return adapter
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/app2/hello/stream',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(apps.map(app => app.close()));
|
||||
await adapter.close();
|
||||
});
|
||||
});*/
|
||||
// Temporarily disabled due to various regressions
|
||||
describe.skip('Hello world (fastify adapter with multiple applications)', () => {
|
||||
it('placeholder', () => {});
|
||||
});
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
import { ConsoleLogger, INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('ForceConsole Option', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
describe('When forceConsole is true', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
let consoleLogSpy: ReturnType<typeof vi.fn>;
|
||||
let consoleErrorSpy: ReturnType<typeof vi.fn>;
|
||||
let processStdoutSpy: ReturnType<typeof vi.fn>;
|
||||
let processStderrSpy: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
consoleLogSpy = vi.spyOn(console, 'log');
|
||||
consoleErrorSpy = vi.spyOn(console, 'error');
|
||||
processStdoutSpy = vi.spyOn(process.stdout, 'write');
|
||||
processStderrSpy = vi.spyOn(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
@@ -34,10 +32,10 @@ describe('ForceConsole Option', () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
consoleLogSpy.mockRestore();
|
||||
consoleErrorSpy.mockRestore();
|
||||
processStdoutSpy.mockRestore();
|
||||
processStderrSpy.mockRestore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -46,14 +44,12 @@ describe('ForceConsole Option', () => {
|
||||
logger.log('Test log message');
|
||||
|
||||
// Should use console.log when forceConsole is true
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
expect(consoleLogSpy).toHaveBeenCalled();
|
||||
// Verify console.log was called with the message
|
||||
const consoleLogCalls = consoleLogSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test log message')),
|
||||
);
|
||||
expect(consoleLogCalls.length).to.be.greaterThan(0);
|
||||
const consoleLogCalls = consoleLogSpy.mock.calls.filter(args =>
|
||||
args.some(arg => String(arg).includes('Test log message')),
|
||||
);
|
||||
expect(consoleLogCalls.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should use console.error instead of process.stderr.write', async () => {
|
||||
@@ -61,14 +57,12 @@ describe('ForceConsole Option', () => {
|
||||
logger.error('Test error message');
|
||||
|
||||
// Should use console.error when forceConsole is true
|
||||
expect(consoleErrorSpy.called).to.be.true;
|
||||
expect(consoleErrorSpy).toHaveBeenCalled();
|
||||
// Verify console.error was called with the message
|
||||
const consoleErrorCalls = consoleErrorSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test error message')),
|
||||
);
|
||||
expect(consoleErrorCalls.length).to.be.greaterThan(0);
|
||||
const consoleErrorCalls = consoleErrorSpy.mock.calls.filter(args =>
|
||||
args.some(arg => String(arg).includes('Test error message')),
|
||||
);
|
||||
expect(consoleErrorCalls.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle GET request with forceConsole option enabled', () => {
|
||||
@@ -77,17 +71,17 @@ describe('ForceConsole Option', () => {
|
||||
});
|
||||
|
||||
describe('When forceConsole is false (default)', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
let consoleLogSpy: ReturnType<typeof vi.fn>;
|
||||
let consoleErrorSpy: ReturnType<typeof vi.fn>;
|
||||
let processStdoutSpy: ReturnType<typeof vi.fn>;
|
||||
let processStderrSpy: ReturnType<typeof vi.fn>;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
consoleLogSpy = vi.spyOn(console, 'log');
|
||||
consoleErrorSpy = vi.spyOn(console, 'error');
|
||||
processStdoutSpy = vi.spyOn(process.stdout, 'write');
|
||||
processStderrSpy = vi.spyOn(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
@@ -102,10 +96,10 @@ describe('ForceConsole Option', () => {
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
consoleLogSpy.mockRestore();
|
||||
consoleErrorSpy.mockRestore();
|
||||
processStdoutSpy.mockRestore();
|
||||
processStderrSpy.mockRestore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -113,31 +107,31 @@ describe('ForceConsole Option', () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleLogSpy.resetHistory();
|
||||
consoleLogSpy.mockClear();
|
||||
|
||||
logger.log('Test log message');
|
||||
|
||||
// When forceConsole is false, should not call console.log
|
||||
expect(consoleLogSpy.called).to.be.false;
|
||||
expect(consoleLogSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not directly call console.error when forceConsole is false', async () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleErrorSpy.resetHistory();
|
||||
consoleErrorSpy.mockClear();
|
||||
|
||||
logger.error('Test error message');
|
||||
|
||||
// When forceConsole is false, should not call console.error
|
||||
expect(consoleErrorSpy.called).to.be.false;
|
||||
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forceConsole is set via NestFactory.create', () => {
|
||||
it('should apply forceConsole to the default logger', async () => {
|
||||
const consoleLogSpy = sinon.spy(console, 'log');
|
||||
const processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
const consoleLogSpy = vi.spyOn(console, 'log');
|
||||
const processStdoutSpy = vi.spyOn(process.stdout, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
@@ -153,10 +147,10 @@ describe('ForceConsole Option', () => {
|
||||
const logger = new ConsoleLogger('AppContext', { forceConsole: true });
|
||||
logger.log('Application started');
|
||||
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
expect(consoleLogSpy).toHaveBeenCalled();
|
||||
|
||||
consoleLogSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
consoleLogSpy.mockRestore();
|
||||
processStdoutSpy.mockRestore();
|
||||
await testApp.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
@Injectable()
|
||||
export class AuthGuard {
|
||||
@@ -33,10 +33,31 @@ function createTestModule(guard) {
|
||||
describe('Guards', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it(`should prevent access (unauthorized)`, async () => {
|
||||
app = (await createTestModule(new AuthGuard())).createNestApplication();
|
||||
|
||||
await app.init();
|
||||
return request(app.getHttpServer()).get('/hello').expect(401);
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(401)
|
||||
.expect(({ body }) => {
|
||||
expect(body.message).toBe('Unauthorized');
|
||||
expect(body.statusCode).toBe(401);
|
||||
});
|
||||
});
|
||||
|
||||
it(`should allow access when guard returns true`, async () => {
|
||||
const allowGuard = { canActivate: () => true };
|
||||
app = (await createTestModule(allowGuard)).createNestApplication();
|
||||
|
||||
await app.init();
|
||||
return request(app.getHttpServer())
|
||||
.get('/hello')
|
||||
.expect(200)
|
||||
.expect('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as request from 'supertest';
|
||||
import request from 'supertest';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Hello world (default adapter)', () => {
|
||||
let server;
|
||||
|
||||
@@ -9,8 +9,8 @@ import { APP_INTERCEPTOR } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Hello world (default adapter)', () => {
|
||||
let server;
|
||||
|
||||
@@ -9,8 +9,8 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { Response } from 'express';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const INCLUDED_VALUE = 'test_included';
|
||||
const RETURN_VALUE = 'test';
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import request from 'supertest';
|
||||
|
||||
const RETURN_VALUE_A = 'test_A';
|
||||
const RETURN_VALUE_B = 'test_B';
|
||||
|
||||
@@ -15,10 +15,9 @@ import {
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
describe('Middleware (FastifyAdapter)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
@@ -117,7 +116,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(RETURN_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(RETURN_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(TestController)`, () => {
|
||||
@@ -126,7 +125,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(SCOPED_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(SCOPED_VALUE));
|
||||
});
|
||||
|
||||
it(`query?test=${QUERY_VALUE} forRoutes(query)`, () => {
|
||||
@@ -138,7 +137,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
test: QUERY_VALUE,
|
||||
},
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(QUERY_VALUE));
|
||||
});
|
||||
|
||||
it(`${QUERY_VALUE}?test=${QUERY_VALUE} forRoutes(${QUERY_VALUE})`, () => {
|
||||
@@ -150,7 +149,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
test: QUERY_VALUE,
|
||||
},
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(QUERY_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(tests/*path)`, () => {
|
||||
@@ -159,7 +158,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/tests/wildcard_nested',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(express_style_wildcard/*)`, () => {
|
||||
@@ -168,7 +167,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/express_style_wildcard/wildcard_nested',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(legacy_style_wildcard/*)`, () => {
|
||||
@@ -177,7 +176,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/legacy_style_wildcard/wildcard_nested',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(req/url/)`, () => {
|
||||
@@ -187,7 +186,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: `/req/url${reqUrl}`,
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(REQ_URL_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(REQ_URL_VALUE));
|
||||
});
|
||||
|
||||
it(`GET forRoutes(POST tests/included)`, () => {
|
||||
@@ -196,7 +195,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'GET',
|
||||
url: '/tests/included',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`POST forRoutes(POST tests/included)`, () => {
|
||||
@@ -205,7 +204,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'POST',
|
||||
url: '/tests/included',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
it(`GET forRoutes(POST /tests/%69ncluded) - ensure middleware is executed correctly with encoded characters`, () => {
|
||||
@@ -214,7 +213,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
method: 'POST',
|
||||
url: '/tests/%69ncluded', // 'i' character is encoded
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
.then(({ payload }) => expect(payload).toEqual(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
@@ -329,7 +328,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/a/b/c',
|
||||
})
|
||||
.then(({ payload }) => {
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -346,7 +345,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/a/b',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -363,7 +362,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/a',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -380,7 +379,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/similar',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -397,7 +396,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/similar/test',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -414,7 +413,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/similar/arbitrary',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
actual: 1,
|
||||
@@ -494,7 +493,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/api/pong',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
@@ -513,7 +512,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/api',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
@@ -531,7 +530,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
url: '/pong',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
expect(payload).toEqual(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
|
||||
@@ -7,10 +7,8 @@ import {
|
||||
NestMiddleware,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '../../../packages/testing';
|
||||
import * as request from 'supertest';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Test } from '../../../packages/testing.js';
|
||||
import request from 'supertest';
|
||||
/**
|
||||
* Number of times that the middleware was executed.
|
||||
*/
|
||||
|
||||
@@ -6,14 +6,14 @@ import {
|
||||
Module,
|
||||
RequestMethod,
|
||||
Version,
|
||||
VERSION_NEUTRAL,
|
||||
VersioningOptions,
|
||||
VersioningType,
|
||||
VERSION_NEUTRAL,
|
||||
} from '@nestjs/common';
|
||||
import { CustomVersioningOptions } from '@nestjs/common/interfaces';
|
||||
import { CustomVersioningOptions } from '@nestjs/common/interfaces/index.js';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
const VERSIONED_VALUE = 'test_versioned';
|
||||
|
||||
@@ -6,8 +6,8 @@ import {
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
const SCOPED_VALUE = 'test_scoped';
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { RouterModule } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import request from 'supertest';
|
||||
import { AppModule } from '../src/app.module.js';
|
||||
|
||||
const RETURN_VALUE = 'test';
|
||||
const SCOPED_VALUE = 'test_scoped';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Controller, Get, INestApplication, Module } from '@nestjs/common';
|
||||
import { RouterModule, Routes } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import request from 'supertest';
|
||||
|
||||
describe('RouterModule', () => {
|
||||
let app: INestApplication;
|
||||
@@ -66,7 +66,7 @@ describe('RouterModule', () => {
|
||||
})
|
||||
class AppModule {}
|
||||
|
||||
before(async () => {
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [MainModule, AppModule],
|
||||
}).compile();
|
||||
|
||||
166
integration/hello-world/e2e/schema-in-pipes.spec.ts
Normal file
166
integration/hello-world/e2e/schema-in-pipes.spec.ts
Normal file
@@ -0,0 +1,166 @@
|
||||
import {
|
||||
ArgumentMetadata,
|
||||
Body,
|
||||
Controller,
|
||||
createParamDecorator,
|
||||
ExecutionContext,
|
||||
Get,
|
||||
INestApplication,
|
||||
Injectable,
|
||||
Module,
|
||||
Param,
|
||||
PipeTransform,
|
||||
Post,
|
||||
Query,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
||||
import request from 'supertest';
|
||||
|
||||
const testSchema: StandardSchemaV1 = {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'test',
|
||||
validate: (value: unknown) => ({ value }),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A pipe that captures the ArgumentMetadata it receives,
|
||||
* so the test can verify that `schema` is propagated.
|
||||
*/
|
||||
@Injectable()
|
||||
class SchemaCaptorPipe implements PipeTransform {
|
||||
static lastMetadata: ArgumentMetadata | undefined;
|
||||
|
||||
transform(value: any, metadata: ArgumentMetadata) {
|
||||
SchemaCaptorPipe.lastMetadata = metadata;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const CustomParam = createParamDecorator(
|
||||
(data: unknown, ctx: ExecutionContext) => {
|
||||
return ctx.switchToHttp().getRequest().query;
|
||||
},
|
||||
);
|
||||
|
||||
@Controller('schema-test')
|
||||
class SchemaTestController {
|
||||
@Post('body')
|
||||
bodyWithSchema(
|
||||
@Body({ schema: testSchema, pipes: [SchemaCaptorPipe] }) body: any,
|
||||
) {
|
||||
return { received: body };
|
||||
}
|
||||
|
||||
@Get('query')
|
||||
queryWithSchema(
|
||||
@Query({ schema: testSchema, pipes: [SchemaCaptorPipe] }) query: any,
|
||||
) {
|
||||
return { received: query };
|
||||
}
|
||||
|
||||
@Get('param/:id')
|
||||
paramWithSchema(
|
||||
@Param('id', { schema: testSchema, pipes: [SchemaCaptorPipe] }) id: string,
|
||||
) {
|
||||
return { received: id };
|
||||
}
|
||||
|
||||
@Get('custom')
|
||||
customWithSchema(
|
||||
@CustomParam({ schema: testSchema, pipes: [SchemaCaptorPipe] }) value: any,
|
||||
) {
|
||||
return { received: value };
|
||||
}
|
||||
|
||||
@Post('body-property')
|
||||
bodyPropertyWithSchema(
|
||||
@Body('name', { schema: testSchema, pipes: [SchemaCaptorPipe] })
|
||||
name: string,
|
||||
) {
|
||||
return { received: name };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
controllers: [SchemaTestController],
|
||||
providers: [SchemaCaptorPipe],
|
||||
})
|
||||
class SchemaTestModule {}
|
||||
|
||||
describe('Schema propagation to pipes', () => {
|
||||
let app: INestApplication;
|
||||
let server: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [SchemaTestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
server = app.getHttpServer();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
SchemaCaptorPipe.lastMetadata = undefined;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should pass schema to pipe via @Body(options)', async () => {
|
||||
await request(server)
|
||||
.post('/schema-test/body')
|
||||
.send({ name: 'test' })
|
||||
.expect(201);
|
||||
|
||||
expect(SchemaCaptorPipe.lastMetadata).toBeDefined();
|
||||
expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema);
|
||||
expect(SchemaCaptorPipe.lastMetadata!.type).toBe('body');
|
||||
});
|
||||
|
||||
it('should pass schema to pipe via @Query(options)', async () => {
|
||||
await request(server).get('/schema-test/query?user=john').expect(200);
|
||||
|
||||
expect(SchemaCaptorPipe.lastMetadata).toBeDefined();
|
||||
expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema);
|
||||
expect(SchemaCaptorPipe.lastMetadata!.type).toBe('query');
|
||||
});
|
||||
|
||||
it('should pass schema to pipe via @Param(property, options)', async () => {
|
||||
await request(server)
|
||||
.get('/schema-test/param/42')
|
||||
.expect(200)
|
||||
.expect({ received: '42' });
|
||||
|
||||
expect(SchemaCaptorPipe.lastMetadata).toBeDefined();
|
||||
expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema);
|
||||
expect(SchemaCaptorPipe.lastMetadata!.type).toBe('param');
|
||||
expect(SchemaCaptorPipe.lastMetadata!.data).toBe('id');
|
||||
});
|
||||
|
||||
it('should pass schema to pipe via createParamDecorator(options)', async () => {
|
||||
await request(server).get('/schema-test/custom?key=val').expect(200);
|
||||
|
||||
expect(SchemaCaptorPipe.lastMetadata).toBeDefined();
|
||||
expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema);
|
||||
expect(SchemaCaptorPipe.lastMetadata!.type).toBe('custom');
|
||||
});
|
||||
|
||||
it('should pass schema to pipe via @Body(property, options)', async () => {
|
||||
await request(server)
|
||||
.post('/schema-test/body-property')
|
||||
.send({ name: 'Alice' })
|
||||
.expect(201)
|
||||
.expect({ received: 'Alice' });
|
||||
|
||||
expect(SchemaCaptorPipe.lastMetadata).toBeDefined();
|
||||
expect(SchemaCaptorPipe.lastMetadata!.schema).toBe(testSchema);
|
||||
expect(SchemaCaptorPipe.lastMetadata!.type).toBe('body');
|
||||
expect(SchemaCaptorPipe.lastMetadata!.data).toBe('name');
|
||||
});
|
||||
});
|
||||
316
integration/hello-world/e2e/standard-schema-serializer.spec.ts
Normal file
316
integration/hello-world/e2e/standard-schema-serializer.spec.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
INestApplication,
|
||||
Module,
|
||||
SerializeOptions,
|
||||
StandardSchemaSerializerInterceptor,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { APP_INTERCEPTOR, Reflector } from '@nestjs/core';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import type { StandardSchemaV1 } from '@standard-schema/spec';
|
||||
import request from 'supertest';
|
||||
|
||||
// ─── Test schemas ──────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Schema that strips out the `password` field (simulating a "safe user" DTO).
|
||||
*/
|
||||
const safeUserSchema: StandardSchemaV1 = {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'test',
|
||||
validate: (value: unknown) => {
|
||||
const { password, ...safe } = value as Record<string, unknown>;
|
||||
return { value: safe };
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Schema that adds a `serialized: true` flag (useful for asserting the schema ran).
|
||||
*/
|
||||
const flagSchema: StandardSchemaV1 = {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'test',
|
||||
validate: (value: unknown) => ({
|
||||
value: { ...(value as any), serialized: true },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Schema that always fails — used for the error case.
|
||||
*/
|
||||
const failingSchema: StandardSchemaV1 = {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'test',
|
||||
validate: () => ({
|
||||
issues: [{ message: 'not allowed' }],
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Async schema — validates that the interceptor awaits promises.
|
||||
*/
|
||||
const asyncSchema: StandardSchemaV1 = {
|
||||
'~standard': {
|
||||
version: 1,
|
||||
vendor: 'test',
|
||||
validate: async (value: unknown) => ({
|
||||
value: { ...(value as any), async: true },
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
// ─── Controllers ───────────────────────────────────────────────
|
||||
|
||||
@Controller('serializer')
|
||||
@UseInterceptors(StandardSchemaSerializerInterceptor)
|
||||
class SerializerTestController {
|
||||
@Get('user')
|
||||
@SerializeOptions({ schema: safeUserSchema })
|
||||
getUser() {
|
||||
return { id: 1, name: 'Alice', password: 'secret123' };
|
||||
}
|
||||
|
||||
@Get('users')
|
||||
@SerializeOptions({ schema: safeUserSchema })
|
||||
getUsers() {
|
||||
return [
|
||||
{ id: 1, name: 'Alice', password: 'pw1' },
|
||||
{ id: 2, name: 'Bob', password: 'pw2' },
|
||||
];
|
||||
}
|
||||
|
||||
@Get('flagged')
|
||||
@SerializeOptions({ schema: flagSchema })
|
||||
getFlagged() {
|
||||
return { id: 1 };
|
||||
}
|
||||
|
||||
@Get('no-schema')
|
||||
getNoSchema() {
|
||||
return { id: 1, secret: 'visible' };
|
||||
}
|
||||
|
||||
@Get('failing')
|
||||
@SerializeOptions({ schema: failingSchema })
|
||||
getFailing() {
|
||||
return { id: 1 };
|
||||
}
|
||||
|
||||
@Get('async')
|
||||
@SerializeOptions({ schema: asyncSchema })
|
||||
getAsync() {
|
||||
return { id: 1 };
|
||||
}
|
||||
|
||||
@Get('primitive')
|
||||
@SerializeOptions({ schema: failingSchema })
|
||||
getPrimitive() {
|
||||
return 'plain string';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller-level schema applied via class decorator — all routes inherit it.
|
||||
*/
|
||||
@Controller('class-level')
|
||||
@UseInterceptors(StandardSchemaSerializerInterceptor)
|
||||
@SerializeOptions({ schema: safeUserSchema })
|
||||
class ClassLevelSerializerController {
|
||||
@Get('user')
|
||||
getUser() {
|
||||
return { id: 1, name: 'Carol', password: 'secret' };
|
||||
}
|
||||
|
||||
@Get('override')
|
||||
@SerializeOptions({ schema: flagSchema })
|
||||
getOverride() {
|
||||
return { id: 1, name: 'Carol', password: 'secret' };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller demonstrating global interceptor registration with a default schema.
|
||||
*/
|
||||
@Controller('global')
|
||||
class GlobalSerializerController {
|
||||
@Get('default')
|
||||
getDefault() {
|
||||
return { id: 1, name: 'Dave', password: 'global-secret' };
|
||||
}
|
||||
|
||||
@Get('override')
|
||||
@SerializeOptions({ schema: flagSchema })
|
||||
getOverride() {
|
||||
return { id: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
controllers: [SerializerTestController, ClassLevelSerializerController],
|
||||
})
|
||||
class SerializerTestModule {}
|
||||
|
||||
@Module({
|
||||
controllers: [GlobalSerializerController],
|
||||
providers: [
|
||||
{
|
||||
provide: APP_INTERCEPTOR,
|
||||
useFactory: (reflector: Reflector) =>
|
||||
new StandardSchemaSerializerInterceptor(reflector, {
|
||||
schema: safeUserSchema,
|
||||
}),
|
||||
inject: [Reflector],
|
||||
},
|
||||
],
|
||||
})
|
||||
class GlobalSerializerTestModule {}
|
||||
|
||||
// ─── Tests ─────────────────────────────────────────────────────
|
||||
|
||||
describe('StandardSchemaSerializerInterceptor (integration)', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
describe('handler-level @SerializeOptions', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [SerializerTestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should strip fields via schema on a single object', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/user')
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
expect(body).toEqual({ id: 1, name: 'Alice' });
|
||||
expect(body).not.toHaveProperty('password');
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply schema to each item in an array response', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/users')
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
expect(body).toEqual([
|
||||
{ id: 1, name: 'Alice' },
|
||||
{ id: 2, name: 'Bob' },
|
||||
]);
|
||||
body.forEach((item: any) =>
|
||||
expect(item).not.toHaveProperty('password'),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should augment response through the schema', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/flagged')
|
||||
.expect(200)
|
||||
.expect({ id: 1, serialized: true });
|
||||
});
|
||||
|
||||
it('should return response unchanged when no schema is set', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/no-schema')
|
||||
.expect(200)
|
||||
.expect({ id: 1, secret: 'visible' });
|
||||
});
|
||||
|
||||
it('should return 500 when schema validation fails', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/failing')
|
||||
.expect(500);
|
||||
});
|
||||
|
||||
it('should handle async schemas', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/async')
|
||||
.expect(200)
|
||||
.expect({ id: 1, async: true });
|
||||
});
|
||||
|
||||
it('should pass primitive values through even when a schema is set', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/serializer/primitive')
|
||||
.expect(200)
|
||||
.expect(({ text }) => {
|
||||
expect(text).toBe('plain string');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('class-level @SerializeOptions', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [SerializerTestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should apply class-level schema to all routes', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/class-level/user')
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
expect(body).toEqual({ id: 1, name: 'Carol' });
|
||||
expect(body).not.toHaveProperty('password');
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow handler-level schema to override class-level', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/class-level/override')
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
// flagSchema adds `serialized: true` but does NOT strip password
|
||||
expect(body).toHaveProperty('serialized', true);
|
||||
expect(body).toHaveProperty('password', 'secret');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('global interceptor with default schema', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [GlobalSerializerTestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should apply the default schema globally', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/global/default')
|
||||
.expect(200)
|
||||
.expect(({ body }) => {
|
||||
expect(body).toEqual({ id: 1, name: 'Dave' });
|
||||
expect(body).not.toHaveProperty('password');
|
||||
});
|
||||
});
|
||||
|
||||
it('should let @SerializeOptions override the global default', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/global/override')
|
||||
.expect(200)
|
||||
.expect({ id: 1, serialized: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HelloModule } from './hello/hello.module';
|
||||
import { HostArrayModule } from './host-array/host-array.module';
|
||||
import { HostModule } from './host/host.module';
|
||||
import { HelloModule } from './hello/hello.module.js';
|
||||
import { HostArrayModule } from './host-array/host-array.module.js';
|
||||
import { HostModule } from './host/host.module.js';
|
||||
|
||||
@Module({
|
||||
imports: [HelloModule, HostModule, HostArrayModule],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Controller, Get, Header, Param } from '@nestjs/common';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { HelloService } from './hello.service.js';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe.js';
|
||||
|
||||
@Controller('hello')
|
||||
export class HelloController {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HelloController } from './hello.controller';
|
||||
import { HelloService } from './hello.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
import { HelloController } from './hello.controller.js';
|
||||
import { HelloService } from './hello.service.js';
|
||||
import { UsersService } from './users/users.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [HelloController],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersService } from './users.service.js';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Controller, Get, Header, HostParam, Param } from '@nestjs/common';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HostArrayService } from './host-array.service';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { HostArrayService } from './host-array.service.js';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe.js';
|
||||
|
||||
@Controller({
|
||||
path: 'host-array',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HostArrayController } from './host-array.controller';
|
||||
import { HostArrayService } from './host-array.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
import { HostArrayController } from './host-array.controller.js';
|
||||
import { HostArrayService } from './host-array.service.js';
|
||||
import { UsersService } from './users/users.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [HostArrayController],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersService } from './users.service.js';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Controller, Get, Header, HostParam, Param } from '@nestjs/common';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { HostService } from './host.service';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe';
|
||||
import { HostService } from './host.service.js';
|
||||
import { UserByIdPipe } from './users/user-by-id.pipe.js';
|
||||
|
||||
@Controller({
|
||||
path: 'host',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { HostController } from './host.controller';
|
||||
import { HostService } from './host.service';
|
||||
import { UsersService } from './users/users.service';
|
||||
import { HostController } from './host.controller.js';
|
||||
import { HostService } from './host.service.js';
|
||||
import { UsersService } from './users/users.service.js';
|
||||
|
||||
@Module({
|
||||
controllers: [HostController],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
|
||||
import { UsersService } from './users.service';
|
||||
import { UsersService } from './users.service.js';
|
||||
|
||||
@Injectable()
|
||||
export class UserByIdPipe implements PipeTransform<string> {
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { BeforeApplicationShutdown, Injectable, Module } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
beforeApplicationShutdown = vi.fn();
|
||||
}
|
||||
|
||||
describe('BeforeApplicationShutdown', () => {
|
||||
@@ -17,13 +14,13 @@ describe('BeforeApplicationShutdown', () => {
|
||||
const app = module.createNestApplication();
|
||||
await app.close();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.beforeApplicationShutdown.called).to.be.true;
|
||||
expect(instance.beforeApplicationShutdown).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
beforeApplicationShutdown = vi.fn();
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -35,7 +32,7 @@ describe('BeforeApplicationShutdown', () => {
|
||||
@Injectable()
|
||||
class AA implements BeforeApplicationShutdown {
|
||||
constructor(private bb: BB) {}
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
beforeApplicationShutdown = vi.fn();
|
||||
}
|
||||
@Module({
|
||||
imports: [B],
|
||||
@@ -53,9 +50,8 @@ describe('BeforeApplicationShutdown', () => {
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(
|
||||
aa.beforeApplicationShutdown,
|
||||
bb.beforeApplicationShutdown,
|
||||
);
|
||||
expect(
|
||||
aa.beforeApplicationShutdown.mock.invocationCallOrder[0],
|
||||
).toBeLessThan(bb.beforeApplicationShutdown.mock.invocationCallOrder[0]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,71 +1,81 @@
|
||||
import { expect } from 'chai';
|
||||
import { spawnSync } from 'child_process';
|
||||
import { join } from 'path';
|
||||
|
||||
const nodeCmd = process.execPath;
|
||||
|
||||
function spawnTsNode(...args: string[]) {
|
||||
return spawnSync(nodeCmd, ['--import', 'jiti/register', ...args]);
|
||||
}
|
||||
|
||||
describe('enableShutdownHooks', () => {
|
||||
it('should call the correct hooks if any shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGHUP');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
it('should call the correct hooks if any shutdown signal gets invoked', () =>
|
||||
new Promise<void>(done => {
|
||||
const result = spawnTsNode(
|
||||
join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).toBe('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).toBe('onApplicationShutdown SIGHUP');
|
||||
done();
|
||||
}));
|
||||
|
||||
it('should call the correct hooks if a specific shutdown signal gets invoked', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGINT',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGINT');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGINT');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
it('should call the correct hooks if a specific shutdown signal gets invoked', () =>
|
||||
new Promise<void>(done => {
|
||||
const result = spawnTsNode(
|
||||
join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGINT',
|
||||
);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).toBe('beforeApplicationShutdown SIGINT');
|
||||
expect(calls[1]).toBe('onApplicationShutdown SIGINT');
|
||||
done();
|
||||
}));
|
||||
|
||||
it('should ignore system signals which are not specified', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGHUP',
|
||||
]);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
it('should ignore system signals which are not specified', () =>
|
||||
new Promise<void>(done => {
|
||||
const result = spawnTsNode(
|
||||
join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'SIGHUP',
|
||||
);
|
||||
expect(result.stdout.toString().trim()).toBe('');
|
||||
done();
|
||||
}));
|
||||
|
||||
it('should ignore system signals if "enableShutdownHooks" was not called', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'NONE',
|
||||
]);
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
it('should ignore system signals if "enableShutdownHooks" was not called', () =>
|
||||
new Promise<void>(done => {
|
||||
const result = spawnTsNode(
|
||||
join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGINT',
|
||||
'NONE',
|
||||
);
|
||||
expect(result.stdout.toString().trim()).toBe('');
|
||||
done();
|
||||
}));
|
||||
|
||||
it('should call the correct hooks with useProcessExit option', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
'SIGHUP',
|
||||
'graceful',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGHUP');
|
||||
expect(result.status).to.equal(0);
|
||||
done();
|
||||
}).timeout(10000);
|
||||
it('should call the correct hooks with useProcessExit option', () =>
|
||||
new Promise<void>(done => {
|
||||
const result = spawnTsNode(
|
||||
join(import.meta.dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
'SIGHUP',
|
||||
'graceful',
|
||||
);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).toBe('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).toBe('onApplicationShutdown SIGHUP');
|
||||
expect(result.status).toBe(0);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as Sinon from 'sinon';
|
||||
import {
|
||||
BeforeApplicationShutdown,
|
||||
Injectable,
|
||||
OnApplicationBootstrap,
|
||||
OnApplicationShutdown,
|
||||
OnModuleDestroy,
|
||||
OnModuleInit,
|
||||
BeforeApplicationShutdown,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable
|
||||
@@ -18,11 +17,11 @@ class TestInjectable
|
||||
OnApplicationShutdown,
|
||||
BeforeApplicationShutdown
|
||||
{
|
||||
onApplicationBootstrap = Sinon.spy();
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
onModuleDestroy = Sinon.spy();
|
||||
onModuleInit = Sinon.spy();
|
||||
onApplicationBootstrap = vi.fn();
|
||||
beforeApplicationShutdown = vi.fn();
|
||||
onApplicationShutdown = vi.fn();
|
||||
onModuleDestroy = vi.fn();
|
||||
onModuleInit = vi.fn();
|
||||
}
|
||||
|
||||
describe('Lifecycle Hook Order', () => {
|
||||
@@ -36,12 +35,17 @@ describe('Lifecycle Hook Order', () => {
|
||||
await app.close();
|
||||
|
||||
const instance = module.get(TestInjectable);
|
||||
Sinon.assert.callOrder(
|
||||
const order = [
|
||||
instance.onModuleInit,
|
||||
instance.onApplicationBootstrap,
|
||||
instance.onModuleDestroy,
|
||||
instance.beforeApplicationShutdown,
|
||||
instance.onApplicationShutdown,
|
||||
);
|
||||
];
|
||||
for (let i = 0; i < order.length - 1; i++) {
|
||||
expect(order[i].mock.invocationCallOrder[0]).toBeLessThan(
|
||||
order[i + 1].mock.invocationCallOrder[0],
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Injectable, Module, OnApplicationBootstrap } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnApplicationBootstrap {
|
||||
onApplicationBootstrap = Sinon.spy();
|
||||
onApplicationBootstrap = vi.fn();
|
||||
}
|
||||
|
||||
describe('OnApplicationBootstrap', () => {
|
||||
@@ -17,7 +14,8 @@ describe('OnApplicationBootstrap', () => {
|
||||
const app = module.createNestApplication();
|
||||
await app.init();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onApplicationBootstrap.called).to.be.true;
|
||||
expect(instance.onApplicationBootstrap).toHaveBeenCalled();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not throw an error when onApplicationBootstrap is null', async () => {
|
||||
@@ -28,7 +26,8 @@ describe('OnApplicationBootstrap', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not throw an error when onApplicationBootstrap is undefined', async () => {
|
||||
@@ -39,7 +38,8 @@ describe('OnApplicationBootstrap', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@@ -80,6 +80,7 @@ describe('OnApplicationBootstrap', () => {
|
||||
await app.init();
|
||||
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
expect(instance.field).toBe('b-field_a-field');
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Injectable, Module, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnApplicationShutdown {
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
onApplicationShutdown = vi.fn();
|
||||
}
|
||||
|
||||
describe('OnApplicationShutdown', () => {
|
||||
@@ -17,13 +14,13 @@ describe('OnApplicationShutdown', () => {
|
||||
const app = module.createNestApplication();
|
||||
await app.close();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onApplicationShutdown.called).to.be.true;
|
||||
expect(instance.onApplicationShutdown).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements OnApplicationShutdown {
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
onApplicationShutdown = vi.fn();
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -35,7 +32,7 @@ describe('OnApplicationShutdown', () => {
|
||||
@Injectable()
|
||||
class AA implements OnApplicationShutdown {
|
||||
constructor(private bb: BB) {}
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
onApplicationShutdown = vi.fn();
|
||||
}
|
||||
@Module({
|
||||
imports: [B],
|
||||
@@ -53,6 +50,8 @@ describe('OnApplicationShutdown', () => {
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(aa.onApplicationShutdown, bb.onApplicationShutdown);
|
||||
expect(aa.onApplicationShutdown.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
bb.onApplicationShutdown.mock.invocationCallOrder[0],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Injectable, Module, OnModuleDestroy } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnModuleDestroy {
|
||||
onModuleDestroy = Sinon.spy();
|
||||
onModuleDestroy = vi.fn();
|
||||
}
|
||||
|
||||
describe('OnModuleDestroy', () => {
|
||||
@@ -17,7 +14,7 @@ describe('OnModuleDestroy', () => {
|
||||
const app = module.createNestApplication();
|
||||
await app.close();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onModuleDestroy.called).to.be.true;
|
||||
expect(instance.onModuleDestroy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleDestroy is null', async () => {
|
||||
@@ -26,7 +23,8 @@ describe('OnModuleDestroy', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleDestroy is undefined', async () => {
|
||||
@@ -37,13 +35,14 @@ describe('OnModuleDestroy', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements OnModuleDestroy {
|
||||
onModuleDestroy = Sinon.spy();
|
||||
onModuleDestroy = vi.fn();
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -55,7 +54,7 @@ describe('OnModuleDestroy', () => {
|
||||
@Injectable()
|
||||
class AA implements OnModuleDestroy {
|
||||
constructor(private bb: BB) {}
|
||||
onModuleDestroy = Sinon.spy();
|
||||
onModuleDestroy = vi.fn();
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -74,6 +73,8 @@ describe('OnModuleDestroy', () => {
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(aa.onModuleDestroy, bb.onModuleDestroy);
|
||||
expect(aa.onModuleDestroy.mock.invocationCallOrder[0]).toBeLessThan(
|
||||
bb.onModuleDestroy.mock.invocationCallOrder[0],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { Injectable, Module, OnModuleInit } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as Sinon from 'sinon';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable implements OnModuleInit {
|
||||
onModuleInit = Sinon.spy();
|
||||
onModuleInit = vi.fn();
|
||||
}
|
||||
|
||||
describe('OnModuleInit', () => {
|
||||
@@ -17,7 +14,8 @@ describe('OnModuleInit', () => {
|
||||
const app = module.createNestApplication();
|
||||
await app.init();
|
||||
const instance = module.get(TestInjectable);
|
||||
expect(instance.onModuleInit.called).to.be.true;
|
||||
expect(instance.onModuleInit).toHaveBeenCalled();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleInit is null', async () => {
|
||||
@@ -26,7 +24,8 @@ describe('OnModuleInit', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not throw an error when onModuleInit is undefined', async () => {
|
||||
@@ -35,7 +34,8 @@ describe('OnModuleInit', () => {
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init().then(obj => expect(obj).to.not.be.undefined);
|
||||
await app.init().then(obj => expect(obj).not.toBeUndefined());
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@@ -109,6 +109,7 @@ describe('OnModuleInit', () => {
|
||||
await app.init();
|
||||
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('c-field_b-field_a-field');
|
||||
expect(instance.field).toBe('c-field_b-field_a-field');
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["vitest/globals"],
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"esModuleInterop": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"target": "ES2023",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Controller, Injectable, Module } from '@nestjs/common';
|
||||
|
||||
@@ -29,35 +28,23 @@ export class AppModule {}
|
||||
|
||||
describe('Circular custom providers', () => {
|
||||
it('should throw an exception (useClass + regular provider)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toThrow(
|
||||
'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw an exception (2 factories)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] },
|
||||
{ provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] },
|
||||
],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "ABC". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] },
|
||||
{ provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] },
|
||||
],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toThrow(
|
||||
'A circular dependency has been detected inside "ABC"',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { CircularModule } from '../src/circular-modules/circular.module';
|
||||
import { CircularService } from '../src/circular-modules/circular.service';
|
||||
import { InputModule } from '../src/circular-modules/input.module';
|
||||
import { InputService } from '../src/circular-modules/input.service';
|
||||
import { CircularModule } from '../src/circular-modules/circular.module.js';
|
||||
import { CircularService } from '../src/circular-modules/circular.service.js';
|
||||
import { InputModule } from '../src/circular-modules/input.module.js';
|
||||
import { InputService } from '../src/circular-modules/input.service.js';
|
||||
|
||||
describe('Circular dependency (modules)', () => {
|
||||
it('should resolve circular dependency between providers', async () => {
|
||||
@@ -14,7 +13,7 @@ describe('Circular dependency (modules)', () => {
|
||||
const inputService = testingModule.get<InputService>(InputService);
|
||||
const circularService = testingModule.get<CircularService>(CircularService);
|
||||
|
||||
expect(inputService.service).to.be.eql(circularService);
|
||||
expect(circularService.service).to.be.eql(inputService);
|
||||
expect(inputService.service).toEqual(circularService);
|
||||
expect(circularService.service).toEqual(inputService);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { CircularPropertiesModule } from '../src/circular-properties/circular-properties.module';
|
||||
import { CircularService } from '../src/circular-properties/circular.service';
|
||||
import { InputPropertiesModule } from '../src/circular-properties/input-properties.module';
|
||||
import { InputService } from '../src/circular-properties/input.service';
|
||||
import { CircularPropertiesModule } from '../src/circular-properties/circular-properties.module.js';
|
||||
import { CircularService } from '../src/circular-properties/circular.service.js';
|
||||
import { InputPropertiesModule } from '../src/circular-properties/input-properties.module.js';
|
||||
import { InputService } from '../src/circular-properties/input.service.js';
|
||||
|
||||
describe('Circular properties dependency (modules)', () => {
|
||||
it('should resolve circular dependency between providers', async () => {
|
||||
@@ -14,7 +13,7 @@ describe('Circular properties dependency (modules)', () => {
|
||||
const inputService = testingModule.get<InputService>(InputService);
|
||||
const circularService = testingModule.get<CircularService>(CircularService);
|
||||
|
||||
expect(inputService.service).to.be.eql(circularService);
|
||||
expect(circularService.service).to.be.eql(inputService);
|
||||
expect(inputService.service).toEqual(circularService);
|
||||
expect(circularService.service).toEqual(inputService);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { CircularModule } from '../src/circular-structure-dynamic-module/circular.module';
|
||||
import { InputService } from '../src/circular-structure-dynamic-module/input.service';
|
||||
import { CircularModule } from '../src/circular-structure-dynamic-module/circular.module.js';
|
||||
import { InputService } from '../src/circular-structure-dynamic-module/input.service.js';
|
||||
|
||||
describe('Circular structure for dynamic modules', () => {
|
||||
it('should resolve circular structure with dynamic modules', async () => {
|
||||
@@ -11,6 +10,6 @@ describe('Circular structure for dynamic modules', () => {
|
||||
const testingModule = await builder.compile();
|
||||
const inputService = testingModule.get<InputService>(InputService);
|
||||
|
||||
expect(inputService).to.be.instanceof(InputService);
|
||||
expect(inputService).toBeInstanceOf(InputService);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { CircularModule } from '../src/circular/circular.module';
|
||||
import { CircularService } from '../src/circular/circular.service';
|
||||
import { InputService } from '../src/circular/input.service';
|
||||
import { CircularModule } from '../src/circular/circular.module.js';
|
||||
import { CircularService } from '../src/circular/circular.service.js';
|
||||
import { InputService } from '../src/circular/input.service.js';
|
||||
|
||||
describe('Circular dependency', () => {
|
||||
it('should resolve circular dependency between providers', async () => {
|
||||
@@ -13,7 +12,7 @@ describe('Circular dependency', () => {
|
||||
const inputService = testingModule.get<InputService>(InputService);
|
||||
const circularService = testingModule.get<CircularService>(CircularService);
|
||||
|
||||
expect(inputService.service).to.be.eql(circularService);
|
||||
expect(circularService.service).to.be.eql(inputService);
|
||||
expect(inputService.service).toEqual(circularService);
|
||||
expect(circularService.service).toEqual(inputService);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { CoreInjectablesModule } from '../src/core-injectables/core-injectables.module';
|
||||
import { ApplicationConfig, ModuleRef } from '@nestjs/core';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CoreInjectablesModule } from '../src/core-injectables/core-injectables.module.js';
|
||||
|
||||
describe('Core Injectables', () => {
|
||||
let testingModule: TestingModule;
|
||||
@@ -13,26 +12,30 @@ describe('Core Injectables', () => {
|
||||
testingModule = await builder.compile();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await testingModule.close();
|
||||
});
|
||||
|
||||
it('should provide ApplicationConfig as core injectable', () => {
|
||||
const applicationConfig =
|
||||
testingModule.get<ApplicationConfig>(ApplicationConfig);
|
||||
|
||||
applicationConfig.setGlobalPrefix('/api');
|
||||
|
||||
expect(applicationConfig).to.not.be.undefined;
|
||||
expect(applicationConfig.getGlobalPrefix()).to.be.eq('/api');
|
||||
expect(applicationConfig).not.toBeUndefined();
|
||||
expect(applicationConfig.getGlobalPrefix()).toBe('/api');
|
||||
});
|
||||
|
||||
it('should provide ModuleRef as core injectable', () => {
|
||||
const moduleRef = testingModule.get<ModuleRef>(ModuleRef);
|
||||
expect(moduleRef).to.not.be.undefined;
|
||||
expect(moduleRef).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should provide the current Module as provider', () => {
|
||||
const module = testingModule.get<CoreInjectablesModule>(
|
||||
CoreInjectablesModule,
|
||||
);
|
||||
expect(module).to.not.be.undefined;
|
||||
expect(module.constructor.name).to.be.eq('CoreInjectablesModule');
|
||||
expect(module).not.toBeUndefined();
|
||||
expect(module.constructor.name).toBe('CoreInjectablesModule');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { DefaultsModule } from '../src/defaults/defaults.module';
|
||||
import { DefaultsService } from '../src/defaults/defaults.service';
|
||||
import { DefaultsModule } from '../src/defaults/defaults.module.js';
|
||||
import { DefaultsService } from '../src/defaults/defaults.service.js';
|
||||
|
||||
describe('Injector', () => {
|
||||
describe('when optional', () => {
|
||||
@@ -10,7 +9,7 @@ describe('Injector', () => {
|
||||
imports: [DefaultsModule],
|
||||
});
|
||||
const app = await builder.compile();
|
||||
expect(app.get(DefaultsService).coreService.default).to.be.true;
|
||||
expect(app.get(DefaultsService).coreService.default).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,36 +1,30 @@
|
||||
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception';
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception';
|
||||
import { UnknownExportException } from '@nestjs/core/errors/exceptions/unknown-export.exception';
|
||||
import { RuntimeException } from '@nestjs/core/errors/exceptions/runtime.exception.js';
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js';
|
||||
import { UnknownExportException } from '@nestjs/core/errors/exceptions/unknown-export.exception.js';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as chai from 'chai';
|
||||
import * as chaiAsPromised from 'chai-as-promised';
|
||||
import {
|
||||
DYNAMIC_TOKEN,
|
||||
DYNAMIC_VALUE,
|
||||
NestDynamicModule,
|
||||
} from '../src/dynamic/dynamic.module';
|
||||
import { ExportsModule } from '../src/exports/exports.module';
|
||||
import { InjectModule } from '../src/inject/inject.module';
|
||||
import { InjectSameNameModule } from '../src/inject/inject-same-name.module';
|
||||
} from '../src/dynamic/dynamic.module.js';
|
||||
import { ExportsModule } from '../src/exports/exports.module.js';
|
||||
import { InjectSameNameModule } from '../src/inject/inject-same-name.module.js';
|
||||
import { InjectModule } from '../src/inject/inject.module.js';
|
||||
import {
|
||||
SelfInjectionProviderModule,
|
||||
SelfInjectionProviderCustomTokenModule,
|
||||
SelfInjectionForwardProviderModule,
|
||||
} from '../src/self-injection/self-injection-provider.module';
|
||||
chai.use(chaiAsPromised);
|
||||
SelfInjectionProviderCustomTokenModule,
|
||||
SelfInjectionProviderModule,
|
||||
} from '../src/self-injection/self-injection-provider.module.js';
|
||||
|
||||
describe('Injector', () => {
|
||||
describe('when "providers" and "exports" properties are inconsistent', () => {
|
||||
it(`should fail with "UnknownExportException"`, async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [ExportsModule],
|
||||
});
|
||||
await builder.compile();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceof(UnknownExportException);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [ExportsModule],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownExportException,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,20 +34,16 @@ describe('Injector', () => {
|
||||
imports: [InjectSameNameModule],
|
||||
});
|
||||
|
||||
await expect(builder.compile()).to.eventually.be.fulfilled;
|
||||
await expect(builder.compile()).resolves.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Nest cannot resolve dependencies', () => {
|
||||
it(`should fail with "RuntimeException"`, async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [InjectModule],
|
||||
});
|
||||
await builder.compile();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceof(RuntimeException);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [InjectModule],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(RuntimeException);
|
||||
});
|
||||
|
||||
describe('due to self-injection providers', () => {
|
||||
@@ -62,9 +52,7 @@ describe('Injector', () => {
|
||||
imports: [SelfInjectionProviderModule],
|
||||
});
|
||||
|
||||
await expect(
|
||||
builder.compile(),
|
||||
).to.eventually.be.rejected.and.be.an.instanceOf(
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownDependenciesException,
|
||||
);
|
||||
});
|
||||
@@ -73,9 +61,7 @@ describe('Injector', () => {
|
||||
imports: [SelfInjectionForwardProviderModule],
|
||||
});
|
||||
|
||||
await expect(
|
||||
builder.compile(),
|
||||
).to.eventually.be.rejected.and.be.an.instanceOf(
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownDependenciesException,
|
||||
);
|
||||
});
|
||||
@@ -84,9 +70,7 @@ describe('Injector', () => {
|
||||
imports: [SelfInjectionProviderCustomTokenModule],
|
||||
});
|
||||
|
||||
await expect(
|
||||
builder.compile(),
|
||||
).to.eventually.be.rejected.and.be.an.instanceOf(
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownDependenciesException,
|
||||
);
|
||||
});
|
||||
@@ -99,7 +83,7 @@ describe('Injector', () => {
|
||||
imports: [NestDynamicModule.byObject()],
|
||||
});
|
||||
const app = await builder.compile();
|
||||
expect(app.get(DYNAMIC_TOKEN)).to.be.eql(DYNAMIC_VALUE);
|
||||
expect(app.get(DYNAMIC_TOKEN)).toEqual(DYNAMIC_VALUE);
|
||||
});
|
||||
|
||||
it(`should return provider via token (exported by token)`, async () => {
|
||||
@@ -107,7 +91,7 @@ describe('Injector', () => {
|
||||
imports: [NestDynamicModule.byName()],
|
||||
});
|
||||
const app = await builder.compile();
|
||||
expect(app.get(DYNAMIC_TOKEN)).to.be.eql(DYNAMIC_VALUE);
|
||||
expect(app.get(DYNAMIC_TOKEN)).toEqual(DYNAMIC_VALUE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { ScopedModule, STATIC_FACTORY } from '../src/scoped/scoped.module';
|
||||
import { ScopedService } from '../src/scoped/scoped.service';
|
||||
import { TransientService } from '../src/scoped/transient.service';
|
||||
import { ScopedModule, STATIC_FACTORY } from '../src/scoped/scoped.module.js';
|
||||
import { ScopedService } from '../src/scoped/scoped.service.js';
|
||||
import { TransientService } from '../src/scoped/transient.service.js';
|
||||
|
||||
describe('Providers introspection', () => {
|
||||
let testingModule: TestingModule;
|
||||
@@ -17,18 +16,22 @@ describe('Providers introspection', () => {
|
||||
moduleRef = testingModule.get(ModuleRef);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await testingModule.close();
|
||||
});
|
||||
|
||||
it('should properly introspect a transient provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(TransientService);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.TRANSIENT);
|
||||
expect(introspectionResult.scope).toBe(Scope.TRANSIENT);
|
||||
});
|
||||
|
||||
it('should properly introspect a singleton provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(STATIC_FACTORY);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.DEFAULT);
|
||||
expect(introspectionResult.scope).toBe(Scope.DEFAULT);
|
||||
});
|
||||
|
||||
it('should properly introspect a request-scoped provider', async () => {
|
||||
const introspectionResult = moduleRef.introspect(ScopedService);
|
||||
expect(introspectionResult.scope).to.be.equal(Scope.REQUEST);
|
||||
expect(introspectionResult.scope).toBe(Scope.REQUEST);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { Global, Inject, Injectable, Module, Scope } from '@nestjs/common';
|
||||
|
||||
@Global()
|
||||
@@ -64,11 +62,11 @@ export class Dependant {
|
||||
) {}
|
||||
|
||||
public checkDependencies() {
|
||||
expect(this.transientProvider).to.be.instanceOf(TransientProvider);
|
||||
expect(this.foreignTransientProvider).to.be.instanceOf(
|
||||
expect(this.transientProvider).toBeInstanceOf(TransientProvider);
|
||||
expect(this.foreignTransientProvider).toBeInstanceOf(
|
||||
ForeignTransientProvider,
|
||||
);
|
||||
expect(this.requestProvider).to.be.instanceOf(RequestProvider);
|
||||
expect(this.requestProvider).toBeInstanceOf(RequestProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,9 +147,9 @@ describe('Many global modules', () => {
|
||||
const moduleRef = await moduleBuilder.compile();
|
||||
|
||||
const dependant = await moduleRef.resolve(Dependant);
|
||||
const checkDependenciesSpy = sinon.spy(dependant, 'checkDependencies');
|
||||
const checkDependenciesSpy = vi.spyOn(dependant, 'checkDependencies');
|
||||
dependant.checkDependencies();
|
||||
|
||||
expect(checkDependenciesSpy.called).to.be.true;
|
||||
expect(checkDependenciesSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { MultipleProvidersModule } from '../src/multiple-providers/multiple-providers.module';
|
||||
import { MultipleProvidersModule } from '../src/multiple-providers/multiple-providers.module.js';
|
||||
|
||||
describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
describe('get()', () => {
|
||||
@@ -20,7 +19,7 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
// @ts-expect-error: make sure "multiProviderInstances" is string[] not string
|
||||
multiProviderInstances.charAt;
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
expect(multiProviderInstances).toEqual(['A', 'B', 'C']);
|
||||
});
|
||||
});
|
||||
describe('resolve()', () => {
|
||||
@@ -41,7 +40,7 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
// @ts-expect-error: make sure "multiProviderInstances" is string[] not string
|
||||
multiProviderInstances.charAt;
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
expect(multiProviderInstances).toEqual(['A', 'B', 'C']);
|
||||
});
|
||||
|
||||
it('should return an array of default-scoped providers', async () => {
|
||||
@@ -61,7 +60,7 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
// @ts-expect-error: make sure "multiProviderInstances" is string[] not string
|
||||
multiProviderInstances.charAt;
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
expect(multiProviderInstances).toEqual(['A', 'B', 'C']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Scope } from '@nestjs/common';
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception';
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Optional factory provider deps', () => {
|
||||
describe('when dependency is optional', () => {
|
||||
@@ -20,7 +19,7 @@ describe('Optional factory provider deps', () => {
|
||||
}).compile();
|
||||
|
||||
const factoryProvider = moduleRef.get('FACTORY');
|
||||
expect(factoryProvider).to.equal('OPTIONAL_DEP_VALUE');
|
||||
expect(factoryProvider).toBe('OPTIONAL_DEP_VALUE');
|
||||
});
|
||||
});
|
||||
describe('otherwise', () => {
|
||||
@@ -37,7 +36,7 @@ describe('Optional factory provider deps', () => {
|
||||
}).compile();
|
||||
|
||||
const factoryProvider = moduleRef.get('FACTORY');
|
||||
expect(factoryProvider).to.equal(defaultValue);
|
||||
expect(factoryProvider).toBe(defaultValue);
|
||||
});
|
||||
it('"undefined" should be injected into the factory function (scoped provider)', async () => {
|
||||
const MY_PROVIDER = 'MY_PROVIDER';
|
||||
@@ -70,7 +69,7 @@ describe('Optional factory provider deps', () => {
|
||||
],
|
||||
}).compile();
|
||||
|
||||
expect(await module.resolve(MY_PROVIDER)).to.deep.equal({
|
||||
expect(await module.resolve(MY_PROVIDER)).toEqual({
|
||||
first: undefined,
|
||||
second: 'second',
|
||||
});
|
||||
@@ -80,56 +79,44 @@ describe('Optional factory provider deps', () => {
|
||||
describe('otherwise', () => {
|
||||
describe('and dependency is not registered', () => {
|
||||
it('should error out', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'FACTORY',
|
||||
useFactory: () => 'RETURNED_VALUE',
|
||||
inject: ['MISSING_DEP'],
|
||||
},
|
||||
],
|
||||
});
|
||||
await builder.compile();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(UnknownDependenciesException);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'FACTORY',
|
||||
useFactory: () => 'RETURNED_VALUE',
|
||||
inject: ['MISSING_DEP'],
|
||||
},
|
||||
],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownDependenciesException,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('and dependency is registered but it cannot be instantiated', () => {
|
||||
it('should error out', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'POSSIBLY_MISSING_DEP',
|
||||
useFactory: () => null,
|
||||
inject: ['MISSING_DEP'],
|
||||
},
|
||||
{
|
||||
provide: 'FACTORY',
|
||||
useFactory: () => 'RETURNED_VALUE',
|
||||
inject: [{ token: 'POSSIBLY_MISSING_DEP', optional: false }],
|
||||
},
|
||||
],
|
||||
});
|
||||
await builder.compile();
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(UnknownDependenciesException);
|
||||
expect(err.message).to
|
||||
.equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument "MISSING_DEP" at index [0] is available in the RootTestModule context.
|
||||
|
||||
Potential solutions:
|
||||
- Is RootTestModule a valid NestJS module?
|
||||
- If "MISSING_DEP" is a provider, is it part of the current RootTestModule?
|
||||
- If "MISSING_DEP" is exported from a separate @Module, is that module imported within RootTestModule?
|
||||
@Module({
|
||||
imports: [ /* the Module containing "MISSING_DEP" */ ]
|
||||
})
|
||||
|
||||
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`);
|
||||
}
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{
|
||||
provide: 'POSSIBLY_MISSING_DEP',
|
||||
useFactory: () => null,
|
||||
inject: ['MISSING_DEP'],
|
||||
},
|
||||
{
|
||||
provide: 'FACTORY',
|
||||
useFactory: () => 'RETURNED_VALUE',
|
||||
inject: [{ token: 'POSSIBLY_MISSING_DEP', optional: false }],
|
||||
},
|
||||
],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toSatisfy((err: any) => {
|
||||
expect(err).toBeInstanceOf(UnknownDependenciesException);
|
||||
expect(err.message).toContain(
|
||||
`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?)`,
|
||||
);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception.js';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { DependencyService } from '../src/properties/dependency.service';
|
||||
import { PropertiesModule } from '../src/properties/properties.module';
|
||||
import { PropertiesService } from '../src/properties/properties.service';
|
||||
import { UnknownDependenciesException } from '@nestjs/core/errors/exceptions/unknown-dependencies.exception';
|
||||
import { DependencyService } from '../src/properties/dependency.service.js';
|
||||
import { PropertiesModule } from '../src/properties/properties.module.js';
|
||||
import { PropertiesService } from '../src/properties/properties.service.js';
|
||||
|
||||
describe('Injector', () => {
|
||||
it('should resolve property-based dependencies', async () => {
|
||||
@@ -13,32 +12,25 @@ describe('Injector', () => {
|
||||
const app = await builder.compile();
|
||||
const dependency = app.get(DependencyService);
|
||||
|
||||
expect(app.get(PropertiesService).service).to.be.eql(dependency);
|
||||
expect(app.get(PropertiesService).token).to.be.true;
|
||||
expect(app.get(PropertiesService).symbolToken).to.be.true;
|
||||
expect(app.get(PropertiesService).service).toEqual(dependency);
|
||||
expect(app.get(PropertiesService).token).toBe(true);
|
||||
expect(app.get(PropertiesService).symbolToken).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw UnknownDependenciesException when dependency is not met', async () => {
|
||||
let exception;
|
||||
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
DependencyService,
|
||||
PropertiesService,
|
||||
{
|
||||
provide: 'token',
|
||||
useValue: true,
|
||||
},
|
||||
// symbol token is missing here
|
||||
],
|
||||
});
|
||||
const app = await builder.compile();
|
||||
app.get(DependencyService);
|
||||
} catch (e) {
|
||||
exception = e;
|
||||
}
|
||||
|
||||
expect(exception).to.be.instanceOf(UnknownDependenciesException);
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
DependencyService,
|
||||
PropertiesService,
|
||||
{
|
||||
provide: 'token',
|
||||
useValue: true,
|
||||
},
|
||||
// symbol token is missing here
|
||||
],
|
||||
});
|
||||
await expect(builder.compile()).rejects.toBeInstanceOf(
|
||||
UnknownDependenciesException,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user