{
  "config": {
    "argv": [
      "/opt/hostedtoolcache/node/20.20.2/x64/bin/node",
      "/home/runner/work/dr-card-ui/dr-card-ui/node_modules/.bin/playwright",
      "test",
      "tests/e2e/test-gate.spec.ts"
    ],
    "configFile": "/home/runner/work/dr-card-ui/dr-card-ui/playwright.config.ts",
    "rootDir": "/home/runner/work/dr-card-ui/dr-card-ui/tests/e2e",
    "failOnFlakyTests": false,
    "forbidOnly": true,
    "fullyParallel": true,
    "globalSetup": null,
    "globalTeardown": null,
    "globalTimeout": 0,
    "grep": {},
    "grepInvert": null,
    "maxFailures": 0,
    "metadata": {
      "ci": {
        "commitHref": "https://github.com/dangjingtao/dr-card-ui/commit/c55e7e0fb997ef229c5e90c053cf08b500dc0204",
        "commitHash": "c55e7e0fb997ef229c5e90c053cf08b500dc0204",
        "prHref": "https://github.com/dangjingtao/dr-card-ui/pull/207",
        "prTitle": "promote(dev→test): WebView 88 标题栏/TabBar 修复 (#82)",
        "prBaseHash": "06ec5f4b3bb85bb996adfdc3404f204758a82aae",
        "buildHref": "https://github.com/dangjingtao/dr-card-ui/actions/runs/38235540443"
      },
      "gitCommit": {
        "shortHash": "c55e7e0",
        "hash": "c55e7e0fb997ef229c5e90c053cf08b500dc0204",
        "subject": "Merge 2605a6e9462b95579aceeee9ef47b4f007d73cb3 into 06ec5f4b3bb85bb996adfdc3404f204758a82aae",
        "body": "Merge 2605a6e9462b95579aceeee9ef47b4f007d73cb3 into 06ec5f4b3bb85bb996adfdc3404f204758a82aae\n",
        "author": {
          "name": "Tomz Dang",
          "email": "dangjingtao@163.com",
          "time": 1791800334000
        },
        "committer": {
          "name": "GitHub",
          "email": "noreply@github.com",
          "time": 1791800334000
        },
        "branch": "HEAD"
      },
      "gitDiff": "diff --git a/src/layouts/MobileLayout.tsx b/src/layouts/MobileLayout.tsx\nindex 0983975..eedcf29 100644\n--- a/src/layouts/MobileLayout.tsx\n+++ b/src/layouts/MobileLayout.tsx\n@@ -217,7 +217,7 @@ export default function MobileLayout() {\n           <Toast message=\"用户信息过期\" />\n         </div>\n       )}\n-      <div className=\"app-background flex h-dvh flex-col overflow-hidden pt-[env(safe-area-inset-top)] text-text-primary\">\n+      <div className=\"app-background flex h-screen supports-[height:100dvh]:h-dvh flex-col overflow-hidden pt-[env(safe-area-inset-top)] text-text-primary\">\n         {/* T013R7：min-w-0 防止 TitleBar 第三列 action 被撑大撑出页面右侧 */}\n         <div className=\"min-w-0 shrink-0\">\n           {titleBarMode !== 'hidden' && (\ndiff --git a/tests/e2e/formal-h5.spec.ts b/tests/e2e/formal-h5.spec.ts\nindex 8ebb473..4d0b309 100644\n--- a/tests/e2e/formal-h5.spec.ts\n+++ b/tests/e2e/formal-h5.spec.ts\n@@ -197,6 +197,43 @@ test('@formal-h5 H021 active formal H5 fills a wide WebView', async ({ page }) =\n   expect(runtimeErrors, runtimeErrors.join('\\n')).toEqual([])\n })\n \n+test('@formal-h5 H082 older WebView keeps title and TabBar outside the scroller', async ({ page }) => {\n+  const runtimeErrors = collectRuntimeErrors(page)\n+\n+  await page.setViewportSize({ width: 375, height: 568 })\n+  await page.goto('/?newcomer=off', { waitUntil: 'domcontentloaded' })\n+  await expectHealthyFormalRoute(page)\n+\n+  const scroll = page.locator('[data-page-scroll]')\n+  const shell = scroll.locator('..')\n+  // The first height class is the Chromium 88 fallback; the supports variant\n+  // lets newer WebViews use the dynamic viewport without losing that fallback.\n+  await expect(shell).toHaveClass(/\\bh-screen\\b/)\n+  await expect(shell).toHaveClass(/supports-\\[height:100dvh\\]:h-dvh/)\n+\n+  const positions = () => page.evaluate(() => {\n+    const title = document.querySelector('[data-title-bar]')\n+    const nav = document.querySelector('nav[aria-label=\"主导航\"]')\n+    if (!title || !nav) throw new Error('H082 app shell controls not found')\n+    return {\n+      titleTop: title.getBoundingClientRect().top,\n+      navTop: nav.getBoundingClientRect().top,\n+      documentScroll: window.scrollY,\n+    }\n+  })\n+\n+  await expect.poll(() => scroll.evaluate((node) => node.scrollHeight > node.clientHeight)).toBe(true)\n+  const before = await positions()\n+  await scroll.evaluate((node) => { node.scrollTop = node.scrollHeight })\n+  expect(await scroll.evaluate((node) => node.scrollTop)).toBeGreaterThan(0)\n+  const after = await positions()\n+\n+  expect(after.documentScroll).toBe(0)\n+  expect(Math.abs(after.titleTop - before.titleTop)).toBeLessThanOrEqual(1)\n+  expect(Math.abs(after.navTop - before.navTop)).toBeLessThanOrEqual(1)\n+  expect(runtimeErrors, runtimeErrors.join('\\n')).toEqual([])\n+})\n+\n test('@formal-h5 H021 App title bar stays reachable while content scrolls', async ({ page }) => {\n   const runtimeErrors = collectRuntimeErrors(page)",
      "actualWorkers": 2
    },
    "preserveOutput": "always",
    "projects": [
      {
        "outputDir": "/home/runner/work/dr-card-ui/dr-card-ui/test-results/artifacts",
        "repeatEach": 1,
        "retries": 1,
        "metadata": {
          "ci": {
            "commitHref": "https://github.com/dangjingtao/dr-card-ui/commit/c55e7e0fb997ef229c5e90c053cf08b500dc0204",
            "commitHash": "c55e7e0fb997ef229c5e90c053cf08b500dc0204",
            "prHref": "https://github.com/dangjingtao/dr-card-ui/pull/207",
            "prTitle": "promote(dev→test): WebView 88 标题栏/TabBar 修复 (#82)",
            "prBaseHash": "06ec5f4b3bb85bb996adfdc3404f204758a82aae",
            "buildHref": "https://github.com/dangjingtao/dr-card-ui/actions/runs/38235540443"
          },
          "gitCommit": {
            "shortHash": "c55e7e0",
            "hash": "c55e7e0fb997ef229c5e90c053cf08b500dc0204",
            "subject": "Merge 2605a6e9462b95579aceeee9ef47b4f007d73cb3 into 06ec5f4b3bb85bb996adfdc3404f204758a82aae",
            "body": "Merge 2605a6e9462b95579aceeee9ef47b4f007d73cb3 into 06ec5f4b3bb85bb996adfdc3404f204758a82aae\n",
            "author": {
              "name": "Tomz Dang",
              "email": "dangjingtao@163.com",
              "time": 1791800334000
            },
            "committer": {
              "name": "GitHub",
              "email": "noreply@github.com",
              "time": 1791800334000
            },
            "branch": "HEAD"
          },
          "gitDiff": "diff --git a/src/layouts/MobileLayout.tsx b/src/layouts/MobileLayout.tsx\nindex 0983975..eedcf29 100644\n--- a/src/layouts/MobileLayout.tsx\n+++ b/src/layouts/MobileLayout.tsx\n@@ -217,7 +217,7 @@ export default function MobileLayout() {\n           <Toast message=\"用户信息过期\" />\n         </div>\n       )}\n-      <div className=\"app-background flex h-dvh flex-col overflow-hidden pt-[env(safe-area-inset-top)] text-text-primary\">\n+      <div className=\"app-background flex h-screen supports-[height:100dvh]:h-dvh flex-col overflow-hidden pt-[env(safe-area-inset-top)] text-text-primary\">\n         {/* T013R7：min-w-0 防止 TitleBar 第三列 action 被撑大撑出页面右侧 */}\n         <div className=\"min-w-0 shrink-0\">\n           {titleBarMode !== 'hidden' && (\ndiff --git a/tests/e2e/formal-h5.spec.ts b/tests/e2e/formal-h5.spec.ts\nindex 8ebb473..4d0b309 100644\n--- a/tests/e2e/formal-h5.spec.ts\n+++ b/tests/e2e/formal-h5.spec.ts\n@@ -197,6 +197,43 @@ test('@formal-h5 H021 active formal H5 fills a wide WebView', async ({ page }) =\n   expect(runtimeErrors, runtimeErrors.join('\\n')).toEqual([])\n })\n \n+test('@formal-h5 H082 older WebView keeps title and TabBar outside the scroller', async ({ page }) => {\n+  const runtimeErrors = collectRuntimeErrors(page)\n+\n+  await page.setViewportSize({ width: 375, height: 568 })\n+  await page.goto('/?newcomer=off', { waitUntil: 'domcontentloaded' })\n+  await expectHealthyFormalRoute(page)\n+\n+  const scroll = page.locator('[data-page-scroll]')\n+  const shell = scroll.locator('..')\n+  // The first height class is the Chromium 88 fallback; the supports variant\n+  // lets newer WebViews use the dynamic viewport without losing that fallback.\n+  await expect(shell).toHaveClass(/\\bh-screen\\b/)\n+  await expect(shell).toHaveClass(/supports-\\[height:100dvh\\]:h-dvh/)\n+\n+  const positions = () => page.evaluate(() => {\n+    const title = document.querySelector('[data-title-bar]')\n+    const nav = document.querySelector('nav[aria-label=\"主导航\"]')\n+    if (!title || !nav) throw new Error('H082 app shell controls not found')\n+    return {\n+      titleTop: title.getBoundingClientRect().top,\n+      navTop: nav.getBoundingClientRect().top,\n+      documentScroll: window.scrollY,\n+    }\n+  })\n+\n+  await expect.poll(() => scroll.evaluate((node) => node.scrollHeight > node.clientHeight)).toBe(true)\n+  const before = await positions()\n+  await scroll.evaluate((node) => { node.scrollTop = node.scrollHeight })\n+  expect(await scroll.evaluate((node) => node.scrollTop)).toBeGreaterThan(0)\n+  const after = await positions()\n+\n+  expect(after.documentScroll).toBe(0)\n+  expect(Math.abs(after.titleTop - before.titleTop)).toBeLessThanOrEqual(1)\n+  expect(Math.abs(after.navTop - before.navTop)).toBeLessThanOrEqual(1)\n+  expect(runtimeErrors, runtimeErrors.join('\\n')).toEqual([])\n+})\n+\n test('@formal-h5 H021 App title bar stays reachable while content scrolls', async ({ page }) => {\n   const runtimeErrors = collectRuntimeErrors(page)",
          "actualWorkers": 2
        },
        "id": "chromium-mobile",
        "name": "chromium-mobile",
        "testDir": "/home/runner/work/dr-card-ui/dr-card-ui/tests/e2e",
        "testIgnore": [],
        "testMatch": [
          "**/*.@(spec|test).?(c|m)[jt]s?(x)"
        ],
        "timeout": 30000
      }
    ],
    "quiet": false,
    "reporter": [
      [
        "line",
        null
      ],
      [
        "html",
        {
          "outputFolder": "playwright-report",
          "open": "never"
        }
      ],
      [
        "json",
        {
          "outputFile": "test-results/playwright-results.json"
        }
      ]
    ],
    "reportSlowTests": {
      "max": 5,
      "threshold": 300000
    },
    "shard": null,
    "tags": [],
    "updateSnapshots": "missing",
    "updateSourceMethod": "patch",
    "version": "1.63.0",
    "workers": 2,
    "webServer": null
  },
  "suites": [
    {
      "title": "test-gate.spec.ts",
      "file": "test-gate.spec.ts",
      "column": 0,
      "line": 0,
      "specs": [
        {
          "title": "@test-gate build identity matches the production-like test policy",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 0,
                  "parallelIndex": 0,
                  "status": "passed",
                  "duration": 46,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:19:57.065Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-c3b6e0ce5d8c5d8734e5",
          "file": "test-gate.spec.ts",
          "line": 31,
          "column": 1
        },
        {
          "title": "@test-gate browser entry does not start the app router",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 1,
                  "parallelIndex": 1,
                  "status": "passed",
                  "duration": 1123,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:19:57.076Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-8ed37c8041402a2a6b34",
          "file": "test-gate.spec.ts",
          "line": 40,
          "column": 1
        },
        {
          "title": "@test-gate / shows the unsupported-host notice in a browser",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 0,
                  "parallelIndex": 0,
                  "status": "passed",
                  "duration": 1193,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:19:57.164Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-b030d57053896c93a550",
          "file": "test-gate.spec.ts",
          "line": 51,
          "column": 3
        },
        {
          "title": "@test-gate /profile shows the unsupported-host notice in a browser",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 1,
                  "parallelIndex": 1,
                  "status": "passed",
                  "duration": 1032,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:19:58.970Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-8605535a2807e17fcccc",
          "file": "test-gate.spec.ts",
          "line": 51,
          "column": 3
        },
        {
          "title": "@test-gate /settings shows the unsupported-host notice in a browser",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 0,
                  "parallelIndex": 0,
                  "status": "passed",
                  "duration": 988,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:19:59.033Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-4a2fdc9884c1725de4f8",
          "file": "test-gate.spec.ts",
          "line": 51,
          "column": 3
        },
        {
          "title": "@test-gate gated browser entry issues no business API request",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 1,
                  "parallelIndex": 1,
                  "status": "passed",
                  "duration": 1019,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:20:00.023Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-09f38a2b8ed735e192d8",
          "file": "test-gate.spec.ts",
          "line": 65,
          "column": 1
        },
        {
          "title": "@test-gate injected native host bypasses the notice and starts the app",
          "ok": true,
          "tags": [
            "test-gate"
          ],
          "tests": [
            {
              "timeout": 30000,
              "annotations": [],
              "expectedStatus": "passed",
              "projectId": "chromium-mobile",
              "projectName": "chromium-mobile",
              "results": [
                {
                  "workerIndex": 0,
                  "parallelIndex": 0,
                  "status": "passed",
                  "duration": 1032,
                  "errors": [],
                  "stdout": [],
                  "stderr": [],
                  "retry": 0,
                  "startTime": "2026-10-12T10:20:00.046Z",
                  "annotations": [],
                  "attachments": []
                }
              ],
              "status": "expected"
            }
          ],
          "id": "37c772ca39f4c8e5dd7a-8caf237c4d42513fff2f",
          "file": "test-gate.spec.ts",
          "line": 80,
          "column": 1
        }
      ]
    }
  ],
  "errors": [],
  "stats": {
    "startTime": "2026-10-12T10:19:56.242Z",
    "duration": 4898.476,
    "expected": 7,
    "skipped": 0,
    "unexpected": 0,
    "flaky": 0
  }
}