From f094e362cc737996501d1737b49065f76ceb170d Mon Sep 17 00:00:00 2001 From: Valentin Millet Date: Mon, 14 Sep 2026 19:11:50 +0200 Subject: [PATCH] Wipe the config directory of the e2e profile too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tauri-plugin-window-state writes .window-state.json under app_config_dir(), which on Linux is not app_data_dir() — so the window geometry survived the wipe and a run opened on the window the previous one closed with. Windows cannot tell the two directories apart, which is why it passed there. Co-Authored-By: Claude Opus 5 --- docs/architecture.md | 5 +++++ e2e/support/profile.ts | 31 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/docs/architecture.md b/docs/architecture.md index e71c8ff..2824766 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2145,6 +2145,11 @@ consequences, and they are rules rather than observations: `tsx e2e/reset-profile.ts && wdio run …`. Not from a hook: nothing orders a wdio hook against the service's own `onPrepare`, and a wipe from inside meets a living process, a locked database and an open WAL. A separate process beforehand has no ordering to get wrong. + ⚠️ The profile is **two** directories, not one: `tauri-plugin-window-state` writes + `.window-state.json` under `app_config_dir()` while everything else the application + writes lives under `app_data_dir()`. Windows cannot tell them apart, so wiping only the + data directory passed there and left the window geometry behind on Linux — where a run + then opened on the window the previous one closed with. - **`before()` buys each file a fresh front end and nothing more.** `browser.refresh()` reboots Angular and every store over the same database; it resets no data. - **A spec file establishes its own preconditions.** It seeds what it needs, and it does not diff --git a/e2e/support/profile.ts b/e2e/support/profile.ts index 0626be9..5fd0392 100644 --- a/e2e/support/profile.ts +++ b/e2e/support/profile.ts @@ -18,6 +18,25 @@ function e2eDataDir(): string { return xdg ? join(xdg, IDENTIFIER) : join(process.env['HOME'] ?? '', '.local/share', IDENTIFIER); } +/** + * ⚠️ A second directory, and on Linux it is **not** the first one. + * + * `tauri-plugin-window-state` writes `.window-state.json` under `app_config_dir()`, + * while everything else the application writes lives under `app_data_dir()`. Windows + * cannot tell the two apart — both are `%APPDATA%\` — which is why wiping + * only the data directory looked complete: on Linux the geometry survived the wipe, so + * a run inherited the window of the run before it and the first one to end on an + * unusual size handed it to every run after. CI never saw it, its runners being new + * each time; a developer running the suite twice did. + */ +function e2eConfigDir(): string { + if (process.platform === 'win32') { + return join(process.env['APPDATA'] ?? '', IDENTIFIER); + } + const xdg = process.env['XDG_CONFIG_HOME']; + return xdg ? join(xdg, IDENTIFIER) : join(process.env['HOME'] ?? '', '.config', IDENTIFIER); +} + /** * ⚠️ Under the **data** directory, not the config one. `tauri-plugin-store` resolves a * relative path against `BaseDirectory::AppData`, and `PreferencesService` passes it no @@ -49,9 +68,12 @@ export function homeSpaceMarker(): string { * no ordering to get wrong. */ export function resetProfile(): void { - const directory = e2eDataDir(); + // Both, and `new Set` because on Windows they are the same path. + const directories = [...new Set([e2eDataDir(), e2eConfigDir()])]; - rmSync(directory, { recursive: true, force: true }); + for (const directory of directories) { + rmSync(directory, { recursive: true, force: true }); + } rmSync(homeSpaceMarker(), { force: true }); // ⚠️ `force` covers "it was not there", which is the ordinary case — but it also @@ -62,9 +84,10 @@ export function resetProfile(): void { // the first-launch scenario, the only one that can resolve it, had already failed. // // Say so here rather than let fifteen files disagree about why. - if (existsSync(directory)) { + const survivor = directories.find((directory) => existsSync(directory)); + if (survivor) { throw new Error( - `the e2e profile at ${directory} could not be wiped — an application from a previous run is probably still holding it open`, + `the e2e profile at ${survivor} could not be wiped — an application from a previous run is probably still holding it open`, ); } }