Skip to content

Commit ceda4b0

Browse files
refactor(sdk/e2e-schematics): e2e-schematics strict mode support (#497)
1 parent d5841ee commit ceda4b0

File tree

28 files changed

+360
-140
lines changed

28 files changed

+360
-140
lines changed

libs/sdk/e2e-schematics/src/generators/component-e2e/index.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('component-e2e', () => {
5252
expect(
5353
tree
5454
.read(`${config['test-storybook-e2e'].sourceRoot}/support/index.ts`)
55-
.toString()
55+
?.toString()
5656
).toContain('percy');
5757
});
5858

libs/sdk/e2e-schematics/src/generators/component-e2e/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ export default async function (tree: Tree, schema: Partial<Schema>) {
8484

8585
// Delete boilerplate files from the storybook project.
8686
let indexFile = tree.read(`${projectConfig.sourceRoot}/index.html`, 'utf8');
87+
88+
// istanbul ignore if
89+
if (!indexFile) {
90+
indexFile = '';
91+
}
92+
8793
indexFile = indexFile.replace(
8894
'<link rel="icon" type="image/x-icon" href="favicon.ico" />',
8995
''

libs/sdk/e2e-schematics/src/generators/configure-percy/index.spec.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ describe('configure-percy', () => {
1414
});
1515
expect(tree.exists('apps/cypress/src/support/index.ts')).toBeTruthy();
1616
expect(
17-
tree.read('apps/cypress/src/support/index.ts').toString()
17+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
18+
tree.read('apps/cypress/src/support/index.ts')!.toString()
1819
).not.toContain('percy');
1920
await configurePercy(tree, { name: 'cypress' });
20-
expect(tree.read('apps/cypress/src/support/index.ts').toString()).toContain(
21-
'percy'
22-
);
21+
expect(
22+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
23+
tree.read('apps/cypress/src/support/index.ts')!.toString()
24+
).toContain('percy');
2325
});
2426

2527
it('should not make changes without cypress.json', async () => {
@@ -50,7 +52,8 @@ describe('configure-percy', () => {
5052
);
5153
await configurePercy(tree, { name: 'cypress' });
5254
const config = JSON.parse(
53-
tree.read(`apps/cypress/cypress.json`).toString()
55+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
56+
tree.read(`apps/cypress/cypress.json`)!.toString()
5457
);
5558
expect(config.video).toBeFalsy();
5659
});

libs/sdk/e2e-schematics/src/generators/configure-percy/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ export default async function (tree: Tree, schema: Schema) {
3636
if (!tree.isFile(filePath)) {
3737
tree.write(filePath, importPercyCypress);
3838
} else {
39-
const content = tree.read(filePath).toString();
39+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
40+
const content = tree.read(filePath)!.toString();
4041
if (!content.includes(importPercyCypress)) {
4142
insertStatement(tree, filePath, importPercyCypress);
4243
}

libs/sdk/e2e-schematics/src/generators/configure-storybook/index.spec.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,26 +28,30 @@ describe('configure-storybook', () => {
2828
expect(tree.exists(`apps/test-app/.storybook/preview.js`)).toBeFalsy();
2929
expect(tree.exists(`apps/test-app/.storybook/preview.ts`)).toBeTruthy();
3030
const e2eConfig = readProjectConfiguration(tree, `test-app-e2e`);
31-
expect(e2eConfig.targets.e2e.options.devServerTarget).toEqual(
31+
expect(e2eConfig.targets?.e2e.options.devServerTarget).toEqual(
3232
`test-app:storybook`
3333
);
34-
expect(e2eConfig.targets.e2e.configurations.ci.skipServe).toBeTruthy();
35-
updateJson(tree, getWorkspacePath(tree), (json) => {
36-
delete json['projects']['test-app'].architect.build.options;
34+
expect(e2eConfig.targets?.e2e.configurations?.ci.skipServe).toBeTruthy();
35+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
36+
updateJson(tree, getWorkspacePath(tree)!, (json) => {
37+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
38+
delete (json as any)['projects']['test-app'].architect.build.options;
3739
return json;
3840
});
3941
await configureStorybook(tree, { name: 'test-app' });
4042
expect(
41-
readProjectConfiguration(tree, `test-app`).targets.build.options.styles
43+
readProjectConfiguration(tree, `test-app`).targets?.build.options.styles
4244
.length
4345
).toBeGreaterThan(0);
44-
updateJson(tree, getWorkspacePath(tree), (json) => {
45-
json['projects']['test-app'].architect.build.options = {};
46+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
47+
updateJson(tree, getWorkspacePath(tree)!, (json) => {
48+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
49+
(json as any)['projects']['test-app'].architect.build.options = {};
4650
return json;
4751
});
4852
await configureStorybook(tree, { name: 'test-app' });
4953
expect(
50-
readProjectConfiguration(tree, `test-app`).targets.build.options.styles
54+
readProjectConfiguration(tree, `test-app`).targets?.build.options.styles
5155
.length
5256
).toBeGreaterThan(0);
5357
});
@@ -90,8 +94,11 @@ describe('configure-storybook', () => {
9094
await configureStorybook(tree, { name: 'test-app' });
9195
expect(tree.exists(`apps/test-app/.storybook/tsconfig.json`)).toBeTruthy();
9296
expect(
93-
JSON.parse(tree.read(`apps/test-app/.storybook/tsconfig.json`).toString())
94-
.include
97+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
98+
JSON.parse(
99+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
100+
tree.read(`apps/test-app/.storybook/tsconfig.json`)!.toString()
101+
).include
95102
).toBeTruthy();
96103
});
97104

@@ -116,7 +123,8 @@ describe('configure-storybook', () => {
116123
await configureStorybook(tree, { name: 'test-app' });
117124
await configureStorybook(tree, { name: 'test-app', ansiColor: false });
118125
const e2eConfig = readProjectConfiguration(tree, `test-app-e2e`);
119-
expect(e2eConfig.targets.e2e).toBeFalsy();
126+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
127+
expect(e2eConfig.targets!.e2e).toBeFalsy();
120128
expect(errorSpy).toHaveBeenCalledWith(
121129
`Project "test-app-e2e" does not have an e2e target with @nrwl/cypress:cypress`
122130
);

libs/sdk/e2e-schematics/src/generators/configure-storybook/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ import { Schema } from './schema';
2121
export default async function (tree: Tree, schema: Schema) {
2222
const projects = getStorybookProjects(tree, schema.name);
2323
const workspacePath = getWorkspacePath(tree);
24+
25+
// istanbul ignore if
26+
if (!workspacePath) {
27+
throw new Error(`Unable to determine workspace file path`);
28+
}
29+
2430
projects.forEach((project, projectName) => {
2531
updateJson(tree, workspacePath, (angularJson) => {
2632
const targets = Object.keys(angularJson.projects[projectName].architect);

libs/sdk/e2e-schematics/src/generators/init/index.spec.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,26 +26,33 @@ describe('init generator', () => {
2626
});
2727
await generator(appTree, { ansiColor: false });
2828
const config = readProjectConfiguration(appTree, 'storybook');
29-
expect(config.targets.storybook).toBeDefined();
29+
expect(config.targets?.storybook).toBeDefined();
3030
expect(
3131
appTree.isFile(
3232
joinPathFragments(
33-
config.sourceRoot,
33+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
34+
config.sourceRoot!,
3435
'lib/preview-wrapper/preview-wrapper-decorators.ts'
3536
)
3637
)
3738
).toBeTruthy();
3839
expect(
39-
appTree.read(joinPathFragments(config.sourceRoot, 'index.ts'), 'utf-8')
40+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
41+
appTree.read(joinPathFragments(config.sourceRoot!, 'index.ts'), 'utf-8')
4042
).toMatchSnapshot();
4143
});
4244

4345
it('should error without a "storybook" project', async () => {
4446
try {
4547
await generator(appTree, { ansiColor: false });
46-
fail();
48+
fail('should have thrown');
4749
} catch (e) {
48-
expect(e.message).toEqual('Storybook project not found');
50+
if (!(e instanceof Error)) {
51+
fail('should have thrown an error');
52+
}
53+
expect(e.message).toBe(
54+
'Storybook project or storybook project information not found'
55+
);
4956
}
5057
});
5158

libs/sdk/e2e-schematics/src/generators/init/index.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ import { Schema } from './schema';
2626
*/
2727
export default async function (tree: Tree, options: Schema) {
2828
const storybookConfig = getProjects(tree).get('storybook');
29-
if (!storybookConfig) {
30-
throw new Error('Storybook project not found');
29+
if (
30+
!(storybookConfig && storybookConfig.targets && storybookConfig.sourceRoot)
31+
) {
32+
throw new Error(
33+
'Storybook project or storybook project information not found'
34+
);
3135
}
3236
if (!storybookConfig.targets.storybook) {
3337
// nx g @nrwl/angular:storybook-configuration storybook --no-configureCypress --no-generateStories --no-generateCypressSpecs

libs/sdk/e2e-schematics/src/generators/stories/index.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ describe('stories generator', () => {
4141
).toMatchSnapshot();
4242
});
4343

44+
it('should error if a project name is not given', async () => {
45+
await applicationGenerator(appTree, {
46+
name: 'test',
47+
});
48+
await storybookConfigurationGenerator(appTree, {
49+
name: 'test',
50+
generateCypressSpecs: false,
51+
configureCypress: true,
52+
linter: Linter.None,
53+
generateStories: false,
54+
});
55+
options.project = undefined;
56+
try {
57+
await storiesGenerator(appTree, options);
58+
} catch (e) {
59+
if (!(e instanceof Error)) {
60+
fail('should have thrown error');
61+
}
62+
expect(e.message).toBe('Project name not specified');
63+
}
64+
});
65+
4466
it('should generate folder path', async () => {
4567
await applicationGenerator(appTree, {
4668
name: 'test',

libs/sdk/e2e-schematics/src/generators/stories/index.ts

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,28 @@ function normalizeOptions(
4949
tree: Tree,
5050
options: StoriesGeneratorSchema
5151
): NormalizedSchema {
52+
if (!options.project) {
53+
throw new Error('Project name not specified');
54+
}
55+
5256
const projects = getProjects(tree);
5357
const projectConfig = getStorybookProject(tree, options);
54-
const projectDirectory = projectConfig.sourceRoot;
58+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
59+
const projectDirectory = projectConfig.sourceRoot!;
5560
const projectName = options.project;
5661
const projectRoot = projectConfig.root;
5762
const e2eProjectConfig = projects.get(
5863
options.cypressProject || `${projectName}-e2e`
5964
);
60-
const e2eSourceRoot = e2eProjectConfig.sourceRoot;
65+
66+
let e2eSourceRoot: string | undefined;
67+
68+
// istanbul ignore else
69+
if (e2eProjectConfig) {
70+
e2eSourceRoot = e2eProjectConfig.sourceRoot;
71+
} else {
72+
e2eSourceRoot = undefined;
73+
}
6174

6275
return {
6376
...options,
@@ -135,9 +148,11 @@ export default async function (tree: Tree, options: StoriesGeneratorSchema) {
135148

136149
// Look for a directory to group this story in
137150
const paths = filepath
138-
.substring(normalizedOptions.projectConfig.sourceRoot.length + 1)
151+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
152+
.substring(normalizedOptions.projectConfig.sourceRoot!.length + 1)
139153
.split('/');
140-
const filename = paths.pop();
154+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
155+
const filename = paths.pop()!;
141156
let componentGroup = '';
142157
for (let i = paths.length - 1; i >= 0; i--) {
143158
// Skip directory with the same name as the component or a set of generic names.
@@ -180,7 +195,8 @@ export default async function (tree: Tree, options: StoriesGeneratorSchema) {
180195
normalizedOptions.projectDirectory,
181196
componentFilePath
182197
);
183-
if (module && module.module.classDeclaration.name.text) {
198+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
199+
if (module && module.module.classDeclaration.name!.text) {
184200
let importPath = normalizePath(
185201
relative(
186202
filepath.substring(0, filepath.lastIndexOf('/')),
@@ -191,13 +207,15 @@ export default async function (tree: Tree, options: StoriesGeneratorSchema) {
191207
importPath = `./${importPath}`;
192208
}
193209
const moduleImportTransformer = getInsertImportTransformer(
194-
module.module.classDeclaration.name.text,
210+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
211+
module.module.classDeclaration.name!.text,
195212
importPath
196213
);
197214
const moduleMetadataImportsTransformer =
198215
getInsertIdentifierToArrayTransformer(
199216
'imports',
200-
module.module.classDeclaration.name.text
217+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
218+
module.module.classDeclaration.name!.text
201219
);
202220
applyTransformersToPath(tree, filepath, [
203221
moduleImportTransformer,
@@ -235,11 +253,13 @@ export default async function (tree: Tree, options: StoriesGeneratorSchema) {
235253
tree.delete(filepath);
236254
return;
237255
}
238-
let spec = tree.read(filepath, 'utf8');
256+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
257+
let spec = tree.read(filepath, 'utf8')!;
239258
const visitIdMatch = spec.match(matchCypressVisitId);
240259
if (visitIdMatch) {
241260
const id = visitIdMatch[1];
242-
spec = spec
261+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
262+
spec = spec!
243263
// Inject the theme in the url and use the updated id
244264
.replace(
245265
matchCypressVisitId,

0 commit comments

Comments
 (0)