From 57001979a857f5aa9d81db253d02ee74489d230c Mon Sep 17 00:00:00 2001 From: libo Date: Fri, 20 Mar 2026 09:29:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(drawer):=20=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E6=8A=BD=E5=B1=89=E6=BB=9A=E5=8A=A8=E6=9D=A1=E6=A0=B7=E5=BC=8F?= =?UTF-8?q?=E5=B9=B6=E9=98=BB=E6=AD=A2=E6=BB=9A=E5=8A=A8=E7=A9=BF=E9=80=8F?= =?UTF-8?q?=E8=87=B3=E5=A4=96=E9=83=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/content.css | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/content.css b/src/content.css index 82000e3..c3eae85 100644 --- a/src/content.css +++ b/src/content.css @@ -357,6 +357,24 @@ body.ld-drawer-page-open #ld-drawer-root { flex: 1; min-height: 0; overflow: auto; + overscroll-behavior: contain; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar { + width: 6px; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-track { + background: transparent; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb { + background: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent); + border-radius: 999px; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb:hover { + background: color-mix(in srgb, var(--tertiary, #3b82f6) 64%, transparent); } #ld-drawer-root .ld-drawer-content { From 1d97017df66b96f0dc40d6b0fa239f4b535c2dde Mon Sep 17 00:00:00 2001 From: TwoMonkeys Date: Fri, 20 Mar 2026 21:42:26 +0800 Subject: [PATCH 2/2] test: cover drawer scroll containment --- doc/agent-smoke-cases.md | 25 +++++++ doc/testing-standard.md | 2 + scripts/agent-smoke.sh | 142 +++++++++++++++++++++++++++++++++++++++ src/content.css | 2 + 4 files changed, 171 insertions(+) diff --git a/doc/agent-smoke-cases.md b/doc/agent-smoke-cases.md index cf90013..37c9978 100644 --- a/doc/agent-smoke-cases.md +++ b/doc/agent-smoke-cases.md @@ -221,6 +221,31 @@ bash scripts/agent-smoke.sh --cdp-port 9222 --cases AGENT-CHROME-001,AGENT-CHROM 3. 刷新完成后按钮恢复为 `刷新` 4. 过程中 `location.href` 保持在列表页,抽屉不关闭 +### AGENT-CHROME-013:抽屉滚动不穿透页面 + +范围: +`#8` 回归用例,2026-03-20 新增 + +状态: +当前不在默认批量脚本中 + +建议目标: +优先选一条抽屉内容足够长、可稳定滚动的公开主题 + +步骤: + +1. 打开任一列表话题抽屉 +2. 在 `选项` 中切到 `浮层模式` +3. 让外层列表页保持在非顶部位置 +4. 把抽屉内容滚到底 +5. 将鼠标保持在抽屉内容区内,继续向下滚轮 + +断言: + +1. 外层页面滚动位置保持不变,不应被抽屉内滚动带着继续下移 +2. 抽屉保持打开 +3. `location.href` 仍然停留在列表页 + ## 3. 记录格式 执行完用例后,建议按下面格式留证据: diff --git a/doc/testing-standard.md b/doc/testing-standard.md index b5308c2..1f8c4c7 100644 --- a/doc/testing-standard.md +++ b/doc/testing-standard.md @@ -151,6 +151,8 @@ bash scripts/agent-smoke.sh --cdp-port 9222 1. `AGENT-CHROME-006` 浮层模式下,抽屉打开时点击另一个列表主题,应直接切换抽屉内容,不能先关闭再第二次打开 +2. `AGENT-CHROME-013` + 抽屉内容滚到底后继续滚轮时,外层列表页不应继续跟着滚动 ## 4. PR 证据格式 diff --git a/scripts/agent-smoke.sh b/scripts/agent-smoke.sh index 6bbce0e..d037849 100755 --- a/scripts/agent-smoke.sh +++ b/scripts/agent-smoke.sh @@ -737,6 +737,147 @@ run_010() { fi } +drawer_body_state() { + local script + script=$(cat <<'EOF' +(() => { + const body = document.querySelector("#ld-drawer-root .ld-drawer-body"); + if (!(body instanceof HTMLElement)) { + return { ok: false, reason: "drawer-body-not-found" }; + } + + const rect = body.getBoundingClientRect(); + return { + ok: true, + bodyTop: Math.round(body.scrollTop), + maxScrollTop: Math.round(body.scrollHeight - body.clientHeight), + clientHeight: Math.round(body.clientHeight), + centerX: Math.round(rect.left + rect.width / 2), + centerY: Math.round(rect.top + Math.min(rect.height / 2, 300)) + }; +})() +EOF +) + ab_eval "$script" +} + +page_scroll_state() { + local script + script=$(cat <<'EOF' +(() => ({ + docTop: Math.round(document.scrollingElement?.scrollTop || 0), + pageOpen: document.body.classList.contains("ld-drawer-page-open"), + overlay: document.body.classList.contains("ld-drawer-mode-overlay"), + title: document.querySelector("#ld-drawer-root .ld-drawer-title")?.textContent?.trim() || null +}))() +EOF +) + ab_eval "$script" +} + +set_page_scroll_top() { + local top=$1 + local top_json script + top_json=$(printf '%s' "$top" | jq -Rs 'tonumber') + script=$(cat < { + const top = $top_json; + window.scrollTo(0, top); + return { + docTop: Math.round(document.scrollingElement?.scrollTop || 0), + windowY: Math.round(window.scrollY) + }; +})() +EOF +) + ab_eval "$script" +} + +prepare_scrollable_drawer_topic() { + local index=0 + local open_result body_state + + while [ "$index" -lt 5 ]; do + open_result=$(open_topic_by_index "$index") || { + index=$((index + 1)) + continue + } + ensure_settings_closed + body_state=$(drawer_body_state) + + if [ "$(echo "$body_state" | jq -r '.ok')" = "true" ] \ + && [ "$(echo "$body_state" | jq -r '.maxScrollTop')" -ge 1200 ]; then + echo "$open_result" + return 0 + fi + + index=$((index + 1)) + done + + return 1 +} + +run_013() { + local case_id="AGENT-CHROME-013" + local open_result overlay_result scroll_result body_state before after + local center_x center_y before_doc_top after_doc_top + + open_result=$(prepare_scrollable_drawer_topic) || { + record_fail "$case_id" "前 5 个列表主题里没有足够长、可滚动的抽屉内容" + return 0 + } + + overlay_result=$(set_overlay_mode) + if [ "$(echo "$overlay_result" | jq -r '.ok')" != "true" ]; then + record_fail "$case_id" "无法切到浮层模式" + return 0 + fi + + ensure_settings_closed + scroll_result=$(set_page_scroll_top 900) + body_state=$(ab_eval '(() => { + const body = document.querySelector("#ld-drawer-root .ld-drawer-body"); + if (!(body instanceof HTMLElement)) { + return { ok: false, reason: "drawer-body-not-found" }; + } + + body.scrollTop = body.scrollHeight; + const rect = body.getBoundingClientRect(); + return { + ok: true, + bodyTop: Math.round(body.scrollTop), + maxScrollTop: Math.round(body.scrollHeight - body.clientHeight), + centerX: Math.round(rect.left + rect.width / 2), + centerY: Math.round(rect.top + Math.min(rect.height / 2, 300)) + }; + })()') + + if [ "$(echo "$body_state" | jq -r '.ok')" != "true" ]; then + record_fail "$case_id" "无法读取抽屉滚动容器" + return 0 + fi + + center_x=$(echo "$body_state" | jq -r '.centerX') + center_y=$(echo "$body_state" | jq -r '.centerY') + ab mouse move "$center_x" "$center_y" >/dev/null + before=$(page_scroll_state) + ab mouse wheel 1200 >/dev/null + ab wait 300 >/dev/null + after=$(page_scroll_state) + before_doc_top=$(echo "$before" | jq -r '.docTop') + after_doc_top=$(echo "$after" | jq -r '.docTop') + + if [ "$(echo "$scroll_result" | jq -r '.docTop')" -ge 800 ] \ + && [ "$before_doc_top" -ge 800 ] \ + && [ "$after_doc_top" = "$before_doc_top" ] \ + && [ "$(echo "$after" | jq -r '.pageOpen')" = "true" ] \ + && [ "$(echo "$after" | jq -r '.overlay')" = "true" ]; then + record_pass "$case_id" "title=$(echo "$after" | jq -r '.title // empty') docTop=$after_doc_top" + else + record_fail "$case_id" "scroll=$(echo "$scroll_result" | jq -c '.') body=$(echo "$body_state" | jq -c '{bodyTop, maxScrollTop, centerX, centerY}') before=$(echo "$before" | jq -c '{docTop, pageOpen, overlay, title}') after=$(echo "$after" | jq -c '{docTop, pageOpen, overlay, title}')" + fi +} + run_case() { local case_id=$1 case "$case_id" in @@ -749,6 +890,7 @@ run_case() { AGENT-CHROME-008) run_008 ;; AGENT-CHROME-009) run_009 ;; AGENT-CHROME-010) run_010 ;; + AGENT-CHROME-013) run_013 ;; *) record_fail "$case_id" "未知用例 ID" ;; diff --git a/src/content.css b/src/content.css index c3eae85..e679656 100644 --- a/src/content.css +++ b/src/content.css @@ -358,6 +358,8 @@ body.ld-drawer-page-open #ld-drawer-root { min-height: 0; overflow: auto; overscroll-behavior: contain; + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent) transparent; } #ld-drawer-root .ld-drawer-body::-webkit-scrollbar {