diff --git a/.circleci/config.yml b/.circleci/config.yml index a6dc1376ba..b2943743d6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -78,7 +78,7 @@ jobs: - run: name: Collecting https://codeclimate.com/github/ike18t/ng-mocks command: | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 \ + curl -sL https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 \ > $HOME/codeclimate chmod +x $HOME/codeclimate $HOME/codeclimate before-build diff --git a/docs/articles/api/MockRender.md b/docs/articles/api/MockRender.md index 366d379749..dbbc4c20bc 100644 --- a/docs/articles/api/MockRender.md +++ b/docs/articles/api/MockRender.md @@ -379,7 +379,7 @@ Especially, in cases, when the same setup should be used in different tests. For example, we have 5 tests and every test calls `MockRender(MyComponent)`. It means that every time a middleware component has been created and injected into `TestBed`, -whereas `MockRender` could have reused the existing middleware component and simply to create a new fixture out of it. +whereas `MockRender` could reuse the existing middleware component and simply would create a new fixture out of it. In such situations, `MockRenderFactory` can be used instead of `MockRender`. It accepts `bindings` and `providers`, but instead of an instant render, @@ -390,14 +390,11 @@ and then 5 tests should call the factory in order to create fixtures. ```ts describe('Maximum performance', () => { + const factory = MockRenderFactory(MyComponent, ['input1', 'input2']); + ngMocks.faster(); - beforeAll(() => MockBuilder(MyComponent, MyModule)); - - let factory: MockRenderFactory; - beforeAll(() => { - factory = MockRenderFactory(MyComponent, ['input1', 'input2']); - }); + beforeAll(() => factory.configureTestBed()); it('covers one case', () => { const fixture = factory({input1: 1}); diff --git a/e2e/a10/package.json b/e2e/a10/package.json index 6d70bf0e97..9607cb278b 100644 --- a/e2e/a10/package.json +++ b/e2e/a10/package.json @@ -6,6 +6,7 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015:ivy -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5:ivy && npm run test:jasmine:es5:no-ivy && npm run test:jasmine:es2015:ivy && npm run test:jasmine:es2015:no-ivy", "test:jasmine:es5:ivy": "ng test --ts-config ./tsconfig.es5ivy.spec.json --progress=false", "test:jasmine:es5:no-ivy": "ng test --ts-config ./tsconfig.es5noivy.spec.json --progress=false", @@ -13,11 +14,11 @@ "test:jasmine:es2015:no-ivy": "ng test --ts-config ./tsconfig.es2015noivy.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5:ivy && npm run test:jest:es5:no-ivy &&npm run test:jest:es2015:ivy &&npm run test:jest:es2015:no-ivy", - "test:jest:es5:ivy": "jest -w 1 --config jest.es5ivy.js", - "test:jest:es5:no-ivy": "jest -w 1 --config jest.es5noivy.js", - "test:jest:es2015:ivy": "jest -w 1 --config jest.es2015ivy.js", - "test:jest:es2015:no-ivy": "jest -w 1 --config jest.es2015noivy.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5:ivy": "jest -w 2 --config jest.es5ivy.js", + "test:jest:es5:no-ivy": "jest -w 2 --config jest.es5noivy.js", + "test:jest:es2015:ivy": "jest -w 2 --config jest.es2015ivy.js", + "test:jest:es2015:no-ivy": "jest -w 2 --config jest.es2015noivy.js", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a11/package.json b/e2e/a11/package.json index 35b20bce5a..84a961a400 100644 --- a/e2e/a11/package.json +++ b/e2e/a11/package.json @@ -7,6 +7,7 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015:ivy -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5:ivy && npm run test:jasmine:es5:no-ivy && npm run test:jasmine:es2015:ivy && npm run test:jasmine:es2015:no-ivy", "test:jasmine:es5:ivy": "ng test --ts-config ./tsconfig.es5ivy.spec.json --progress=false", "test:jasmine:es5:no-ivy": "ng test --ts-config ./tsconfig.es5noivy.spec.json --progress=false", @@ -14,11 +15,11 @@ "test:jasmine:es2015:no-ivy": "ng test --ts-config ./tsconfig.es2015noivy.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5:ivy && npm run test:jest:es5:no-ivy &&npm run test:jest:es2015:ivy &&npm run test:jest:es2015:no-ivy", - "test:jest:es5:ivy": "jest -w 1 --config jest.es5ivy.js", - "test:jest:es5:no-ivy": "jest -w 1 --config jest.es5noivy.js", - "test:jest:es2015:ivy": "jest -w 1 --config jest.es2015ivy.js", - "test:jest:es2015:no-ivy": "jest -w 1 --config jest.es2015noivy.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5:ivy": "jest -w 2 --config jest.es5ivy.js", + "test:jest:es5:no-ivy": "jest -w 2 --config jest.es5noivy.js", + "test:jest:es2015:ivy": "jest -w 2 --config jest.es2015ivy.js", + "test:jest:es2015:no-ivy": "jest -w 2 --config jest.es2015noivy.js", + "test:jest:debug": "jest -w 2 --watch" }, "peerDependencies": { "ng-mocks": "*" diff --git a/e2e/a12/package.json b/e2e/a12/package.json index a63f9f0af0..3d69fda455 100644 --- a/e2e/a12/package.json +++ b/e2e/a12/package.json @@ -7,14 +7,15 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015:ivy -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5:ivy && npm run test:jasmine:es2015:ivy", "test:jasmine:es5:ivy": "ng test --ts-config ./tsconfig.es5ivy.spec.json --progress=false", "test:jasmine:es2015:ivy": "ng test --ts-config ./tsconfig.es2015ivy.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5:ivy && npm run test:jest:es2015:ivy", - "test:jest:es5:ivy": "jest -w 1 --config jest.es5ivy.js", - "test:jest:es2015:ivy": "jest -w 1 --config jest.es2015ivy.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5:ivy": "jest -w 2 --config jest.es5ivy.js", + "test:jest:es2015:ivy": "jest -w 2 --config jest.es2015ivy.js", + "test:jest:debug": "jest -w 2 --watch" }, "peerDependencies": { "ng-mocks": "*" diff --git a/e2e/a5es2015/package.json b/e2e/a5es2015/package.json index 7a0ec706e7..ff4f7b430d 100644 --- a/e2e/a5es2015/package.json +++ b/e2e/a5es2015/package.json @@ -6,10 +6,11 @@ "start": "ng serve", "build": "ng build --prod", "test": "npm run test:jasmine -- --progress=false && npm run test:jest", + "test:debug": "npm run test:jasmine -- --browsers=Chrome --watch", "test:jasmine": "ng test", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", - "test:jest": "jest -w 1", - "test:jest:debug": "jest -w 1 --watch" + "test:jest": "jest -w 2", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a5es5/package.json b/e2e/a5es5/package.json index 61df7a5464..96116af481 100644 --- a/e2e/a5es5/package.json +++ b/e2e/a5es5/package.json @@ -6,10 +6,11 @@ "start": "ng serve", "build": "ng build --prod", "test": "npm run test:jasmine -- --progress=false && npm run test:jest", + "test:debug": "npm run test:jasmine -- --browsers=Chrome --watch", "test:jasmine": "ng test", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", - "test:jest": "jest -w 1", - "test:jest:debug": "jest -w 1 --watch" + "test:jest": "jest -w 2", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a6/package.json b/e2e/a6/package.json index f171ebb4ff..ccf533593d 100644 --- a/e2e/a6/package.json +++ b/e2e/a6/package.json @@ -6,14 +6,15 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015 -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5 && npm run test:jasmine:es2015", "test:jasmine:es5": "ng test --ts-config ./tsconfig.es5.spec.json --progress=false", "test:jasmine:es2015": "ng test --ts-config ./tsconfig.es2015.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5 && npm run test:jest:es2015", - "test:jest:es5": "jest -w 1 --config ./jest.es5.js", - "test:jest:es2015": "jest -w 1 --config ./jest.es2015.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5": "jest -w 2 --config ./jest.es5.js", + "test:jest:es2015": "jest -w 2 --config ./jest.es2015.js", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a7/package.json b/e2e/a7/package.json index a74c28fe51..bf69e8bbc8 100644 --- a/e2e/a7/package.json +++ b/e2e/a7/package.json @@ -6,14 +6,15 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015 -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5 && npm run test:jasmine:es2015", "test:jasmine:es5": "ng test --ts-config ./tsconfig.es5.spec.json --progress=false", "test:jasmine:es2015": "ng test --ts-config ./tsconfig.es2015.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5 && npm run test:jest:es2015", - "test:jest:es5": "jest -w 1 --config ./jest.es5.js", - "test:jest:es2015": "jest -w 1 --config ./jest.es2015.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5": "jest -w 2 --config ./jest.es5.js", + "test:jest:es2015": "jest -w 2 --config ./jest.es2015.js", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a8/package.json b/e2e/a8/package.json index 557db5ae7b..e88b174cf7 100644 --- a/e2e/a8/package.json +++ b/e2e/a8/package.json @@ -6,14 +6,15 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015 -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5 && npm run test:jasmine:es2015", "test:jasmine:es5": "ng test --ts-config ./tsconfig.es5.spec.json --progress=false", "test:jasmine:es2015": "ng test --ts-config ./tsconfig.es2015.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5 && npm run test:jest:es2015", - "test:jest:es5": "jest -w 1 --config ./jest.es5.js", - "test:jest:es2015": "jest -w 1 --config ./jest.es2015.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5": "jest -w 2 --config ./jest.es5.js", + "test:jest:es2015": "jest -w 2 --config ./jest.es2015.js", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/a9/package.json b/e2e/a9/package.json index d50721238b..2386d0e6a6 100644 --- a/e2e/a9/package.json +++ b/e2e/a9/package.json @@ -6,6 +6,7 @@ "start": "ng serve", "build": "ng build", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015:ivy -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5:ivy && npm run test:jasmine:es5:no-ivy && npm run test:jasmine:es2015:ivy && npm run test:jasmine:es2015:no-ivy", "test:jasmine:es5:ivy": "ng test --ts-config ./tsconfig.es5ivy.spec.json --progress=false", "test:jasmine:es5:no-ivy": "ng test --ts-config ./tsconfig.es5noivy.spec.json --progress=false", @@ -13,11 +14,11 @@ "test:jasmine:es2015:no-ivy": "ng test --ts-config ./tsconfig.es2015noivy.spec.json --progress=false", "test:jasmine:debug": "ng test -- --watch --browsers Chrome", "test:jest": "npm run test:jest:es5:ivy && npm run test:jest:es5:no-ivy &&npm run test:jest:es2015:ivy &&npm run test:jest:es2015:no-ivy", - "test:jest:es5:ivy": "jest -w 1 --config jest.es5ivy.js", - "test:jest:es5:no-ivy": "jest -w 1 --config jest.es5noivy.js", - "test:jest:es2015:ivy": "jest -w 1 --config jest.es2015ivy.js", - "test:jest:es2015:no-ivy": "jest -w 1 --config jest.es2015noivy.js", - "test:jest:debug": "jest -w 1 --watch" + "test:jest:es5:ivy": "jest -w 2 --config jest.es5ivy.js", + "test:jest:es5:no-ivy": "jest -w 2 --config jest.es5noivy.js", + "test:jest:es2015:ivy": "jest -w 2 --config jest.es2015ivy.js", + "test:jest:es2015:no-ivy": "jest -w 2 --config jest.es2015noivy.js", + "test:jest:debug": "jest -w 2 --watch" }, "private": true, "peerDependencies": { diff --git a/e2e/am/package.json b/e2e/am/package.json index d55e8e790d..746d8b1e69 100644 --- a/e2e/am/package.json +++ b/e2e/am/package.json @@ -9,6 +9,7 @@ "build:es5:ivy": "npm run build -- --ts-config ./tsconfig.es5ivy.app.json --prod", "build:es2015:ivy": "npm run build -- --ts-config ./tsconfig.es2015ivy.app.json --prod", "test": "npm run test:jasmine && npm run test:jest", + "test:debug": "npm run test:jasmine:es2015:ivy -- --browsers=Chrome --watch", "test:jasmine": "npm run test:jasmine:es5:ivy && npm run test:jasmine:es2015:ivy", "test:jasmine:es5:ivy": "ng test --ts-config ./tsconfig.es5ivy.spec.json --progress=false", "test:jasmine:es2015:ivy": "ng test --ts-config ./tsconfig.es2015ivy.spec.json --progress=false", diff --git a/libs/ng-mocks/src/lib/common/ng-mocks-stack.ts b/libs/ng-mocks/src/lib/common/ng-mocks-stack.ts index 9f6554ac4b..80dc29961c 100644 --- a/libs/ng-mocks/src/lib/common/ng-mocks-stack.ts +++ b/libs/ng-mocks/src/lib/common/ng-mocks-stack.ts @@ -2,14 +2,16 @@ import ngMocksUniverse from './ng-mocks-universe'; export interface NgMocksStack { id: object; + level: 'root' | 'suite' | 'test'; mockInstance?: any[]; } type NgMocksStackCallback = (state: NgMocksStack, stack: NgMocksStack[]) => void; -// istanbul ignore next -const stack: NgMocksStack[] = ngMocksUniverse.global.get('reporter-stack') ?? []; +const stackRoot: NgMocksStack = { id: {}, level: 'root' }; +const stack: NgMocksStack[] = ngMocksUniverse.global.get('reporter-stack') || [{ ...stackRoot }]; ngMocksUniverse.global.set('reporter-stack', stack); +const current = () => stack[stack.length - 1]; // istanbul ignore next const listenersPush: NgMocksStackCallback[] = ngMocksUniverse.global.get('reporter-stack-push') ?? []; @@ -19,10 +21,10 @@ ngMocksUniverse.global.set('reporter-stack-push', listenersPush); const listenersPop: NgMocksStackCallback[] = ngMocksUniverse.global.get('reporter-stack-pop') ?? []; ngMocksUniverse.global.set('reporter-stack-pop', listenersPop); -const stackPush = () => { +const stackPush = (level: NgMocksStack['level']) => { const id = {}; ngMocksUniverse.global.set('reporter-stack-id', id); - const state = { id }; + const state = { id, level }; stack.push(state); for (const callback of listenersPush) { @@ -31,10 +33,11 @@ const stackPush = () => { }; const stackPop = () => { const state = stack.pop(); + + // this code is actually needed for jest tests. // istanbul ignore if if (stack.length === 0) { - const id = {}; - stack.push({ id }); + stack.push({ ...stackRoot }); } // istanbul ignore else @@ -49,18 +52,38 @@ const stackPop = () => { const reporterStack: jasmine.CustomReporter = { jasmineDone: stackPop, - jasmineStarted: stackPush, + jasmineStarted: () => stackPush('root'), specDone: stackPop, - specStarted: stackPush, + specStarted: () => stackPush('test'), suiteDone: stackPop, - suiteStarted: stackPush, + suiteStarted: () => stackPush('suite'), +}; + +const messageCore = [ + 'ng-mocks cannot install own spec reporter.', + 'This affects its core features for MockInstance and MockRender.', + 'Please report an issue on github.', + 'If you use jest v27, please add to its config testRunner=jest-jasmine2 for now', + 'and upvote the issue on github: /~https://github.com/facebook/jest/issues/11483.', +].join(' '); + +// istanbul ignore next +const messageCoreChecker = () => { + if (current().level === 'root') { + throw new Error(messageCore); + } }; const install = () => { if (!ngMocksUniverse.global.has('reporter-stack-install')) { - jasmine.getEnv().addReporter(reporterStack); - ngMocksUniverse.global.set('reporter-stack-install', true); - stackPush(); + // istanbul ignore if + // tslint:disable-next-line strict-type-predicates + if (typeof jasmine === 'undefined') { + messageCoreChecker(); + } else { + jasmine.getEnv().addReporter(reporterStack); + ngMocksUniverse.global.set('reporter-stack-install', true); + } } return ngMocksUniverse.global.has('reporter-stack-install'); @@ -100,6 +123,7 @@ const unsubscribePop = (callback: NgMocksStackCallback) => { }; export default { + current, install, subscribePop, subscribePush, diff --git a/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts b/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts index 4cd4f1befb..b931004995 100644 --- a/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts +++ b/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts @@ -1,5 +1,4 @@ import { Component, Directive } from '@angular/core'; -import { TestBed } from '@angular/core/testing'; import coreDefineProperty from '../common/core.define-property'; import { Type } from '../common/core.types'; @@ -39,11 +38,7 @@ const generateWrapper = ({ bindings, options, inputs }: any) => { } } } - Component(options)(MockRenderComponent); - TestBed.configureTestingModule({ - declarations: [MockRenderComponent], - }); return MockRenderComponent; }; diff --git a/libs/ng-mocks/src/lib/mock-render/mock-render-factory.ts b/libs/ng-mocks/src/lib/mock-render/mock-render-factory.ts index 2268fbf346..a0e2763163 100644 --- a/libs/ng-mocks/src/lib/mock-render/mock-render-factory.ts +++ b/libs/ng-mocks/src/lib/mock-render/mock-render-factory.ts @@ -5,6 +5,7 @@ import coreDefineProperty from '../common/core.define-property'; import { AnyType, Type } from '../common/core.types'; import funcImportExists from '../common/func.import-exists'; import { isNgDef } from '../common/func.is-ng-def'; +import ngMocksStack from '../common/ng-mocks-stack'; import ngMocksUniverse from '../common/ng-mocks-universe'; import { ngMocks } from '../mock-helper/mock-helper'; import helperDefinePropertyDescriptor from '../mock-service/helper.define-property-descriptor'; @@ -13,10 +14,11 @@ import { MockService } from '../mock-service/mock-service'; import funcCreateWrapper from './func.create-wrapper'; import funcInstallPropReader from './func.install-prop-reader'; import funcReflectTemplate from './func.reflect-template'; -import { IMockRenderOptions, MockedComponentFixture } from './types'; +import { IMockRenderFactoryOptions, MockedComponentFixture } from './types'; interface MockRenderFactory { bindings: keyof F; + configureTestBed: () => void; declaration: AnyType; >(params?: Partial, detectChanges?: boolean): MockedComponentFixture; } @@ -87,8 +89,35 @@ const flushTestBed = (flags: Record): void => { } }; -const generateFactory = (componentCtor: Type, bindings: undefined | null | string[], template: any) => { +const generateFactoryInstall = (ctor: AnyType, options: IMockRenderFactoryOptions) => () => { + const testBed: TestBed & { + _compiler?: { + declarations?: Array>; + }; + _declarations?: Array>; + declarations?: Array>; + } = getTestBed(); + const declarations = testBed._compiler?.declarations || testBed.declarations || testBed._declarations; + if (!declarations || declarations.indexOf(ctor) === -1) { + flushTestBed(options); + try { + TestBed.configureTestingModule({ + declarations: [ctor], + }); + } catch (e) { + handleFixtureError(e); + } + } +}; + +const generateFactory = ( + componentCtor: Type, + bindings: undefined | null | string[], + template: any, + options: IMockRenderFactoryOptions, +) => { const result = (params: any, detectChanges?: boolean) => { + result.configureTestBed(); const fixture: any = TestBed.createComponent(componentCtor); funcInstallPropReader(fixture.componentInstance, params ?? {}, bindings ?? []); @@ -108,6 +137,7 @@ const generateFactory = (componentCtor: Type, bindings: undefined | null | }; result.declaration = componentCtor; result.bindings = bindings; + result.configureTestBed = generateFactoryInstall(componentCtor, options); return result; }; @@ -118,7 +148,7 @@ const generateFactory = (componentCtor: Type, bindings: undefined | null | function MockRenderFactory( template: InjectionToken, bindings?: undefined | null, - options?: IMockRenderOptions, + options?: IMockRenderFactoryOptions, ): MockRenderFactory; /** @@ -127,7 +157,7 @@ function MockRenderFactory( function MockRenderFactory( template: AnyType, bindings: undefined | null, - options?: IMockRenderOptions, + options?: IMockRenderFactoryOptions, ): MockRenderFactory; /** @@ -136,7 +166,7 @@ function MockRenderFactory( function MockRenderFactory( template: AnyType, bindings: TKeys[], - options?: IMockRenderOptions, + options?: IMockRenderFactoryOptions, ): MockRenderFactory; /** @@ -144,8 +174,8 @@ function MockRenderFactory( */ function MockRenderFactory( template: AnyType, - bindings: TKeys, - options?: IMockRenderOptions, + bindings: TKeys[], + options?: IMockRenderFactoryOptions, ): MockRenderFactory; /** @@ -174,27 +204,25 @@ function MockRenderFactory(template: string): MockRenderFacto */ function MockRenderFactory( template: string, - bindings?: undefined | null, - options?: IMockRenderOptions, + bindings: TKeys[], + options?: IMockRenderFactoryOptions, ): MockRenderFactory; function MockRenderFactory( template: string | AnyType | InjectionToken, bindings?: undefined | null | TKeys[], - options: IMockRenderOptions = {}, + options: IMockRenderFactoryOptions = {}, ): any { funcImportExists(template, 'MockRender'); const meta: Directive = typeof template === 'string' || isNgDef(template, 't') ? {} : funcReflectTemplate(template); - - flushTestBed(options); - try { - const componentCtor: any = funcCreateWrapper(template, meta, bindings, options); - - return generateFactory(componentCtor, bindings, template); - } catch (e) { - handleFixtureError(e); + const componentCtor: any = funcCreateWrapper(template, meta, bindings, options); + const factory = generateFactory(componentCtor, bindings, template, options); + if (ngMocksStack.current().level !== 'root' && options.configureTestBed !== false) { + factory.configureTestBed(); } + + return factory; } export { MockRenderFactory }; diff --git a/libs/ng-mocks/src/lib/mock-render/types.ts b/libs/ng-mocks/src/lib/mock-render/types.ts index 4b7515c931..033d1dd630 100644 --- a/libs/ng-mocks/src/lib/mock-render/types.ts +++ b/libs/ng-mocks/src/lib/mock-render/types.ts @@ -15,6 +15,10 @@ export interface IMockRenderOptions { reset?: boolean; } +export interface IMockRenderFactoryOptions extends IMockRenderOptions { + configureTestBed?: boolean; +} + export interface MockedComponentFixture> extends ComponentFixture { point: MockedDebugElement; } diff --git a/tests/mock-render-factory/install.spec.ts b/tests/mock-render-factory/install.spec.ts new file mode 100644 index 0000000000..7bbbb60284 --- /dev/null +++ b/tests/mock-render-factory/install.spec.ts @@ -0,0 +1,68 @@ +import { Component, Input, NgModule } from '@angular/core'; +import { MockBuilder, MockRenderFactory, ngMocks } from 'ng-mocks'; + +@Component({ + selector: 'target', + template: `{{ value }}`, +}) +class TargetComponent { + @Input() public readonly value: number | null = null; +} + +@NgModule({ + declarations: [TargetComponent], +}) +class TargetModule {} + +describe('mock-render-factory:install', () => { + describe('explicit', () => { + const factory = MockRenderFactory(TargetComponent, ['value']); + + ngMocks.faster(); + beforeAll(() => MockBuilder(TargetComponent, TargetModule)); + beforeAll(() => factory.configureTestBed()); + + it('renders 1 value', () => { + const fixture = factory({ value: 1 }); + expect(ngMocks.formatText(fixture)).toEqual('1'); + }); + + it('renders 2 value', () => { + const fixture = factory({ value: 2 }); + expect(ngMocks.formatText(fixture)).toEqual('2'); + }); + }); + + describe('auto', () => { + const factory = MockRenderFactory(TargetComponent, ['value']); + + ngMocks.faster(); + beforeAll(() => MockBuilder(TargetComponent, TargetModule)); + + it('installs and renders 1 value', () => { + const fixture = factory({ value: 1 }); + expect(ngMocks.formatText(fixture)).toEqual('1'); + }); + + it('does not install and renders 2 value', () => { + const fixture = factory({ value: 2 }); + expect(ngMocks.formatText(fixture)).toEqual('2'); + }); + }); + + describe('each', () => { + const factory = MockRenderFactory(TargetComponent, ['value']); + + beforeEach(() => MockBuilder(TargetComponent, TargetModule)); + + it('installs and renders 1 value', () => { + const fixture = factory({ value: 1 }); + expect(ngMocks.formatText(fixture)).toEqual('1'); + }); + + it('installs and renders 2 value', () => { + const fixture = factory({ value: 2 }); + expect(ngMocks.formatText(fixture)).toEqual('2'); + }); + }); +}); diff --git a/tests/mock-render-factory/tpl.spec.ts b/tests/mock-render-factory/tpl.spec.ts new file mode 100644 index 0000000000..16ec353aaa --- /dev/null +++ b/tests/mock-render-factory/tpl.spec.ts @@ -0,0 +1,76 @@ +import { Component, Input, NgModule } from '@angular/core'; +import { MockBuilder, MockRenderFactory, ngMocks } from 'ng-mocks'; + +@Component({ + selector: 'target', + template: `{{ i1 }}:{{ i2 }}:{{ i3 }}`, +}) +class TargetComponent { + public readonly i0 = 0; + @Input() public readonly i1: number | null = 1; + @Input() public readonly i2: number | null = 2; + @Input() public readonly i3: number | null = 3; + public readonly i4 = 4; +} + +@NgModule({ + declarations: [TargetComponent], +}) +class TargetModule {} + +describe('mock-render-factory:tpl', () => { + describe('w/o bindings', () => { + const factory = MockRenderFactory(TargetComponent); + + ngMocks.faster(); + + beforeAll(() => MockBuilder(TargetComponent, TargetModule)); + beforeAll(() => factory.configureTestBed()); + + it('binds all inputs', () => { + const fixture = factory(); + expect(ngMocks.formatText(fixture)).toEqual('::'); + }); + }); + + describe('w/ empty bindings', () => { + const factory = MockRenderFactory(TargetComponent, []); + + ngMocks.faster(); + + beforeAll(() => MockBuilder(TargetComponent, TargetModule)); + beforeAll(() => factory.configureTestBed()); + + it('renders default values', () => { + const fixture = factory(); + expect(ngMocks.formatText(fixture)).toEqual('1:2:3'); + }); + }); + + describe('w/ bindings', () => { + const factory = MockRenderFactory( + '{{ i0 }}--{{ i4 }}', + ['i0', 'i1', 'i2', 'i4'], + ); + + ngMocks.faster(); + + beforeAll(() => MockBuilder(TargetComponent, TargetModule)); + beforeAll(() => factory.configureTestBed()); + + it('renders undefined everywhere on empty params', () => { + const fixture = factory(); + expect(ngMocks.formatText(fixture)).toEqual('-::3-'); + }); + + it('renders provided params', () => { + const fixture = factory({ + i0: 5, + i1: 6, + i2: 7, + i4: 8, + }); + expect(ngMocks.formatText(fixture)).toEqual('5-6:7:3-8'); + }); + }); +});