From b314447e977b9f053499af7b45a7899e0f575cb4 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 3 Nov 2026 14:45:17 +0100 Subject: [PATCH 1/3] test: share writeFixture helper across unit tests check-runtime-boundary.test.ts had a local writeFixture that creates parent directories and writes a file at a slash-separated relative path, while preview-extension-brand.test.ts (#14784) did the same by hand with 20 direct Deno.writeTextFileSync calls. Move writeFixture to tests/utils.ts and route every fixture write in both files through it. The three inline project fixtures in preview-extension-brand.test.ts now call writeProject, which writes the same files. The tests scope of the runtime boundary baseline drops from 895 back to 875. --- tests/unit/preview-extension-brand.test.ts | 58 +++++++------------ .../unit/tools/check-runtime-boundary.test.ts | 9 +-- tests/utils.ts | 9 +++ tools/runtime-boundary-baseline.json | 2 +- 4 files changed, 33 insertions(+), 45 deletions(-) diff --git a/tests/unit/preview-extension-brand.test.ts b/tests/unit/preview-extension-brand.test.ts index 89ec76a5bfe..8e214f79fc9 100644 --- a/tests/unit/preview-extension-brand.test.ts +++ b/tests/unit/preview-extension-brand.test.ts @@ -15,7 +15,6 @@ import { unitTest } from "../test.ts"; import { assert, assertEquals, assertRejects } from "../asserts.ts"; -import { ensureDirSync } from "../../src/deno_ral/fs.ts"; import { join } from "../../src/deno_ral/path.ts"; import { LightDarkBrandDarkFlag } from "../../src/core/brand/brand.ts"; import { singleFileProjectContext } from "../../src/project/types/single-file/single-file.ts"; @@ -28,7 +27,7 @@ import { notebookContext } from "../../src/render/notebook/notebook-context.ts"; import { renderServices } from "../../src/command/render/render-services.ts"; import { RenderOptions } from "../../src/command/render/types.ts"; import { initYamlIntelligenceResourcesFromFilesystem } from "../../src/core/schema/utils.ts"; -import { withTempDir } from "../utils.ts"; +import { withTempDir, writeFixture } from "../utils.ts"; const BRAND_YML = `color: palette: @@ -56,13 +55,13 @@ function writeExtension( projectMetadata: string, name = "test-extension", ) { - const extensionDir = join(dir, "_extensions", name); - ensureDirSync(extensionDir); - Deno.writeTextFileSync( - join(extensionDir, "_extension.yml"), + const extensionDir = `_extensions/${name}`; + writeFixture( + dir, + `${extensionDir}/_extension.yml`, extensionYml(projectMetadata), ); - Deno.writeTextFileSync(join(extensionDir, "brand.yml"), BRAND_YML); + writeFixture(dir, `${extensionDir}/brand.yml`, BRAND_YML); } // The palette entry is unique to the extension's brand.yml, so asserting it @@ -92,7 +91,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, " brand: brand.yml\n"); // preview builds the context this way: no renderOptions. @@ -120,11 +119,7 @@ unitTest( let project; try { - Deno.writeTextFileSync( - join(tmpDir, "_quarto.yml"), - "project:\n type: default\n", - ); - Deno.writeTextFileSync(join(tmpDir, "index.qmd"), DOCUMENT); + writeProject(tmpDir); writeExtension(tmpDir, " brand: brand.yml\n"); // preview builds the context this way: no renderOptions. @@ -155,7 +150,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, " brand: brand.yml\n"); // render builds the context this way: renderOptions present. @@ -187,7 +182,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, ' output-dir: "_out"\n'); project = await singleFileProjectContext( @@ -224,7 +219,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, ' output-dir: "_out"\n'); project = await singleFileProjectContext(file, notebookContext()); @@ -256,7 +251,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, MALFORMED_METADATA); // preview and inspect must degrade to "metadata not applied" here. @@ -283,11 +278,7 @@ unitTest( let project; try { - Deno.writeTextFileSync( - join(tmpDir, "_quarto.yml"), - "project:\n type: default\n", - ); - Deno.writeTextFileSync(join(tmpDir, "index.qmd"), DOCUMENT); + writeProject(tmpDir); writeExtension(tmpDir, MALFORMED_METADATA); project = await projectContext(tmpDir, notebookContext()); @@ -319,7 +310,7 @@ unitTest( const renderOptions = { services, flags: {} } as RenderOptions; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, MALFORMED_METADATA); await assertRejects( @@ -348,11 +339,7 @@ unitTest( const renderOptions = { services, flags: {} } as RenderOptions; try { - Deno.writeTextFileSync( - join(tmpDir, "_quarto.yml"), - "project:\n type: default\n", - ); - Deno.writeTextFileSync(join(tmpDir, "index.qmd"), DOCUMENT); + writeProject(tmpDir); writeExtension(tmpDir, MALFORMED_METADATA); await assertRejects( @@ -380,7 +367,7 @@ unitTest( let project; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, MALFORMED_METADATA, "broken-extension"); writeExtension(tmpDir, " brand: brand.yml\n", "brand-extension"); @@ -425,11 +412,8 @@ async function recordWarnings(run: () => Promise): Promise { } function writeProject(dir: string) { - Deno.writeTextFileSync( - join(dir, "_quarto.yml"), - "project:\n type: default\n", - ); - Deno.writeTextFileSync(join(dir, "index.qmd"), DOCUMENT); + writeFixture(dir, "_quarto.yml", "project:\n type: default\n"); + writeFixture(dir, "index.qmd", DOCUMENT); } unitTest( @@ -532,7 +516,7 @@ unitTest( previous = await projectContext(tmpDir, nbContext); assert(previous !== undefined, "projectContext must resolve"); - Deno.writeTextFileSync(join(tmpDir, "_quarto.yml"), "project: [\n"); + writeFixture(tmpDir, "_quarto.yml", "project: [\n"); const refreshed = await refreshProjectContext( previous, @@ -584,7 +568,7 @@ unitTest( await withTempDir(async (tmpDir) => { const file = join(tmpDir, "test.qmd"); - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, MALFORMED_METADATA, "fatal-single-extension"); await assertRejects( @@ -609,7 +593,7 @@ unitTest( let project: ProjectContext | undefined; try { - Deno.writeTextFileSync(file, DOCUMENT); + writeFixture(tmpDir, "test.qmd", DOCUMENT); writeExtension(tmpDir, MALFORMED_METADATA, "message-extension"); const warnings = await recordWarnings(async () => { diff --git a/tests/unit/tools/check-runtime-boundary.test.ts b/tests/unit/tools/check-runtime-boundary.test.ts index c8f5f9c21b9..3ee4f97a53e 100644 --- a/tests/unit/tools/check-runtime-boundary.test.ts +++ b/tests/unit/tools/check-runtime-boundary.test.ts @@ -12,7 +12,8 @@ import { unitTest } from "../../test.ts"; import { assert, assertEquals, assertRejects, assertThrows } from "../../asserts.ts"; import { join, resolve } from "../../../src/deno_ral/path.ts"; import { quartoDevBinCmd } from "../../quarto-cmd.ts"; -import { ensureDirSync, safeRemoveSync } from "../../../src/deno_ral/fs.ts"; +import { safeRemoveSync } from "../../../src/deno_ral/fs.ts"; +import { writeFixture } from "../../utils.ts"; import { execProcess } from "../../../src/core/process.ts"; import { createTempContext } from "../../../src/core/temp.ts"; import { @@ -43,12 +44,6 @@ const DENO = ["De", "no"].join("") + "."; const FROM = ["fr", "om"].join(""); const IMPORT = ["im", "port"].join(""); -function writeFixture(root: string, rel: string, contents: string) { - const parts = rel.split("/"); - ensureDirSync(join(root, ...parts.slice(0, -1))); - Deno.writeTextFileSync(join(root, ...parts), contents); -} - function makeRepo(root: string) { writeFixture(root, "src/quarto.ts", "export {};\n"); // src: 2 calls, 1 std file (import-map name, double quotes) diff --git a/tests/utils.ts b/tests/utils.ts index dd01ff6b18f..a9bbf6a36c9 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -11,6 +11,7 @@ import { kMetadataFormat, kOutputExt, kOutputFile } from "../src/config/constant import { pathWithForwardSlashes, safeExistsSync } from "../src/core/path.ts"; import { readYaml } from "../src/core/yaml.ts"; import { isWindows } from "../src/deno_ral/platform.ts"; +import { ensureDirSync } from "../src/deno_ral/fs.ts"; import { originalRealPathSync } from "../src/deno_ral/original-real-path.ts"; import { bookOutputStem } from "../src/project/types/book/book-shared.ts"; import { ProjectConfig } from "../src/project/types.ts"; @@ -33,6 +34,14 @@ export async function withTempDir( } } +// Write contents at the slash-separated path rel under root, creating any +// missing parent directories. +export function writeFixture(root: string, rel: string, contents: string) { + const parts = rel.split("/"); + ensureDirSync(join(root, ...parts.slice(0, -1))); + Deno.writeTextFileSync(join(root, ...parts), contents); +} + // Run fn from dir and restore the process cwd afterward. export async function withCwd( dir: string, diff --git a/tools/runtime-boundary-baseline.json b/tools/runtime-boundary-baseline.json index e817208d66f..729d34db7b6 100644 --- a/tools/runtime-boundary-baseline.json +++ b/tools/runtime-boundary-baseline.json @@ -16,7 +16,7 @@ "stdImportFiles": 3 }, "tests": { - "denoCalls": 895, + "denoCalls": 875, "stdImportFiles": 176 } } From aa5136ba31ebfaba3fd5ac2947544932ed22c5f8 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 3 Nov 2026 14:51:13 +0100 Subject: [PATCH 2/3] test: add writeDefaultProject helper for default-type project fixtures Twenty test sites wrote the same default-type _quarto.yml by hand, sixteen of them in brand.test.ts's repeated setups. Add writeDefaultProject to tests/utils.ts on top of writeFixture and use it in brand.test.ts, check-info-project.test.ts, preview-brand-cache.test.ts and preview-extension-brand.test.ts. Fixtures that write custom _quarto.yml content (a render list, a title, per-case content) keep their own writes. The tests scope of the runtime boundary baseline drops from 875 to 856. --- tests/smoke/check/check-info-project.test.ts | 3 +- tests/smoke/use/brand.test.ts | 33 ++++++++++---------- tests/unit/preview-brand-cache.test.ts | 11 ++----- tests/unit/preview-extension-brand.test.ts | 4 +-- tests/utils.ts | 5 +++ tools/runtime-boundary-baseline.json | 2 +- 6 files changed, 30 insertions(+), 28 deletions(-) diff --git a/tests/smoke/check/check-info-project.test.ts b/tests/smoke/check/check-info-project.test.ts index 2ddfe37b67a..58d4afcc431 100644 --- a/tests/smoke/check/check-info-project.test.ts +++ b/tests/smoke/check/check-info-project.test.ts @@ -9,6 +9,7 @@ */ import { dirname, join, resolve } from "../../../src/deno_ral/path.ts"; +import { writeDefaultProject } from "../../utils.ts"; import { execProcess } from "../../../src/core/process.ts"; import { originalRealPathSync } from "../../../src/deno_ral/original-real-path.ts"; import { isWindows } from "../../../src/deno_ral/platform.ts"; @@ -30,7 +31,7 @@ function tempDir(prefix: string): string { // The tree must exist at registration: the harness enters `cwd` before setup. function tempProject(): string { const dir = tempDir("quarto-check-info-project"); - Deno.writeTextFileSync(join(dir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(dir); Deno.mkdirSync(join(dir, "sub")); return dir; } diff --git a/tests/smoke/use/brand.test.ts b/tests/smoke/use/brand.test.ts index 1e21348674d..6f8c26c3722 100644 --- a/tests/smoke/use/brand.test.ts +++ b/tests/smoke/use/brand.test.ts @@ -1,6 +1,7 @@ import { testQuartoCmd, ExecuteOutput, Verify } from "../../test.ts"; import { fileExists, folderExists, noErrorsOrWarnings, printsMessage } from "../../verify.ts"; import { join, fromFileUrl, dirname } from "../../../src/deno_ral/path.ts"; +import { writeDefaultProject } from "../../utils.ts"; import { ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts"; import { pathWithForwardSlashes } from "../../../src/core/path.ts"; @@ -90,7 +91,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(basicDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(basicDir); return Promise.resolve(); }, cwd: () => basicDir, @@ -137,7 +138,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(dryRunDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(dryRunDir); return Promise.resolve(); }, cwd: () => dryRunDir, @@ -188,7 +189,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(forceOverwriteDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(forceOverwriteDir); // Create existing _brand directory with files const brandDir = join(forceOverwriteDir, "_brand"); ensureDirSync(brandDir); @@ -257,7 +258,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(dryRunOverwriteDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(dryRunOverwriteDir); // Create existing _brand directory with _brand.yml and extra.txt (not logo.png) const brandDir = join(dryRunOverwriteDir, "_brand"); ensureDirSync(brandDir); @@ -285,7 +286,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(errorFlagDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(errorFlagDir); return Promise.resolve(); }, cwd: () => errorFlagDir, @@ -336,7 +337,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(multiFileDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(multiFileDir); return Promise.resolve(); }, cwd: () => multiFileDir, @@ -384,7 +385,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(nestedDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(nestedDir); return Promise.resolve(); }, cwd: () => nestedDir, @@ -464,7 +465,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(nestedOverwriteDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(nestedOverwriteDir); // Create existing _brand/images directory with files const imagesDir = join(nestedOverwriteDir, "_brand", "images"); ensureDirSync(imagesDir); @@ -521,7 +522,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(dryRunNestedDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(dryRunNestedDir); // Create existing _brand/images directory with only logo.png (not header.png) const imagesDir = join(dryRunNestedDir, "_brand", "images"); ensureDirSync(imagesDir); @@ -565,7 +566,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(nestedNewSubdirDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(nestedNewSubdirDir); // Create _brand/ but NOT images/ subdirectory const brandDir = join(nestedNewSubdirDir, "_brand"); ensureDirSync(brandDir); @@ -610,7 +611,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(dryRunNewSubdirDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(dryRunNewSubdirDir); // Create _brand/ but NOT images/ subdirectory const brandDir = join(dryRunNewSubdirDir, "_brand"); ensureDirSync(brandDir); @@ -659,7 +660,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(emptyDirCleanupDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(emptyDirCleanupDir); // Create _brand/extras/ with a file not in source const extrasDir = join(emptyDirCleanupDir, "_brand", "extras"); ensureDirSync(extrasDir); @@ -708,7 +709,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(deepNestedCleanupDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(deepNestedCleanupDir); // Create _brand/deep/nested/path/ with a file not in source const deepDir = join(deepNestedCleanupDir, "_brand", "deep", "nested", "path"); ensureDirSync(deepDir); @@ -772,7 +773,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(brandExtDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(brandExtDir); return Promise.resolve(); }, cwd: () => brandExtDir, @@ -807,7 +808,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(brandExtDryRunDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(brandExtDryRunDir); return Promise.resolve(); }, cwd: () => brandExtDryRunDir, @@ -853,7 +854,7 @@ testQuartoCmd( ], { setup: () => { - Deno.writeTextFileSync(join(brandExtSubdirDir, "_quarto.yml"), "project:\n type: default\n"); + writeDefaultProject(brandExtSubdirDir); return Promise.resolve(); }, cwd: () => brandExtSubdirDir, diff --git a/tests/unit/preview-brand-cache.test.ts b/tests/unit/preview-brand-cache.test.ts index a77b4ff7760..ab0bee1cb6e 100644 --- a/tests/unit/preview-brand-cache.test.ts +++ b/tests/unit/preview-brand-cache.test.ts @@ -23,6 +23,7 @@ import { projectContext } from "../../src/project/project-context.ts"; import { notebookContext } from "../../src/render/notebook/notebook-context.ts"; import { initYamlIntelligenceResourcesFromFilesystem } from "../../src/core/schema/utils.ts"; import { safeRemoveSync } from "../../src/core/path.ts"; +import { writeDefaultProject } from "../utils.ts"; const BRAND_YML = `color: palette: @@ -108,10 +109,7 @@ unitTest( try { // A real multi-file project with a _quarto.yml, no brand declared. - Deno.writeTextFileSync( - join(tmpDir, "_quarto.yml"), - "project:\n type: default\n", - ); + writeDefaultProject(tmpDir); Deno.writeTextFileSync( join(tmpDir, "index.qmd"), "---\ntitle: test\nformat: typst\n---\n", @@ -150,10 +148,7 @@ unitTest( try { // A real multi-file project with a _quarto.yml and a _brand.yml present. - Deno.writeTextFileSync( - join(tmpDir, "_quarto.yml"), - "project:\n type: default\n", - ); + writeDefaultProject(tmpDir); Deno.writeTextFileSync( join(tmpDir, "index.qmd"), "---\ntitle: test\nformat: typst\n---\n", diff --git a/tests/unit/preview-extension-brand.test.ts b/tests/unit/preview-extension-brand.test.ts index 8e214f79fc9..b22b74d41c1 100644 --- a/tests/unit/preview-extension-brand.test.ts +++ b/tests/unit/preview-extension-brand.test.ts @@ -27,7 +27,7 @@ import { notebookContext } from "../../src/render/notebook/notebook-context.ts"; import { renderServices } from "../../src/command/render/render-services.ts"; import { RenderOptions } from "../../src/command/render/types.ts"; import { initYamlIntelligenceResourcesFromFilesystem } from "../../src/core/schema/utils.ts"; -import { withTempDir, writeFixture } from "../utils.ts"; +import { withTempDir, writeDefaultProject, writeFixture } from "../utils.ts"; const BRAND_YML = `color: palette: @@ -412,7 +412,7 @@ async function recordWarnings(run: () => Promise): Promise { } function writeProject(dir: string) { - writeFixture(dir, "_quarto.yml", "project:\n type: default\n"); + writeDefaultProject(dir); writeFixture(dir, "index.qmd", DOCUMENT); } diff --git a/tests/utils.ts b/tests/utils.ts index a9bbf6a36c9..103e283f32a 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -42,6 +42,11 @@ export function writeFixture(root: string, rel: string, contents: string) { Deno.writeTextFileSync(join(root, ...parts), contents); } +// Make dir a default-type project by writing its _quarto.yml. +export function writeDefaultProject(dir: string) { + writeFixture(dir, "_quarto.yml", "project:\n type: default\n"); +} + // Run fn from dir and restore the process cwd afterward. export async function withCwd( dir: string, diff --git a/tools/runtime-boundary-baseline.json b/tools/runtime-boundary-baseline.json index 729d34db7b6..7692258360a 100644 --- a/tools/runtime-boundary-baseline.json +++ b/tools/runtime-boundary-baseline.json @@ -16,7 +16,7 @@ "stdImportFiles": 3 }, "tests": { - "denoCalls": 875, + "denoCalls": 856, "stdImportFiles": 176 } } From deefa5409692aad2dc5448ada4928e007fcd3b10 Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 3 Nov 2026 15:14:27 +0100 Subject: [PATCH 3/3] test: write nested fixtures with writeFixture Many tests created a directory with ensureDirSync or Deno.mkdirSync only to write a file into it. writeFixture creates missing parent directories, so those pairs collapse into one call. Directories that matter on their own stay explicit: cwd targets, chmod'ed or empty directories, and roots that must exist before any file is written. Fixtures whose directory is later made unreadable (the locked/ directory) now get it from writeFixture; the mode before the chmod is the same. The tests scope of the runtime boundary baseline drops from 856 to 773. --- tests/smoke/axe/axe-exit-codes.test.ts | 15 ++++--- .../check/check-project-config-only.test.ts | 4 +- .../check-render-temp-in-project.test.ts | 7 ++-- .../project/project-unreadable-dir.test.ts | 6 +-- tests/smoke/use/brand.test.ts | 42 ++++++------------- tests/smoke/use/template-ai-config.test.ts | 32 ++++++++------ tests/unit/axe-artifacts.test.ts | 8 ++-- tests/unit/axe-mode-discovery.test.ts | 6 +-- tests/unit/core/deno-std-cache.test.ts | 10 ++--- tests/unit/path.test.ts | 8 ++-- tests/unit/project-cache.test.ts | 7 ++-- .../project-context-input-files.test.ts | 12 +++--- .../project-context-unreadable-dir.test.ts | 8 ++-- .../project-detect-denied-path.test.ts | 10 ++--- tests/unit/render/cleanup.test.ts | 9 ++-- tests/unit/sass-cache.test.ts | 5 +-- .../smoke-all-prerender-isolation.test.ts | 38 ++++++++--------- .../unit/tools/chrome-headless-shell.test.ts | 25 ++++------- tests/unit/typst-staging.test.ts | 19 ++++----- tests/utils.ts | 5 +-- tools/runtime-boundary-baseline.json | 2 +- 21 files changed, 117 insertions(+), 161 deletions(-) diff --git a/tests/smoke/axe/axe-exit-codes.test.ts b/tests/smoke/axe/axe-exit-codes.test.ts index ebadd870c0e..89e6e69b771 100644 --- a/tests/smoke/axe/axe-exit-codes.test.ts +++ b/tests/smoke/axe/axe-exit-codes.test.ts @@ -21,19 +21,20 @@ import { existsSync } from "../../../src/deno_ral/fs.ts"; import { join } from "../../../src/deno_ral/path.ts"; import { execProcess } from "../../../src/core/process.ts"; import { unitTest } from "../../test.ts"; +import { writeFixture } from "../../utils.ts"; import { quartoDevBinCmd, quartoSpawnEnvOptions } from "../../quarto-cmd.ts"; import { AxeFindings } from "../../../src/command/call/axe/schemas.ts"; const workingDir = Deno.makeTempDirSync({ prefix: "quarto-axe-exit" }); -Deno.mkdirSync(join(workingDir, "site")); -Deno.writeTextFileSync( - join(workingDir, "site", "index.html"), +writeFixture( + workingDir, + "site/index.html", ` exit codes

Exit codes

`, ); -Deno.writeTextFileSync(join(workingDir, "site", "dot.png"), "not-really-a-png"); +writeFixture(workingDir, "site/dot.png", "not-really-a-png"); function findings(): AxeFindings { return JSON.parse( @@ -158,10 +159,8 @@ unitTest( const notABrowser = join(workingDir, "not-a-browser"); Deno.writeTextFileSync(notABrowser, ""); // a previous scan's summary must not survive to be read as current - const artifacts = join(workingDir, "_axe-checks"); - Deno.mkdirSync(artifacts, { recursive: true }); - const staleFindings = join(artifacts, "findings.json"); - Deno.writeTextFileSync(staleFindings, `{"stale": true}`); + writeFixture(workingDir, "_axe-checks/findings.json", `{"stale": true}`); + const staleFindings = join(workingDir, "_axe-checks", "findings.json"); const result = await execProcess({ cmd: quartoDevBinCmd(), diff --git a/tests/smoke/check/check-project-config-only.test.ts b/tests/smoke/check/check-project-config-only.test.ts index bcefe567d36..78a84be3f1a 100644 --- a/tests/smoke/check/check-project-config-only.test.ts +++ b/tests/smoke/check/check-project-config-only.test.ts @@ -15,6 +15,7 @@ import { makeUnreadableDir, removeProject, tempProject, + writeFixture, } from "../../utils.ts"; import { assert, assertEquals } from "testing/asserts"; @@ -23,8 +24,7 @@ import { assert, assertEquals } from "testing/asserts"; if (canMakeUnreadableDir) { const projectDir = tempProject("quarto-check-project"); const locked = join(projectDir, "locked"); - Deno.mkdirSync(locked); - Deno.writeTextFileSync(join(locked, "doc.qmd"), "# Locked\n"); + writeFixture(projectDir, "locked/doc.qmd", "# Locked\n"); const output = join(projectDir, "check-info.json"); let restore: (() => void) | undefined; testQuartoCmdJson( diff --git a/tests/smoke/check/check-render-temp-in-project.test.ts b/tests/smoke/check/check-render-temp-in-project.test.ts index bc6985b3c51..d7dc17ead91 100644 --- a/tests/smoke/check/check-render-temp-in-project.test.ts +++ b/tests/smoke/check/check-render-temp-in-project.test.ts @@ -21,6 +21,7 @@ import { canMakeUnreadableDir, makeUnreadableDir, tempProject, + writeFixture, } from "../../utils.ts"; import { checkRender } from "../../../src/command/check/check-render.ts"; import { renderServices } from "../../../src/command/render/render-services.ts"; @@ -36,8 +37,7 @@ unitTest( async () => { const projectDir = tempProject("quarto-check-render-temp"); const locked = join(projectDir, "locked"); - Deno.mkdirSync(locked); - Deno.writeTextFileSync(join(locked, "doc.qmd"), "# Locked\n"); + writeFixture(projectDir, "locked/doc.qmd", "# Locked\n"); const tmp = join(projectDir, "tmp"); Deno.mkdirSync(tmp); const restore = makeUnreadableDir(locked); @@ -72,8 +72,7 @@ unitTest( await initYamlIntelligenceResourcesFromFilesystem(); const projectDir = tempProject("quarto-check-render-temp"); const locked = join(projectDir, "locked"); - Deno.mkdirSync(locked); - Deno.writeTextFileSync(join(locked, "doc.qmd"), "# Locked\n"); + writeFixture(projectDir, "locked/doc.qmd", "# Locked\n"); const restore = makeUnreadableDir(locked); const services = { ...renderServices(notebookContext()), diff --git a/tests/smoke/project/project-unreadable-dir.test.ts b/tests/smoke/project/project-unreadable-dir.test.ts index 7dcd9c6eacc..49eb48501db 100644 --- a/tests/smoke/project/project-unreadable-dir.test.ts +++ b/tests/smoke/project/project-unreadable-dir.test.ts @@ -16,6 +16,7 @@ import { makeUnreadableDir, removeProject, tempProject, + writeFixture, } from "../../utils.ts"; import { assert, assertEquals } from "testing/asserts"; @@ -42,11 +43,10 @@ function registerUnreadableDirTest( ) { const root = normalizePath(tempProject("quarto-unreadable-project")); if (quartoYml) { - Deno.writeTextFileSync(join(root, "_quarto.yml"), quartoYml); + writeFixture(root, "_quarto.yml", quartoYml); } const locked = join(root, "locked"); - Deno.mkdirSync(locked); - Deno.writeTextFileSync(join(locked, "doc.qmd"), "# Locked\n"); + writeFixture(root, "locked/doc.qmd", "# Locked\n"); let restore: (() => void) | undefined; testQuartoCmd(cmd, ["index.qmd"], [verify(root, locked)], { cwd: () => join(root, "sub"), diff --git a/tests/smoke/use/brand.test.ts b/tests/smoke/use/brand.test.ts index 6f8c26c3722..dea5240f1e7 100644 --- a/tests/smoke/use/brand.test.ts +++ b/tests/smoke/use/brand.test.ts @@ -1,7 +1,7 @@ import { testQuartoCmd, ExecuteOutput, Verify } from "../../test.ts"; import { fileExists, folderExists, noErrorsOrWarnings, printsMessage } from "../../verify.ts"; import { join, fromFileUrl, dirname } from "../../../src/deno_ral/path.ts"; -import { writeDefaultProject } from "../../utils.ts"; +import { writeDefaultProject, writeFixture } from "../../utils.ts"; import { ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts"; import { pathWithForwardSlashes } from "../../../src/core/path.ts"; @@ -191,12 +191,10 @@ testQuartoCmd( setup: () => { writeDefaultProject(forceOverwriteDir); // Create existing _brand directory with files - const brandDir = join(forceOverwriteDir, "_brand"); - ensureDirSync(brandDir); // This file exists in source - should be overwritten - Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old Brand\n"); + writeFixture(forceOverwriteDir, "_brand/_brand.yml", "meta:\n name: Old Brand\n"); // This file does NOT exist in source - should be preserved - Deno.writeTextFileSync(join(brandDir, "unrelated.txt"), "keep me"); + writeFixture(forceOverwriteDir, "_brand/unrelated.txt", "keep me"); return Promise.resolve(); }, cwd: () => forceOverwriteDir, @@ -260,10 +258,8 @@ testQuartoCmd( setup: () => { writeDefaultProject(dryRunOverwriteDir); // Create existing _brand directory with _brand.yml and extra.txt (not logo.png) - const brandDir = join(dryRunOverwriteDir, "_brand"); - ensureDirSync(brandDir); - Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old Brand\n"); - Deno.writeTextFileSync(join(brandDir, "extra.txt"), "extra file not in source"); + writeFixture(dryRunOverwriteDir, "_brand/_brand.yml", "meta:\n name: Old Brand\n"); + writeFixture(dryRunOverwriteDir, "_brand/extra.txt", "extra file not in source"); return Promise.resolve(); }, cwd: () => dryRunOverwriteDir, @@ -467,12 +463,10 @@ testQuartoCmd( setup: () => { writeDefaultProject(nestedOverwriteDir); // Create existing _brand/images directory with files - const imagesDir = join(nestedOverwriteDir, "_brand", "images"); - ensureDirSync(imagesDir); // This file exists in source - should be overwritten - Deno.writeTextFileSync(join(imagesDir, "logo.png"), "old logo\n"); + writeFixture(nestedOverwriteDir, "_brand/images/logo.png", "old logo\n"); // This file does NOT exist in source - should be preserved - Deno.writeTextFileSync(join(imagesDir, "unrelated.png"), "keep me nested"); + writeFixture(nestedOverwriteDir, "_brand/images/unrelated.png", "keep me nested"); return Promise.resolve(); }, cwd: () => nestedOverwriteDir, @@ -524,11 +518,9 @@ testQuartoCmd( setup: () => { writeDefaultProject(dryRunNestedDir); // Create existing _brand/images directory with only logo.png (not header.png) - const imagesDir = join(dryRunNestedDir, "_brand", "images"); - ensureDirSync(imagesDir); - Deno.writeTextFileSync(join(imagesDir, "logo.png"), "old logo\n"); + writeFixture(dryRunNestedDir, "_brand/images/logo.png", "old logo\n"); // Also create _brand.yml so we're only testing nested behavior - Deno.writeTextFileSync(join(dryRunNestedDir, "_brand", "_brand.yml"), "meta:\n name: Old\n"); + writeFixture(dryRunNestedDir, "_brand/_brand.yml", "meta:\n name: Old\n"); return Promise.resolve(); }, cwd: () => dryRunNestedDir, @@ -568,9 +560,7 @@ testQuartoCmd( setup: () => { writeDefaultProject(nestedNewSubdirDir); // Create _brand/ but NOT images/ subdirectory - const brandDir = join(nestedNewSubdirDir, "_brand"); - ensureDirSync(brandDir); - Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old Brand\n"); + writeFixture(nestedNewSubdirDir, "_brand/_brand.yml", "meta:\n name: Old Brand\n"); return Promise.resolve(); }, cwd: () => nestedNewSubdirDir, @@ -613,9 +603,7 @@ testQuartoCmd( setup: () => { writeDefaultProject(dryRunNewSubdirDir); // Create _brand/ but NOT images/ subdirectory - const brandDir = join(dryRunNewSubdirDir, "_brand"); - ensureDirSync(brandDir); - Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old\n"); + writeFixture(dryRunNewSubdirDir, "_brand/_brand.yml", "meta:\n name: Old\n"); return Promise.resolve(); }, cwd: () => dryRunNewSubdirDir, @@ -662,9 +650,7 @@ testQuartoCmd( setup: () => { writeDefaultProject(emptyDirCleanupDir); // Create _brand/extras/ with a file not in source - const extrasDir = join(emptyDirCleanupDir, "_brand", "extras"); - ensureDirSync(extrasDir); - Deno.writeTextFileSync(join(extrasDir, "orphan.txt"), "this file will be removed"); + writeFixture(emptyDirCleanupDir, "_brand/extras/orphan.txt", "this file will be removed"); return Promise.resolve(); }, cwd: () => emptyDirCleanupDir, @@ -711,9 +697,7 @@ testQuartoCmd( setup: () => { writeDefaultProject(deepNestedCleanupDir); // Create _brand/deep/nested/path/ with a file not in source - const deepDir = join(deepNestedCleanupDir, "_brand", "deep", "nested", "path"); - ensureDirSync(deepDir); - Deno.writeTextFileSync(join(deepDir, "orphan.txt"), "deeply nested orphan"); + writeFixture(deepNestedCleanupDir, "_brand/deep/nested/path/orphan.txt", "deeply nested orphan"); return Promise.resolve(); }, cwd: () => deepNestedCleanupDir, diff --git a/tests/smoke/use/template-ai-config.test.ts b/tests/smoke/use/template-ai-config.test.ts index 904cad28042..a0104830822 100644 --- a/tests/smoke/use/template-ai-config.test.ts +++ b/tests/smoke/use/template-ai-config.test.ts @@ -9,6 +9,7 @@ import { testQuartoCmd } from "../../test.ts"; import { fileExists, noErrorsOrWarnings, pathDoNotExists } from "../../verify.ts"; +import { writeFixture } from "../../utils.ts"; import { join } from "../../../src/deno_ral/path.ts"; import { ensureDirSync } from "../../../src/deno_ral/fs.ts"; @@ -16,29 +17,34 @@ const tempDir = Deno.makeTempDirSync(); // Create a mock template source with AI config files const templateSourceDir = join(tempDir, "template-source"); -ensureDirSync(templateSourceDir); -Deno.writeTextFileSync( - join(templateSourceDir, "template.qmd"), +writeFixture( + templateSourceDir, + "template.qmd", "---\ntitle: Template Document\n---\n\nThis is a template document." ); -Deno.writeTextFileSync( - join(templateSourceDir, "CLAUDE.md"), +writeFixture( + templateSourceDir, + "CLAUDE.md", "# Claude Configuration\n\nThis should be excluded." ); -Deno.writeTextFileSync( - join(templateSourceDir, "AGENTS.md"), +writeFixture( + templateSourceDir, + "AGENTS.md", "# Agents Configuration\n\nThis should be excluded." ); -Deno.writeTextFileSync( - join(templateSourceDir, "CLAUDE.local.md"), +writeFixture( + templateSourceDir, + "CLAUDE.local.md", "# Claude Local Configuration\n\nThis should be excluded." ); -Deno.writeTextFileSync( - join(templateSourceDir, "AGENTS.local.md"), +writeFixture( + templateSourceDir, + "AGENTS.local.md", "# Agents Local Configuration\n\nThis should be excluded." ); -Deno.writeTextFileSync( - join(templateSourceDir, "README.md"), +writeFixture( + templateSourceDir, + "README.md", "# Template README\n\nThis should also be excluded." ); diff --git a/tests/unit/axe-artifacts.test.ts b/tests/unit/axe-artifacts.test.ts index 8f690ffda91..6397a4d0998 100644 --- a/tests/unit/axe-artifacts.test.ts +++ b/tests/unit/axe-artifacts.test.ts @@ -14,7 +14,7 @@ import { unitTest } from "../test.ts"; import { assert, assertEquals } from "testing/asserts"; import { join, resolve } from "../../src/deno_ral/path.ts"; import { isMac, isWindows } from "../../src/deno_ral/platform.ts"; -import { withTempDir } from "../utils.ts"; +import { withTempDir, writeFixture } from "../utils.ts"; import { effectiveReportFile, reportDestinationError, @@ -90,8 +90,7 @@ unitTest( ); const fullDir = join(dir, "occupied"); - Deno.mkdirSync(fullDir); - Deno.writeTextFileSync(join(fullDir, "keep.txt"), "keep"); + writeFixture(dir, "occupied/keep.txt", "keep"); assert( reportDestinationError(fullDir) !== undefined, "a non-empty directory destination must be rejected", @@ -177,13 +176,12 @@ unitTest( await withTempDir(async (dir) => { const outputDir = join(dir, "_axe-checks"); const cellsDir = join(outputDir, "cells"); - Deno.mkdirSync(cellsDir, { recursive: true }); // A cell the previous scan wrote — this run would regenerate it while // scanning, then silently overwrite it with markdown once the report // is written, corrupting a raw per-cell axe payload. const existingCell = join(cellsDir, "index__1440x900__light.json"); - Deno.writeTextFileSync(existingCell, "{}"); + writeFixture(cellsDir, "index__1440x900__light.json", "{}"); assert( reportDestinationError(existingCell, [], cellsDir) !== undefined, "an existing cell file must be rejected as a --report destination", diff --git a/tests/unit/axe-mode-discovery.test.ts b/tests/unit/axe-mode-discovery.test.ts index 461653cf499..01aeb11d79b 100644 --- a/tests/unit/axe-mode-discovery.test.ts +++ b/tests/unit/axe-mode-discovery.test.ts @@ -15,7 +15,6 @@ import { unitTest } from "../test.ts"; import { assertEquals, assertThrows } from "testing/asserts"; import { join } from "../../src/deno_ral/path.ts"; -import { ensureDirSync } from "../../src/deno_ral/fs.ts"; import { applyThemesFilter, AxePage, @@ -24,7 +23,7 @@ import { sniffRedirectStub, } from "../../src/command/call/axe/discover.ts"; import { axeScanConfig } from "../../src/command/call/axe/config.ts"; -import { withTempDir } from "../utils.ts"; +import { withTempDir, writeFixture } from "../utils.ts"; // The inline before-body script, boiled down to its marker. Quarto emits it // iff the page has two modes. @@ -133,8 +132,7 @@ unitTest( /** A site dir on disk: every entry is a page written with static one-mode HTML. */ function writeSite(dir: string, pages: string[]) { for (const page of pages) { - ensureDirSync(join(dir, page, "..")); - Deno.writeTextFileSync(join(dir, page), kStaticHtml); + writeFixture(dir, page, kStaticHtml); } } diff --git a/tests/unit/core/deno-std-cache.test.ts b/tests/unit/core/deno-std-cache.test.ts index 4b0fca45572..ec3e514e573 100644 --- a/tests/unit/core/deno-std-cache.test.ts +++ b/tests/unit/core/deno-std-cache.test.ts @@ -5,17 +5,13 @@ */ import { ensureDirSync } from "../../../src/deno_ral/fs.ts"; -import { dirname, join } from "../../../src/deno_ral/path.ts"; +import { join } from "../../../src/deno_ral/path.ts"; import { directoryListingsMatch } from "../../../src/core/run/deno.ts"; import { unitTest } from "../../test.ts"; import { assertEquals } from "../../asserts.ts"; -import { withTempDir } from "../../utils.ts"; +import { withTempDir, writeFixture } from "../../utils.ts"; -const touch = (root: string, rel: string) => { - const file = join(root, rel); - ensureDirSync(dirname(file)); - Deno.writeTextFileSync(file, ""); -}; +const touch = (root: string, rel: string) => writeFixture(root, rel, ""); const populate = (root: string, rels: string[]) => { ensureDirSync(root); diff --git a/tests/unit/path.test.ts b/tests/unit/path.test.ts index 899a41102bb..2be987eae4f 100644 --- a/tests/unit/path.test.ts +++ b/tests/unit/path.test.ts @@ -18,7 +18,7 @@ import { skipHiddenWithin, } from "../../src/core/path.ts"; import { existsSync, walkSync } from "../../src/deno_ral/fs.ts"; -import { docs } from "../utils.ts"; +import { docs, writeFixture } from "../utils.ts"; const workingDir = Deno.makeTempDirSync({ prefix: "quarto-test" }); const emptyDir = join(workingDir, "empty"); @@ -42,8 +42,7 @@ unitTest("path - removeIfEmptyDir", async () => { removeIfEmptyDir(emptyDir); assert(!existsSync(emptyDir), "Empty directory was not removed"); - Deno.mkdirSync(emptyDir); - Deno.writeTextFileSync(join(emptyDir, "foo.txt"), "Hello World"); + writeFixture(workingDir, "empty/foo.txt", "Hello World"); removeIfEmptyDir(emptyDir); assert(existsSync(emptyDir), "Non-empty directory was removed"); @@ -124,8 +123,7 @@ unitTest("path - skipHiddenWithin only skips hidden paths below dir", async () = try { const root = join(tmp, ".parent", "proj"); for (const file of ["a.qmd", "x.y/b.qmd", ".git/c", "sub/.venv/d"]) { - Deno.mkdirSync(join(root, file, ".."), { recursive: true }); - Deno.writeTextFileSync(join(root, file), ""); + writeFixture(tmp, `.parent/proj/${file}`, ""); } for ( const dir of [root, root + SEP, root.replaceAll("\\", "/")] diff --git a/tests/unit/project-cache.test.ts b/tests/unit/project-cache.test.ts index 92864fa51dd..c4c87c5b667 100644 --- a/tests/unit/project-cache.test.ts +++ b/tests/unit/project-cache.test.ts @@ -14,11 +14,11 @@ import { assertStringIncludes, } from "../asserts.ts"; import { join } from "../../src/deno_ral/path.ts"; -import { ensureDirSync, existsSync } from "../../src/deno_ral/fs.ts"; +import { existsSync } from "../../src/deno_ral/fs.ts"; import { v8Branch } from "../../src/deno_ral/platform.ts"; import { md5HashBytes } from "../../src/core/hash.ts"; import { createProjectCache } from "../../src/core/cache/cache.ts"; -import { withTempDir } from "../utils.ts"; +import { withTempDir, writeFixture } from "../utils.ts"; import { noErrors, printsMessage } from "../verify.ts"; import { damageKvRow, @@ -35,9 +35,8 @@ unitTest( async () => { await withTempDir(async (scratch) => { const legacyDir = join(scratch, "project-cache"); - ensureDirSync(legacyDir); const legacy = join(legacyDir, "deno-kv-file"); - Deno.writeTextFileSync(legacy, LEGACY_SENTINEL); + writeFixture(legacyDir, "deno-kv-file", LEGACY_SENTINEL); const cache = await createProjectCache(scratch); try { diff --git a/tests/unit/project/project-context-input-files.test.ts b/tests/unit/project/project-context-input-files.test.ts index 5802618ecce..e569fcc3b91 100644 --- a/tests/unit/project/project-context-input-files.test.ts +++ b/tests/unit/project/project-context-input-files.test.ts @@ -10,13 +10,13 @@ import { unitTest } from "../../test.ts"; import { assert, assertEquals } from "testing/asserts"; -import { dirname, join, relative } from "../../../src/deno_ral/path.ts"; +import { join, relative } from "../../../src/deno_ral/path.ts"; import { normalizePath } from "../../../src/core/path.ts"; import { projectContext } from "../../../src/project/project-context.ts"; import { ProjectContext } from "../../../src/project/types.ts"; import { notebookContext } from "../../../src/render/notebook/notebook-context.ts"; import { initYamlIntelligenceResourcesFromFilesystem } from "../../../src/core/schema/utils.ts"; -import { docs, withTempDir } from "../../utils.ts"; +import { docs, withTempDir, writeFixture } from "../../utils.ts"; const kDoc = "---\ntitle: test\n---\n\nHello\n"; @@ -30,13 +30,11 @@ const kIgnored = [ ]; function writeProject(dir: string, quartoYml: string) { - Deno.writeTextFileSync(join(dir, "_quarto.yml"), quartoYml); + writeFixture(dir, "_quarto.yml", quartoYml); for (const file of [...kInputs, ...kIgnored]) { - const path = join(dir, file); - Deno.mkdirSync(dirname(path), { recursive: true }); - Deno.writeTextFileSync(path, kDoc); + writeFixture(dir, file, kDoc); } - Deno.writeTextFileSync(join(dir, "notes.txt"), "not an input"); + writeFixture(dir, "notes.txt", "not an input"); } function relativeInputs(context: ProjectContext) { diff --git a/tests/unit/project/project-context-unreadable-dir.test.ts b/tests/unit/project/project-context-unreadable-dir.test.ts index 5e42b367b9f..b495ba83a42 100644 --- a/tests/unit/project/project-context-unreadable-dir.test.ts +++ b/tests/unit/project/project-context-unreadable-dir.test.ts @@ -30,6 +30,7 @@ import { canMakeUnreadableDir, makeUnreadableDir, withTempDir, + writeFixture, } from "../../utils.ts"; function sessionTempDirs(root: string) { @@ -60,12 +61,11 @@ if (canMakeUnreadableDir) { await withTempDir(async (tmp) => { const root = normalizePath(tmp); if (quartoYml) { - Deno.writeTextFileSync(join(root, "_quarto.yml"), quartoYml); + writeFixture(root, "_quarto.yml", quartoYml); } - Deno.writeTextFileSync(join(root, "index.qmd"), "# Hello\n"); + writeFixture(root, "index.qmd", "# Hello\n"); const locked = join(root, "locked"); - Deno.mkdirSync(locked); - Deno.writeTextFileSync(join(locked, "doc.qmd"), "# Locked\n"); + writeFixture(root, "locked/doc.qmd", "# Locked\n"); const restore = makeUnreadableDir(locked); let walkError: unknown; try { diff --git a/tests/unit/project/project-detect-denied-path.test.ts b/tests/unit/project/project-detect-denied-path.test.ts index e0b510c18fd..ea9385bd42f 100644 --- a/tests/unit/project/project-detect-denied-path.test.ts +++ b/tests/unit/project/project-detect-denied-path.test.ts @@ -16,7 +16,7 @@ import { isWindows } from "../../../src/deno_ral/platform.ts"; import { projectContext } from "../../../src/project/project-context.ts"; import { notebookContext } from "../../../src/render/notebook/notebook-context.ts"; import { initYamlIntelligenceResourcesFromFilesystem } from "../../../src/core/schema/utils.ts"; -import { withTempDir } from "../../utils.ts"; +import { withTempDir, writeFixture } from "../../utils.ts"; // `config/` and `content/` at the root and `work/doc.qmd` below it, with no // _quarto.yml, so only the Hugo detectors can claim the root. @@ -24,14 +24,10 @@ function hugoLikeTree(tmp: string, configToml: boolean) { const root = normalizePath(tmp); Deno.mkdirSync(join(root, "config", "_default"), { recursive: true }); if (configToml) { - Deno.writeTextFileSync( - join(root, "config", "_default", "config.toml"), - "", - ); + writeFixture(root, "config/_default/config.toml", ""); } Deno.mkdirSync(join(root, "content")); - Deno.mkdirSync(join(root, "work")); - Deno.writeTextFileSync(join(root, "work", "doc.qmd"), "# Hello\n"); + writeFixture(root, "work/doc.qmd", "# Hello\n"); return root; } diff --git a/tests/unit/render/cleanup.test.ts b/tests/unit/render/cleanup.test.ts index f5c5e1a91ff..c060ee0cfb7 100644 --- a/tests/unit/render/cleanup.test.ts +++ b/tests/unit/render/cleanup.test.ts @@ -5,7 +5,8 @@ */ import { assert } from "testing/asserts"; -import { ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts"; +import { existsSync } from "../../../src/deno_ral/fs.ts"; +import { writeFixture } from "../../utils.ts"; import { join } from "../../../src/deno_ral/path.ts"; import { unitTest } from "../../test.ts"; import { renderCleanup } from "../../../src/command/render/cleanup.ts"; @@ -46,11 +47,9 @@ unitTest("renderCleanup - narrows ipynb supporting despite path separator mismat const filesDir = join(projectDir, "index_files"); const figureHtmlDir = join(filesDir, "figure-html"); const figureIpynbDir = join(filesDir, "figure-ipynb"); - ensureDirSync(figureHtmlDir); - ensureDirSync(figureIpynbDir); const figureHtmlPlot = join(figureHtmlDir, "plot.png"); - Deno.writeTextFileSync(figureHtmlPlot, "html-figure"); - Deno.writeTextFileSync(join(figureIpynbDir, "plot.png"), "ipynb-figure"); + writeFixture(figureHtmlDir, "plot.png", "html-figure"); + writeFixture(figureIpynbDir, "plot.png", "ipynb-figure"); // Engine reports the supporting dir with forward slashes (as knitr does). const supportingForwardSlash = filesDir.replaceAll("\\", "/"); diff --git a/tests/unit/sass-cache.test.ts b/tests/unit/sass-cache.test.ts index e75ae1f768b..3ea21e8a66a 100644 --- a/tests/unit/sass-cache.test.ts +++ b/tests/unit/sass-cache.test.ts @@ -23,7 +23,7 @@ import { v8Branch } from "../../src/deno_ral/platform.ts"; import { safeRemoveIfExists } from "../../src/core/path.ts"; import { createTempContext, TempContext } from "../../src/core/temp.ts"; import { sassCache } from "../../src/core/sass/cache.ts"; -import { withTempDir } from "../utils.ts"; +import { withTempDir, writeFixture } from "../utils.ts"; import { noErrors, printsMessage } from "../verify.ts"; import { md5HashAsync } from "../../src/core/hash.ts"; import { @@ -86,9 +86,8 @@ unitTest( async () => { await withTempDir(async (root) => { const cachePath = join(root, "sass"); - ensureDirSync(cachePath); const legacy = join(cachePath, "sass.kv"); - Deno.writeTextFileSync(legacy, LEGACY_SENTINEL); + writeFixture(cachePath, "sass.kv", LEGACY_SENTINEL); const temp = createTempContext(); try { diff --git a/tests/unit/smoke-all-prerender-isolation.test.ts b/tests/unit/smoke-all-prerender-isolation.test.ts index a7e163f548b..5c853e7c371 100644 --- a/tests/unit/smoke-all-prerender-isolation.test.ts +++ b/tests/unit/smoke-all-prerender-isolation.test.ts @@ -5,6 +5,7 @@ */ import { assert, assertEquals } from "testing/asserts"; import { unitTest } from "../test.ts"; +import { writeFixture } from "../utils.ts"; import { dirname, fromFileUrl, join } from "../../src/deno_ral/path.ts"; import { safeExistsSync } from "../../src/core/path.ts"; @@ -67,10 +68,10 @@ unitTest( try { const smokeAllDir = join(tmpRoot, "smoke-all"); const projectDir = join(smokeAllDir, "_prerender-crash"); - Deno.mkdirSync(projectDir, { recursive: true }); - Deno.writeTextFileSync( - join(projectDir, "_quarto.yml"), + writeFixture( + projectDir, + "_quarto.yml", "project:\n type: default\n render:\n" + " - broken-b.qmd\n - broken-c.qmd\n", ); @@ -78,8 +79,9 @@ unitTest( // This file does not request a project pre-render. It sorts before files // that do, but project.render excludes it. Its HTML test spec registers // cleanup paths for a skipped file. - Deno.writeTextFileSync( - join(projectDir, "broken-a.qmd"), + writeFixture( + projectDir, + "broken-a.qmd", [ "---", "title: a", @@ -96,20 +98,14 @@ unitTest( ); // Pre-create outputs for broken-a.qmd. Because project.render excludes // the file, their removal verifies cleanup for a skipped file. - Deno.writeTextFileSync( - join(projectDir, "broken-a.html"), - "sentinel\n", - ); + writeFixture(projectDir, "broken-a.html", "sentinel\n"); const supportDir = join(projectDir, "broken-a_files"); - Deno.mkdirSync(supportDir, { recursive: true }); - Deno.writeTextFileSync( - join(supportDir, "sentinel.txt"), - "sentinel\n", - ); + writeFixture(supportDir, "sentinel.txt", "sentinel\n"); // This file requests the project pre-render and makes it fail. - Deno.writeTextFileSync( - join(projectDir, "broken-b.qmd"), + writeFixture( + projectDir, + "broken-b.qmd", [ "---", "title: b", @@ -125,8 +121,9 @@ unitTest( ); // A second file requests the same project pre-render, verifying that it // runs once per project. - Deno.writeTextFileSync( - join(projectDir, "broken-c.qmd"), + writeFixture( + projectDir, + "broken-c.qmd", [ "---", "title: c", @@ -141,8 +138,9 @@ unitTest( // This healthy control file sorts after the broken project and must // still run. - Deno.writeTextFileSync( - join(smokeAllDir, "healthy-canary.qmd"), + writeFixture( + smokeAllDir, + "healthy-canary.qmd", ["---", "title: healthy", "---", "", "# healthy", ""].join("\n"), ); diff --git a/tests/unit/tools/chrome-headless-shell.test.ts b/tests/unit/tools/chrome-headless-shell.test.ts index 0b6df4fd6b4..559c6106d3e 100644 --- a/tests/unit/tools/chrome-headless-shell.test.ts +++ b/tests/unit/tools/chrome-headless-shell.test.ts @@ -5,7 +5,7 @@ */ import { unitTest } from "../../test.ts"; -import { withTempDir } from "../../utils.ts"; +import { withTempDir, writeFixture } from "../../utils.ts"; import { assert, assertEquals } from "testing/asserts"; import { join } from "../../../src/deno_ral/path.ts"; import { existsSync, safeRemoveSync } from "../../../src/deno_ral/fs.ts"; @@ -104,10 +104,8 @@ unitTest("isInstalled - returns false when only binary exists (no version file)" const { platform } = detectChromePlatform(); const binName = chromeHeadlessShellBinaryName(); // CfT layout (all platforms, including Playwright CDN arm64): chrome-headless-shell-{platform}/binary - const subdir = join(tempDir, `chrome-headless-shell-${platform}`); - Deno.mkdirSync(subdir); const target = isWindows ? `${binName}.exe` : binName; - Deno.writeTextFileSync(join(subdir, target), "fake"); + writeFixture(tempDir, `chrome-headless-shell-${platform}/${target}`, "fake"); assertEquals(isInstalled(tempDir), false); } finally { safeRemoveSync(tempDir, { recursive: true }); @@ -120,10 +118,8 @@ unitTest("isInstalled - returns true when version file and binary exist", async noteInstalledVersion(tempDir, "145.0.0.0"); const { platform } = detectChromePlatform(); const binName = chromeHeadlessShellBinaryName(); - const subdir = join(tempDir, `chrome-headless-shell-${platform}`); - Deno.mkdirSync(subdir); const target = isWindows ? `${binName}.exe` : binName; - Deno.writeTextFileSync(join(subdir, target), "fake"); + writeFixture(tempDir, `chrome-headless-shell-${platform}/${target}`, "fake"); assertEquals(isInstalled(tempDir), true); } finally { @@ -150,24 +146,19 @@ unitTest("isInstalled - returns true when version file and binary exist", async // ".exe" whenever the host is Windows, so the fixture matches that to exercise // the same code path off-Linux. function writeLegacyPlaywrightFixture(dir: string): string { - const legacyDir = join(dir, "chrome-linux"); - Deno.mkdirSync(legacyDir, { recursive: true }); const target = isWindows ? "headless_shell.exe" : "headless_shell"; - const path = join(legacyDir, target); - Deno.writeTextFileSync(path, "fake"); - return path; + writeFixture(dir, `chrome-linux/${target}`, "fake"); + return join(dir, "chrome-linux", target); } // Current CfT fixture: chrome-headless-shell-{platform}/chrome-headless-shell. function writeCftFixture(dir: string): string { const { platform } = detectChromePlatform(); const binName = chromeHeadlessShellBinaryName(); - const subdir = join(dir, `chrome-headless-shell-${platform}`); - Deno.mkdirSync(subdir, { recursive: true }); const target = isWindows ? `${binName}.exe` : binName; - const path = join(subdir, target); - Deno.writeTextFileSync(path, "fake"); - return path; + const rel = `chrome-headless-shell-${platform}/${target}`; + writeFixture(dir, rel, "fake"); + return join(dir, rel); } unitTest( diff --git a/tests/unit/typst-staging.test.ts b/tests/unit/typst-staging.test.ts index 0ced91a7700..679163a6e90 100644 --- a/tests/unit/typst-staging.test.ts +++ b/tests/unit/typst-staging.test.ts @@ -7,6 +7,7 @@ */ import { unitTest } from "../test.ts"; +import { writeFixture } from "../utils.ts"; import { assert, assertEquals } from "testing/asserts"; import { join } from "../../src/deno_ral/path.ts"; import { existsSync } from "../../src/deno_ral/fs.ts"; @@ -27,12 +28,12 @@ function createFakePackage( content = "default", ): string { const pkgDir = join(baseDir, namespace, name, version); - ensureDirSync(pkgDir); - Deno.writeTextFileSync( - join(pkgDir, "typst.toml"), + writeFixture( + pkgDir, + "typst.toml", `[package]\nname = "${name}"\nversion = "${version}"\n`, ); - Deno.writeTextFileSync(join(pkgDir, "lib.typ"), `// ${content}\n`); + writeFixture(pkgDir, "lib.typ", `// ${content}\n`); return pkgDir; } @@ -270,12 +271,10 @@ unitTest("stageSelectedPackages - preserves package contents", async () => { // Create package with subdirectory const pkgDir = join(source, "preview/cetz/0.4.1"); - ensureDirSync(pkgDir); - Deno.writeTextFileSync(join(pkgDir, "typst.toml"), '[package]\nname = "cetz"\n'); - Deno.writeTextFileSync(join(pkgDir, "lib.typ"), "// main\n"); - ensureDirSync(join(pkgDir, "src")); - Deno.writeTextFileSync(join(pkgDir, "src/draw.typ"), "// draw\n"); - Deno.writeTextFileSync(join(pkgDir, "src/canvas.typ"), "// canvas\n"); + writeFixture(pkgDir, "typst.toml", '[package]\nname = "cetz"\n'); + writeFixture(pkgDir, "lib.typ", "// main\n"); + writeFixture(pkgDir, "src/draw.typ", "// draw\n"); + writeFixture(pkgDir, "src/canvas.typ", "// canvas\n"); const needed: NeededPackage[] = [ { namespace: "preview", name: "cetz", version: "0.4.1" }, diff --git a/tests/utils.ts b/tests/utils.ts index 103e283f32a..18a48728988 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -327,9 +327,8 @@ export function makeUnreadableDir(dir: string): () => void { // temp dirs live under the /var -> /private/var symlink). export function tempProject(prefix: string): string { const dir = originalRealPathSync(Deno.makeTempDirSync({ prefix })); - Deno.writeTextFileSync(join(dir, "_quarto.yml"), "project:\n type: website\n"); - Deno.mkdirSync(join(dir, "sub")); - Deno.writeTextFileSync(join(dir, "sub", "index.qmd"), "# Hello\n"); + writeFixture(dir, "_quarto.yml", "project:\n type: website\n"); + writeFixture(dir, "sub/index.qmd", "# Hello\n"); return dir; } diff --git a/tools/runtime-boundary-baseline.json b/tools/runtime-boundary-baseline.json index 7692258360a..ff1d9deb21d 100644 --- a/tools/runtime-boundary-baseline.json +++ b/tools/runtime-boundary-baseline.json @@ -16,7 +16,7 @@ "stdImportFiles": 3 }, "tests": { - "denoCalls": 856, + "denoCalls": 773, "stdImportFiles": 176 } }