Skip to content

Commit

Permalink
feat: implement ng add for store and effects packages
Browse files Browse the repository at this point in the history
  • Loading branch information
vitaliy-bobrov authored and brandonroberts committed May 23, 2018
1 parent a478c0d commit db94db7
Show file tree
Hide file tree
Showing 46 changed files with 1,126 additions and 11 deletions.
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/dist
/modules/schematics/src/*/files/*
/modules/**/schematics/**/files/*
/tmp
package-lock.json
package.json
Expand Down
1 change: 1 addition & 0 deletions modules/effects/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ ng_package(
entry_point = "modules/effects/index.js",
packages = [
"//modules/effects/migrations:npm_package",
"//modules/effects/schematics:npm_package",
"//modules/effects/schematics-core:npm_package",
],
deps = [
Expand Down
9 changes: 9 additions & 0 deletions modules/effects/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
"type": "git",
"url": "https://github.com/ngrx/platform.git"
},
"keywords": [
"RxJS",
"Angular",
"Redux",
"NgRx",
"Schematics",
"Angular CLI"
],
"author": "NgRx",
"license": "MIT",
"bugs": {
Expand All @@ -17,6 +25,7 @@
"@ngrx/store": "0.0.0-PLACEHOLDER",
"rxjs": "RXJS_VERSION"
},
"schematics": "MODULE_SCHEMATICS_COLLECTION",
"ng-update": {
"packageGroup": "NG_UPDATE_PACKAGE_GROUP",
"migrations": "NG_UPDATE_MIGRATIONS"
Expand Down
5 changes: 5 additions & 0 deletions modules/effects/schematics-core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,9 @@ export const stringUtils = {
};

export { updatePackage } from './utility/update';

export { parseName } from './utility/parse-name';

export { addPackageToPackageJson } from './utility/package';

export { platformVersion } from './utility/libs-version';
1 change: 1 addition & 0 deletions modules/effects/schematics-core/utility/libs-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const platformVersion = '^6.0.0';
27 changes: 27 additions & 0 deletions modules/effects/schematics-core/utility/package.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Tree } from '@angular-devkit/schematics';

/**
* Adds a package to the package.json
*/
export function addPackageToPackageJson(
host: Tree,
type: string,
pkg: string,
version: string
): Tree {
if (host.exists('package.json')) {
const sourceText = host.read('package.json')!.toString('utf-8');
const json = JSON.parse(sourceText);
if (!json[type]) {
json[type] = {};
}

if (!json[type][pkg]) {
json[type][pkg] = version;
}

host.overwrite('package.json', JSON.stringify(json, null, 2));
}

return host;
}
33 changes: 33 additions & 0 deletions modules/effects/schematics/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])

load("//tools:defaults.bzl", "ts_library", "npm_package")

ts_library(
name = "schematics",
srcs = glob(
[
"**/*.ts",
],
exclude = [
"**/*.spec.ts",
"**/files/**/*",
],
),
module_name = "@ngrx/effects/schematics",
deps = [
"//modules/effects/schematics-core",
],
)

npm_package(
name = "npm_package",
srcs = [
":collection.json",
] + glob([
"**/files/**/*",
"**/schema.json",
]),
deps = [
":schematics",
],
)
10 changes: 10 additions & 0 deletions modules/effects/schematics/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"schematics": {
"ng-add": {
"aliases": ["init"],
"factory": "./ng-add",
"schema": "./ng-add/schema.json",
"description": "Add root side effect class"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Injectable } from '@angular/core';
import { Actions, Effect } from '@ngrx/effects';

@Injectable()
export class <%= classify(name) %>Effects {
constructor(private actions$: Actions) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { TestBed, inject } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { Observable } from 'rxjs';

import { <%= classify(name) %>Effects } from './<%= dasherize(name) %>.effects';

describe('<%= classify(name) %>Effects', () => {
let actions$: Observable<any>;
let effects: <%= classify(name) %>Effects;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
<%= classify(name) %>Effects,
provideMockActions(() => actions$)
]
});

effects = TestBed.get(<%= classify(name) %>Effects);
});

it('should be created', () => {
expect(effects).toBeTruthy();
});
});
185 changes: 185 additions & 0 deletions modules/effects/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import {
SchematicTestRunner,
UnitTestTree,
} from '@angular-devkit/schematics/testing';
import { getFileContent } from '@schematics/angular/utility/test';
import * as path from 'path';
import { Schema as RootEffectOptions } from './schema';
import {
getTestProjectPath,
createWorkspace,
createAppModuleWithEffects,
} from '../../../schematics-core/testing';

describe('Effect ng-add Schematic', () => {
const schematicRunner = new SchematicTestRunner(
'@ngrx/effects',
path.join(__dirname, '../collection.json')
);

const defaultOptions: RootEffectOptions = {
name: 'foo',
skipPackageJson: false,
project: 'bar',
spec: true,
module: undefined,
flat: false,
group: false,
};

const projectPath = getTestProjectPath();

let appTree: UnitTestTree;

beforeEach(() => {
appTree = createWorkspace(schematicRunner, appTree);
});

it('should update package.json', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const packageJson = JSON.parse(getFileContent(tree, '/package.json'));

expect(packageJson.dependencies['@ngrx/effects']).toBeDefined();
});

it('should skip package.json update', () => {
const options = { ...defaultOptions, skipPackageJson: true };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const packageJson = JSON.parse(getFileContent(tree, '/package.json'));

expect(packageJson.dependencies['@ngrx/effects']).toBeUndefined();
});

it('should create an effect', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const files = tree.files;
expect(
files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`)
).toBeGreaterThanOrEqual(0);
});

it('should not be provided by default', () => {
const options = { ...defaultOptions };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
expect(content).not.toMatch(
/import { FooEffects } from '.\/foo\/foo.effects'/
);
});

it('should import into a specified module', () => {
const options = { ...defaultOptions, module: 'app.module.ts' };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);
expect(content).toMatch(/import { FooEffects } from '.\/foo\/foo.effects'/);
});

it('should fail if specified module does not exist', () => {
const options = {
...defaultOptions,
module: `${projectPath}/src/app/app.moduleXXX.ts`,
};
let thrownError: Error | null = null;
try {
schematicRunner.runSchematic('effects', options, appTree);
} catch (err) {
thrownError = err;
}
expect(thrownError).toBeDefined();
});

it('should respect the spec flag', () => {
const options = { ...defaultOptions, spec: false };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const files = tree.files;
expect(
files.indexOf(`${projectPath}/src/app/foo/foo.effects.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/foo/foo.effects.spec.ts`)
).toEqual(-1);
});

it('should register the root effect in the provided module', () => {
const options = { ...defaultOptions, module: 'app.module.ts' };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(`${projectPath}/src/app/app.module.ts`);

expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/);
});

it('should add an effect to the empty array of registered effects', () => {
const storeModule = `${projectPath}/src/app/store.module.ts`;
const options = {
...defaultOptions,
module: 'store.module.ts',
};
appTree = createAppModuleWithEffects(
appTree,
storeModule,
'EffectsModule.forRoot([])'
);

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(storeModule);

expect(content).toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/);
});

it('should add an effect to the existing registered root effects', () => {
const storeModule = `${projectPath}/src/app/store.module.ts`;
const options = {
...defaultOptions,
module: 'store.module.ts',
};
appTree = createAppModuleWithEffects(
appTree,
storeModule,
'EffectsModule.forRoot([UserEffects])'
);

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(storeModule);

expect(content).toMatch(
/EffectsModule\.forRoot\(\[UserEffects, FooEffects\]\)/
);
});

it('should not add an effect to registered effects defined with a variable', () => {
const storeModule = `${projectPath}/src/app/store.module.ts`;
const options = { ...defaultOptions, module: 'store.module.ts' };
appTree = createAppModuleWithEffects(
appTree,
storeModule,
'EffectsModule.forRoot(effects)'
);

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const content = tree.readContent(storeModule);

expect(content).not.toMatch(/EffectsModule\.forRoot\(\[FooEffects\]\)/);
});

it('should group within an "effects" folder if group is set', () => {
const options = { ...defaultOptions, flat: true, spec: false, group: true };

const tree = schematicRunner.runSchematic('ng-add', options, appTree);
const files = tree.files;
expect(
files.indexOf(`${projectPath}/src/app/effects/foo.effects.ts`)
).toBeGreaterThanOrEqual(0);
});
});
Loading

0 comments on commit db94db7

Please sign in to comment.