honkit
Version:
HonKit is building beautiful books using Markdown.
195 lines (194 loc) • 8.73 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const watch_1 = __importDefault(require("../watch"));
const tmpdir_1 = require("../../fs/tmpdir");
/**
* Normalize path separators to forward slashes for cross-platform comparison
*/
function normalizePath(filepath) {
return filepath.replace(/\\/g, "/");
}
describe("watch", () => {
// Increase timeout for file system operations (Windows may need more time)
jest.setTimeout(30000);
let tempDir;
let watchers = [];
beforeEach(() => {
tempDir = (0, tmpdir_1.createTmpDirWithRealPath)("honkit-watch-test-");
});
afterEach(async () => {
// Close all watchers
await Promise.all(watchers.map((w) => w.close()));
watchers = [];
// Cleanup temp directory
if (tempDir && fs_1.default.existsSync(tempDir)) {
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
}
});
function createFile(relativePath, content = "# Test") {
const filePath = path_1.default.join(tempDir, relativePath);
const dir = path_1.default.dirname(filePath);
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
}
fs_1.default.writeFileSync(filePath, content);
return filePath;
}
function modifyFile(relativePath, content) {
const filePath = path_1.default.join(tempDir, relativePath);
fs_1.default.writeFileSync(filePath, content);
return filePath;
}
/**
* Wait for watcher to be ready and stable
*/
async function waitForReady(watcher) {
await new Promise((resolve) => {
watcher.on("ready", resolve);
});
// Give the watcher a moment to stabilize after ready event
// Windows needs more time for file system operations
await new Promise((resolve) => setTimeout(resolve, 500));
}
/**
* Wait for a file change to be detected that matches the predicate
* Paths are normalized to forward slashes for cross-platform compatibility
*/
function waitForChange(watcher, predicate, timeoutMs = 20000) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error(`Timeout waiting for file change after ${timeoutMs}ms`));
}, timeoutMs);
const handler = (_event, filepath) => {
const fullPath = normalizePath(path_1.default.resolve(tempDir, filepath));
if (predicate(fullPath)) {
clearTimeout(timeout);
watcher.off("all", handler);
resolve(fullPath);
}
};
watcher.on("all", handler);
});
}
describe("issue #491 - custom output folder handling", () => {
it("should detect changes in custom output folder when not specified in options", async () => {
createFile("README.md", "# README");
createFile("output/orphan.md", "# Orphan in output folder");
const detectedFiles = [];
// Start watching WITHOUT specifying output folder
const watcher = (0, watch_1.default)({
watchDir: tempDir,
callback: (error, filepath) => {
if (error || !filepath)
return;
detectedFiles.push(normalizePath(filepath));
}
});
watchers.push(watcher);
await waitForReady(watcher);
// Modify file in output folder and wait for detection
modifyFile("output/orphan.md", "# Modified");
const detected = await waitForChange(watcher, (f) => f.includes("output/orphan.md"));
expect(detected).toContain("output/orphan.md");
});
it("should NOT detect changes in custom output folder when specified in options", async () => {
createFile("README.md", "# README");
createFile("output/orphan.md", "# Orphan in output folder");
const detectedFiles = [];
// Start watching WITH output folder specified - it should be ignored
const watcher = (0, watch_1.default)({
watchDir: tempDir,
outputFolder: "output",
callback: (error, filepath) => {
if (error || !filepath)
return;
detectedFiles.push(normalizePath(filepath));
}
});
watchers.push(watcher);
await waitForReady(watcher);
// Modify both files - output should be ignored, README should be detected
modifyFile("output/orphan.md", "# Modified");
modifyFile("README.md", "# Modified README");
// Wait for README change to be detected (proves watcher is working)
await waitForChange(watcher, (f) => f.includes("README.md"));
// Verify output folder change was NOT detected
const outputChanges = detectedFiles.filter((f) => f.includes("output/"));
expect(outputChanges).toHaveLength(0);
});
it("should handle absolute output folder paths", async () => {
createFile("README.md", "# README");
createFile("docs-output/file.md", "# File");
const detectedFiles = [];
const absoluteOutputPath = path_1.default.join(tempDir, "docs-output");
const watcher = (0, watch_1.default)({
watchDir: tempDir,
outputFolder: absoluteOutputPath,
callback: (error, filepath) => {
if (error || !filepath)
return;
detectedFiles.push(normalizePath(filepath));
}
});
watchers.push(watcher);
await waitForReady(watcher);
// Modify both files
modifyFile("docs-output/file.md", "# Modified");
modifyFile("README.md", "# Modified README");
// Wait for README change
await waitForChange(watcher, (f) => f.includes("README.md"));
// Verify docs-output was NOT detected
const outputChanges = detectedFiles.filter((f) => f.includes("docs-output/"));
expect(outputChanges).toHaveLength(0);
});
});
describe("default _book folder", () => {
it("should NOT detect changes in _book folder", async () => {
createFile("file1.md", "# File 1");
createFile("file2.md", "# File 2");
createFile("_book/output.md", "# Output");
const detectedFiles = [];
const watcher = (0, watch_1.default)({
watchDir: tempDir,
callback: (error, filepath) => {
if (error || !filepath)
return;
detectedFiles.push(normalizePath(filepath));
}
});
watchers.push(watcher);
await waitForReady(watcher);
// First modify file1 to ensure watcher is working
modifyFile("file1.md", "# Modified File 1");
await waitForChange(watcher, (f) => f.includes("file1.md"));
// Now modify _book file (should be ignored)
modifyFile("_book/output.md", "# Modified");
// Modify file2 to confirm watcher still works
modifyFile("file2.md", "# Modified File 2");
await waitForChange(watcher, (f) => f.includes("file2.md"));
// Verify _book was NOT detected
const bookChanges = detectedFiles.filter((f) => f.includes("_book"));
expect(bookChanges).toHaveLength(0);
});
});
describe("source files", () => {
it("should detect changes to markdown files in source directory", async () => {
createFile("README.md", "# README");
createFile("orphan.md", "# Orphan - not in SUMMARY");
const watcher = (0, watch_1.default)({
watchDir: tempDir,
callback: () => { }
});
watchers.push(watcher);
await waitForReady(watcher);
modifyFile("orphan.md", "# Modified orphan");
const detected = await waitForChange(watcher, (f) => f.includes("orphan.md"));
expect(detected).toContain("orphan.md");
});
});
});