feat: persist weekly availability defaults (Closes #1256)
This commit is contained in:
1 parent
ea3204c78c
commit
1ae8533702
8 files changed
+255
-17
No files matched your search
@@ -65,6 +65,8 @@ def test_release_artifact_plans_seven_touch_safe_mobile_dates(
|
||||
route.fulfill(status=200, content_type="application/json", body=json.dumps({
|
||||
"revision": body["base_revision"] + 1,
|
||||
"timezone": body["timezone"], "days": body["days"],
|
||||
**({"availability_defaults": body["availability_defaults"]}
|
||||
if "availability_defaults" in body else {}),
|
||||
}))
|
||||
return
|
||||
route.fulfill(status=200, content_type="application/json", body=json.dumps({
|
||||
@@ -107,6 +109,29 @@ def test_release_artifact_plans_seven_touch_safe_mobile_dates(
|
||||
"Week Ahead overview · no changes made."
|
||||
)
|
||||
expect(page.locator("#confirm-week-plan")).to_be_hidden()
|
||||
availability = page.locator("#open-week-availability")
|
||||
expect(availability).to_be_visible()
|
||||
availability_bounds = availability.bounding_box()
|
||||
assert availability_bounds and availability_bounds["height"] >= 44
|
||||
availability.click()
|
||||
editor = page.locator("#week-availability-editor")
|
||||
expect(editor).to_be_visible()
|
||||
capacities = editor.locator("[data-weekday-capacity]")
|
||||
expect(capacities).to_have_count(7)
|
||||
for index, minutes in enumerate((480, 480, 480, 480, 360, 0, 0)):
|
||||
capacities.nth(index).fill(str(minutes))
|
||||
bounds = capacities.nth(index).bounding_box()
|
||||
assert bounds and bounds["height"] >= 44
|
||||
assert page.evaluate("document.documentElement.scrollWidth <= window.innerWidth")
|
||||
page.locator("#save-week-availability").click()
|
||||
expect(editor).to_be_hidden()
|
||||
expect(page.locator("#week-review-status")).to_have_text("Weekly availability saved.")
|
||||
assert saved[-1]["availability_defaults"] == [480, 480, 480, 480, 360, 0, 0]
|
||||
availability.click()
|
||||
page.locator("#disable-week-availability").click()
|
||||
expect(editor).to_be_hidden()
|
||||
expect(page.locator("#week-review-status")).to_have_text("Weekly availability disabled.")
|
||||
assert "availability_defaults" not in saved[-1]
|
||||
edit_week = page.locator("#edit-week-plan")
|
||||
expect(edit_week).to_be_visible()
|
||||
remove_actions = cards.first.locator("[data-week-unplan]")
|
||||
|
||||
@@ -51,6 +51,42 @@ def test_week_plan_is_encrypted_account_scoped_revisioned_and_preserves_other_da
|
||||
assert conflict.value.snapshot == updated
|
||||
|
||||
|
||||
def test_week_plan_persists_bounded_weekly_availability_with_the_revisioned_plan(tmp_path):
|
||||
path = tmp_path / "today.sqlite3"
|
||||
store = TodayStore(path, encryption_key=b"w" * 32)
|
||||
defaults = [0, 480, 420, 360, 300, 240, 0]
|
||||
|
||||
saved = store.replace_week(
|
||||
"timmy", base_revision=0, days=sample_days(), timezone="UTC",
|
||||
availability_defaults=defaults,
|
||||
)
|
||||
|
||||
assert saved["availability_defaults"] == defaults
|
||||
assert store.get_week("timmy")["availability_defaults"] == defaults
|
||||
retained = path.read_bytes()
|
||||
assert b'"availability_defaults"' not in retained
|
||||
|
||||
with pytest.raises(ValueError, match="seven weekday capacities"):
|
||||
store.replace_week(
|
||||
"timmy", base_revision=1, days=sample_days(), timezone="UTC",
|
||||
availability_defaults=[60] * 6,
|
||||
)
|
||||
|
||||
|
||||
def test_week_update_model_accepts_zero_capacity_weekday_defaults():
|
||||
payload = main.WeekPlanUpdate.model_validate({
|
||||
"base_revision": 2, "timezone": "UTC", "days": [],
|
||||
"availability_defaults": [0, 480, 480, 480, 480, 300, 0],
|
||||
})
|
||||
|
||||
assert payload.model_dump()["availability_defaults"] == [0, 480, 480, 480, 480, 300, 0]
|
||||
with pytest.raises(ValueError):
|
||||
main.WeekPlanUpdate.model_validate({
|
||||
"base_revision": 2, "timezone": "UTC", "days": [],
|
||||
"availability_defaults": [60] * 8,
|
||||
})
|
||||
|
||||
|
||||
def test_start_day_plan_selects_earliest_nonempty_week_date_without_mutation(tmp_path):
|
||||
store = TodayStore(
|
||||
tmp_path / "today.sqlite3", encryption_key=b"r" * 32,
|
||||
@@ -507,6 +543,7 @@ def test_reschedule_today_item_atomically_moves_it_into_selected_week_day(tmp_pa
|
||||
"timmy",
|
||||
base_revision=0,
|
||||
timezone="UTC",
|
||||
availability_defaults=[0, 480, 480, 480, 480, 0, 0],
|
||||
days=[{
|
||||
"plan_date": "2026-08-24",
|
||||
"ids": [active_id],
|
||||
@@ -531,6 +568,7 @@ def test_reschedule_today_item_atomically_moves_it_into_selected_week_day(tmp_pa
|
||||
)
|
||||
|
||||
assert result["today"]["ids"] == [other_id]
|
||||
assert result["week"]["availability_defaults"] == [0, 480, 480, 480, 480, 0, 0]
|
||||
assert result["week"]["days"] == [{
|
||||
"plan_date": "2026-08-24", "ids": [], "capacity_minutes": 120, "estimates": {},
|
||||
}, {
|
||||
|
||||
@@ -22,6 +22,23 @@ const createOutboxCoordinator = require({json.dumps(str(COORDINATOR))});
|
||||
return json.loads(completed.stdout)
|
||||
|
||||
|
||||
def test_weekly_availability_editor_is_mobile_safe_and_wired_to_durable_defaults():
|
||||
markup = INDEX.read_text()
|
||||
styles = CSS.read_text()
|
||||
source = CONTROLLER.read_text()
|
||||
|
||||
assert 'id="open-week-availability"' in markup
|
||||
assert 'id="week-availability-editor"' in markup
|
||||
assert markup.count('data-weekday-capacity=') == 7
|
||||
assert 'id="save-week-availability"' in markup
|
||||
assert 'id="disable-week-availability"' in markup
|
||||
assert 'aria-labelledby="week-availability-title"' in markup
|
||||
assert ".week-availability-grid input { min-height:44px" in styles
|
||||
assert "grid-template-columns:1fr" in styles
|
||||
assert "stageAvailabilityDefaults(values)" in source
|
||||
assert "Weekly availability saved" in source
|
||||
|
||||
|
||||
def test_conflicted_pull_review_has_accessible_touch_safe_recovery_actions():
|
||||
markup = INDEX.read_text()
|
||||
styles = CSS.read_text()
|
||||
@@ -177,6 +194,36 @@ console.log(JSON.stringify({before,moved,after,pending:week.pending()}));
|
||||
assert result["pending"]["days"][2]["estimates"] == {"shared": 30}
|
||||
|
||||
|
||||
def test_week_controller_applies_weekday_defaults_to_new_dates_and_keeps_date_overrides():
|
||||
result = run_controller("""
|
||||
const values=new Map();const requests=[];
|
||||
const storage={getItem:key=>values.get(key)||null,setItem:(key,value)=>values.set(key,value),removeItem:key=>values.delete(key)};
|
||||
const fetchJson=async(url,options={})=>{
|
||||
requests.push(options.body?JSON.parse(options.body):null);
|
||||
if(!options.method)return {revision:3,timezone:'UTC',availability_defaults:[60,120,180,240,300,0,0],days:[
|
||||
{plan_date:'2026-08-22',ids:['override'],capacity_minutes:45,estimates:{override:30}}
|
||||
]};
|
||||
return {revision:4,...JSON.parse(options.body)};
|
||||
};
|
||||
const week=createWeekPlan({storage,getLogin:()=> 'timmy',fetchJson,
|
||||
localDate:()=> '2026-08-20',timeZone:()=> 'UTC'});
|
||||
await week.load();
|
||||
const inherited=week.day('2026-08-21');
|
||||
const overridden=week.day('2026-08-22');
|
||||
const staged=week.stageAvailabilityDefaults([90,120,180,240,300,0,0]);
|
||||
await week.flush();
|
||||
console.log(JSON.stringify({inherited,overridden,staged,request:requests.at(-1),state:week.state()}));
|
||||
""")
|
||||
|
||||
assert result["inherited"]["capacity_minutes"] == 300
|
||||
assert result["inherited"]["capacity_source"] == "default"
|
||||
assert result["overridden"]["capacity_minutes"] == 45
|
||||
assert result["overridden"]["capacity_source"] == "override"
|
||||
assert result["staged"]["sync_pending"] is True
|
||||
assert result["request"]["availability_defaults"] == [90, 120, 180, 240, 300, 0, 0]
|
||||
assert result["state"]["availability_defaults"] == [90, 120, 180, 240, 300, 0, 0]
|
||||
|
||||
|
||||
def test_week_controller_applies_seven_imported_capacities_in_one_local_transition():
|
||||
result = run_controller("""
|
||||
const values=new Map();let writes=0;
|
||||
|
||||
Reference in new issue
Block a user