From 96614054b2b70fc1bcbebc967d0094633f6f2336 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期五, 28 八月 2026 18:01:22 +0800
Subject: [PATCH] 修复:确保浏览器验收进程完整清理

---
 tests/e2e/device-registration.spec.ts |   45 ++++++++++++++--------
 tests/e2e/support/ticket04-stack.ts   |   27 ++++++++++---
 2 files changed, 50 insertions(+), 22 deletions(-)

diff --git a/tests/e2e/device-registration.spec.ts b/tests/e2e/device-registration.spec.ts
index 695d629..6c70b26 100644
--- a/tests/e2e/device-registration.spec.ts
+++ b/tests/e2e/device-registration.spec.ts
@@ -14,33 +14,46 @@
   await expect(page).toHaveURL(/\/dashboard$/)
 }
 
-async function registerCabinet(page: Page, name: string) {
-  await page.goto('/device-cabinet/new')
+interface RegisterDeviceOptions {
+  createPath: string
+  listPath: RegExp
+  nameLabel: string
+  name: string
+  fillAdditionalFields?: () => Promise<void>
+}
+
+async function registerDevice(page: Page, options: RegisterDeviceOptions) {
+  await page.goto(options.createPath)
   await page.getByRole('button', { name: '选择设备' }).click()
   const row = page.locator('.device-table tbody tr').filter({ has: page.getByRole('button', { name: '选择' }) }).first()
   await expect(row).toBeVisible()
   const code = (await row.locator('td').first().textContent())?.trim() || ''
   await row.getByRole('button', { name: '选择' }).click()
-  await page.locator('label').filter({ hasText: '检测柜名称' }).locator('input').fill(name)
-  await page.locator('label').filter({ hasText: '规格/容量' }).locator('input').fill('120')
+  await page.locator('label').filter({ hasText: options.nameLabel }).locator('input').fill(options.name)
+  await options.fillAdditionalFields?.()
   await page.getByRole('button', { name: '保存', exact: true }).click()
-  await expect(page).toHaveURL(/\/device-cabinet$/)
+  await expect(page).toHaveURL(options.listPath)
   await expect(page.locator('tbody tr').filter({ hasText: code })).toBeVisible()
   return code
 }
 
+async function registerCabinet(page: Page, name: string) {
+  return registerDevice(page, {
+    createPath: '/device-cabinet/new',
+    listPath: /\/device-cabinet$/,
+    nameLabel: '检测柜名称',
+    name,
+    fillAdditionalFields: () => page.locator('label').filter({ hasText: '规格/容量' }).locator('input').fill('120'),
+  })
+}
+
 async function registerCollector(page: Page, name: string) {
-  await page.goto('/device-collector/new')
-  await page.getByRole('button', { name: '选择设备' }).click()
-  const row = page.locator('.device-table tbody tr').filter({ has: page.getByRole('button', { name: '选择' }) }).first()
-  await expect(row).toBeVisible()
-  const code = (await row.locator('td').first().textContent())?.trim() || ''
-  await row.getByRole('button', { name: '选择' }).click()
-  await page.locator('label').filter({ hasText: '采集器名称' }).locator('input').fill(name)
-  await page.getByRole('button', { name: '保存', exact: true }).click()
-  await expect(page).toHaveURL(/\/device-collector$/)
-  await expect(page.locator('tbody tr').filter({ hasText: code })).toBeVisible()
-  return code
+  return registerDevice(page, {
+    createPath: '/device-collector/new',
+    listPath: /\/device-collector$/,
+    nameLabel: '采集器名称',
+    name,
+  })
 }
 
 async function deleteRegisteredDevice(page: Page, path: string, code: string) {
diff --git a/tests/e2e/support/ticket04-stack.ts b/tests/e2e/support/ticket04-stack.ts
index f27855f..f20a229 100644
--- a/tests/e2e/support/ticket04-stack.ts
+++ b/tests/e2e/support/ticket04-stack.ts
@@ -63,6 +63,21 @@
   throw new Error(message)
 }
 
+function waitForProcessClose(child: ChildProcess, timeoutMs: number) {
+  if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve(true)
+  return new Promise<boolean>((resolvePromise) => {
+    const onClose = () => {
+      clearTimeout(timer)
+      resolvePromise(true)
+    }
+    const timer = setTimeout(() => {
+      child.off('close', onClose)
+      resolvePromise(false)
+    }, timeoutMs)
+    child.once('close', onClose)
+  })
+}
+
 export class Ticket04Stack {
   private docker!: DockerCommand
   private backend: ChildProcess | null = null
@@ -180,18 +195,18 @@
   private async stopBackend() {
     const backend = this.backend
     this.backend = null
-    if (!backend || backend.exitCode !== null) return
+    if (!backend || backend.exitCode !== null || backend.signalCode !== null) return
+    const gracefulClose = waitForProcessClose(backend, 15_000)
     backend.kill('SIGTERM')
-    await Promise.race([
-      new Promise<void>(resolvePromise => backend.once('exit', () => resolvePromise())),
-      new Promise<void>(resolvePromise => setTimeout(resolvePromise, 15_000)),
-    ])
-    if (backend.exitCode === null) {
+    if (await gracefulClose) return
+    if (backend.exitCode === null && backend.signalCode === null) {
+      const forcedClose = waitForProcessClose(backend, 15_000)
       if (process.platform === 'win32') {
         spawnSync('taskkill.exe', ['/pid', String(backend.pid), '/t', '/f'], { windowsHide: true })
       } else {
         backend.kill('SIGKILL')
       }
+      await forcedClose
     }
   }
 }

--
Gitblit v1.7.1