fix: E2E node-direct, timeouts réalistes 25/30 min (complément BUG-080)
This commit is contained in:
+15
-7
@@ -6,7 +6,7 @@ Format basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.1.0/),
|
||||
et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
> **En cours de développement** : les changements à venir sont listés dans la section
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.28.13**.
|
||||
> [Unreleased](#unreleased). La dernière version livrée est **2.28.14**.
|
||||
|
||||
---
|
||||
|
||||
@@ -14,6 +14,10 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
---
|
||||
|
||||
## [2.28.14] — 2026-09-27
|
||||
|
||||
---
|
||||
|
||||
## [2.28.13] — 2026-09-27
|
||||
|
||||
### Modifié
|
||||
@@ -78,14 +82,18 @@ et [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
### Corrigé
|
||||
|
||||
- **BUG-080 — harnais E2E local anti-blocage (plus de run pendu toute la nuit).**
|
||||
`run-e2e-local.ps1/.sh` : `npx --yes` (jamais de prompt interactif),
|
||||
`run-e2e-local.ps1` : Playwright lancé via `node` direct sur la CLI locale
|
||||
(jamais de prompt interactif, `Start-Process` ne sachant pas exécuter `npx` ;
|
||||
paramètre `$Arguments`, `$Args` étant une variable automatique qui l'écraserait),
|
||||
installation Chromium sautée si déjà présent (`E2E_INSTALL_BROWSERS=1`
|
||||
pour forcer), étapes `install`/`test` bornées (`E2E_TIMEOUT_SEC`,
|
||||
défaut 900 s / 600 s, exit 124 au dépassement) ; `playwright.config.ts` :
|
||||
`globalTimeout` (15 min en local, 30 min en CI, `E2E_GLOBAL_TIMEOUT_MS`
|
||||
pour surcharger) ; `e2e-server.ps1` : pidfile resynchronisé sur le vrai
|
||||
propriétaire du port et `stop` qui tue l'arbre complet (fini les serveurs
|
||||
orphelins qui squattent le port 2029). Garde-fou : `tests/test_e2e_harness.py`.
|
||||
défaut 1800 s / 600 s, exit 124 au dépassement — au-delà du globalTimeout
|
||||
pour un abandon propre avec rapport) ; `run-e2e-local.sh` : `npx --yes` +
|
||||
mêmes bornes ; `playwright.config.ts` : `globalTimeout` (25 min en local,
|
||||
30 min en CI, `E2E_GLOBAL_TIMEOUT_MS` pour surcharger) ; `e2e-server.ps1` :
|
||||
pidfile resynchronisé sur le vrai propriétaire du port et `stop` qui tue
|
||||
l'arbre complet (fini les serveurs orphelins qui squattent le port 2029).
|
||||
Garde-fous : `tests/test_e2e_harness.py` (8 tests).
|
||||
|
||||
---
|
||||
|
||||
|
||||
+3
-3
@@ -4,7 +4,7 @@
|
||||
|
||||
**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -976,8 +976,8 @@ Ce projet est sous licence **MIT** — voir le fichier [LICENSE](LICENSE) pour l
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.28.13).
|
||||
Consultez le [CHANGELOG.md](./CHANGELOG.md) pour l'historique complet de toutes les versions (v1.0.0 → v2.28.14).
|
||||
|
||||
---
|
||||
|
||||
*Projet : ObsiGate | Version : 2.28.13 | Dernière mise à jour : Septembre 2026*
|
||||
*Projet : ObsiGate | Version : 2.28.14 | Dernière mise à jour : Septembre 2026*
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
**Ultra-light web gateway for your Obsidian vaults** — Access, browse, and search all your Obsidian notes from any device via a modern, responsive web interface.
|
||||
|
||||
[]()
|
||||
[]()
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://www.docker.com/)
|
||||
[](https://www.python.org/)
|
||||
@@ -1151,8 +1151,8 @@ This project is licensed under the **MIT License** - see the [LICENSE](LICENSE)
|
||||
|
||||
## 📝 Changelog
|
||||
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.28.13).
|
||||
See [CHANGELOG.md](./CHANGELOG.md) for the complete version history (v1.0.0 → v2.28.14).
|
||||
|
||||
---
|
||||
|
||||
*Project: ObsiGate | Version: 2.28.13 | Last updated: September 2026*
|
||||
*Project: ObsiGate | Version: 2.28.14 | Last updated: September 2026*
|
||||
|
||||
Generated
+1
-1
@@ -2626,7 +2626,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.28.13"
|
||||
version = "2.28.14"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"env_logger",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "obsigate-desktop"
|
||||
version = "2.28.13"
|
||||
version = "2.28.14"
|
||||
description = "ObsiGate Desktop — Porte d'entrée native pour vos vaults Obsidian"
|
||||
authors = ["Bruno Charest"]
|
||||
edition = "2021"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/nicedoc/obsigate/main/desktop/tauri.conf.schema.json",
|
||||
"productName": "ObsiGate",
|
||||
"version": "2.28.13",
|
||||
"version": "2.28.14",
|
||||
"identifier": "com.obsigate.desktop",
|
||||
"build": {
|
||||
"frontendDist": "../frontend",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
# ObsiGate — Roadmap
|
||||
|
||||
> **Version :** 2.28.13 | **Dernière mise à jour :** 2026-09-27
|
||||
> **Version :** 2.28.14 | **Dernière mise à jour :** 2026-09-27
|
||||
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
|
||||
> vers les fonctionnalités livrées.
|
||||
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "obsigate",
|
||||
"version": "2.28.13",
|
||||
"version": "2.28.14",
|
||||
"description": "**Porte d'entrée web ultra-léger pour vos vaults Obsidian** — Accédez, naviguez et recherchez dans toutes vos notes Obsidian depuis n'importe quel appareil via une interface web moderne et responsive.",
|
||||
"main": "patch.js",
|
||||
"directories": {
|
||||
|
||||
@@ -9,12 +9,13 @@ export default defineConfig({
|
||||
reporter: process.env.CI ? 'github' : 'list',
|
||||
timeout: 60000,
|
||||
expect: { timeout: 10000 },
|
||||
// BUG-080 : la suite (~130 tests, workers: 1) ne doit jamais pendre toute
|
||||
// la nuit. Au-delà du timeout global, Playwright abandonne avec un échec
|
||||
// explicite au lieu de bloquer. Surchargable : E2E_GLOBAL_TIMEOUT_MS.
|
||||
// BUG-080 : la suite (~120 tests, workers: 1, ~10-15 s/test sur un poste
|
||||
// chargé) ne doit jamais pendre toute la nuit. Au-delà du timeout global,
|
||||
// Playwright abandonne avec un échec explicite au lieu de bloquer.
|
||||
// Surchargable : E2E_GLOBAL_TIMEOUT_MS.
|
||||
globalTimeout: Number(
|
||||
process.env.E2E_GLOBAL_TIMEOUT_MS ??
|
||||
(process.env.CI ? 30 * 60 * 1000 : 15 * 60 * 1000),
|
||||
(process.env.CI ? 30 * 60 * 1000 : 25 * 60 * 1000),
|
||||
),
|
||||
reportSlowTests: process.env.CI ? null : { max: 5, threshold: 30000 },
|
||||
|
||||
|
||||
+28
-12
@@ -10,7 +10,7 @@
|
||||
conditions que le job CI `e2e`), lance la suite Playwright puis nettoie.
|
||||
|
||||
.PARAMETER PlaywrightArgs
|
||||
Arguments transmis à `npx playwright test`, ex. `-g "image viewer"`,
|
||||
Arguments transmis à `playwright test` (via `node`), ex. `-g "image viewer"`,
|
||||
`--headed`.
|
||||
|
||||
.EXAMPLE
|
||||
@@ -35,15 +35,20 @@ $ServerLog = "data/e2e-server.log"
|
||||
$ServerErrLog = "data/e2e-server.err.log"
|
||||
|
||||
# BUG-080 : garde-fous anti-blocage (un run E2E ne doit jamais pendre toute la nuit).
|
||||
$TestTimeoutSec = if ($env:E2E_TIMEOUT_SEC) { [int]$env:E2E_TIMEOUT_SEC } else { 900 }
|
||||
# E2E_TIMEOUT_SEC dépasse volontairement le globalTimeout Playwright (25 min en
|
||||
# local) pour que ce soit Playwright qui abandonne proprement (avec rapport) en premier.
|
||||
$TestTimeoutSec = if ($env:E2E_TIMEOUT_SEC) { [int]$env:E2E_TIMEOUT_SEC } else { 1800 }
|
||||
$BrowserTimeoutSec = if ($env:E2E_BROWSER_INSTALL_TIMEOUT_SEC) { [int]$env:E2E_BROWSER_INSTALL_TIMEOUT_SEC } else { 600 }
|
||||
|
||||
function Invoke-NativeWithTimeout([string]$Label, [int]$TimeoutSec, [string]$Exe, [string[]]$Args) {
|
||||
function Invoke-NativeWithTimeout([string]$Label, [int]$TimeoutSec, [string]$Exe, [string[]]$Arguments) {
|
||||
# Lance un processus natif en gardant la sortie console en direct, et le
|
||||
# tue après $TimeoutSec s'il n'a pas terminé (exit 124, comme `timeout`).
|
||||
# NOTE : le paramètre NE DOIT PAS s'appeler `$Args` (variable automatique
|
||||
# PowerShell qui l'écraserait → `node` lancé sans arguments, exit 0
|
||||
# silencieux immédiat en lisant un stdin vide).
|
||||
$stamp = Get-Date -Format "HH:mm:ss"
|
||||
Write-Host "[$stamp] $Label (timeout ${TimeoutSec}s)..."
|
||||
$proc = Start-Process -FilePath $Exe -ArgumentList $Args -NoNewWindow -PassThru
|
||||
$proc = Start-Process -FilePath $Exe -ArgumentList $Arguments -NoNewWindow -PassThru
|
||||
$proc | Wait-Process -Timeout $TimeoutSec -ErrorAction SilentlyContinue
|
||||
if (-not $proc.HasExited) {
|
||||
Write-Host "[ERR] $Label : timeout après ${TimeoutSec}s, arrêt du processus (PID $($proc.Id))."
|
||||
@@ -70,7 +75,7 @@ function Assert-Command([string]$Name, [string]$Hint) {
|
||||
}
|
||||
|
||||
Assert-Command "uv" "Installez-le : https://docs.astral.sh/uv/"
|
||||
Assert-Command "npx" "Installez Node.js (>= 20)."
|
||||
Assert-Command "node" "Installez Node.js (>= 20)."
|
||||
|
||||
# ----- Venv Python 3.11 (créé une seule fois) -----
|
||||
$Python = ".venv-e2e/Scripts/python.exe"
|
||||
@@ -130,21 +135,32 @@ try {
|
||||
}
|
||||
Write-Host "[OK] Serveur prêt."
|
||||
|
||||
# ----- Browsers Playwright (no-op s'ils sont déjà installés) -----
|
||||
# BUG-080 : `--yes` (jamais de prompt interactif npx qui pend), skip si un
|
||||
# chromium est déjà présent (sauf E2E_INSTALL_BROWSERS=1), timeout dédié.
|
||||
# ----- Playwright via node direct (pas npx) -----
|
||||
# BUG-080 : `Start-Process` ne peut pas lancer `npx` (ni le `.ps1` ni le
|
||||
# `.cmd` ne sont des applications Win32 directes) → on appelle la CLI
|
||||
# locale via `node.exe`, sans prompt interactif possible. Skip de
|
||||
# l'install si un chromium est déjà présent (sauf E2E_INSTALL_BROWSERS=1),
|
||||
# timeouts dédiés sur chaque étape.
|
||||
$PlaywrightCli = Join-Path $Root "node_modules/@playwright/test/cli.js"
|
||||
if (-not (Test-Path -LiteralPath $PlaywrightCli)) {
|
||||
throw "[ERR] $PlaywrightCli introuvable. Lancez d'abord : npm ci"
|
||||
}
|
||||
if (($env:E2E_INSTALL_BROWSERS -eq "1") -or (-not (Test-ChromiumInstalled))) {
|
||||
$code = Invoke-NativeWithTimeout "npx playwright install chromium" $BrowserTimeoutSec "npx" @("--yes", "playwright", "install", "chromium")
|
||||
$code = Invoke-NativeWithTimeout "playwright install chromium" $BrowserTimeoutSec "node" @($PlaywrightCli, "install", "chromium")
|
||||
if ($code -ne 0) { exit $code }
|
||||
} else {
|
||||
Write-Host "[INFO] Chromium Playwright déjà installé, étape sautée (E2E_INSTALL_BROWSERS=1 pour forcer)."
|
||||
}
|
||||
|
||||
# ----- Exécution de la suite (projet CI : chromium-desktop) -----
|
||||
Write-Host "[INFO] BASE_URL=$BaseUrl npx playwright test --project=chromium-desktop $($PlaywrightArgs -join ' ')"
|
||||
Write-Host "[INFO] BASE_URL=$BaseUrl node $PlaywrightCli test --project=chromium-desktop $($PlaywrightArgs -join ' ')"
|
||||
$env:BASE_URL = $BaseUrl
|
||||
$testArgs = @("--yes", "playwright", "test", "--project=chromium-desktop") + @($PlaywrightArgs)
|
||||
$exitCode = Invoke-NativeWithTimeout "playwright test" $TestTimeoutSec "npx" $testArgs
|
||||
$testArgs = @($PlaywrightCli, "test", "--project=chromium-desktop")
|
||||
if ($PlaywrightArgs) { $testArgs += @($PlaywrightArgs) }
|
||||
$exitCode = Invoke-NativeWithTimeout "playwright test" $TestTimeoutSec "node" $testArgs
|
||||
} catch {
|
||||
Write-Host "[ERR] $($_.Exception.Message)"
|
||||
$exitCode = 1
|
||||
} finally {
|
||||
Write-Host "[INFO] Arrêt du serveur (PID $($server.Id))..."
|
||||
if (-not $server.HasExited) { Stop-Process -Id $server.Id -Force -ErrorAction SilentlyContinue }
|
||||
|
||||
@@ -23,18 +23,34 @@ def _read(rel: str) -> str:
|
||||
class TestE2ELocalPs:
|
||||
SCRIPT = "scripts/run-e2e-local.ps1"
|
||||
|
||||
def test_npx_never_prompts(self):
|
||||
"""`npx --yes` partout : aucun prompt « Ok to proceed? » qui pend."""
|
||||
def test_playwright_via_node_no_npx(self):
|
||||
"""Playwright est lancé via `node` direct, jamais via `npx`.
|
||||
|
||||
`Start-Process` ne peut pas exécuter `npx` (ni le `.ps1` ni le
|
||||
`.cmd` ne sont des applications Win32 directes : "%1 is not a valid
|
||||
Win32 application"), et `npx` sans `--yes` peut pendre sur un prompt
|
||||
interactif. Seules les mentions en commentaires/logs sont tolérées.
|
||||
"""
|
||||
content = _read(self.SCRIPT)
|
||||
# Aucune invocation nue `npx ...` / `& npx ...` (toujours via le helper
|
||||
# avec `--yes`) ; les mentions dans commentaires/Write-Host sont OK.
|
||||
bare = [
|
||||
line.strip()
|
||||
for line in content.splitlines()
|
||||
if re.match(r"^\s*(?:&\s*)?npx\s", line)
|
||||
]
|
||||
assert not bare, f"invocations npx nues (sans --yes) : {bare}"
|
||||
assert content.count("--yes") >= 2, "au moins install + test en --yes"
|
||||
assert not bare, f"invocations npx nues : {bare}"
|
||||
assert "node_modules/@playwright/test/cli.js" in content, (
|
||||
"CLI Playwright locale attendue (via node)"
|
||||
)
|
||||
# `$Args` est une variable automatique PowerShell : un paramètre de
|
||||
# ce nom serait écrasé (helper lancé sans arguments → exit 0 muet).
|
||||
# (commentaires `#` exclus : la mise en garde elle-même le cite).
|
||||
code_lines = [
|
||||
line for line in content.splitlines()
|
||||
if not line.strip().startswith("#")
|
||||
]
|
||||
assert not re.search(r"\$Args\b", "\n".join(code_lines)), (
|
||||
"BUG-080 : paramètre `$Args` interdit (shadowing par $args automatique)"
|
||||
)
|
||||
|
||||
def test_browser_install_skippable(self):
|
||||
"""Install navigateurs sautée si chromium déjà présent (sauf forçage)."""
|
||||
@@ -43,12 +59,16 @@ class TestE2ELocalPs:
|
||||
assert "E2E_INSTALL_BROWSERS" in content
|
||||
|
||||
def test_test_step_has_timeout(self):
|
||||
"""L'étape `playwright test` est bornée (E2E_TIMEOUT_SEC, défaut 900)."""
|
||||
"""L'étape `playwright test` est bornée (E2E_TIMEOUT_SEC, défaut 1800).
|
||||
|
||||
Le défaut dépasse le globalTimeout Playwright (25 min en local) pour
|
||||
que ce soit Playwright qui abandonne proprement (avec rapport) en premier.
|
||||
"""
|
||||
content = _read(self.SCRIPT)
|
||||
assert "E2E_TIMEOUT_SEC" in content
|
||||
assert "Wait-Process -Timeout" in content
|
||||
assert re.search(r"E2E_TIMEOUT_SEC.*else\s*\{\s*900\s*\}", content), (
|
||||
"défaut E2E_TIMEOUT_SEC=900 attendu"
|
||||
assert re.search(r"E2E_TIMEOUT_SEC.*else\s*\{\s*1800\s*\}", content), (
|
||||
"défaut E2E_TIMEOUT_SEC=1800 attendu"
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user