From 6faf2f66c974ee68f3ca8ae5bc72e8cc2af2c66b Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland Date: Fri, 14 Jun 2024 15:28:13 -0600 Subject: [PATCH] Add tests for SourcePlugin Also properly clean up fixture dirs when running tests Resolves #2586 --- package-lock.json | 8 +- package.json | 2 +- src/lib/converter/plugins/SourcePlugin.ts | 5 +- src/lib/converter/utils/base-path.ts | 106 ---------- src/lib/converter/utils/repository.ts | 122 ++++++----- src/lib/utils/array.ts | 5 - src/lib/utils/paths.ts | 18 +- src/test/Repository.test.ts | 196 ++++++++++++++---- src/test/packages.test.ts | 6 +- src/test/slow/entry-point.test.ts | 28 +-- src/test/utils/fs.test.ts | 17 +- .../utils/options/readers/tsconfig.test.ts | 18 +- .../utils/options/readers/typedoc.test.ts | 12 +- .../utils/{base-path.test.ts => path.test.ts} | 14 +- src/test/utils/plugins.test.ts | 13 +- 15 files changed, 292 insertions(+), 278 deletions(-) delete mode 100644 src/lib/converter/utils/base-path.ts rename src/test/utils/{base-path.test.ts => path.test.ts} (53%) diff --git a/package-lock.json b/package-lock.json index 835a7befe..5ee02104a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "@types/markdown-it": "^14.1.1", "@types/mocha": "^10.0.6", "@types/node": "18", - "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#8abd1494280116ff5318dde2c50ad01e1663790c", + "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe", "c8": "^9.1.0", "esbuild": "^0.21.3", "eslint": "^9.3.0", @@ -1111,12 +1111,12 @@ }, "node_modules/@typestrong/fs-fixture-builder": { "version": "0.0.0", - "resolved": "git+ssh://git@github.com/TypeStrong/fs-fixture-builder.git#8abd1494280116ff5318dde2c50ad01e1663790c", - "integrity": "sha512-DS1emSwvN8RjElPNzV00/qkICp2R/fuiEkraaFNVTFTXcJXLqQ1KESTJXxSbSFE8AUsVgxa/XHG51pfSM0i0kw==", + "resolved": "git+ssh://git@github.com/TypeStrong/fs-fixture-builder.git#34113409e3a171e68ce5e2b55461ef5c35591cfe", + "integrity": "sha512-CfD3fWF5hYyTawCF+I3V7acIxk96M+TIwVFFotGC+K2J9nbXCrD5xmQyE2dBDEIn1VjQjKf8pc/L34ubDmcfhA==", "dev": true, "license": "MIT", "bin": { - "capture-fs-fixture": "dist/capture-fixture.ts" + "capture-fs-fixture": "dist/capture-fixture.js" } }, "node_modules/acorn": { diff --git a/package.json b/package.json index d17094046..6b1f02916 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "@types/markdown-it": "^14.1.1", "@types/mocha": "^10.0.6", "@types/node": "18", - "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#8abd1494280116ff5318dde2c50ad01e1663790c", + "@typestrong/fs-fixture-builder": "github:TypeStrong/fs-fixture-builder#34113409e3a171e68ce5e2b55461ef5c35591cfe", "c8": "^9.1.0", "esbuild": "^0.21.3", "eslint": "^9.3.0", diff --git a/src/lib/converter/plugins/SourcePlugin.ts b/src/lib/converter/plugins/SourcePlugin.ts index ba15ffe00..2f329dcaa 100644 --- a/src/lib/converter/plugins/SourcePlugin.ts +++ b/src/lib/converter/plugins/SourcePlugin.ts @@ -12,7 +12,6 @@ import { isNamedNode } from "../utils/nodes"; import { relative } from "path"; import { SourceReference } from "../../models"; import { gitIsInstalled, RepositoryManager } from "../utils/repository"; -import { BasePath } from "../utils/base-path"; /** * A handler that attaches source file information to reflections. @@ -78,7 +77,7 @@ export class SourcePlugin extends ConverterComponent { const symbol = reflection.project.getSymbolFromReflection(reflection); for (const node of symbol?.declarations || []) { const sourceFile = node.getSourceFile(); - const fileName = BasePath.normalize(sourceFile.fileName); + const fileName = normalizePath(sourceFile.fileName); this.fileNames.add(fileName); let position: ts.LineAndCharacter; @@ -113,7 +112,7 @@ export class SourcePlugin extends ConverterComponent { if (this.disableSources || !sig) return; const sourceFile = sig.getSourceFile(); - const fileName = BasePath.normalize(sourceFile.fileName); + const fileName = normalizePath(sourceFile.fileName); this.fileNames.add(fileName); const position = ts.getLineAndCharacterOfPosition( diff --git a/src/lib/converter/utils/base-path.ts b/src/lib/converter/utils/base-path.ts deleted file mode 100644 index 429071032..000000000 --- a/src/lib/converter/utils/base-path.ts +++ /dev/null @@ -1,106 +0,0 @@ -import * as Path from "path"; - -/** - * Helper class that determines the common base path of a set of files. - * - * In the first step all files must be passed to {@link add}. Afterwards {@link trim} - * can be used to retrieve the shortest path relative to the determined base path. - */ -export class BasePath { - /** - * List of known base paths. - */ - private basePaths: string[] = []; - - /** - * Add the given file path to this set of base paths. - * - * @param fileName The absolute filename that should be added to the base path. - */ - add(fileName: string) { - const fileDir = Path.dirname(BasePath.normalize(fileName)); - const filePath = fileDir.split("/"); - - basePaths: for (let n = 0, c = this.basePaths.length; n < c; n++) { - const basePath = this.basePaths[n].split("/"); - const mMax = Math.min(basePath.length, filePath.length); - for (let m = 0; m < mMax; m++) { - if (basePath[m] === filePath[m]) { - continue; - } - - if (m < 1) { - // No match at all, try next known base path - continue basePaths; - } else { - // Partial match, trim the known base path - if (m < basePath.length) { - this.basePaths[n] = basePath.slice(0, m).join("/"); - } - return; - } - } - - // Complete match, exit - this.basePaths[n] = basePath.splice(0, mMax).join("/"); - return; - } - - // Unknown base path, add it - this.basePaths.push(fileDir); - } - - /** - * Trim the given filename by the determined base paths. - * - * @param fileName The absolute filename that should be trimmed. - * @returns The trimmed version of the filename. - */ - trim(fileName: string): string { - fileName = BasePath.normalize(fileName); - for (let n = 0, c = this.basePaths.length; n < c; n++) { - const basePath = this.basePaths[n]; - if (fileName.substring(0, basePath.length) === basePath) { - return fileName.substring(basePath.length + 1); - } - } - - return fileName; - } - - /** - * Reset this instance, ignore all paths already passed to {@link add}. - */ - reset() { - this.basePaths = []; - } - - /** - * Normalize the given path. - * - * @param path The path that should be normalized. - * @returns Normalized version of the given path. - */ - static normalize(path: string): string { - if (process.platform === "win32") { - // Ensure forward slashes - path = path.replace(/\\/g, "/"); - - // Msys2 git on windows will give paths which use unix-style - // absolute paths, like /c/users/you. Since the rest of TypeDoc - // expects drive letters, convert it to that here. - path = path.replace( - /^\/([a-zA-Z])\//, - (_m, m1: string) => `${m1}:/`, - ); - - // Make Windows drive letters upper case - path = path.replace( - /^([^:]+):\//, - (_m, m1: string) => m1.toUpperCase() + ":/", - ); - } - - return path; - } -} diff --git a/src/lib/converter/utils/repository.ts b/src/lib/converter/utils/repository.ts index e08b38aa5..6fd9aca51 100644 --- a/src/lib/converter/utils/repository.ts +++ b/src/lib/converter/utils/repository.ts @@ -1,9 +1,8 @@ import { spawnSync } from "child_process"; -import type { Logger } from "../../utils"; -import { BasePath } from "../utils/base-path"; +import { normalizePath, type Logger } from "../../utils"; import { NonEnumerable } from "../../utils/general"; -import { dirname } from "path"; -import { insertSorted } from "../../utils/array"; +import { dirname, join } from "path"; +import { existsSync } from "fs"; const TEN_MEGABYTES = 1024 * 10000; @@ -80,7 +79,7 @@ export class GitRepository implements Repository { if (out.status === 0) { out.stdout.split("\0").forEach((file) => { if (file !== "") { - this.files.add(BasePath.normalize(path + "/" + file)); + this.files.add(normalizePath(path + "/" + file)); } }); } @@ -126,9 +125,6 @@ export class GitRepository implements Repository { gitRemote: string, logger: Logger, ): GitRepository | undefined { - const topLevel = git("-C", path, "rev-parse", "--show-toplevel"); - if (topLevel.status !== 0) return; - gitRevision ||= git("-C", path, "rev-parse", "HEAD").stdout.trim(); if (!gitRevision) return; // Will only happen in a repo with no commits. @@ -148,11 +144,7 @@ export class GitRepository implements Repository { if (!urlTemplate) return; - return new GitRepository( - BasePath.normalize(topLevel.stdout.replace("\n", "")), - gitRevision, - urlTemplate, - ); + return new GitRepository(normalizePath(path), gitRevision, urlTemplate); } } @@ -176,19 +168,26 @@ export class GitRepository implements Repository { * expensive (~20-300ms depending on the machine, antivirus, etc.) we check for * `.git` folders manually, and only call git if one is found. * - * Symlinks further complicate this. + * Symlinked files have the potential to further complicate this. If TypeScript's + * `preserveSymlinks` option is on, then this may be passed the path to a symlinked + * file. Unlike TypeScript, we will resolve the path, as the repo link should really + * point to the actual file. */ export class RepositoryManager { - ignoredPaths = new Set(); - repositories: Repository[] = []; + private cache = new Map(); + private assumedRepo = new AssumedRepository( + this.basePath, + this.gitRevision, + this.sourceLinkTemplate, + ); constructor( - readonly basePath: string, - readonly gitRevision: string, - readonly gitRemote: string, - readonly sourceLinkTemplate: string, - readonly disableGit: boolean, - readonly logger: Logger, + private basePath: string, + private gitRevision: string, + private gitRemote: string, + private sourceLinkTemplate: string, + private disableGit: boolean, + private logger: Logger, ) {} /** @@ -199,54 +198,49 @@ export class RepositoryManager { */ getRepository(fileName: string): Repository | undefined { if (this.disableGit) { - return new AssumedRepository( - this.basePath, - this.gitRevision, - this.sourceLinkTemplate, - ); - } - // Check for known non-repositories - const dirName = dirname(fileName); - if (this.ignoredPaths.has(dirName)) { - return; + return this.assumedRepo; } + return this.getRepositoryFolder(normalizePath(dirname(fileName))); + } - // Check for known repositories - for (const repo of this.repositories) { - if (fileName.startsWith(repo.path)) { - return repo; - } + private getRepositoryFolder(dir: string): Repository | undefined { + if (this.cache.has(dir)) { + return this.cache.get(dir); } - // If git has been disabled, and this is outside of our base path, - // then we shouldn't create links to the file. - if (this.disableGit) return; - - // Try to create a new repository - const repository = GitRepository.tryCreateRepository( - dirName, - this.sourceLinkTemplate, - this.gitRevision, - this.gitRemote, - this.logger, - ); - if (repository) { - insertSorted( - this.repositories, - repository, - (repo) => repo.path.length < repository.path.length, - ); - return repository; - } + if (existsSync(join(dir, ".git"))) { + // This might just be a git repo, or we might be in some self-recursive symlink + // loop, and the repo is actually somewhere else. Ask Git where the repo actually is. + const repo = git("-C", dir, "rev-parse", "--show-toplevel"); + if (repo.status === 0) { + const repoDir = repo.stdout.replace("\n", ""); + // This check is only necessary if we're in a symlink loop, otherwise + // it will always be true. + if (!this.cache.has(repoDir)) { + this.cache.set( + repoDir, + GitRepository.tryCreateRepository( + repoDir, + this.sourceLinkTemplate, + this.gitRevision, + this.gitRemote, + this.logger, + ), + ); + } - // No repository found, add all parent paths to ignored paths - // as they definitely don't contain git repos. - this.ignoredPaths.add(dirName); - let index = dirName.lastIndexOf("/"); - while (index > 0) { - this.ignoredPaths.add(dirName.slice(0, index)); - index = dirName.lastIndexOf("/", index - 1); + this.cache.set(dir, this.cache.get(repoDir)); + } else { + // Not a git repo, probably corrupt. + this.cache.set(dir, undefined); + } + } else { + // We may be at the root of the file system, in which case there is no repo. + this.cache.set(dir, undefined); + this.cache.set(dir, this.getRepositoryFolder(dirname(dir))); } + + return this.cache.get(dir); } } diff --git a/src/lib/utils/array.ts b/src/lib/utils/array.ts index 78d7a0a5e..f9d27da9a 100644 --- a/src/lib/utils/array.ts +++ b/src/lib/utils/array.ts @@ -29,11 +29,6 @@ export function insertOrderSorted( return arr; } -export function insertSorted(arr: T[], item: T, less: (a: T) => boolean) { - const index = binaryFindPartition(arr, less); - arr.splice(index === -1 ? arr.length : index, 0, item); -} - /** * Performs a binary search of a given array, returning the index of the first item * for which `partition` returns true. Returns the -1 if there are no items in `arr` diff --git a/src/lib/utils/paths.ts b/src/lib/utils/paths.ts index cfef4e857..2f25c4192 100644 --- a/src/lib/utils/paths.ts +++ b/src/lib/utils/paths.ts @@ -37,5 +37,21 @@ export function nicePath(absPath: string) { * @returns The normalized path. */ export function normalizePath(path: string) { - return path.replace(/\\/g, "/"); + if (process.platform === "win32") { + // Ensure forward slashes + path = path.replace(/\\/g, "/"); + + // Msys2 git on windows will give paths which use unix-style + // absolute paths, like /c/users/you. Since the rest of TypeDoc + // expects drive letters, convert it to that here. + path = path.replace(/^\/([a-zA-Z])\//, (_m, m1: string) => `${m1}:/`); + + // Make Windows drive letters upper case + path = path.replace( + /^([^:]+):\//, + (_m, m1: string) => m1.toUpperCase() + ":/", + ); + } + + return path; } diff --git a/src/test/Repository.test.ts b/src/test/Repository.test.ts index f29c01e76..f316e72b0 100644 --- a/src/test/Repository.test.ts +++ b/src/test/Repository.test.ts @@ -3,13 +3,29 @@ import { guessSourceUrlTemplate, RepositoryManager, } from "../lib/converter/utils/repository"; -import { strictEqual as equal, ok } from "assert"; -import { tempdirProject } from "@typestrong/fs-fixture-builder"; +import { deepStrictEqual as equal, ok } from "assert"; +import { type Project, tempdirProject } from "@typestrong/fs-fixture-builder"; import { spawnSync } from "child_process"; import { TestLogger } from "./TestLogger"; import { join } from "path"; import { normalizePath } from "../lib/utils/paths"; +function git(cwd: string, ...args: string[]) { + const env = { + GIT_AUTHOR_NAME: "test", + GIT_AUTHOR_EMAIL: "test@example.com", + GIT_AUTHOR_DATE: "2024-03-31T22:04:50.119Z", + GIT_COMMITTER_NAME: "test", + GIT_COMMITTER_EMAIL: "test@example.com", + GIT_COMMITTER_DATE: "2024-03-31T22:04:50.119Z", + }; + return spawnSync("git", ["-C", cwd, ...args], { + encoding: "utf-8", + windowsHide: true, + env, + }); +} + describe("Repository", function () { describe("guessSourceUrlTemplate helper", () => { it("handles a personal GitHub HTTPS URL", () => { @@ -110,23 +126,7 @@ describe("Repository", function () { }); describe("getURL", () => { - const project = tempdirProject(); - function git(...args: string[]) { - const env = { - GIT_AUTHOR_NAME: "test", - GIT_AUTHOR_EMAIL: "test@example.com", - GIT_AUTHOR_DATE: "2024-03-31T22:04:50.119Z", - GIT_COMMITTER_NAME: "test", - GIT_COMMITTER_EMAIL: "test@example.com", - GIT_COMMITTER_DATE: "2024-03-31T22:04:50.119Z", - }; - return spawnSync("git", ["-C", project.cwd, ...args], { - encoding: "utf-8", - windowsHide: true, - env, - }); - } - + using project = tempdirProject(); afterEach(() => { project.rm(); }); @@ -135,10 +135,11 @@ describe("Repository", function () { project.addFile("test.js", "console.log('hi!')"); project.write(); - git("init", "-b", "test"); - git("add", "."); - git("commit", "-m", "Test commit"); + git(project.cwd, "init", "-b", "test"); + git(project.cwd, "add", "."); + git(project.cwd, "commit", "-m", "Test commit"); git( + project.cwd, "remote", "add", "origin", @@ -162,25 +163,146 @@ describe("Repository", function () { }); }); -describe("RepositoryManager", () => { +describe("RepositoryManager - no git", () => {}); + +describe("RepositoryManager - git enabled", () => { + let fix: Project; const logger = new TestLogger(); - let manager: RepositoryManager; - - beforeEach(() => { - manager = new RepositoryManager( - "", - "revision", - "remote", - "{path}:{line}", - false, - logger, + const manager = new RepositoryManager( + "", + "revision", + "remote", + "link:{path}", + false, // disable git + logger, + ); + + before(() => { + function createRepo(path: string) { + git(path, "init", "-b", "test"); + git(path, "add", "."); + git(path, "commit", "-m", "Test commit"); + } + + fix = tempdirProject(); + fix.addFile("root.txt"); + fix.addFile(".gitignore", "/ignored"); + fix.addSymlink("self", "."); + fix.addSymlink("sub", "subfolder"); + fix.dir("subfolder", (dir) => { + dir.addFile("sub.txt"); + }); + fix.dir("ignored", (dir) => { + dir.addFile("ignored.txt"); + }); + fix.dir("sub_repo", (dir) => { + dir.addFile("repo.txt"); + }); + + fix.write(); + createRepo(join(fix.cwd, "sub_repo")); + createRepo(fix.cwd); + }); + after(() => { + fix.rm(); + }); + + afterEach(() => { + logger.expectNoOtherMessages(); + logger.reset(); + }); + + it("Handles the simplest case", () => { + const root = join(fix.cwd, "root.txt"); + const repo = manager.getRepository(root) as GitRepository; + ok(repo); + equal(repo.getURL(root, 1), "link:root.txt"); + equal( + repo.files, + new Set( + [ + ".gitignore", + "root.txt", + "self", + "sub", + "sub_repo", + "subfolder/sub.txt", + ].map((f) => normalizePath(join(fix.cwd, f))), + ), ); }); - it("Does not return a repository if the directory has already been checked", () => { - manager.ignoredPaths.add("/test"); - equal(manager.getRepository("/test/test.js"), undefined); + it("Handles a recursive self-symlink", () => { + const root = join(fix.cwd, "self/self/self/root.txt"); + const repo = manager.getRepository(root) as GitRepository; + ok(repo); + // Ideally, this would probably be link:root.txt, but I'll + // settle for not crashing right now. + equal(repo.getURL(root, 1), undefined); + equal( + repo.files, + new Set( + [ + ".gitignore", + "root.txt", + "self", + "sub", + "sub_repo", + "subfolder/sub.txt", + ].map((f) => normalizePath(join(fix.cwd, f))), + ), + ); + }); + + it("Handles a nested repository", () => { + const sub = join(fix.cwd, "sub_repo/repo.txt"); + const repo = manager.getRepository(sub) as GitRepository; + ok(repo); + equal(repo.path, normalizePath(join(fix.cwd, "sub_repo"))); + equal(repo.getURL(sub, 1), "link:repo.txt"); + equal(repo.files.size, 1); + }); + + it("Caches repositories", () => { + // Load cache + for (const path in [ + "root.txt", + "sub_repo/repo.txt", + "ignored/ignored.txt", + "subfolder/sub.txt", + ]) { + manager.getRepository(join(fix.cwd, path)); + } + + const root = join(fix.cwd, "root.txt"); + const rootIndirect = join(fix.cwd, "self/self/self/root.txt"); + const subfolder = join(fix.cwd, "subfolder/sub.txt"); + const repo = manager.getRepository(root) as GitRepository; + const repo2 = manager.getRepository(rootIndirect) as GitRepository; + const repo3 = manager.getRepository(subfolder) as GitRepository; + ok(repo === repo2); + ok(repo === repo3); + + const sub = join(fix.cwd, "sub_repo/repo.txt"); + const subRepo = manager.getRepository(sub) as GitRepository; + const subRepo2 = manager.getRepository(sub) as GitRepository; + ok(subRepo === subRepo2); + + equal( + manager["cache"], + new Map([ + [normalizePath(fix.cwd), repo], + [normalizePath(join(fix.cwd, "self/self/self")), repo], + [normalizePath(join(fix.cwd, "sub_repo")), subRepo], + [normalizePath(join(fix.cwd, "subfolder")), repo], + ]), + ); }); - it("Uses the existing repository if one exists"); + it("Handles .gitignored paths", () => { + const ign = join(fix.cwd, "ignored/ignored.txt"); + const repo = manager.getRepository(ign); + equal(repo?.path, normalizePath(fix.cwd)); + equal(repo.getURL(ign, 1), undefined); + }); }); diff --git a/src/test/packages.test.ts b/src/test/packages.test.ts index e40a568a3..6b10befc5 100644 --- a/src/test/packages.test.ts +++ b/src/test/packages.test.ts @@ -8,11 +8,7 @@ import { TestLogger } from "./TestLogger"; import { createMinimatch } from "../lib/utils/paths"; describe("Packages support", () => { - let project: ReturnType; - - beforeEach(() => { - project = tempdirProject(); - }); + using project = tempdirProject(); afterEach(() => { project.rm(); diff --git a/src/test/slow/entry-point.test.ts b/src/test/slow/entry-point.test.ts index 14b7837ea..c44f805af 100644 --- a/src/test/slow/entry-point.test.ts +++ b/src/test/slow/entry-point.test.ts @@ -1,20 +1,24 @@ -import { tempdirProject } from "@typestrong/fs-fixture-builder"; +import { type Project, tempdirProject } from "@typestrong/fs-fixture-builder"; import { deepStrictEqual as equal, ok } from "assert"; import { join } from "path"; import { Application, EntryPointStrategy } from "../.."; -const fixture = tempdirProject(); -fixture.addJsonFile("tsconfig.json", { - include: ["."], -}); -fixture.addJsonFile("package.json", { - main: "index.ts", -}); -fixture.addFile("index.ts", "export function fromIndex() {}"); -fixture.addFile("extra.ts", "export function extra() {}"); - describe("Entry Points", () => { + let fixture: Project; + let tsconfig: string; + beforeEach(() => { + fixture = tempdirProject(); + tsconfig = join(fixture.cwd, "tsconfig.json"); + + fixture.addJsonFile("tsconfig.json", { + include: ["."], + }); + fixture.addJsonFile("package.json", { + main: "index.ts", + }); + fixture.addFile("index.ts", "export function fromIndex() {}"); + fixture.addFile("extra.ts", "export function extra() {}"); fixture.write(); }); @@ -22,8 +26,6 @@ describe("Entry Points", () => { fixture.rm(); }); - const tsconfig = join(fixture.cwd, "tsconfig.json"); - it("Supports expanding existing paths", async () => { const app = await Application.bootstrap({ tsconfig, diff --git a/src/test/utils/fs.test.ts b/src/test/utils/fs.test.ts index bc0c52709..0d0a95133 100644 --- a/src/test/utils/fs.test.ts +++ b/src/test/utils/fs.test.ts @@ -6,15 +6,6 @@ import { basename, dirname, resolve, normalize } from "path"; import { getCommonDirectory, glob } from "../../lib/utils/fs"; describe("fs.ts", () => { - let fix: Project; - beforeEach(() => { - fix = tempdirProject(); - }); - - afterEach(() => { - fix.rm(); - }); - describe("getCommonDirectory", () => { it("Returns the empty string if no files are provided", () => { equal(getCommonDirectory([]), ""); @@ -38,6 +29,14 @@ describe("fs.ts", () => { }); describe("glob", () => { + let fix: Project; + beforeEach(() => { + fix = tempdirProject(); + }); + afterEach(() => { + fix.rm(); + }); + it("handles root match", () => { fix.write(); diff --git a/src/test/utils/options/readers/tsconfig.test.ts b/src/test/utils/options/readers/tsconfig.test.ts index 783a087a7..9bf5cf80b 100644 --- a/src/test/utils/options/readers/tsconfig.test.ts +++ b/src/test/utils/options/readers/tsconfig.test.ts @@ -29,7 +29,6 @@ describe("Options - TSConfigReader", () => { if (noErrors) { logger.expectNoOtherMessages(); } - project.rm(); } it("Errors if the file cannot be found", async () => { @@ -46,8 +45,7 @@ describe("Options - TSConfigReader", () => { function testError(name: string, file: object) { it(name, async () => { - const project = tempdirProject(); - after(() => project.rm()); + using project = tempdirProject(); project.addJsonFile("tsconfig.json", file); await readWithProject(project, true, false); equal(logger.hasErrors(), true, "No error was logged"); @@ -77,7 +75,7 @@ describe("Options - TSConfigReader", () => { }); it("Errors if a tsconfig file cannot be parsed", async () => { - const project = tempdirProject(); + using project = tempdirProject(); project.addFile("tsconfig.json", '{"test}'); await readWithProject(project, true, false); logger.expectMessage("error: *"); @@ -102,7 +100,7 @@ describe("Options - TSConfigReader", () => { }); it("Reads typedocOptions from extended tsconfig files", async () => { - const project = tempdirProject(); + using project = tempdirProject(); project.addFile("file.ts", "export const abc = 123"); project.addJsonFile("tsconfig.json", { extends: ["./base.tsconfig.json"], @@ -120,7 +118,7 @@ describe("Options - TSConfigReader", () => { }); async function readTsconfig(tsconfig: object) { - const project = tempdirProject(); + using project = tempdirProject(); project.addFile("file.ts", "export const abc = 123"); project.addJsonFile("tsconfig.json", tsconfig); @@ -148,7 +146,7 @@ describe("Options - TSConfigReader", () => { }); it("Does not set excludeInternal by stripInternal if already set", async () => { - const project = tempdirProject(); + using project = tempdirProject(); project.addJsonFile("tsconfig.json", { compilerOptions: { stripInternal: true }, }); @@ -160,7 +158,7 @@ describe("Options - TSConfigReader", () => { }); it("Correctly handles folder names ending with .json (#1712)", async () => { - const project = tempdirProject(); + using project = tempdirProject(); project.addJsonFile("tsconfig.json", { compilerOptions: { strict: true }, }); @@ -169,7 +167,7 @@ describe("Options - TSConfigReader", () => { }); async function testTsdoc(tsdoc: object, cb?: () => void, reset = true) { - const project = tempdirProject(); + using project = tempdirProject(); project.addFile("file.ts", "export const abc = 123"); project.addJsonFile("tsconfig.json", {}); project.addJsonFile("tsdoc.json", tsdoc); @@ -241,7 +239,7 @@ describe("Options - TSConfigReader", () => { }); it("Handles extends in tsdoc.json", async () => { - const project = tempdirProject(); + using project = tempdirProject(); project.addFile("file.ts", "export const abc = 123"); project.addJsonFile("tsconfig.json", {}); project.addJsonFile("tsdoc.json", { extends: ["./tsdoc2.json"] }); diff --git a/src/test/utils/options/readers/typedoc.test.ts b/src/test/utils/options/readers/typedoc.test.ts index 00feb45f1..a3a4a34f2 100644 --- a/src/test/utils/options/readers/typedoc.test.ts +++ b/src/test/utils/options/readers/typedoc.test.ts @@ -39,10 +39,10 @@ describe("Options - TypeDocReader", () => { const logger = new TestLogger(); project.write(); + after(() => project.rm()); options.reset(); options.setValue("options", project.cwd); await options.read(logger); - project.rm(); logger.expectNoOtherMessages(); equal(options.getValue("name"), "extends"); @@ -55,10 +55,10 @@ describe("Options - TypeDocReader", () => { const logger = new TestLogger(); project.write(); + after(() => project.rm()); options.reset(); options.setValue("options", project.cwd); await options.read(logger); - project.rm(); logger.expectNoOtherMessages(); equal(options.getValue("name"), "js"); @@ -94,8 +94,8 @@ describe("Options - TypeDocReader", () => { options.setValue("options", project.cwd); const logger = new TestLogger(); project.write(); + after(() => project.rm()); await options.read(logger); - project.rm(); logger.expectMessage(message); }); } @@ -158,6 +158,7 @@ describe("Options - TypeDocReader", () => { "export default { pretty: false }", ); project.write(); + after(() => project.rm()); const logger = new TestLogger(); const options = new Options(new Internationalization(null).proxy); @@ -166,13 +167,13 @@ describe("Options - TypeDocReader", () => { await options.read(logger); equal(logger.hasErrors(), false); - project.rm(); }); it("Handles errors when reading config files", async () => { const project = fsProject("errors"); project.addFile("typedoc.config.mjs", "throw new Error('hi')"); project.write(); + after(() => project.rm()); const logger = new TestLogger(); const options = new Options(new Internationalization(null).proxy); @@ -180,7 +181,6 @@ describe("Options - TypeDocReader", () => { options.addReader(new TypeDocReader()); await options.read(logger); - project.rm(); logger.expectMessage( "error: Failed to parse */typedoc.config.mjs, ensure it exists and exports an object", ); @@ -191,6 +191,7 @@ describe("Options - TypeDocReader", () => { const project = fsProject("errors2"); project.addFile("typedoc.config.cjs", "throw 123"); project.write(); + after(() => project.rm()); const logger = new TestLogger(); const options = new Options(new Internationalization(null).proxy); @@ -198,7 +199,6 @@ describe("Options - TypeDocReader", () => { options.addReader(new TypeDocReader()); await options.read(logger); - project.rm(); logger.expectMessage( "error: Failed to parse */typedoc.config.cjs, ensure it exists and exports an object", ); diff --git a/src/test/utils/base-path.test.ts b/src/test/utils/path.test.ts similarity index 53% rename from src/test/utils/base-path.test.ts rename to src/test/utils/path.test.ts index a58e76614..70c84df3d 100644 --- a/src/test/utils/base-path.test.ts +++ b/src/test/utils/path.test.ts @@ -1,27 +1,27 @@ import { equal } from "assert"; -import { BasePath } from "../../lib/converter/utils/base-path"; +import { normalizePath } from "../../lib/utils"; -describe("BasePath.normalize", () => { +describe("normalizePath", () => { const winTest = process.platform === "win32" ? it : it.skip; const nixTest = process.platform === "win32" ? it.skip : it; winTest("Returns paths with forward slashes", () => { equal( - BasePath.normalize("test\\test\\another/forward"), + normalizePath("test\\test\\another/forward"), "test/test/another/forward", ); }); winTest("Normalizes drive letters", () => { - equal(BasePath.normalize("c:\\foo"), "C:/foo"); - equal(BasePath.normalize("D:/foo"), "D:/foo"); + equal(normalizePath("c:\\foo"), "C:/foo"); + equal(normalizePath("D:/foo"), "D:/foo"); }); winTest("Checks for unix style paths", () => { - equal(BasePath.normalize("/c/users/you"), "C:/users/you"); + equal(normalizePath("/c/users/you"), "C:/users/you"); }); nixTest("Returns the original path", () => { - equal(BasePath.normalize("/c/users\\foo"), "/c/users\\foo"); + equal(normalizePath("/c/users\\foo"), "/c/users\\foo"); }); }); diff --git a/src/test/utils/plugins.test.ts b/src/test/utils/plugins.test.ts index c68b81baa..17705534c 100644 --- a/src/test/utils/plugins.test.ts +++ b/src/test/utils/plugins.test.ts @@ -1,4 +1,4 @@ -import { type Project, tempdirProject } from "@typestrong/fs-fixture-builder"; +import { tempdirProject } from "@typestrong/fs-fixture-builder"; import type { Application } from "../../index"; import { loadPlugins } from "../../lib/utils/plugins"; import { TestLogger } from "../TestLogger"; @@ -6,21 +6,16 @@ import { join, resolve } from "path"; import { Internationalization } from "../../lib/internationalization/internationalization"; describe("loadPlugins", () => { - let project: Project; let logger: TestLogger; const fakeApp = { i18n: new Internationalization(null).proxy, } as any as Application; beforeEach(() => { - project = tempdirProject(); logger = fakeApp.logger = new TestLogger(); }); - afterEach(() => { - project.rm(); - }); - it("Should support loading a basic plugin", async () => { + using project = tempdirProject(); project.addJsonFile("package.json", { type: "commonjs", main: "index.js", @@ -34,6 +29,7 @@ describe("loadPlugins", () => { }); it("Should support loading a ESM plugin", async () => { + using project = tempdirProject(); project.addJsonFile("package.json", { type: "module", main: "index.js", @@ -47,6 +43,7 @@ describe("loadPlugins", () => { }); it("Should handle errors when requiring plugins", async () => { + using project = tempdirProject(); project.addJsonFile("package.json", { type: "commonjs", main: "index.js", @@ -60,6 +57,7 @@ describe("loadPlugins", () => { }); it("Should handle errors when loading plugins", async () => { + using project = tempdirProject(); project.addJsonFile("package.json", { type: "commonjs", main: "index.js", @@ -76,6 +74,7 @@ describe("loadPlugins", () => { }); it("Should handle plugins without a load method", async () => { + using project = tempdirProject(); project.addJsonFile("package.json", { type: "commonjs", main: "index.js",