diff --git a/tests/skill_script_zhihu_navigate_test.py b/tests/skill_script_zhihu_navigate_test.py index 8b4f3f2..9d67f71 100644 --- a/tests/skill_script_zhihu_navigate_test.py +++ b/tests/skill_script_zhihu_navigate_test.py @@ -25,36 +25,107 @@ def run_open_creator_entry(*, body_text: str, selectors: dict[str, list[dict]]) function createNode(spec) {{ const node = {{ + id: String(spec?.id ?? ''), tagName: String(spec?.tagName || 'DIV').toUpperCase(), textContent: String(spec?.textContent ?? ''), innerText: String(spec?.innerText ?? spec?.textContent ?? ''), href: String(spec?.href ?? ''), + className: String(spec?.className ?? ''), + role: String(spec?.role ?? ''), + tabIndex: Number.isFinite(spec?.tabIndex) ? Number(spec.tabIndex) : -1, clicked: false, click() {{ this.clicked = true; }}, + getAttribute(name) {{ + if (name === 'class') {{ + return this.className; + }} + if (name === 'role') {{ + return this.role; + }} + if (name === 'href') {{ + return this.href; + }} + if (name === 'tabindex' && this.tabIndex >= 0) {{ + return String(this.tabIndex); + }} + return ''; + }}, getBoundingClientRect() {{ return {{ width: spec?.visible === false ? 0 : 120, height: spec?.visible === false ? 0 : 32, }}; }}, + parentElement: null, + closest(selector) {{ + let current = this; + while (current) {{ + if (matchesSelector(current, selector)) {{ + return current; + }} + current = current.parentElement; + }} + return null; + }}, }}; return node; }} const created = new Map(); + const nodesById = new Map(); + function matchesSelector(node, selector) {{ + const parts = selector.split(',').map((part) => part.trim()).filter(Boolean); + return parts.some((part) => {{ + if (part === 'a[href]') {{ + return node.tagName === 'A' && Boolean(node.href); + }} + if (part === 'button') {{ + return node.tagName === 'BUTTON'; + }} + if (part === '[role=\"button\"]' || part === "[role='button']") {{ + return node.role === 'button'; + }} + if (part === '[tabindex]') {{ + return node.tabIndex >= 0; + }} + if (part === 'div') {{ + return node.tagName === 'DIV'; + }} + if (part === 'span') {{ + return node.tagName === 'SPAN'; + }} + return false; + }}); + }} function createNodeList(selector) {{ const specs = selectorMap[selector] || []; return specs.map((spec, index) => {{ const key = `${{selector}}#${{index}}`; if (!created.has(key)) {{ - created.set(key, createNode(spec)); + const node = createNode(spec); + created.set(key, node); + if (node.id) {{ + nodesById.set(node.id, node); + }} }} return created.get(key); }}); }} + for (const selector of Object.keys(selectorMap)) {{ + createNodeList(selector); + }} + for (const node of created.values()) {{ + const parentId = selectorMap && Object.values(selectorMap) + .flat() + .find((spec) => String(spec?.id ?? '') === node.id)?.parentId; + if (parentId && nodesById.has(parentId)) {{ + node.parentElement = nodesById.get(parentId); + }} + }} + const bodyNode = createNode({{ tagName: 'BODY', textContent: bodyText, innerText: bodyText }}); const context = {{ args: {{ desired_target: 'article_editor' }}, @@ -141,6 +212,32 @@ class SkillScriptZhihuNavigateTest(unittest.TestCase): self.assertEqual(payload["result"]["status"], "creator_entry_clicked") self.assertTrue(payload["created"]["a[href], button, [role='button']#0"]["clicked"]) + def test_open_creator_entry_clicks_clickable_ancestor_for_nested_write_text(self): + payload = run_open_creator_entry( + body_text="创作者中心 写文章", + selectors={ + "a[href], button, [role='button']": [], + "div, span, [tabindex]": [ + { + "id": "ancestor", + "tagName": "div", + "className": "creator-entry", + "tabIndex": 0, + "textContent": "", + }, + { + "id": "label", + "parentId": "ancestor", + "tagName": "span", + "textContent": "写文章", + }, + ], + }, + ) + + self.assertEqual(payload["result"]["status"], "creator_entry_clicked") + self.assertTrue(payload["created"]["div, span, [tabindex]#0"]["clicked"]) + if __name__ == "__main__": unittest.main()