Rust library to generate static websites

fix: e2e tests

+119 -6
+95 -5
e2e/tests/hot-reload.spec.ts
··· 15 15 test.describe("Hot Reload", () => { 16 16 const fixturePath = resolve(__dirname, "..", "fixtures", "hot-reload"); 17 17 const indexPath = resolve(fixturePath, "src", "pages", "index.rs"); 18 - let originalContent: string; 18 + const mainPath = resolve(fixturePath, "src", "main.rs"); 19 + let originalIndexContent: string; 20 + let originalMainContent: string; 19 21 20 22 test.beforeAll(async () => { 21 23 // Save original content 22 - originalContent = readFileSync(indexPath, "utf-8"); 24 + originalIndexContent = readFileSync(indexPath, "utf-8"); 25 + originalMainContent = readFileSync(mainPath, "utf-8"); 23 26 }); 24 27 25 28 test.afterEach(async () => { 26 29 // Restore original content after each test 27 - writeFileSync(indexPath, originalContent, "utf-8"); 30 + writeFileSync(indexPath, originalIndexContent, "utf-8"); 31 + writeFileSync(mainPath, originalMainContent, "utf-8"); 28 32 // Wait a bit for the rebuild 29 33 await new Promise((resolve) => setTimeout(resolve, 2000)); 30 34 }); 31 35 32 36 test.afterAll(async () => { 33 37 // Restore original content 34 - writeFileSync(indexPath, originalContent, "utf-8"); 38 + writeFileSync(indexPath, originalIndexContent, "utf-8"); 39 + writeFileSync(mainPath, originalMainContent, "utf-8"); 40 + }); 41 + 42 + test("should recompile when Rust code changes (dependencies)", async ({ page, devServer }) => { 43 + await page.goto(devServer.url); 44 + 45 + // Verify initial content 46 + await expect(page.locator("#title")).toHaveText("Original Title"); 47 + 48 + // Clear logs to track what happens after this point 49 + devServer.clearLogs(); 50 + 51 + // Modify main.rs - this is a tracked dependency, should trigger recompile 52 + const modifiedMain = originalMainContent.replace( 53 + "BuildOptions::default()", 54 + "BuildOptions::default() // Modified comment", 55 + ); 56 + writeFileSync(mainPath, modifiedMain, "utf-8"); 57 + 58 + // Wait for rebuild to complete - look for "finished" in logs 59 + await new Promise((resolve) => { 60 + const checkInterval = setInterval(() => { 61 + const logs = devServer.getLogs(50).join("\n"); 62 + if (logs.includes("finished") || logs.includes("Rebuild")) { 63 + clearInterval(checkInterval); 64 + resolve(null); 65 + } 66 + }, 100); 67 + 68 + // Timeout after 15 seconds 69 + setTimeout(() => { 70 + clearInterval(checkInterval); 71 + resolve(null); 72 + }, 15000); 73 + }); 74 + 75 + // Check logs to verify it actually recompiled (ran cargo) 76 + const logs = devServer.getLogs(50).join("\n"); 77 + expect(logs).toContain("rebuilding"); 78 + expect(logs).not.toContain("Rerunning binary"); 79 + expect(logs).not.toContain("rerunning binary"); 80 + }); 81 + 82 + test("should rerun without recompile when template changes (non-dependencies)", async ({ 83 + page, 84 + devServer 85 + }) => { 86 + await page.goto(devServer.url); 87 + 88 + // Verify initial content 89 + await expect(page.locator("#title")).toHaveText("Original Title"); 90 + 91 + // Prepare to wait for actual reload 92 + const currentUrl = page.url(); 93 + 94 + // Clear logs to track what happens after this point 95 + devServer.clearLogs(); 96 + 97 + // Modify the template in index.rs - this should NOT require recompilation 98 + // since it's just the HTML template, not the actual Rust code structure 99 + const modifiedContent = originalIndexContent.replace( 100 + 'h1 id="title" { "Original Title" }', 101 + 'h1 id="title" { "Template Updated" }', 102 + ); 103 + writeFileSync(indexPath, modifiedContent, "utf-8"); 104 + 105 + // Wait for the page to reload 106 + await page.waitForURL(currentUrl, { timeout: 15000 }); 107 + 108 + // Verify the updated content 109 + await expect(page.locator("#title")).toHaveText("Template Updated", { timeout: 15000 }); 110 + 111 + // Give logs time to be captured 112 + await new Promise((resolve) => setTimeout(resolve, 1000)); 113 + 114 + // Check logs to verify it did NOT recompile 115 + const logs = devServer.getLogs(50).join("\n"); 116 + 117 + // Should see "rerunning binary" or similar message 118 + const hasRerunMessage = logs.toLowerCase().includes("rerunning") || 119 + logs.toLowerCase().includes("rerun"); 120 + expect(hasRerunMessage).toBe(true); 121 + 122 + // Should NOT see cargo compilation messages 123 + expect(logs).not.toContain("Compiling"); 124 + expect(logs.toLowerCase()).not.toContain("rebuilding"); 35 125 }); 36 126 37 127 test("should show updated content after file changes", async ({ page, devServer }) => { ··· 44 134 const currentUrl = page.url(); 45 135 46 136 // Modify the file 47 - const modifiedContent = originalContent.replace( 137 + const modifiedContent = originalIndexContent.replace( 48 138 'h1 id="title" { "Original Title" }', 49 139 'h1 id="title" { "Another Update" }', 50 140 );
+24 -1
e2e/tests/test-utils.ts
··· 23 23 port: number; 24 24 /** Stop the dev server */ 25 25 stop: () => Promise<void>; 26 + /** Get recent log output (last N lines) */ 27 + getLogs: (lines?: number) => string[]; 28 + /** Clear captured logs */ 29 + clearLogs: () => void; 26 30 } 27 31 28 32 /** ··· 56 60 57 61 // Capture output to detect when server is ready 58 62 let serverReady = false; 63 + const capturedLogs: string[] = []; 59 64 60 65 const outputPromise = new Promise<number>((resolve, reject) => { 61 66 const timeout = setTimeout(() => { ··· 64 69 65 70 childProcess.stdout?.on("data", (data: Buffer) => { 66 71 const output = data.toString(); 72 + // Capture all stdout logs 73 + output.split("\n").filter(line => line.trim()).forEach(line => { 74 + capturedLogs.push(line); 75 + }); 67 76 68 77 // Look for "waiting for requests" to know server is ready 69 78 if (output.includes("waiting for requests")) { ··· 75 84 }); 76 85 77 86 childProcess.stderr?.on("data", (data: Buffer) => { 78 - // Only log errors, not all stderr output 79 87 const output = data.toString(); 88 + // Capture all stderr logs 89 + output.split("\n").filter(line => line.trim()).forEach(line => { 90 + capturedLogs.push(line); 91 + }); 92 + 93 + // Only log errors to console, not all stderr output 80 94 if (output.toLowerCase().includes("error")) { 81 95 console.error(`[maudit dev] ${output}`); 82 96 } ··· 112 126 } 113 127 }, 5000); 114 128 }); 129 + }, 130 + getLogs: (lines?: number) => { 131 + if (lines) { 132 + return capturedLogs.slice(-lines); 133 + } 134 + return [...capturedLogs]; 135 + }, 136 + clearLogs: () => { 137 + capturedLogs.length = 0; 115 138 }, 116 139 }; 117 140 }