Skip to content

Commit b028e11

Browse files
authored
test: refactor ng-add to make it easier to add moar tests (#974)
1 parent 0639ca0 commit b028e11

File tree

8 files changed

+113
-54
lines changed

8 files changed

+113
-54
lines changed
Lines changed: 22 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'
22
import { Tree } from '@angular-devkit/schematics'
3-
import { beforeEach, describe, expect, it } from '@jest/globals'
3+
import { beforeEach, describe } from '@jest/globals'
44
import { join } from 'path'
55
import { Schema as NgAddSchema } from './schema'
6-
import { Schema as NgNewSchema } from '@schematics/angular/ng-new/schema'
7-
import { LIB_NAME } from '../testing/lib-name'
6+
import { ProviderTestCase } from './testing/provider-test-case'
7+
import { shouldAddRootProvider } from './testing/should-add-root-provider'
8+
import { createTestApp } from '../testing/create-test-app'
89

910
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/ng-add/index.spec.ts
1011
// https://github.com/angular/components/blob/18.2.8/src/material/schematics/ng-add/index.spec.ts
@@ -24,6 +25,12 @@ describe('ng-add schematic', () => {
2425
join(__dirname, '..', 'collection.json'),
2526
)
2627
})
28+
29+
const CORE_PROVIDER = new ProviderTestCase({
30+
name: 'core',
31+
symbol: 'provideNgxMetaCore',
32+
})
33+
2734
;([true, false] as const).forEach((standalone) => {
2835
const appKind = standalone ? 'standalone' : 'module-based'
2936
describe(`when the app is ${appKind}`, () => {
@@ -34,58 +41,19 @@ describe('ng-add schematic', () => {
3441
})
3542
})
3643

37-
it('should add core provider', async () => {
38-
const tree = await runner.runSchematic<NgAddSchema>(
39-
SCHEMATIC_NAME,
40-
defaultOptions,
41-
appTree,
42-
)
43-
const appConfigOrAppModule = getAppConfigOrAppModuleContents(
44-
tree,
45-
standalone,
46-
)
47-
expect(appConfigOrAppModule).toContain(
48-
`import { provideNgxMetaCore } from '${LIB_NAME}/core`,
49-
)
50-
expect(stripWhitespace(appConfigOrAppModule)).toMatch(
51-
/providers:\[.*provideNgxMetaCore\(\).*]/,
52-
)
44+
describe('by default', () => {
45+
let tree: Tree
46+
47+
beforeEach(async () => {
48+
tree = await runner.runSchematic<NgAddSchema>(
49+
SCHEMATIC_NAME,
50+
defaultOptions,
51+
appTree,
52+
)
53+
})
54+
55+
shouldAddRootProvider(CORE_PROVIDER, () => tree, standalone)
5356
})
5457
})
5558
})
5659
})
57-
58-
// https://github.com/FortAwesome/angular-fontawesome/blob/0.15.0/projects/schematics/src/ng-add/index.spec.ts#L107
59-
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/test-app.ts
60-
const createTestApp = async (
61-
runner: SchematicTestRunner,
62-
options: Omit<NgNewSchema, 'version'> & Partial<Pick<NgNewSchema, 'version'>>,
63-
) => {
64-
return runner.runExternalSchematic<NgNewSchema>(
65-
'@schematics/angular',
66-
'ng-new',
67-
{
68-
version: '9.0.0',
69-
directory: '.',
70-
...options,
71-
},
72-
)
73-
}
74-
75-
const getAppConfigOrAppModuleContents = (tree: Tree, standalone: boolean) =>
76-
standalone
77-
? getFileContent(tree, '/src/app/app.config.ts')
78-
: getFileContent(tree, '/src/app/app.module.ts')
79-
80-
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/file-content.ts
81-
const getFileContent = (tree: Tree, filePath: string): string => {
82-
const contentBuffer = tree.read(filePath)
83-
84-
if (!contentBuffer) {
85-
throw new Error(`Cannot read "${filePath}" because it does not exist.`)
86-
}
87-
88-
return contentBuffer.toString()
89-
}
90-
91-
const stripWhitespace = (value: string) => value.replace(/\s/g, '')
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { Tree } from '@angular-devkit/schematics'
2+
import { getFileContent } from '../../testing/get-file-content'
3+
4+
export const getAppConfigOrAppModuleContent = (
5+
tree: Tree,
6+
standalone: boolean,
7+
) =>
8+
standalone
9+
? getFileContent(tree, '/src/app/app.config.ts')
10+
: getFileContent(tree, '/src/app/app.module.ts')
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export class ProviderTestCase {
2+
readonly name: string
3+
readonly symbol: string
4+
readonly code: string
5+
readonly entrypoint: string
6+
7+
constructor(opts: {
8+
name: string
9+
symbol: string
10+
code?: string
11+
entrypoint?: string
12+
}) {
13+
this.name = opts.name
14+
this.symbol = opts.symbol
15+
this.code = opts.code ?? `${this.symbol}()`
16+
this.entrypoint = opts.entrypoint ?? this.name
17+
}
18+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { ProviderTestCase } from './provider-test-case'
2+
import { Tree } from '@angular-devkit/schematics'
3+
import { expect, it } from '@jest/globals'
4+
import { getAppConfigOrAppModuleContent } from './get-app-config-or-app-module-content'
5+
import { LIB_NAME } from '../../testing/lib-name'
6+
import { stripWhitespace } from '../../testing/strip-whitespace'
7+
import { regexpEscape } from '../../testing/regexp-escape'
8+
9+
export const shouldAddRootProvider = (
10+
providerTestCase: ProviderTestCase,
11+
treeFactory: () => Tree,
12+
standalone: boolean,
13+
) => {
14+
it(`should add ${providerTestCase.name} provider`, () => {
15+
const appConfigOrAppModuleContents = getAppConfigOrAppModuleContent(
16+
treeFactory(),
17+
standalone,
18+
)
19+
expect(appConfigOrAppModuleContents).toContain(
20+
`import { ${providerTestCase.symbol} } from '${LIB_NAME}/${providerTestCase.entrypoint}`,
21+
)
22+
expect(stripWhitespace(appConfigOrAppModuleContents)).toMatch(
23+
new RegExp(`providers:\\[.*${regexpEscape(providerTestCase.code)}.*]`),
24+
)
25+
})
26+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'
2+
import { Schema as NgNewSchema } from '@schematics/angular/ng-new/schema'
3+
4+
// https://github.com/FortAwesome/angular-fontawesome/blob/0.15.0/projects/schematics/src/ng-add/index.spec.ts#L107
5+
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/test-app.ts
6+
export const createTestApp = async (
7+
runner: SchematicTestRunner,
8+
options: Omit<NgNewSchema, 'version'> & Partial<Pick<NgNewSchema, 'version'>>,
9+
) => {
10+
return runner.runExternalSchematic<NgNewSchema>(
11+
'@schematics/angular',
12+
'ng-new',
13+
{
14+
version: '9.0.0',
15+
directory: '.',
16+
...options,
17+
},
18+
)
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Tree } from '@angular-devkit/schematics'
2+
3+
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/file-content.ts
4+
export const getFileContent = (tree: Tree, filePath: string): string => {
5+
const contentBuffer = tree.read(filePath)
6+
7+
if (!contentBuffer) {
8+
throw new Error(`Cannot read "${filePath}" because it does not exist.`)
9+
}
10+
11+
return contentBuffer.toString()
12+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// https://stackoverflow.com/a/9310752/3263250
2+
// https://github.com/tc39/proposal-regex-escaping
3+
export const regexpEscape = (string: string) =>
4+
string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// https://github.com/angular/angular-cli/blob/18.2.9/packages/schematics/angular/utility/standalone/rules_spec.ts#L45-L47
2+
export const stripWhitespace = (value: string) => value.replace(/\s/g, '')

0 commit comments

Comments
 (0)