Skip to content

Commit 64d8c47

Browse files
clydinangular-robot[bot]
authored andcommitted
test(@angular-devkit/build-angular): reduce harness usage of deprecated TestProjectHost
The Builder test harness previously used the deprecated `TestProjectHost` to perform a variety of file operations during builder unit tests. However, the `TestProjectHost` is deprecated and uses several layers of rxjs to perform the file operations. The test harness now uses the Node.js `fs` builtin to directly perform the file operations. This removes several layers of indirection between the harness and the actual underlying file operations. The removal of the rxjs operation chaining also reduces stack traces and makes debugging test issues less complicated.
1 parent 952a02c commit 64d8c47

File tree

1 file changed

+31
-27
lines changed

1 file changed

+31
-27
lines changed

packages/angular_devkit/build_angular/src/testing/builder-harness.ts

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ import {
2121
} from '@angular-devkit/architect';
2222
import { WorkspaceHost } from '@angular-devkit/architect/node';
2323
import { TestProjectHost } from '@angular-devkit/architect/testing';
24-
import { getSystemPath, join, json, logging, normalize } from '@angular-devkit/core';
24+
import { getSystemPath, json, logging } from '@angular-devkit/core';
25+
import { existsSync, readFileSync, readdirSync } from 'node:fs';
26+
import fs from 'node:fs/promises';
27+
import { dirname, join } from 'node:path';
2528
import { Observable, Subject, from as observableFrom, of as observableOf } from 'rxjs';
2629
import { catchError, finalize, first, map, mergeMap, shareReplay } from 'rxjs/operators';
2730
import { BuilderWatcherFactory, WatcherNotifier } from './file-watching';
@@ -85,6 +88,10 @@ export class BuilderHarness<T> {
8588
this.schemaRegistry.addPostTransform(json.schema.transforms.addUndefinedDefaults);
8689
}
8790

91+
private resolvePath(path: string): string {
92+
return join(getSystemPath(this.host.root()), path);
93+
}
94+
8895
useProject(name: string, metadata: Record<string, unknown> = {}): this {
8996
if (!name) {
9097
throw new Error('Project name cannot be an empty string.');
@@ -215,7 +222,7 @@ export class BuilderHarness<T> {
215222
};
216223
const context = new HarnessBuilderContext(
217224
this.builderInfo,
218-
getSystemPath(this.host.root()),
225+
this.resolvePath('.'),
219226
contextHost,
220227
useNativeFileWatching ? undefined : this.watcherNotifier,
221228
);
@@ -282,13 +289,12 @@ export class BuilderHarness<T> {
282289
}
283290

284291
async writeFile(path: string, content: string | Buffer): Promise<void> {
285-
this.host
286-
.scopedSync()
287-
.write(normalize(path), typeof content === 'string' ? Buffer.from(content) : content);
292+
const fullPath = this.resolvePath(path);
288293

289-
this.watcherNotifier?.notify([
290-
{ path: getSystemPath(join(this.host.root(), path)), type: 'modified' },
291-
]);
294+
await fs.mkdir(dirname(fullPath), { recursive: true });
295+
await fs.writeFile(fullPath, content, 'utf-8');
296+
297+
this.watcherNotifier?.notify([{ path: fullPath, type: 'modified' }]);
292298
}
293299

294300
async writeFiles(files: Record<string, string | Buffer>): Promise<void> {
@@ -297,11 +303,12 @@ export class BuilderHarness<T> {
297303
: undefined;
298304

299305
for (const [path, content] of Object.entries(files)) {
300-
this.host
301-
.scopedSync()
302-
.write(normalize(path), typeof content === 'string' ? Buffer.from(content) : content);
306+
const fullPath = this.resolvePath(path);
307+
308+
await fs.mkdir(dirname(fullPath), { recursive: true });
309+
await fs.writeFile(fullPath, content, 'utf-8');
303310

304-
watchEvents?.push({ path: getSystemPath(join(this.host.root(), path)), type: 'modified' });
311+
watchEvents?.push({ path: fullPath, type: 'modified' });
305312
}
306313

307314
if (watchEvents) {
@@ -310,11 +317,11 @@ export class BuilderHarness<T> {
310317
}
311318

312319
async removeFile(path: string): Promise<void> {
313-
this.host.scopedSync().delete(normalize(path));
320+
const fullPath = this.resolvePath(path);
314321

315-
this.watcherNotifier?.notify([
316-
{ path: getSystemPath(join(this.host.root(), path)), type: 'deleted' },
317-
]);
322+
await fs.unlink(fullPath);
323+
324+
this.watcherNotifier?.notify([{ path: fullPath, type: 'deleted' }]);
318325
}
319326

320327
async modifyFile(
@@ -323,27 +330,24 @@ export class BuilderHarness<T> {
323330
): Promise<void> {
324331
const content = this.readFile(path);
325332
await this.writeFile(path, await modifier(content));
326-
327-
this.watcherNotifier?.notify([
328-
{ path: getSystemPath(join(this.host.root(), path)), type: 'modified' },
329-
]);
330333
}
331334

332335
hasFile(path: string): boolean {
333-
return this.host.scopedSync().exists(normalize(path));
336+
const fullPath = this.resolvePath(path);
337+
338+
return existsSync(fullPath);
334339
}
335340

336341
hasFileMatch(directory: string, pattern: RegExp): boolean {
337-
return this.host
338-
.scopedSync()
339-
.list(normalize(directory))
340-
.some((name) => pattern.test(name));
342+
const fullPath = this.resolvePath(directory);
343+
344+
return readdirSync(fullPath).some((name) => pattern.test(name));
341345
}
342346

343347
readFile(path: string): string {
344-
const content = this.host.scopedSync().read(normalize(path));
348+
const fullPath = this.resolvePath(path);
345349

346-
return Buffer.from(content).toString('utf8');
350+
return readFileSync(fullPath, 'utf-8');
347351
}
348352

349353
private validateProjectName(name: string): void {

0 commit comments

Comments
 (0)