/
githubmirror
/
angular-cli
Обзор
Документация
Войти
/
githubmirror
/
angular-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
packages/schematics/angular/utility/standalone/rules_spec.ts
487 строк
14 KB
Paweł Fraś
fix(@schematics/angular): move 'provideZoneChangeDetection' to the root module
07 янв 2026, 22:30
07 янв 2026, 22:30
9006ec0
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import { Rule, SchematicContext, Tree, callRule } from '@angular-devkit/schematics'; import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; import { join } from 'node:path'; import { addRootImport, addRootProvider } from './rules'; describe('standalone utilities', () => { const projectName = 'test'; let host: Tree; async function setupProject(standalone = false) { const schematicRunner = new SchematicTestRunner( '@schematics/angular', require.resolve('../../collection.json'), ); host = await schematicRunner.runSchematic('workspace', { name: 'workspace', newProjectRoot: '/', version: '6.0.0', }); host = await schematicRunner.runSchematic( 'application', { name: projectName, standalone, routing: false, }, host, ); } afterEach(() => { // Clear the host so it doesn't leak between tests. host = null as unknown as Tree; }); function stripWhitespace(str: string) { return str.replace(/\s/g, ''); } function assertContains(source: string, targetString: string) { expect(stripWhitespace(source)).toContain(stripWhitespace(targetString)); } function getPathWithinProject(path: string): string { return join('/', projectName, 'src', path); } function readFile(projectPath: string): string { return host.readText(getPathWithinProject(projectPath)); } async function testRule(rule: Rule, tree: Tree): Promise<void> { await callRule(rule, tree, {} as unknown as SchematicContext).toPromise(); } describe('addRootImport', () => { it('should add a root import to an NgModule-based app', async () => { await setupProject(); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}.forRoot([])`, ), host, ); const content = readFile('app/app-module.ts'); assertContains(content, `import { MyModule } from '@my/module';`); assertContains(content, `imports: [BrowserModule, MyModule.forRoot([])]`); }); it('should add a root import to a standalone app', async () => { await setupProject(true); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('app/app.config.ts'); assertContains(content, `importProvidersFrom`); assertContains(content, `import { MyModule } from '@my/module';`); assertContains(content, `importProvidersFrom(MyModule)`); }); it('should add a root import to a standalone app whose app config does not have a providers array', async () => { await setupProject(true); host.overwrite( getPathWithinProject('app/app.config.ts'), ` import { ApplicationConfig } from '@angular/core'; export const appConfig: ApplicationConfig = {}; `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('app/app.config.ts'); assertContains( content, `import { ApplicationConfig, importProvidersFrom } from '@angular/core';`, ); assertContains(content, `import { MyModule } from '@my/module';`); assertContains(content, `providers: [importProvidersFrom(MyModule)]`); }); it('should add a root import to a standalone app with a config with providers', async () => { await setupProject(true); host.overwrite( getPathWithinProject('app/app.config.ts'), ` import { ApplicationConfig } from '@angular/core'; export const appConfig: ApplicationConfig = { providers: [ {provide: 'foo', useValue: 123} ] }; `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('app/app.config.ts'); assertContains( content, `import { ApplicationConfig, importProvidersFrom } from '@angular/core';`, ); assertContains(content, `import { MyModule } from '@my/module';`); assertContains( content, `providers: [ {provide: 'foo', useValue: 123}, importProvidersFrom(MyModule) ]`, ); }); it( 'should add a root import to a standalone app whose app config does not have have ' + 'a providers array, but has another property', async () => { await setupProject(true); host.overwrite( getPathWithinProject('app/app.config.ts'), ` import { ApplicationConfig } from '@angular/core'; export const appConfig: ApplicationConfig = { otherProp: {}, }; `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('app/app.config.ts'); assertContains( content, `import { ApplicationConfig, importProvidersFrom } from '@angular/core';`, ); assertContains(content, `import { MyModule } from '@my/module';`); assertContains( content, ` export const appConfig: ApplicationConfig = { otherProp: {}, providers: [importProvidersFrom(MyModule)] }; `, ); }, ); it('should add a root import to a standalone app with an inline app config', async () => { await setupProject(true); host.overwrite( getPathWithinProject('main.ts'), ` import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent, {}); `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('main.ts'); assertContains(content, `import { importProvidersFrom } from '@angular/core';`); assertContains(content, `import { MyModule } from '@my/module';`); assertContains( content, `bootstrapApplication(AppComponent, { providers: [importProvidersFrom(MyModule)] });`, ); }); it('should add a root import to a standalone app without an app config', async () => { await setupProject(true); host.overwrite( getPathWithinProject('main.ts'), ` import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; bootstrapApplication(AppComponent); `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('main.ts'); assertContains(content, `import { importProvidersFrom } from '@angular/core';`); assertContains(content, `import { MyModule } from '@my/module';`); assertContains( content, `bootstrapApplication(AppComponent, { providers: [importProvidersFrom(MyModule)] });`, ); }); it('should add a root import to a standalone app with a merged app config', async () => { await setupProject(true); host.overwrite( getPathWithinProject('main.ts'), ` import { mergeApplicationConfig } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app.component'; bootstrapApplication(AppComponent, mergeApplicationConfig(a, b)); `, ); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); const content = readFile('main.ts'); assertContains( content, `import { mergeApplicationConfig, importProvidersFrom } from '@angular/core';`, ); assertContains(content, `import { MyModule } from '@my/module';`); assertContains( content, `bootstrapApplication(AppComponent, mergeApplicationConfig(a, b, { providers: [importProvidersFrom(MyModule)] }));`, ); }); it('should alias symbols that conflict with existing code', async () => { await setupProject(); await testRule( addRootImport( projectName, ({ code, external }) => code`${external('BrowserModule', '@my/module')}.forRoot([])`, ), host, ); const content = readFile('app/app-module.ts'); assertContains(content, `import { BrowserModule as BrowserModule_alias } from '@my/module';`); assertContains(content, `imports: [BrowserModule, BrowserModule_alias.forRoot([])]`); }); it('should throw an error if the bootstrapApplication code has no arguments', async () => { await setupProject(true); const mainPath = getPathWithinProject('main.ts'); host.overwrite( mainPath, ` import { bootstrapApplication } from '@angular/platform-browser'; bootstrapApplication(); `, ); const promise = testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); await expectAsync(promise).toBeRejectedWithError( `Cannot add provider to invalid bootstrapApplication call in ${mainPath}`, ); }); it('should throw an error if the bootstrapApplication call cannot be analyzed', async () => { await setupProject(true); const mainPath = getPathWithinProject('main.ts'); host.overwrite( mainPath, ` import { bootstrapApplication } from '@angular/platform-browser'; import { AppComponent } from './app/app.component'; import { appConfig } from '@external/app-config'; bootstrapApplication(AppComponent, appConfig); `, ); const promise = testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); await expectAsync(promise).toBeRejectedWithError( `Cannot statically analyze bootstrapApplication call in ${mainPath}`, ); }); it('should throw an error if there is no bootstrapApplication call', async () => { await setupProject(true); host.overwrite(getPathWithinProject('main.ts'), ''); const promise = testRule( addRootImport( projectName, ({ code, external }) => code`${external('MyModule', '@my/module')}`, ), host, ); await expectAsync(promise).toBeRejectedWithError('Bootstrap call not found'); }); }); describe('addRootProvider', () => { it('should add a root provider to an NgModule-based app', async () => { await setupProject(); await testRule( addRootProvider( projectName, ({ code, external }) => code`{ provide: ${external('SOME_TOKEN', '@my/module')}, useValue: 123 }`, ), host, ); const content = readFile('app/app-module.ts'); assertContains(content, `import { SOME_TOKEN } from '@my/module';`); assertContains( content, `providers: [provideBrowserGlobalErrorListeners(),{ provide: SOME_TOKEN, useValue: 123 },]`, ); }); it('should add a root provider to a standalone app', async () => { await setupProject(true); await testRule( addRootProvider( projectName, ({ code, external }) => code`${external('provideModule', '@my/module')}([])`, ), host, ); const content = readFile('app/app.config.ts'); assertContains(content, `import { provideModule } from '@my/module';`); assertContains( content, `providers: [ provideBrowserGlobalErrorListeners(), provideModule([]), ]`, ); }); it('should add a root provider to a standalone app when providers contain a trailing comma', async () => { await setupProject(true); const configPath = 'app/app.config.ts'; host.overwrite( getPathWithinProject(configPath), ` import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core'; import { provideRouter } from '@angular/router'; export const appConfig: ApplicationConfig = { providers: [ provideBrowserGlobalErrorListeners(), provideRouter([]), ] }; `, ); await testRule( addRootProvider( projectName, ({ code, external }) => code`${external('provideModule', '@my/module')}([])`, ), host, ); const content = readFile('app/app.config.ts'); assertContains(content, `import { provideModule } from '@my/module';`); assertContains( content, `providers: [provideBrowserGlobalErrorListeners(), provideRouter([]),provideModule([]),]`, ); }); }); });