diff --git a/AGENTS.md b/AGENTS.md index 7f77e95..4c66372 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -94,6 +94,7 @@ bash scripts/agent-smoke.sh --cdp-port 9222 - 验证 Chrome ZIP 与 Firefox XPI 能被正常产出 - 验证产物中包含 `manifest.json`、`src/content.js`、`src/content.css` - 验证 Firefox 产物中的 `browser_specific_settings.gecko.id` 存在 +- 验证 Firefox 产物中的 `browser_specific_settings.gecko.data_collection_permissions.required` 为 `["none"]` - 验证默认批量 agent Chrome smoke 用例 对应 CI: diff --git a/doc/testing-standard.md b/doc/testing-standard.md index 1f8c4c7..22040f7 100644 --- a/doc/testing-standard.md +++ b/doc/testing-standard.md @@ -51,6 +51,7 @@ bash scripts/check-release-artifacts.sh /tmp/linux-do-sidepeek-dist/linux-do-sid 1. Chrome ZIP 与 Firefox XPI 能正常产出 2. 产物中包含 `manifest.json`、`src/content.js`、`src/content.css` 3. Firefox 产物中的 `browser_specific_settings.gecko.id` 存在 +4. Firefox 产物中的 `browser_specific_settings.gecko.data_collection_permissions.required` 为 `["none"]` ### Tier 2:agent 辅助 Chrome smoke diff --git a/manifest.json b/manifest.json index 1702a48..37cfc2e 100644 --- a/manifest.json +++ b/manifest.json @@ -5,7 +5,12 @@ "description": "Preview Linux.do topics in a right-side drawer without leaving the current page.", "browser_specific_settings": { "gecko": { - "id": "@linux-do-sidepeek" + "id": "@linux-do-sidepeek", + "data_collection_permissions": { + "required": [ + "none" + ] + } } }, "host_permissions": [ diff --git a/scripts/agent-smoke.sh b/scripts/agent-smoke.sh index d037849..6d48287 100755 --- a/scripts/agent-smoke.sh +++ b/scripts/agent-smoke.sh @@ -161,23 +161,95 @@ EOF click_topic_link_by_index() { local index=$1 + local mode=${2:-any} local script script=$(cat < { - const candidates = Array.from(document.querySelectorAll("#list-area a.title[data-topic-id]")).filter((link) => { + const mode = $(printf '%s' "$mode" | jq -Rs .); + + function getTopicMeta(link) { + try { + const parsed = new URL(link.href, location.href); + const segments = parsed.pathname.replace(/\/+$/, "").split("/").filter(Boolean); + if (segments[0] !== "t") { + return { targeted: false, targetSegments: [] }; + } + + const first = segments[1] || ""; + const second = segments[2] || ""; + const firstIsNumber = /^\\d+$/.test(first); + const secondIsNumber = /^\\d+$/.test(second); + let extraSegments = []; + + if (firstIsNumber) { + extraSegments = segments.slice(3).filter(Boolean); + } else if (secondIsNumber) { + extraSegments = segments.slice(4).filter(Boolean); + } + + return { + targeted: extraSegments.length > 0, + targetSegments: extraSegments + }; + } catch { + return { targeted: false, targetSegments: [] }; + } + } + + const candidates = Array.from(document.querySelectorAll("#list-area a.title[data-topic-id]")).map((link) => { const text = link.textContent?.trim(); - return Boolean(text) && !link.closest("#ld-drawer-root"); - }); + if (!text || link.closest("#ld-drawer-root")) { + return null; + } + + const meta = getTopicMeta(link); + return { + link, + text, + href: link.href, + targeted: meta.targeted, + targetSegments: meta.targetSegments + }; + }).filter(Boolean); + + const selectedCandidates = (() => { + if (mode === "untargeted") { + return candidates.filter((candidate) => !candidate.targeted); + } - const link = candidates[$index]; - if (!link) { - return { ok: false, reason: "topic-not-found", count: candidates.length }; + if (mode === "refreshable") { + return candidates.filter((candidate) => !candidate.targeted || ( + candidate.targetSegments.length === 1 && candidate.targetSegments[0] === "last" + )); + } + + return candidates; + })(); + + const candidate = selectedCandidates[$index]; + if (!candidate) { + return { + ok: false, + reason: candidates.length > 0 && selectedCandidates.length === 0 + ? "topic-not-found-after-filter" + : "topic-not-found", + count: candidates.length, + filteredCount: selectedCandidates.length, + mode + }; } - const text = link.textContent.trim(); - const href = link.href; - link.click(); - return { ok: true, text, href, count: candidates.length }; + candidate.link.click(); + return { + ok: true, + text: candidate.text, + href: candidate.href, + count: candidates.length, + filteredCount: selectedCandidates.length, + targeted: candidate.targeted, + targetSegments: candidate.targetSegments, + mode + }; })() EOF ) @@ -186,9 +258,10 @@ EOF open_topic_by_index() { local index=$1 + local mode=${2:-any} local result navigate_latest - result=$(click_topic_link_by_index "$index") + result=$(click_topic_link_by_index "$index" "$mode") if [ "$(echo "$result" | jq -r '.ok')" != "true" ]; then echo "$result" return 1 @@ -233,6 +306,28 @@ EOF ab_eval "$script" } +wait_for_refresh_idle() { + local attempts=0 + local state='{}' + + while [ "$attempts" -lt 20 ]; do + state=$(refresh_button_state) + if [ "$(echo "$state" | jq -r '.exists')" = "true" ] \ + && [ "$(echo "$state" | jq -r '.hidden')" = "false" ] \ + && [ "$(echo "$state" | jq -r '.disabled')" = "false" ] \ + && [ "$(echo "$state" | jq -r '.text // empty')" = "刷新" ]; then + echo "$state" + return 0 + fi + + ab wait 300 >/dev/null + attempts=$((attempts + 1)) + done + + echo "$state" + return 1 +} + set_overlay_mode() { local script ensure_settings_open @@ -682,14 +777,18 @@ run_009() { run_010() { local case_id="AGENT-CHROME-010" - local original_settings setup_result before click after final_state + local open_result original_settings setup_result before click after final_state local passed=0 local detail="" - open_topic_by_index 0 >/dev/null || { - record_fail "$case_id" "无法打开抽屉" + if ! open_result=$(open_topic_by_index 0 refreshable); then + if [ "$(echo "$open_result" | jq -r '.reason // empty')" = "topic-not-found-after-filter" ]; then + record_skip "$case_id" "当前列表页没有可用于刷新断言的普通主题或 /last 主题链接" + else + record_fail "$case_id" "无法打开抽屉: $open_result" + fi return 0 - } + fi original_settings=$(settings_values) if [ -z "$(echo "$original_settings" | jq -r '.previewMode // empty')" ]; then @@ -705,8 +804,9 @@ run_010() { before=$(refresh_button_state) click=$(click_refresh_button) - ab wait 1800 >/dev/null - after=$(refresh_button_state) + if ! after=$(wait_for_refresh_idle); then + : + fi final_state=$(drawer_state) restore_settings_subset "$original_settings" diff --git a/scripts/check-release-artifacts.sh b/scripts/check-release-artifacts.sh index 1493d46..25fac04 100755 --- a/scripts/check-release-artifacts.sh +++ b/scripts/check-release-artifacts.sh @@ -47,4 +47,10 @@ unzip -p "$FIREFOX_XPI" manifest.json | jq -e ' | type == "string" and length > 0 ' >/dev/null +echo "[artifact-check] firefox data collection permissions" +unzip -p "$FIREFOX_XPI" manifest.json | jq -e ' + .browser_specific_settings.gecko.data_collection_permissions.required + | type == "array" and . == ["none"] +' >/dev/null + echo "[artifact-check] ok" diff --git a/src/content.css b/src/content.css index e679656..1f062b2 100644 --- a/src/content.css +++ b/src/content.css @@ -362,20 +362,40 @@ body.ld-drawer-page-open #ld-drawer-root { scrollbar-color: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent) transparent; } -#ld-drawer-root .ld-drawer-body::-webkit-scrollbar { +#ld-drawer-root .ld-drawer-settings-card, +#ld-drawer-root .ld-drawer-reply-panel { + overscroll-behavior: contain; +} + +#ld-drawer-root .ld-drawer-settings-card, +#ld-drawer-root .ld-drawer-reply-panel { + scrollbar-width: thin; + scrollbar-color: color-mix(in srgb, var(--tertiary, #3b82f6) 40%, transparent) transparent; +} + +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar { width: 6px; + height: 6px; } -#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-track { +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-track, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-track, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar-track { background: transparent; } -#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb { +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-thumb, +#ld-drawer-root .ld-drawer-reply-panel::-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 { +#ld-drawer-root .ld-drawer-body::-webkit-scrollbar-thumb:hover, +#ld-drawer-root .ld-drawer-settings-card::-webkit-scrollbar-thumb:hover, +#ld-drawer-root .ld-drawer-reply-panel::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--tertiary, #3b82f6) 64%, transparent); }