Skip to content

Commit

Permalink
fix(core): excluding StoreDevtoolsModule by default #589
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed May 31, 2021
1 parent fda5d7a commit c376e93
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ tab_width = 2
trim_trailing_whitespace = true

[*.md]
max_line_length = 50
trim_trailing_whitespace = false

[README.md]
max_line_length = 50
10 changes: 10 additions & 0 deletions docs/articles/guides/libraries/ngrx.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,13 @@ beforeEach(() =>
.keep(EffectsFeatureModule)
);
```

## StoreDevtoolsModule and modules with meta reducers

:::warning
A mock meta reducer stops all reducers in the store
:::

If you have a module which has a meta reducer,
then **please don't forget to keep it too** if you plan to keep store modules for testing.
Otherwise, no actions will be reduced and the store will be always empty.
19 changes: 18 additions & 1 deletion libs/ng-mocks/src/lib/common/ng-mocks-universe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,25 @@ ngMocksUniverse.hasBuildDeclaration = (def: any): boolean => {
const hasBuildDeclaration = (def: any): boolean => ngMocksUniverse.hasBuildDeclaration(def);
const getBuildDeclaration = (def: any): any => ngMocksUniverse.getBuildDeclaration(def);

ngMocksUniverse.isExcludedDef = (def: any): boolean => hasBuildDeclaration(def) && getBuildDeclaration(def) === null;
ngMocksUniverse.isExcludedDef = (def: any): boolean => {
const resolution = ngMocksUniverse.getResolution(def);
if (resolution && resolution !== 'exclude') {
return false;
}

return hasBuildDeclaration(def) && getBuildDeclaration(def) === null;
};

ngMocksUniverse.isProvidedDef = (def: any): boolean => hasBuildDeclaration(def) && getBuildDeclaration(def) !== null;

// excluding StoreDevtoolsModule by default
// istanbul ignore next
try {
// tslint:disable-next-line no-require-imports no-implicit-dependencies no-var-requires
const { StoreDevtoolsModule } = require('@ngrx/store-devtools');
ngMocksUniverse.getDefaults().set(StoreDevtoolsModule, ['exclude']);
} catch {
// nothing to do
}

export default ((): NgMocksUniverse => ngMocksUniverse)();
39 changes: 38 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@
"@ng-select/ng-select": "6.1.0",
"@ngrx/effects": "12.0.0",
"@ngrx/store": "12.0.0",
"@ngrx/store-devtools": "12.0.0",
"@semantic-release/changelog": "5.0.1",
"@semantic-release/exec": "5.0.0",
"@semantic-release/git": "9.0.0",
Expand Down
1 change: 1 addition & 0 deletions tests-angular/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@ng-select/ng-select": "6.1.0",
"@ngrx/effects": "12.0.0",
"@ngrx/store": "12.0.0",
"@ngrx/store-devtools": "12.0.0",
"@types/jasmine": "3.7.6",
"@types/node": "14.17.1",
"jasmine-core": "3.7.1",
Expand Down
73 changes: 73 additions & 0 deletions tests-angular/e2e/src/issue-589/dev-tools.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CommonModule } from '@angular/common';
import { Component, NgModule, OnInit } from '@angular/core';
import {
createAction,
createFeatureSelector,
createReducer,
on,
Store,
StoreModule,
StoreRootModule,
} from '@ngrx/store';
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

const increaseValue = createAction('set-value');
const resetValue = createAction('reset-value');

const myReducer = {
featureKey: 'test',
reducer: createReducer(
0,
on(increaseValue, state => state + 1),
on(resetValue, () => 0),
),
};

const selectValue = createFeatureSelector(myReducer.featureKey);

@Component({
selector: 'target',
template: '{{ value$ | async }}',
})
class MyComponent implements OnInit {
public value$ = this.store.select(selectValue);

public constructor(private readonly store: Store) {}

public ngOnInit(): void {
this.store.dispatch(increaseValue());
}

public reset(): void {
this.store.dispatch(resetValue());
}
}

const metaReducer = state => state;

@NgModule({
declarations: [MyComponent],
exports: [MyComponent],
imports: [
CommonModule,
StoreModule.forRoot(
{
[myReducer.featureKey]: myReducer.reducer,
},
{
metaReducers: [metaReducer],
},
),
],
})
class MyModule {}

describe('issue-589:meta-reducers', () => {
beforeEach(() =>
MockBuilder(MyComponent, MyModule).keep(StoreRootModule),
);

it('keeps meta reducers', () => {
expect(ngMocks.formatText(MockRender(MyComponent))).toEqual('1');
});
});
73 changes: 73 additions & 0 deletions tests-angular/e2e/src/issue-589/meta-reducers.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CommonModule } from '@angular/common';
import { Component, NgModule, OnInit } from '@angular/core';
import {
createAction,
createFeatureSelector,
createReducer,
on,
Store,
StoreModule,
StoreRootModule,
} from '@ngrx/store';
import {
INITIAL_OPTIONS,
StoreDevtoolsModule,
} from '@ngrx/store-devtools';
import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

const increaseValue = createAction('set-value');
const resetValue = createAction('reset-value');

const myReducer = {
featureKey: 'test',
reducer: createReducer(
0,
on(increaseValue, state => state + 1),
on(resetValue, () => 0),
),
};

const selectValue = createFeatureSelector(myReducer.featureKey);

@Component({
selector: 'target',
template: '{{ value$ | async }}',
})
class MyComponent implements OnInit {
public value$ = this.store.select(selectValue);

public constructor(private readonly store: Store) {}

public ngOnInit(): void {
this.store.dispatch(increaseValue());
}

public reset(): void {
this.store.dispatch(resetValue());
}
}

@NgModule({
declarations: [MyComponent],
exports: [MyComponent],
imports: [
CommonModule,
StoreModule.forRoot({
[myReducer.featureKey]: myReducer.reducer,
}),
StoreDevtoolsModule.instrument({
maxAge: 6,
}),
],
})
class MyModule {}

describe('issue-589:dev-tools', () => {
beforeEach(() =>
MockBuilder(MyComponent, MyModule).keep(StoreRootModule),
);

it('excludes StoreDevtoolsModule by default', () => {
expect(ngMocks.formatText(MockRender(MyComponent))).toEqual('1');
});
});
61 changes: 61 additions & 0 deletions tests/issue-589/test.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { InjectionToken, NgModule } from '@angular/core';
import {
MockBuilder,
MockRender,
ngMocks,
NgModuleWithProviders,
} from 'ng-mocks';

const TOKEN = new InjectionToken('TOKEN');

@NgModule()
class TargetModule {
public static forRoot(): NgModuleWithProviders<TargetModule> {
return {
ngModule: TargetModule,
providers: [
{
provide: TOKEN,
useValue: 1,
},
],
};
}
}

@NgModule({
imports: [TargetModule.forRoot()],
})
class MyModule {}

ngMocks.globalExclude(TargetModule);

// Looks like a module with providers has some issues with excluding it globally
// and then mocking in a mock builder setup.
describe('issue-589', () => {
describe('default exclude', () => {
beforeEach(() => MockBuilder(null, MyModule));

it('excludes StoreDevtoolsModule', () => {
expect(() => MockRender(TOKEN)).toThrowError(
/No provider for InjectionToken TOKEN/,
);
});
});

describe('explicit keep', () => {
beforeEach(() => MockBuilder(null, MyModule).keep(TargetModule));

it('excludes StoreDevtoolsModule', () => {
expect(MockRender(TOKEN).point.componentInstance).toEqual(1);
});
});

describe('explicit mock', () => {
beforeEach(() => MockBuilder(null, MyModule).mock(TargetModule));

it('excludes StoreDevtoolsModule', () => {
expect(MockRender(TOKEN).point.componentInstance).toEqual(0);
});
});
});

0 comments on commit c376e93

Please sign in to comment.