Skip to content

Few minor fixes in the Readme #268

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 19, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions modules/schematics/collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@
"description": "Adds initial setup for state management"
},

"selector": {
"aliases": ["se"],
"factory": "./src/selector",
"schema": "./src/selector/schema.json",
"description": "Add selectors"
},

"ng-add": {
"aliases": ["init"],
"factory": "./src/ng-add",
Expand Down
18 changes: 18 additions & 0 deletions modules/schematics/src/feature/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ describe('Feature Schematic', () => {
expect(
files.indexOf(`${projectPath}/src/app/foo.effects.spec.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/foo.selectors.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/foo.selectors.spec.ts`)
).toBeGreaterThanOrEqual(0);
});

it('should create all files of a feature to specified project if provided', () => {
Expand Down Expand Up @@ -82,6 +88,12 @@ describe('Feature Schematic', () => {
expect(
files.indexOf(`${specifiedProjectPath}/src/lib/foo.effects.spec.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${specifiedProjectPath}/src/lib/foo.selectors.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${specifiedProjectPath}/src/lib/foo.selectors.spec.ts`)
).toBeGreaterThanOrEqual(0);
});

it('should create all files of a feature within grouped folders if group is set', () => {
Expand All @@ -104,6 +116,12 @@ describe('Feature Schematic', () => {
expect(
files.indexOf(`${projectPath}/src/app/effects/foo.effects.spec.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/selectors/foo.selectors.ts`)
).toBeGreaterThanOrEqual(0);
expect(
files.indexOf(`${projectPath}/src/app/selectors/foo.selectors.spec.ts`)
).toBeGreaterThanOrEqual(0);
});

it('should respect the path provided for the feature name', () => {
Expand Down
9 changes: 9 additions & 0 deletions modules/schematics/src/feature/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export default function(options: FeatureOptions): Rule {
api: options.api,
creators: options.creators,
}),
schematic('selector', {
flat: options.flat,
group: options.group,
name: options.name,
path: options.path,
project: options.project,
spec: options.spec,
feature: true,
}),
])(host, context);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<% if(feature) { %>import * as from<%= classify(name) %> from '<%= reducerPath %>';
import { select<%= classify(name) %>State } from './<%= dasherize(name) %>.selectors';<% } %>

describe('<%= classify(name) %> Selectors', () => {
it('should select the feature state', () => {
<% if(feature) { %>const result = select<%= classify(name) %>State({
[from<%= classify(name) %>.<%= camelize(name) %>FeatureKey]: {}
});

expect(result).toEqual({});<% } %>
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { createFeatureSelector, createSelector } from '@ngrx/store';
<% if(feature) { %>import * as from<%= classify(name) %> from '<%= reducerPath %>';

export const select<%= classify(name) %>State = createFeatureSelector<from<%= classify(name) %>.State>(
from<%= classify(name) %>.<%= camelize(name) %>FeatureKey
);<% } %>
187 changes: 187 additions & 0 deletions modules/schematics/src/selector/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
import { tags } from '@angular-devkit/core';
import {
SchematicTestRunner,
UnitTestTree,
} from '@angular-devkit/schematics/testing';
import * as path from 'path';
import { Schema as SelectorOptions } from './schema';
import {
getTestProjectPath,
createWorkspace,
} from '../../../schematics-core/testing';

describe('Selector Schematic', () => {
const schematicRunner = new SchematicTestRunner(
'@ngrx/schematics',
path.join(__dirname, '../../collection.json')
);
const defaultOptions: SelectorOptions = {
name: 'foo',
project: 'bar',
spec: true,
};

const projectPath = getTestProjectPath();

let appTree: UnitTestTree;

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

it('should create selector files', () => {
const tree = schematicRunner.runSchematic(
'selector',
defaultOptions,
appTree
);

const selectorsContent = tree.readContent(
`${projectPath}/src/app/foo.selectors.ts`
);
const specContent = tree.readContent(
`${projectPath}/src/app/foo.selectors.spec.ts`
);

expect(cleanString(selectorsContent)).toBe(
cleanString(
tags.stripIndent`import { createFeatureSelector, createSelector } from '@ngrx/store';`
)
);

expect(cleanString(specContent)).toBe(
cleanString(tags.stripIndent`
describe('Foo Selectors', () => {
it('should select the feature state', () => {
**
});
});`)
);
});

it('should not create a spec file if spec is false', () => {
const options = {
...defaultOptions,
spec: false,
};
const tree = schematicRunner.runSchematic('selector', options, appTree);

expect(
tree.files.includes(`${projectPath}/src/app/foo.selectors.spec.ts`)
).toBeFalsy();
});

it('should group selectors if group is true', () => {
const options = {
...defaultOptions,
group: true,
};
const tree = schematicRunner.runSchematic('selector', options, appTree);

expect(
tree.files.includes(`${projectPath}/src/app/selectors/foo.selectors.ts`)
).toBeTruthy();
expect(
tree.files.includes(
`${projectPath}/src/app/selectors/foo.selectors.spec.ts`
)
).toBeTruthy();
});

it('should not flatten selectors if flat is false', () => {
const options = {
...defaultOptions,
flat: false,
};
const tree = schematicRunner.runSchematic('selector', options, appTree);

expect(
tree.files.includes(`${projectPath}/src/app/foo/foo.selectors.ts`)
).toBeTruthy();
expect(
tree.files.includes(`${projectPath}/src/app/foo/foo.selectors.spec.ts`)
).toBeTruthy();
});

describe('With feature flag', () => {
it('should create a selector', () => {
const options = {
...defaultOptions,
feature: true,
};

const tree = schematicRunner.runSchematic('selector', options, appTree);
const selectorsContent = tree.readContent(
`${projectPath}/src/app/foo.selectors.ts`
);
const specContent = tree.readContent(
`${projectPath}/src/app/foo.selectors.spec.ts`
);

expect(cleanString(selectorsContent)).toBe(
cleanString(tags.stripIndent`
import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromFoo from './foo.reducer';

export const selectFooState = createFeatureSelector<fromFoo.State>(
fromFoo.fooFeatureKey
);
`)
);

expect(cleanString(specContent)).toBe(
cleanString(tags.stripIndent`
import * as fromFoo from './foo.reducer';
import { selectFooState } from './foo.selectors';

describe('Foo Selectors', () => {
it('should select the feature state', () => {
const result = selectFooState({
[fromFoo.fooFeatureKey]: {}
});

expect(result).toEqual({});
});
});
`)
);
});

it('should group and nest the selectors within a feature', () => {
const options = {
...defaultOptions,
feature: true,
group: true,
flat: false,
};

const tree = schematicRunner.runSchematic('selector', options, appTree);
const selectorPath = `${projectPath}/src/app/selectors/foo/foo.selectors.ts`;
const specPath = `${projectPath}/src/app/selectors/foo/foo.selectors.spec.ts`;

expect(tree.files.includes(selectorPath)).toBeTruthy();
expect(tree.files.includes(specPath)).toBeTruthy();

const selectorContent = tree.readContent(selectorPath);
expect(selectorContent).toMatch(
/import\ \* as fromFoo from\ \'\.\.\/\.\.\/reducers\/foo\/foo\.reducer';/
);

const specContent = tree.readContent(specPath);
expect(specContent).toMatch(
/import\ \* as fromFoo from\ \'\.\.\/\.\.\/reducers\/foo\/foo\.reducer';/
);
expect(specContent).toMatch(
/import\ \{ selectFooState \} from\ \'\.\/foo\.selectors';/
);
});
});

function cleanString(value: string) {
// ** to mark an empty line (VSCode removes whitespace lines)
return value
.replace(/\r\n/g, '\n')
.replace(/\*\*/g, '')
.trim();
}
});
67 changes: 67 additions & 0 deletions modules/schematics/src/selector/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {
Rule,
SchematicContext,
Tree,
apply,
applyTemplates,
branchAndMerge,
chain,
filter,
mergeWith,
move,
noop,
url,
} from '@angular-devkit/schematics';
import {
getProjectPath,
parseName,
stringUtils,
} from '@ngrx/schematics/schematics-core';
import { Schema as SelectorOptions } from './schema';

export default function(options: SelectorOptions): Rule {
return (host: Tree, context: SchematicContext) => {
options.path = getProjectPath(host, options);

const parsedPath = parseName(options.path, options.name || '');
options.name = parsedPath.name;
options.path = parsedPath.path;

const templateSource = apply(url('./files'), [
options.spec
? noop()
: filter(path => !path.endsWith('.spec.ts.template')),
applyTemplates({
...stringUtils,
'if-flat': (s: string) =>
stringUtils.group(
options.flat ? '' : s,
options.group ? 'selectors' : ''
),
reducerPath: `${relativePath(options)}${stringUtils.dasherize(
options.name
)}.reducer`,
...(options as object),
} as any),
move(parsedPath.path),
]);

return chain([branchAndMerge(chain([mergeWith(templateSource)]))])(
host,
context
);
};
}

function relativePath(options: SelectorOptions) {
if (options.feature) {
return stringUtils.featurePath(
options.group,
options.flat,
'reducers',
stringUtils.dasherize(options.name)
);
}

return '';
}
50 changes: 50 additions & 0 deletions modules/schematics/src/selector/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"$schema": "http://json-schema.org/schema",
"id": "SchematicsNgRxSelector",
"title": "NgRx Selector Options Schema",
"type": "object",
"properties": {
"name": {
"description": "The name of the selector.",
"type": "string",
"$default": {
"$source": "argv",
"index": 0
}
},
"path": {
"type": "string",
"format": "path",
"description": "The path to create the selectors.",
"visible": false
},
"project": {
"type": "string",
"description": "The name of the project.",
"aliases": ["p"]
},
"spec": {
"type": "boolean",
"description": "Specifies if a spec file is generated.",
"default": false
},
"flat": {
"type": "boolean",
"default": true,
"description": "Flag to indicate if a dir is created."
},
"group": {
"type": "boolean",
"default": false,
"description": "Group selector file within 'selectors' folder",
"aliases": ["g"]
},
"feature": {
"type": "boolean",
"default": false,
"description": "Flag to indicate if part of a feature schematic.",
"visible": false
}
},
"required": []
}
Loading