Skip to content

Commit

Permalink
fix(MockBuilder): imports modules with providers on root level #4613
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed Jan 6, 2023
1 parent 30f5bd1 commit 3ed8ae4
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
2 changes: 2 additions & 0 deletions libs/ng-mocks/src/lib/mock-builder/promise/init-ng-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export default (
if (!config.dependency && config.export && !configInstance?.exported && (isNgDef(def, 'i') || !isNgDef(def))) {
handleDef(meta, def, defProviders);
markProviders([def]);
} else if (!config.dependency && isNgDef(def, 'm') && defProviders.has(def)) {
handleDef(meta, def, defProviders);
} else if (!config.dependency && config.export && !configInstance?.exported) {
handleDef(meta, def, defProviders);
} else if (!ngMocksUniverse.touches.has(def) && !config.dependency) {
Expand Down
189 changes: 189 additions & 0 deletions tests/issue-4613/providers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import { Component, Injectable, NgModule } from '@angular/core';

import { isMockOf, MockBuilder, MockRender, ngMocks } from 'ng-mocks';

@Injectable()
class ProviderService {}

@Injectable()
class Provider1Service {}

@Injectable()
class Provider2Service {}

@Component({
selector: 'target',
template: '{{ service.constructor.name }}',
providers: [Provider2Service],
})
class TargetComponent {
constructor(public readonly service: ProviderService) {}
}

@NgModule({
declarations: [TargetComponent],
})
class TargetModule {
static for1() {
return {
ngModule: TargetModule,
providers: [
Provider1Service,
{
provide: ProviderService,
useExisting: Provider1Service,
},
],
};
}

static for2() {
return {
ngModule: TargetModule,
providers: [
Provider2Service,
{
provide: ProviderService,
useExisting: Provider2Service,
},
],
};
}
}

@NgModule({
imports: [TargetModule],
})
class DependencyModule {}

@NgModule({
imports: [TargetModule.for2()],
})
class DependencyWithProvidersModule {}

@NgModule({
providers: [ProviderService],
})
class ProviderModule {}

// @see https://github.com/help-me-mom/ng-mocks/issues/4613
describe('issue-4613', () => {
describe('fails without providers', () => {
beforeEach(() => MockBuilder(TargetComponent, DependencyModule));

it('fails because no provider for ProviderService', () => {
expect(() => MockRender(TargetComponent)).toThrowError(
/No provider for ProviderService/,
);
});
});

describe('works with providers', () => {
beforeEach(() =>
MockBuilder(TargetComponent, [
DependencyModule,
ProviderModule,
]),
);

it('renders ProviderService', () => {
expect(ngMocks.formatText(MockRender(TargetComponent))).toEqual(
'ProviderService',
);
});
});

describe('MockBuilder.mock', () => {
describe('works with nested Provider1Service', () => {
beforeEach(() =>
MockBuilder(
[TargetComponent, ProviderService],
[DependencyModule, TargetModule.for1()],
),
);

it('renders Provider1Service', () => {
expect(
ngMocks.formatText(MockRender(TargetComponent)),
).toEqual('Provider1Service');

expect(
isMockOf(
ngMocks.findInstance(ProviderService),
Provider1Service,
),
).toEqual(true);
});
});

describe('overrides Provider2Service with Provider1Service', () => {
beforeEach(() =>
MockBuilder(
[TargetComponent, ProviderService],
[DependencyWithProvidersModule, TargetModule.for1()],
),
);

it('renders Provider1Service', () => {
expect(
ngMocks.formatText(MockRender(TargetComponent)),
).toEqual('Provider1Service');

expect(
isMockOf(
ngMocks.findInstance(ProviderService),
Provider1Service,
),
).toEqual(true);
});
});
});

describe('MockBuilder.keep', () => {
describe('works with nested Provider1Service', () => {
beforeEach(() =>
MockBuilder([
TargetComponent,
DependencyModule,
TargetModule.for1(),
]),
);

it('renders Provider1Service', () => {
expect(
ngMocks.formatText(MockRender(TargetComponent)),
).toEqual('Provider1Service');

expect(
isMockOf(
ngMocks.findInstance(ProviderService),
Provider1Service,
),
).toEqual(false);
});
});

describe('overrides Provider2Service with Provider1Service', () => {
beforeEach(() =>
MockBuilder([
TargetComponent,
DependencyWithProvidersModule,
TargetModule.for1(),
]),
);

it('renders Provider1Service', () => {
expect(
ngMocks.formatText(MockRender(TargetComponent)),
).toEqual('Provider1Service');

expect(
isMockOf(
ngMocks.findInstance(ProviderService),
Provider1Service,
),
).toEqual(false);
});
});
});
});
46 changes: 46 additions & 0 deletions tests/issue-4613/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Component, Injectable, NgModule } from '@angular/core';
import { TestBed } from '@angular/core/testing';

import { MockBuilder } from 'ng-mocks';

@Injectable()
class TargetService {}

@Component({
selector: 'target',
template: '{{ service.constructor.name }}',
})
class TargetComponent {
constructor(public readonly service: TargetService) {}
}

@NgModule({
declarations: [TargetComponent],
})
class TargetModule {
static forRoot() {
return {
ngModule: TargetModule,
providers: [TargetService],
};
}
}

@NgModule({
imports: [TargetModule],
})
class DependencyModule {}

// @see https://github.com/help-me-mom/ng-mocks/issues/4613
describe('issue-4613', () => {
beforeEach(() =>
MockBuilder(TargetComponent, [
TargetModule.forRoot(),
DependencyModule,
]),
);

it('should create component', () => {
expect(TestBed.createComponent(TargetComponent)).toBeTruthy();
});
});

0 comments on commit 3ed8ae4

Please sign in to comment.