From 96f60b5fd3bac71545fcd55a3f2918aa2963ca06 Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Sat, 26 Sep 2026 10:38:31 +0400 Subject: [PATCH] fix: windows e2e harness --- e2e/app/build.rs | 20 ++++++++++++++++++++ e2e/lib/app.mjs | 11 +++++++++++ e2e/run.mjs | 12 +++++++++++- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 e2e/app/build.rs diff --git a/e2e/app/build.rs b/e2e/app/build.rs new file mode 100644 index 00000000..cbb9fa4b --- /dev/null +++ b/e2e/app/build.rs @@ -0,0 +1,20 @@ +fn main() { + // The harness links Donut as a library, and a dependency's link arguments + // never reach this binary. Without the Common Controls v6 manifest Windows + // loads comctl32 v5, and the app dies at startup with + // STATUS_ENTRYPOINT_NOT_FOUND (0xc0000139) before WebDriver can attach. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") { + let manifest_dir = std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let manifest = manifest_dir + .parent() + .and_then(|e2e| e2e.parent()) + .expect("e2e/app sits two levels below the repository root") + .join("src-tauri") + .join("app.manifest"); + // A plain backslash path: mt.exe rejects the \\?\ form canonicalize gives. + let manifest = manifest.to_str().unwrap().replace('/', "\\"); + println!("cargo:rustc-link-arg=/MANIFEST:EMBED"); + println!("cargo:rustc-link-arg=/MANIFESTINPUT:{manifest}"); + println!("cargo:rerun-if-changed={manifest}"); + } +} diff --git a/e2e/lib/app.mjs b/e2e/lib/app.mjs index f631bb37..f741d83c 100644 --- a/e2e/lib/app.mjs +++ b/e2e/lib/app.mjs @@ -123,6 +123,17 @@ export class AppSession { mkdir(path.join(this.root, "home"), { recursive: true }), mkdir(path.join(this.root, "tmp"), { recursive: true }), mkdir(path.join(this.root, "artifacts"), { recursive: true }), + // Windows expands the known folders from %USERPROFILE%, which points at + // the isolated home. A missing AppData\Roaming makes + // SHGetKnownFolderPath fail, and the app cannot resolve its base + // directories at startup. + ...(process.platform === "win32" + ? ["Roaming", "Local"].map((folder) => + mkdir(path.join(this.root, "home", "AppData", folder), { + recursive: true, + }), + ) + : []), ]); if (this.onboardingCompleted) { const settingsFile = path.join( diff --git a/e2e/run.mjs b/e2e/run.mjs index 0424863c..a88c4d33 100644 --- a/e2e/run.mjs +++ b/e2e/run.mjs @@ -107,7 +107,17 @@ function log(message) { function run(command, args, cwd, env = process.env) { log(`${command} ${args.join(" ")}`); - const result = spawnSync(command, args, { cwd, env, stdio: "inherit" }); + // pnpm is a .cmd shim on Windows, and Node starts those only through a + // shell. The arguments here are fixed words, so joining them is safe. + const result = + isWindows && command === "pnpm" + ? spawnSync([command, ...args].join(" "), { + cwd, + env, + stdio: "inherit", + shell: true, + }) + : spawnSync(command, args, { cwd, env, stdio: "inherit" }); if (result.error) { throw result.error; }