Skip to content
Closed
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
2 changes: 0 additions & 2 deletions goldens/public-api/core/testing/index.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ export interface TestBed {
// (undocumented)
execute(tokens: any[], fn: Function, context?: any): any;
flushEffects(): void;
// @deprecated (undocumented)
get(token: any, notFoundValue?: any): any;
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, options?: TestEnvironmentOptions): void;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: undefined, options: InjectOptions & {
Expand Down
6 changes: 4 additions & 2 deletions packages/common/http/test/module_spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,15 @@ class ReentrantInterceptor implements HttpInterceptor {
describe('HttpClientModule', () => {
let injector: Injector;
beforeEach(() => {
injector = TestBed.configureTestingModule({
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{provide: HTTP_INTERCEPTORS, useClass: InterceptorA, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: InterceptorB, multi: true},
{provide: HTTP_INTERCEPTORS, useClass: InterceptorC, multi: true},
],
});
injector = TestBed.inject(Injector);
});
it('initializes HttpClient properly', (done) => {
injector
Expand Down Expand Up @@ -132,10 +133,11 @@ describe('HttpClientModule', () => {
});
it('allows interceptors to inject HttpClient', (done) => {
TestBed.resetTestingModule();
injector = TestBed.configureTestingModule({
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [{provide: HTTP_INTERCEPTORS, useClass: ReentrantInterceptor, multi: true}],
});
injector = TestBed.inject(Injector);
injector
.get(HttpClient)
.get('/test')
Expand Down
2 changes: 2 additions & 0 deletions packages/core/schematics/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ rollup_bundle(
"//packages/core/schematics/ng-generate/output-migration:index.ts": "output-migration",
"//packages/core/schematics/ng-generate/self-closing-tags-migration:index.ts": "self-closing-tags-migration",
"//packages/core/schematics/migrations/inject-flags:index.ts": "inject-flags",
"//packages/core/schematics/migrations/test-bed-get:index.ts": "test-bed-get",
},
format = "cjs",
link_workspace_root = True,
Expand All @@ -56,6 +57,7 @@ rollup_bundle(
],
deps = [
"//packages/core/schematics/migrations/inject-flags",
"//packages/core/schematics/migrations/test-bed-get",
"//packages/core/schematics/ng-generate/cleanup-unused-imports",
"//packages/core/schematics/ng-generate/control-flow-migration",
"//packages/core/schematics/ng-generate/inject-migration",
Expand Down
5 changes: 5 additions & 0 deletions packages/core/schematics/migrations.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
"version": "20.0.0",
"description": "Replaces usages of the deprecated InjectFlags enum",
"factory": "./bundles/inject-flags#migrate"
},
"test-bed-get": {
"version": "20.0.0",
"description": "Replaces usages of the deprecated TestBed.get method with TestBed.inject",
"factory": "./bundles/test-bed-get#migrate"
}
}
}
25 changes: 25 additions & 0 deletions packages/core/schematics/migrations/test-bed-get/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
load("//tools:defaults.bzl", "ts_library")

package(
default_visibility = [
"//packages/core/schematics:__pkg__",
"//packages/core/schematics/migrations/google3:__pkg__",
"//packages/core/schematics/test:__pkg__",
],
)

ts_library(
name = "test-bed-get",
srcs = glob(["**/*.ts"]),
tsconfig = "//packages/core/schematics:tsconfig.json",
deps = [
"//packages/compiler-cli/private",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/core/schematics/utils",
"//packages/core/schematics/utils/tsurge",
"//packages/core/schematics/utils/tsurge/helpers/angular_devkit",
"@npm//@angular-devkit/schematics",
"@npm//@types/node",
"@npm//typescript",
],
)
24 changes: 24 additions & 0 deletions packages/core/schematics/migrations/test-bed-get/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## Remove `TestBed.get` migration
Replaces the usages of the deprecated `TestBed.get` method with the non-deprecated `TestBed.inject`:

### Before
```typescript
import { TestBed } from '@angular/core/testing';

describe('test', () => {
it('should inject', () => {
console.log(TestBed.get(SOME_TOKEN));
});
});
```

### After
```typescript
import { TestBed } from '@angular/core/testing';

describe('test', () => {
it('should inject', () => {
console.log(TestBed.inject(SOME_TOKEN));
});
});
```
20 changes: 20 additions & 0 deletions packages/core/schematics/migrations/test-bed-get/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*!
* @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} from '@angular-devkit/schematics';
import {TestBedGetMigration} from './test_bed_get_migration';
import {runMigrationInDevkit} from '../../utils/tsurge/helpers/angular_devkit';

export function migrate(): Rule {
return async (tree) => {
await runMigrationInDevkit({
tree,
getMigration: () => new TestBedGetMigration(),
});
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/**
* @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 ts from 'typescript';
import {
confirmAsSerializable,
ProgramInfo,
ProjectFile,
projectFile,
Replacement,
Serializable,
TextUpdate,
TsurgeFunnelMigration,
} from '../../utils/tsurge';
import {getImportSpecifier} from '../../utils/typescript/imports';
import {isReferenceToImport} from '../../utils/typescript/symbol';

export interface CompilationUnitData {
locations: Location[];
}

/** Information about the `get` identifier in `TestBed.get`. */
interface Location {
/** File in which the expression is defined. */
file: ProjectFile;

/** Start of the `get` identifier. */
position: number;
}

/** Name of the method being replaced. */
const METHOD_NAME = 'get';

/** Migration that replaces `TestBed.get` usages with `TestBed.inject`. */
export class TestBedGetMigration extends TsurgeFunnelMigration<
CompilationUnitData,
CompilationUnitData
> {
override async analyze(info: ProgramInfo): Promise<Serializable<CompilationUnitData>> {
const locations: Location[] = [];

for (const sourceFile of info.sourceFiles) {
const specifier = getImportSpecifier(sourceFile, '@angular/core/testing', 'TestBed');

if (specifier === null) {
continue;
}

const typeChecker = info.program.getTypeChecker();
sourceFile.forEachChild(function walk(node) {
if (
ts.isPropertyAccessExpression(node) &&
node.name.text === METHOD_NAME &&
ts.isIdentifier(node.expression) &&
isReferenceToImport(typeChecker, node.expression, specifier)
) {
locations.push({file: projectFile(sourceFile, info), position: node.name.getStart()});
} else {
node.forEachChild(walk);
}
});
}

return confirmAsSerializable({locations});
}

override async migrate(globalData: CompilationUnitData) {
const replacements = globalData.locations.map(({file, position}) => {
return new Replacement(
file,
new TextUpdate({
position: position,
end: position + METHOD_NAME.length,
toInsert: 'inject',
}),
);
});

return confirmAsSerializable({replacements});
}

override async combine(
unitA: CompilationUnitData,
unitB: CompilationUnitData,
): Promise<Serializable<CompilationUnitData>> {
const seen = new Set<string>();
const locations: Location[] = [];
const combined = [...unitA.locations, ...unitB.locations];

for (const location of combined) {
const key = `${location.file.id}#${location.position}`;
if (!seen.has(key)) {
seen.add(key);
locations.push(location);
}
}

return confirmAsSerializable({locations});
}

override async globalMeta(
combinedData: CompilationUnitData,
): Promise<Serializable<CompilationUnitData>> {
return confirmAsSerializable(combinedData);
}

override async stats() {
return {counters: {}};
}
}
Loading