Skip to content

Commit

Permalink
test: refactor ng-add to make it easier to add moar tests (#974)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidlj95 authored Oct 21, 2024
1 parent 0639ca0 commit b028e11
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 54 deletions.
76 changes: 22 additions & 54 deletions projects/ngx-meta/schematics/ng-add/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'
import { Tree } from '@angular-devkit/schematics'
import { beforeEach, describe, expect, it } from '@jest/globals'
import { beforeEach, describe } from '@jest/globals'
import { join } from 'path'
import { Schema as NgAddSchema } from './schema'
import { Schema as NgNewSchema } from '@schematics/angular/ng-new/schema'
import { LIB_NAME } from '../testing/lib-name'
import { ProviderTestCase } from './testing/provider-test-case'
import { shouldAddRootProvider } from './testing/should-add-root-provider'
import { createTestApp } from '../testing/create-test-app'

// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/ng-add/index.spec.ts
// https://github.com/angular/components/blob/18.2.8/src/material/schematics/ng-add/index.spec.ts
Expand All @@ -24,6 +25,12 @@ describe('ng-add schematic', () => {
join(__dirname, '..', 'collection.json'),
)
})

const CORE_PROVIDER = new ProviderTestCase({
name: 'core',
symbol: 'provideNgxMetaCore',
})

;([true, false] as const).forEach((standalone) => {
const appKind = standalone ? 'standalone' : 'module-based'
describe(`when the app is ${appKind}`, () => {
Expand All @@ -34,58 +41,19 @@ describe('ng-add schematic', () => {
})
})

it('should add core provider', async () => {
const tree = await runner.runSchematic<NgAddSchema>(
SCHEMATIC_NAME,
defaultOptions,
appTree,
)
const appConfigOrAppModule = getAppConfigOrAppModuleContents(
tree,
standalone,
)
expect(appConfigOrAppModule).toContain(
`import { provideNgxMetaCore } from '${LIB_NAME}/core`,
)
expect(stripWhitespace(appConfigOrAppModule)).toMatch(
/providers:\[.*provideNgxMetaCore\(\).*]/,
)
describe('by default', () => {
let tree: Tree

beforeEach(async () => {
tree = await runner.runSchematic<NgAddSchema>(
SCHEMATIC_NAME,
defaultOptions,
appTree,
)
})

shouldAddRootProvider(CORE_PROVIDER, () => tree, standalone)
})
})
})
})

// https://github.com/FortAwesome/angular-fontawesome/blob/0.15.0/projects/schematics/src/ng-add/index.spec.ts#L107
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/test-app.ts
const createTestApp = async (
runner: SchematicTestRunner,
options: Omit<NgNewSchema, 'version'> & Partial<Pick<NgNewSchema, 'version'>>,
) => {
return runner.runExternalSchematic<NgNewSchema>(
'@schematics/angular',
'ng-new',
{
version: '9.0.0',
directory: '.',
...options,
},
)
}

const getAppConfigOrAppModuleContents = (tree: Tree, standalone: boolean) =>
standalone
? getFileContent(tree, '/src/app/app.config.ts')
: getFileContent(tree, '/src/app/app.module.ts')

// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/file-content.ts
const getFileContent = (tree: Tree, filePath: string): string => {
const contentBuffer = tree.read(filePath)

if (!contentBuffer) {
throw new Error(`Cannot read "${filePath}" because it does not exist.`)
}

return contentBuffer.toString()
}

const stripWhitespace = (value: string) => value.replace(/\s/g, '')
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Tree } from '@angular-devkit/schematics'
import { getFileContent } from '../../testing/get-file-content'

export const getAppConfigOrAppModuleContent = (
tree: Tree,
standalone: boolean,
) =>
standalone
? getFileContent(tree, '/src/app/app.config.ts')
: getFileContent(tree, '/src/app/app.module.ts')
18 changes: 18 additions & 0 deletions projects/ngx-meta/schematics/ng-add/testing/provider-test-case.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export class ProviderTestCase {
readonly name: string
readonly symbol: string
readonly code: string
readonly entrypoint: string

constructor(opts: {
name: string
symbol: string
code?: string
entrypoint?: string
}) {
this.name = opts.name
this.symbol = opts.symbol
this.code = opts.code ?? `${this.symbol}()`
this.entrypoint = opts.entrypoint ?? this.name
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ProviderTestCase } from './provider-test-case'
import { Tree } from '@angular-devkit/schematics'
import { expect, it } from '@jest/globals'
import { getAppConfigOrAppModuleContent } from './get-app-config-or-app-module-content'
import { LIB_NAME } from '../../testing/lib-name'
import { stripWhitespace } from '../../testing/strip-whitespace'
import { regexpEscape } from '../../testing/regexp-escape'

export const shouldAddRootProvider = (
providerTestCase: ProviderTestCase,
treeFactory: () => Tree,
standalone: boolean,
) => {
it(`should add ${providerTestCase.name} provider`, () => {
const appConfigOrAppModuleContents = getAppConfigOrAppModuleContent(
treeFactory(),
standalone,
)
expect(appConfigOrAppModuleContents).toContain(
`import { ${providerTestCase.symbol} } from '${LIB_NAME}/${providerTestCase.entrypoint}`,
)
expect(stripWhitespace(appConfigOrAppModuleContents)).toMatch(
new RegExp(`providers:\\[.*${regexpEscape(providerTestCase.code)}.*]`),
)
})
}
19 changes: 19 additions & 0 deletions projects/ngx-meta/schematics/testing/create-test-app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { SchematicTestRunner } from '@angular-devkit/schematics/testing'
import { Schema as NgNewSchema } from '@schematics/angular/ng-new/schema'

// https://github.com/FortAwesome/angular-fontawesome/blob/0.15.0/projects/schematics/src/ng-add/index.spec.ts#L107
// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/test-app.ts
export const createTestApp = async (
runner: SchematicTestRunner,
options: Omit<NgNewSchema, 'version'> & Partial<Pick<NgNewSchema, 'version'>>,
) => {
return runner.runExternalSchematic<NgNewSchema>(
'@schematics/angular',
'ng-new',
{
version: '9.0.0',
directory: '.',
...options,
},
)
}
12 changes: 12 additions & 0 deletions projects/ngx-meta/schematics/testing/get-file-content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Tree } from '@angular-devkit/schematics'

// https://github.com/angular/components/blob/18.2.8/src/cdk/schematics/testing/file-content.ts
export const getFileContent = (tree: Tree, filePath: string): string => {
const contentBuffer = tree.read(filePath)

if (!contentBuffer) {
throw new Error(`Cannot read "${filePath}" because it does not exist.`)
}

return contentBuffer.toString()
}
4 changes: 4 additions & 0 deletions projects/ngx-meta/schematics/testing/regexp-escape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// https://stackoverflow.com/a/9310752/3263250
// https://github.com/tc39/proposal-regex-escaping
export const regexpEscape = (string: string) =>
string.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&')
2 changes: 2 additions & 0 deletions projects/ngx-meta/schematics/testing/strip-whitespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// https://github.com/angular/angular-cli/blob/18.2.9/packages/schematics/angular/utility/standalone/rules_spec.ts#L45-L47
export const stripWhitespace = (value: string) => value.replace(/\s/g, '')

0 comments on commit b028e11

Please sign in to comment.