feat: batch-plan Search results into Week Ahead (Closes #1196)
This commit is contained in:
1 parent
f557f06bc5
commit
78839a50fe
8 files changed
+305
-5
No files matched your search
@@ -41,3 +41,37 @@ def test_search_week_plan_sheet_fits_small_phones_with_touch_safe_controls(width
|
||||
"node => node.scrollWidth <= node.clientWidth"
|
||||
)
|
||||
browser.close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("width,height", [(320, 568), (390, 844)])
|
||||
def test_search_week_batch_review_fits_small_phones_without_horizontal_overflow(width, height):
|
||||
html = (FRONTEND / "index.html").read_text()
|
||||
with sync_api.sync_playwright() as playwright:
|
||||
try:
|
||||
browser = playwright.chromium.launch(headless=True)
|
||||
except Exception as error:
|
||||
pytest.skip(f"Chromium unavailable: {error}")
|
||||
page = browser.new_page(viewport={"width": width, "height": height})
|
||||
page.set_content(html)
|
||||
page.add_style_tag(path=FRONTEND / "dashboard.css")
|
||||
page.locator("#search-week-batch-list").evaluate(
|
||||
"""node => node.innerHTML = Array.from({length:4}, (_, index) =>
|
||||
`<div class="search-week-batch-row"><div><span class="small">stackchain/dashboard#${1196+index}</span>
|
||||
<strong>Substantial mobile planning issue ${index+1}</strong></div>
|
||||
<div class="search-week-batch-row-controls"><label>Day<select><option>Fri, Aug 21 · 2 planned</option></select></label>
|
||||
<label>Minutes<input type="number" value="45"></label></div></div>`).join('')"""
|
||||
)
|
||||
page.locator("#search-week-batch-review").evaluate("node => node.hidden = false")
|
||||
page.locator("#cmd-palette").evaluate("node => node.classList.add('open')")
|
||||
|
||||
expect = sync_api.expect
|
||||
expect(page.locator("#search-week-batch-review")).to_be_visible()
|
||||
expect(page.locator("#confirm-search-week-batch")).to_be_visible()
|
||||
for control in page.locator("#search-week-batch-review select, #search-week-batch-review input, #search-week-batch-review button").all():
|
||||
bounds = control.bounding_box()
|
||||
assert bounds and bounds["height"] >= 44
|
||||
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
|
||||
assert page.locator("#search-week-batch-review").evaluate(
|
||||
"node => node.scrollWidth <= node.clientWidth"
|
||||
)
|
||||
browser.close()
|
||||
@@ -83,6 +83,60 @@ process.stdout.write(JSON.stringify({{one,mixed:flow.releaseRepository()}}));
|
||||
assert run_node(script) == {"one": "stackchain/dashboard", "mixed": None}
|
||||
|
||||
|
||||
def test_week_batch_validates_every_row_before_claiming_and_flushes_one_staged_week():
|
||||
script = f"""
|
||||
const createSearchBatchPlan=require({json.dumps(str(SEARCH_BATCH_PLAN))});
|
||||
const createWeekBatchPlan=createSearchBatchPlan.createWeekBatchPlan;
|
||||
const calls=[];
|
||||
const days=[
|
||||
{{date:'2026-08-21',label:'Fri, Aug 21'}},
|
||||
{{date:'2026-08-22',label:'Sat, Aug 22'}},
|
||||
];
|
||||
const state={{
|
||||
'2026-08-21':{{ids:['stackchain/dashboard#1'],capacity_minutes:90,estimates:{{'stackchain/dashboard#1':30}}}},
|
||||
'2026-08-22':{{ids:[],capacity_minutes:60,estimates:{{}}}},
|
||||
}};
|
||||
const week={{
|
||||
load:async()=>{{calls.push('load');}},dates:()=>days,
|
||||
day:date=>state[date],placement:id=>null,
|
||||
place:(id,date,estimate,options)=>{{calls.push(['place',id,date,estimate,options.move]);state[date].ids.push(id);state[date].estimates[id]=estimate;return true;}},
|
||||
flush:async()=>{{calls.push('flush');}},
|
||||
}};
|
||||
const items=[
|
||||
{{kind:'issue',state:'open',repository:'stackchain/dashboard',number:2,title:'Two'}},
|
||||
{{kind:'issue',state:'open',repository:'stackchain/api',number:3,title:'Three'}},
|
||||
];
|
||||
const flow=createWeekBatchPlan({{
|
||||
week,identity:item=>item.repository+'#'+item.number,
|
||||
prepare:async item=>{{calls.push(['claim',item.number]);return {{...item,assigned_to_me:true}};}},
|
||||
}});
|
||||
(async()=>{{
|
||||
const invalid=await flow.run(items,{{
|
||||
'stackchain/dashboard#2':{{date:'2026-08-21',estimate:25}},
|
||||
'stackchain/api#3':{{date:'2026-08-22',estimate:0}},
|
||||
}});
|
||||
const before=calls.slice();
|
||||
const planned=await flow.run(items,{{
|
||||
'stackchain/dashboard#2':{{date:'2026-08-21',estimate:25}},
|
||||
'stackchain/api#3':{{date:'2026-08-22',estimate:20}},
|
||||
}});
|
||||
process.stdout.write(JSON.stringify({{invalid,before,planned,calls}}));
|
||||
}})();
|
||||
"""
|
||||
|
||||
result = run_node(script)
|
||||
assert result["invalid"]["status"] == "invalid"
|
||||
assert result["invalid"]["errors"] == [{"id": "stackchain/api#3", "reason": "estimate-required"}]
|
||||
assert result["before"] == ["load"]
|
||||
assert result["planned"]["status"] == "planned"
|
||||
assert result["planned"]["planned"] == 2
|
||||
assert result["planned"]["failed"] == []
|
||||
assert result["calls"].count("flush") == 1
|
||||
assert [call for call in result["calls"] if isinstance(call, list) and call[0] == "claim"] == [
|
||||
["claim", 2], ["claim", 3],
|
||||
]
|
||||
|
||||
|
||||
def test_mobile_search_exposes_touch_safe_durable_batch_planning_controls():
|
||||
html = HTML.read_text()
|
||||
dashboard = DASHBOARD.read_text()
|
||||
@@ -124,6 +178,29 @@ def test_mobile_search_exposes_touch_safe_durable_batch_planning_controls():
|
||||
assert "BASE + 'static/search-batch-plan.js'" in worker
|
||||
|
||||
|
||||
def test_mobile_search_exposes_batch_week_review_with_per_issue_day_and_estimate_controls():
|
||||
html = HTML.read_text()
|
||||
dashboard = DASHBOARD.read_text()
|
||||
css = CSS.read_text()
|
||||
controller = SEARCH_BATCH_PLAN.read_text()
|
||||
|
||||
assert 'id="week-selected-search-results"' in html
|
||||
assert 'id="search-week-batch-review" role="dialog" aria-modal="true"' in html
|
||||
assert 'id="search-week-batch-list"' in html
|
||||
assert 'id="search-week-batch-summary" aria-live="polite"' in html
|
||||
assert 'id="cancel-search-week-batch"' in html
|
||||
assert 'id="confirm-search-week-batch"' in html
|
||||
assert "createWeekBatchPlan" in controller
|
||||
assert "data-search-week-batch-date" in controller
|
||||
assert "data-search-week-batch-estimate" in controller
|
||||
assert "overload-confirmation-required" in controller
|
||||
assert "weekPlan, localStorage" in dashboard
|
||||
assert ".search-week-batch-review" in css
|
||||
assert "max-height:calc(100dvh - 48px)" in css
|
||||
assert "min-height:44px" in css
|
||||
assert "env(safe-area-inset-bottom)" in css
|
||||
|
||||
|
||||
def test_selected_search_issues_choose_one_future_time_before_durable_batch_defer():
|
||||
script = f"""
|
||||
const createSearchBatchPlan=require({json.dumps(str(SEARCH_BATCH_PLAN))});
|
||||
|
||||
Reference in new issue
Block a user