feat: act on Search and Following comments (Closes #1388)
This commit is contained in:
1 parent
0682bbcc2c
commit
7662cca9e6
9 files changed
+202
-46
No files matched your search
@@ -75,3 +75,41 @@ def test_comment_reactions_are_touch_safe_and_focus_preserving_at_320px():
|
||||
["api/v1/repos/stackchain/api/issues/7/comments/91/reactions/heart", "PUT"],
|
||||
]
|
||||
browser.close()
|
||||
|
||||
|
||||
def test_search_following_conversation_actions_are_touch_safe_at_320px():
|
||||
with sync_playwright() as playwright:
|
||||
browser = playwright.chromium.launch(headless=True)
|
||||
page = browser.new_page(viewport={"width": 320, "height": 568})
|
||||
page.set_content(
|
||||
'<main><section id="search-preview-conversation" class="search-preview-conversation">'
|
||||
'<div id="search-preview-comments"></div>'
|
||||
'<div id="search-preview-conversation-status"></div>'
|
||||
'<button id="retry-search-preview-conversation" hidden></button>'
|
||||
'<button id="load-older-search-preview-comments" hidden></button>'
|
||||
'</section></main>'
|
||||
)
|
||||
page.add_style_tag(path=FRONTEND / "dashboard.css")
|
||||
page.add_script_tag(path=FRONTEND / "search-preview.js")
|
||||
page.add_script_tag(path=FRONTEND / "comment-actions.js")
|
||||
page.evaluate(
|
||||
"""
|
||||
const actions=createCommentActions({fetchJson:async()=>({}),getLogin:()=> 'timmy'});
|
||||
renderSearchPreviewConversation({status:'ready',comments:[
|
||||
{id:41,author:'timmy',body:'Correct this from Following'},
|
||||
{id:42,author:'alexander',body:'React without leaving Search'},
|
||||
]},document,value=>String(value),value=>String(value),value=>String(value),actions);
|
||||
"""
|
||||
)
|
||||
|
||||
expect(page.locator(".search-preview-comment.issue-comment")).to_have_count(2)
|
||||
expect(page.get_by_role("button", name="Edit")).to_have_count(1)
|
||||
expect(page.get_by_role("button", name="Delete")).to_have_count(1)
|
||||
expect(page.get_by_role("button", name="React to this comment")).to_have_count(2)
|
||||
for control in page.locator("[data-comment-action], [data-comment-reactions-open]").all():
|
||||
box = control.bounding_box()
|
||||
assert box is not None and box["height"] >= 44
|
||||
assert page.evaluate(
|
||||
"document.documentElement.scrollWidth > document.documentElement.clientWidth"
|
||||
) is False
|
||||
browser.close()
|
||||
@@ -133,3 +133,75 @@ def test_comment_action_retry_is_a_phone_sized_inline_control():
|
||||
rule = css.split(".conversation-actions-retry", 1)[1].split("}", 1)[0]
|
||||
assert "min-height:44px" in rule
|
||||
assert "max-width:100%" in rule
|
||||
|
||||
|
||||
def test_search_and_following_preview_hydrates_owned_comment_actions_lazily():
|
||||
preview = FRONTEND / "search-preview.js"
|
||||
script = f"""
|
||||
require({json.dumps(str(preview))});
|
||||
const nodes = {{
|
||||
'#search-preview-comments': {{innerHTML:'',textContent:''}},
|
||||
'#search-preview-conversation-status': {{textContent:''}},
|
||||
'#retry-search-preview-conversation': {{hidden:true}},
|
||||
'#load-older-search-preview-comments': {{hidden:true,disabled:false}},
|
||||
}};
|
||||
const document = {{querySelector: selector => nodes[selector]}};
|
||||
const actions = {{actionHtml: comment => comment.author === 'timmy'
|
||||
? '<button data-comment-action="edit">Edit</button><button data-comment-reactions-open>React</button>'
|
||||
: '<button data-comment-reactions-open>React</button>'}};
|
||||
renderSearchPreviewConversation({{
|
||||
status:'ready', reviewedAt:'2026-08-25T08:00:00Z',
|
||||
comments:[
|
||||
{{id:41,author:'timmy',body:'Mine',created_at:'2026-08-25T09:00:00Z'}},
|
||||
{{id:42,author:'alexander',body:'Theirs',created_at:'2026-08-25T07:00:00Z'}},
|
||||
],
|
||||
}}, document, String, String, String, actions);
|
||||
process.stdout.write(JSON.stringify({{html:nodes['#search-preview-comments'].innerHTML}}));
|
||||
"""
|
||||
output = run_node(script)["html"]
|
||||
|
||||
assert output.count('class="search-preview-comment issue-comment') == 2
|
||||
assert output.count("data-comment-reactions-open") == 2
|
||||
assert output.count('data-comment-action="edit"') == 1
|
||||
assert 'data-comment-id="41"' in output
|
||||
assert "new-since-review" in output
|
||||
|
||||
html = (FRONTEND / "index.html").read_text()
|
||||
javascript = (FRONTEND / "dashboard.js").read_text()
|
||||
assert 'id="retry-search-preview-comment-actions"' in html
|
||||
assert "showSearchConversationWithActions" in javascript
|
||||
assert "context:{kind:searchPreviewDetail.kind,item:searchPreviewDetail}" in javascript
|
||||
|
||||
|
||||
def test_search_preview_comment_pager_updates_exact_visible_comment_in_place():
|
||||
preview = FRONTEND / "search-preview.js"
|
||||
script = f"""
|
||||
const createSearchPreview = require({json.dumps(str(preview))});
|
||||
(async () => {{
|
||||
const states=[];
|
||||
const controller=createSearchPreview({{
|
||||
fetchJson:async item=>({{...item,title:'Visible'}}),
|
||||
fetchConversation:async()=>({{comments:[
|
||||
{{id:41,author:'timmy',body:'Old'}},{{id:42,author:'alexander',body:'Keep'}},
|
||||
],older_page:null}}),
|
||||
mutate:async()=>({{}}), onState:state=>states.push(state),
|
||||
}});
|
||||
await controller.open({{repository:'stackchain/api',number:9,kind:'pull'}});
|
||||
await new Promise(resolve=>setTimeout(resolve,0));
|
||||
const pager=controller.commentPager();
|
||||
pager.replace({{id:41,author:'timmy',body:'Corrected'}});
|
||||
const replaced=pager.snapshot().comments;
|
||||
pager.remove(41);
|
||||
process.stdout.write(JSON.stringify({{
|
||||
replaced:replaced.map(comment=>[comment.id,comment.body]),
|
||||
remaining:pager.snapshot().comments.map(comment=>comment.id),
|
||||
published:states.at(-1).conversation.comments.map(comment=>comment.id),
|
||||
}}));
|
||||
}})().catch(error=>{{console.error(error);process.exit(1);}});
|
||||
"""
|
||||
|
||||
assert run_node(script) == {
|
||||
"replaced": [[41, "Corrected"], [42, "Keep"]],
|
||||
"remaining": [42],
|
||||
"published": [42],
|
||||
}
|
||||
@@ -221,7 +221,7 @@ def test_markdown_renderer_allows_only_safe_links_and_keeps_html_inert():
|
||||
|
||||
|
||||
def test_all_read_only_work_bodies_use_the_shared_markdown_renderer():
|
||||
dashboard = (FRONTEND / "dashboard.js").read_text()
|
||||
dashboard = (FRONTEND / "dashboard.js").read_text() + (FRONTEND / "conversation.js").read_text()
|
||||
|
||||
expected_paths = (
|
||||
"renderMarkdown(comment.body || 'No comment body provided.')",
|
||||
|
||||
@@ -82,6 +82,7 @@ async def test_dashboard_mounts_mobile_new_activity_positioning_flow():
|
||||
assert '<script src="static/update-read-position.js"></script>' in html
|
||||
assert 'id="jump-update-new-activity"' in html
|
||||
assert "updateReadPosition.open(String(item.notification_id))" in html
|
||||
assert "updateReadPosition.ready(String(selectedUpdate?.notification_id || ''), newest)" in html
|
||||
source = html + (POSITIONER.parent / "conversation.js").read_text()
|
||||
assert "updateReadPosition.ready(String(getSelectedUpdate()?.notification_id || ''),newest)" in source
|
||||
assert ".update-new-activity" in html
|
||||
assert "BASE + 'static/update-read-position.js'" in (POSITIONER.parent / "service-worker.js").read_text()
|
||||
Reference in new issue
Block a user