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 { ensureDirSync, existsSync } from "../../../src/deno_ral/fs.ts";
import { pathWithForwardSlashes } from "../../../src/core/path.ts";
function filesInSections(
expected: { overwrite?: string[]; create?: string[]; remove?: string[] },
dryRun: boolean
): Verify {
return {
name: "files in correct sections",
verify: (outputs: ExecuteOutput[]) => {
const overwriteHeader = dryRun ? "Would overwrite:" : "Overwritten:";
const createHeader = dryRun ? "Would create:" : "Created:";
const removeHeader = dryRun ? "Would remove:" : "Removed:";
const found: { overwrite: string[]; create: string[]; remove: string[] } = {
overwrite: [],
create: [],
remove: [],
};
let currentSection: "overwrite" | "create" | "remove" | null = null;
for (const output of outputs) {
const line = output.msg;
if (line.includes(overwriteHeader)) {
currentSection = "overwrite";
} else if (line.includes(createHeader)) {
currentSection = "create";
} else if (line.includes(removeHeader)) {
currentSection = "remove";
} else if (currentSection && line.trim().startsWith("- ")) {
const filename = line.trim().slice(2);
found[currentSection].push(pathWithForwardSlashes(filename));
}
}
for (const file of expected.overwrite ?? []) {
if (!found.overwrite.includes(pathWithForwardSlashes(file))) {
throw new Error(`Expected ${file} in overwrite section, found: [${found.overwrite.join(", ")}]`);
}
}
for (const file of expected.create ?? []) {
if (!found.create.includes(pathWithForwardSlashes(file))) {
throw new Error(`Expected ${file} in create section, found: [${found.create.join(", ")}]`);
}
}
for (const file of expected.remove ?? []) {
if (!found.remove.includes(pathWithForwardSlashes(file))) {
throw new Error(`Expected ${file} in remove section, found: [${found.remove.join(", ")}]`);
}
}
return Promise.resolve();
}
};
}
const tempDir = Deno.makeTempDirSync();
const testDir = dirname(fromFileUrl(import.meta.url));
const fixtureDir = join(testDir, "..", "use-brand");
const basicDir = join(tempDir, "basic");
ensureDirSync(basicDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(basicDir, "_brand")),
fileExists(join(basicDir, "_brand", "_brand.yml")),
fileExists(join(basicDir, "_brand", "logo.png")),
folderExists(join(basicDir, "_brand", "fonts")),
fileExists(join(basicDir, "_brand", "fonts", "custom-font.woff2")),
{
name: "README.md should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(basicDir, "_brand", "README.md"))) {
throw new Error("README.md should not be copied - it is not referenced in _brand.yml");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(basicDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => basicDir,
teardown: () => {
try { Deno.removeSync(basicDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - basic installation"
);
const dryRunDir = join(tempDir, "dry-run");
ensureDirSync(dryRunDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--dry-run"],
[
noErrorsOrWarnings,
printsMessage({ level: "INFO", regex: /Would create directory/ }),
filesInSections({ create: ["_brand.yml", "logo.png", "fonts/custom-font.woff2"] }, true),
{
name: "_brand directory should not exist in dry-run mode",
verify: () => {
const brandDir = join(dryRunDir, "_brand");
if (existsSync(brandDir)) {
throw new Error("_brand directory should not exist in dry-run mode");
}
return Promise.resolve();
}
},
{
name: "README.md should not be listed in dry-run output (unreferenced)",
verify: (outputs: ExecuteOutput[]) => {
for (const output of outputs) {
if (output.msg.includes("README.md")) {
throw new Error("README.md should not appear in dry-run output - it is not referenced in _brand.yml");
}
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(dryRunDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => dryRunDir,
teardown: () => {
try { Deno.removeSync(dryRunDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - dry-run mode"
);
const forceOverwriteDir = join(tempDir, "force-overwrite");
ensureDirSync(forceOverwriteDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force"],
[
noErrorsOrWarnings,
{
name: "_brand.yml should be overwritten with new content",
verify: () => {
const content = Deno.readTextFileSync(join(forceOverwriteDir, "_brand", "_brand.yml"));
if (content.includes("Old Brand")) {
throw new Error("_brand.yml should have been overwritten");
}
if (!content.includes("Basic Test Brand")) {
throw new Error("_brand.yml should contain new brand content");
}
return Promise.resolve();
}
},
fileExists(join(forceOverwriteDir, "_brand", "logo.png")),
{
name: "unrelated.txt should be removed",
verify: () => {
if (existsSync(join(forceOverwriteDir, "_brand", "unrelated.txt"))) {
throw new Error("unrelated.txt should have been removed");
}
return Promise.resolve();
}
},
filesInSections({ overwrite: ["_brand.yml"], create: ["logo.png"], remove: ["unrelated.txt"] }, false),
],
{
setup: () => {
Deno.writeTextFileSync(join(forceOverwriteDir, "_quarto.yml"), "project:\n type: default\n");
const brandDir = join(forceOverwriteDir, "_brand");
ensureDirSync(brandDir);
Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old Brand\n");
Deno.writeTextFileSync(join(brandDir, "unrelated.txt"), "keep me");
return Promise.resolve();
},
cwd: () => forceOverwriteDir,
teardown: () => {
try { Deno.removeSync(forceOverwriteDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - force overwrites existing, creates new, removes extra"
);
const dryRunOverwriteDir = join(tempDir, "dry-run-overwrite");
ensureDirSync(dryRunOverwriteDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--dry-run"],
[
noErrorsOrWarnings,
filesInSections({
overwrite: ["_brand.yml"],
create: ["logo.png"],
remove: ["extra.txt"]
}, true),
{
name: "_brand.yml should not be modified in dry-run",
verify: () => {
const content = Deno.readTextFileSync(join(dryRunOverwriteDir, "_brand", "_brand.yml"));
if (!content.includes("Old Brand")) {
throw new Error("_brand.yml should not be modified in dry-run mode");
}
return Promise.resolve();
}
},
{
name: "logo.png should not be created in dry-run",
verify: () => {
if (existsSync(join(dryRunOverwriteDir, "_brand", "logo.png"))) {
throw new Error("logo.png should not be created in dry-run mode");
}
return Promise.resolve();
}
},
{
name: "extra.txt should not be removed in dry-run",
verify: () => {
if (!existsSync(join(dryRunOverwriteDir, "_brand", "extra.txt"))) {
throw new Error("extra.txt should not be removed in dry-run mode");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(dryRunOverwriteDir, "_quarto.yml"), "project:\n type: default\n");
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");
return Promise.resolve();
},
cwd: () => dryRunOverwriteDir,
teardown: () => {
try { Deno.removeSync(dryRunOverwriteDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - dry-run reports overwrite vs create vs remove correctly"
);
const errorFlagDir = join(tempDir, "error-flags");
ensureDirSync(errorFlagDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force", "--dry-run"],
[
printsMessage({ level: "ERROR", regex: /Cannot use --force and --dry-run together/ }),
],
{
setup: () => {
Deno.writeTextFileSync(join(errorFlagDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => errorFlagDir,
teardown: () => {
try { Deno.removeSync(errorFlagDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - error on --force --dry-run"
);
const multiFileDir = join(tempDir, "multi-file");
ensureDirSync(multiFileDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "multi-file-brand"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(multiFileDir, "_brand")),
fileExists(join(multiFileDir, "_brand", "_brand.yml")),
fileExists(join(multiFileDir, "_brand", "logo.png")),
fileExists(join(multiFileDir, "_brand", "favicon.png")),
folderExists(join(multiFileDir, "_brand", "fonts")),
fileExists(join(multiFileDir, "_brand", "fonts", "brand-regular.woff2")),
fileExists(join(multiFileDir, "_brand", "fonts", "brand-bold.woff2")),
{
name: "unused-styles.css should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(multiFileDir, "_brand", "unused-styles.css"))) {
throw new Error("unused-styles.css should not be copied - it is not referenced in _brand.yml");
}
return Promise.resolve();
}
},
{
name: "fonts/unused-italic.woff2 should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(multiFileDir, "_brand", "fonts", "unused-italic.woff2"))) {
throw new Error("fonts/unused-italic.woff2 should not be copied - it is not referenced in _brand.yml");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(multiFileDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => multiFileDir,
teardown: () => {
try { Deno.removeSync(multiFileDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - multi-file installation"
);
const nestedDir = join(tempDir, "nested");
ensureDirSync(nestedDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "nested-brand"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(nestedDir, "_brand")),
fileExists(join(nestedDir, "_brand", "_brand.yml")),
folderExists(join(nestedDir, "_brand", "images")),
fileExists(join(nestedDir, "_brand", "images", "logo.png")),
fileExists(join(nestedDir, "_brand", "images", "header.png")),
{
name: "notes.txt should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(nestedDir, "_brand", "notes.txt"))) {
throw new Error("notes.txt should not be copied - it is not referenced in _brand.yml");
}
return Promise.resolve();
}
},
{
name: "images/extra-icon.png should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(nestedDir, "_brand", "images", "extra-icon.png"))) {
throw new Error("images/extra-icon.png should not be copied - it is not referenced in _brand.yml");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(nestedDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => nestedDir,
teardown: () => {
try { Deno.removeSync(nestedDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - nested directory structure"
);
const noProjectDir = join(tempDir, "no-project");
ensureDirSync(noProjectDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(noProjectDir, "_brand")),
fileExists(join(noProjectDir, "_brand", "_brand.yml")),
fileExists(join(noProjectDir, "_brand", "logo.png")),
],
{
setup: () => {
return Promise.resolve();
},
cwd: () => noProjectDir,
teardown: () => {
try { Deno.removeSync(noProjectDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - single-file mode (no _quarto.yml)"
);
const nestedOverwriteDir = join(tempDir, "nested-overwrite");
ensureDirSync(nestedOverwriteDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "nested-brand"), "--force"],
[
noErrorsOrWarnings,
{
name: "images/logo.png should be overwritten",
verify: () => {
const stats = Deno.statSync(join(nestedOverwriteDir, "_brand", "images", "logo.png"));
if (stats.size < 100) {
throw new Error("images/logo.png should have been overwritten with larger file");
}
return Promise.resolve();
}
},
fileExists(join(nestedOverwriteDir, "_brand", "images", "header.png")),
{
name: "images/unrelated.png should be removed",
verify: () => {
if (existsSync(join(nestedOverwriteDir, "_brand", "images", "unrelated.png"))) {
throw new Error("images/unrelated.png should have been removed");
}
return Promise.resolve();
}
},
filesInSections({
overwrite: ["images/logo.png"],
create: ["_brand.yml", "images/header.png"],
remove: ["images/unrelated.png"]
}, false),
],
{
setup: () => {
Deno.writeTextFileSync(join(nestedOverwriteDir, "_quarto.yml"), "project:\n type: default\n");
const imagesDir = join(nestedOverwriteDir, "_brand", "images");
ensureDirSync(imagesDir);
Deno.writeTextFileSync(join(imagesDir, "logo.png"), "old logo\n");
Deno.writeTextFileSync(join(imagesDir, "unrelated.png"), "keep me nested");
return Promise.resolve();
},
cwd: () => nestedOverwriteDir,
teardown: () => {
try { Deno.removeSync(nestedOverwriteDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - nested overwrite, create, remove in subdirectories"
);
const dryRunNestedDir = join(tempDir, "dry-run-nested");
ensureDirSync(dryRunNestedDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "nested-brand"), "--dry-run"],
[
noErrorsOrWarnings,
filesInSections({
overwrite: ["_brand.yml", "images/logo.png"],
create: ["images/header.png"]
}, true),
{
name: "images/logo.png should not be modified in dry-run",
verify: () => {
const content = Deno.readTextFileSync(join(dryRunNestedDir, "_brand", "images", "logo.png"));
if (content !== "old logo\n") {
throw new Error("images/logo.png should not be modified in dry-run mode");
}
return Promise.resolve();
}
},
{
name: "images/header.png should not be created in dry-run",
verify: () => {
if (existsSync(join(dryRunNestedDir, "_brand", "images", "header.png"))) {
throw new Error("images/header.png should not be created in dry-run mode");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(dryRunNestedDir, "_quarto.yml"), "project:\n type: default\n");
const imagesDir = join(dryRunNestedDir, "_brand", "images");
ensureDirSync(imagesDir);
Deno.writeTextFileSync(join(imagesDir, "logo.png"), "old logo\n");
Deno.writeTextFileSync(join(dryRunNestedDir, "_brand", "_brand.yml"), "meta:\n name: Old\n");
return Promise.resolve();
},
cwd: () => dryRunNestedDir,
teardown: () => {
try { Deno.removeSync(dryRunNestedDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - dry-run reports nested overwrite vs create correctly"
);
const nestedNewSubdirDir = join(tempDir, "nested-new-subdir");
ensureDirSync(nestedNewSubdirDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "nested-brand"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(nestedNewSubdirDir, "_brand", "images")),
fileExists(join(nestedNewSubdirDir, "_brand", "images", "logo.png")),
fileExists(join(nestedNewSubdirDir, "_brand", "images", "header.png")),
{
name: "_brand.yml should be overwritten",
verify: () => {
const content = Deno.readTextFileSync(join(nestedNewSubdirDir, "_brand", "_brand.yml"));
if (content.includes("Old Brand")) {
throw new Error("_brand.yml should have been overwritten");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(nestedNewSubdirDir, "_quarto.yml"), "project:\n type: default\n");
const brandDir = join(nestedNewSubdirDir, "_brand");
ensureDirSync(brandDir);
Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old Brand\n");
return Promise.resolve();
},
cwd: () => nestedNewSubdirDir,
teardown: () => {
try { Deno.removeSync(nestedNewSubdirDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - creates nested subdirectory when _brand exists but subdir doesn't"
);
const dryRunNewSubdirDir = join(tempDir, "dry-run-new-subdir");
ensureDirSync(dryRunNewSubdirDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "nested-brand"), "--dry-run"],
[
noErrorsOrWarnings,
printsMessage({ level: "INFO", regex: /Would create directory/, negate: true }),
filesInSections({
overwrite: ["_brand.yml"],
create: ["images/logo.png", "images/header.png"]
}, true),
{
name: "images/ directory should not be created in dry-run",
verify: () => {
if (existsSync(join(dryRunNewSubdirDir, "_brand", "images"))) {
throw new Error("images/ directory should not be created in dry-run mode");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(dryRunNewSubdirDir, "_quarto.yml"), "project:\n type: default\n");
const brandDir = join(dryRunNewSubdirDir, "_brand");
ensureDirSync(brandDir);
Deno.writeTextFileSync(join(brandDir, "_brand.yml"), "meta:\n name: Old\n");
return Promise.resolve();
},
cwd: () => dryRunNewSubdirDir,
teardown: () => {
try { Deno.removeSync(dryRunNewSubdirDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - dry-run when _brand exists but nested subdir doesn't"
);
const emptyDirCleanupDir = join(tempDir, "empty-dir-cleanup");
ensureDirSync(emptyDirCleanupDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force"],
[
noErrorsOrWarnings,
{
name: "extras/orphan.txt should be removed",
verify: () => {
if (existsSync(join(emptyDirCleanupDir, "_brand", "extras", "orphan.txt"))) {
throw new Error("extras/orphan.txt should have been removed");
}
return Promise.resolve();
}
},
{
name: "extras/ directory should be cleaned up",
verify: () => {
if (existsSync(join(emptyDirCleanupDir, "_brand", "extras"))) {
throw new Error("extras/ directory should have been cleaned up");
}
return Promise.resolve();
}
},
filesInSections({ remove: ["extras/orphan.txt"] }, false),
],
{
setup: () => {
Deno.writeTextFileSync(join(emptyDirCleanupDir, "_quarto.yml"), "project:\n type: default\n");
const extrasDir = join(emptyDirCleanupDir, "_brand", "extras");
ensureDirSync(extrasDir);
Deno.writeTextFileSync(join(extrasDir, "orphan.txt"), "this file will be removed");
return Promise.resolve();
},
cwd: () => emptyDirCleanupDir,
teardown: () => {
try { Deno.removeSync(emptyDirCleanupDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - empty directories cleaned up after file removal"
);
const deepNestedCleanupDir = join(tempDir, "deep-nested-cleanup");
ensureDirSync(deepNestedCleanupDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "basic-brand"), "--force"],
[
noErrorsOrWarnings,
{
name: "deep/nested/path/orphan.txt should be removed",
verify: () => {
if (existsSync(join(deepNestedCleanupDir, "_brand", "deep", "nested", "path", "orphan.txt"))) {
throw new Error("deep/nested/path/orphan.txt should have been removed");
}
return Promise.resolve();
}
},
{
name: "deep/ directory tree should be fully cleaned up",
verify: () => {
if (existsSync(join(deepNestedCleanupDir, "_brand", "deep"))) {
throw new Error("deep/ directory should have been cleaned up recursively");
}
return Promise.resolve();
}
},
filesInSections({ remove: ["deep/nested/path/orphan.txt"] }, false),
],
{
setup: () => {
Deno.writeTextFileSync(join(deepNestedCleanupDir, "_quarto.yml"), "project:\n type: default\n");
const deepDir = join(deepNestedCleanupDir, "_brand", "deep", "nested", "path");
ensureDirSync(deepDir);
Deno.writeTextFileSync(join(deepDir, "orphan.txt"), "deeply nested orphan");
return Promise.resolve();
},
cwd: () => deepNestedCleanupDir,
teardown: () => {
try { Deno.removeSync(deepNestedCleanupDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - deeply nested directories recursively cleaned up"
);
const brandExtDir = join(tempDir, "brand-ext");
ensureDirSync(brandExtDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "brand-extension"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(brandExtDir, "_brand")),
fileExists(join(brandExtDir, "_brand", "_brand.yml")),
fileExists(join(brandExtDir, "_brand", "logo.png")),
{
name: "_extension.yml should not be copied",
verify: () => {
if (existsSync(join(brandExtDir, "_brand", "_extension.yml"))) {
throw new Error("_extension.yml should not be copied from brand extension");
}
return Promise.resolve();
}
},
{
name: "_brand.yml should contain brand extension content",
verify: () => {
const content = Deno.readTextFileSync(join(brandExtDir, "_brand", "_brand.yml"));
if (!content.includes("Test Brand Extension")) {
throw new Error("_brand.yml should contain content from brand.yml");
}
return Promise.resolve();
}
},
{
name: "template.html should not be copied (unreferenced)",
verify: () => {
if (existsSync(join(brandExtDir, "_brand", "template.html"))) {
throw new Error("template.html should not be copied - it is not referenced in brand.yml");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(brandExtDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => brandExtDir,
teardown: () => {
try { Deno.removeSync(brandExtDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - brand extension installation"
);
const brandExtDryRunDir = join(tempDir, "brand-ext-dry-run");
ensureDirSync(brandExtDryRunDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "brand-extension"), "--dry-run"],
[
noErrorsOrWarnings,
filesInSections({ create: ["_brand.yml", "logo.png"] }, true),
{
name: "_brand directory should not exist in dry-run mode",
verify: () => {
if (existsSync(join(brandExtDryRunDir, "_brand"))) {
throw new Error("_brand directory should not exist in dry-run mode");
}
return Promise.resolve();
}
}
],
{
setup: () => {
Deno.writeTextFileSync(join(brandExtDryRunDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => brandExtDryRunDir,
teardown: () => {
try { Deno.removeSync(brandExtDryRunDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - brand extension dry-run shows renamed file"
);
const brandExtSubdirDir = join(tempDir, "brand-ext-subdir");
ensureDirSync(brandExtSubdirDir);
testQuartoCmd(
"use",
["brand", join(fixtureDir, "brand-extension-subdir"), "--force"],
[
noErrorsOrWarnings,
folderExists(join(brandExtSubdirDir, "_brand")),
fileExists(join(brandExtSubdirDir, "_brand", "_brand.yml")),
fileExists(join(brandExtSubdirDir, "_brand", "logo.png")),
folderExists(join(brandExtSubdirDir, "_brand", "images")),
fileExists(join(brandExtSubdirDir, "_brand", "images", "nested-logo.png")),
{
name: "_brand.yml should contain subdir brand content",
verify: () => {
const content = Deno.readTextFileSync(join(brandExtSubdirDir, "_brand", "_brand.yml"));
if (!content.includes("Test Brand Extension Subdir")) {
throw new Error("_brand.yml should contain content from subdir/brand.yml");
}
return Promise.resolve();
}
},
],
{
setup: () => {
Deno.writeTextFileSync(join(brandExtSubdirDir, "_quarto.yml"), "project:\n type: default\n");
return Promise.resolve();
},
cwd: () => brandExtSubdirDir,
teardown: () => {
try { Deno.removeSync(brandExtSubdirDir, { recursive: true }); } catch { }
return Promise.resolve();
}
},
"quarto use brand - brand extension with subdir brand file"
);