97 lines
3.4 KiB
Python
97 lines
3.4 KiB
Python
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from src.views import dashboard
|
|
|
|
|
|
TODAY_WORK = Path(__file__).parents[1] / "frontend" / "today-work.js"
|
|
|
|
|
|
def run_node(script):
|
|
return subprocess.run(
|
|
["node", "-e", script], check=True, capture_output=True, text=True
|
|
).stdout
|
|
|
|
|
|
def test_today_queue_is_account_scoped_ordered_unique_and_bounded():
|
|
script = f"""
|
|
const createTodayWork = require({json.dumps(str(TODAY_WORK))});
|
|
const values = new Map();
|
|
const storage = {{
|
|
getItem: key => values.has(key) ? values.get(key) : null,
|
|
setItem: (key, value) => values.set(key, value),
|
|
removeItem: key => values.delete(key),
|
|
}};
|
|
let login = 'timmy';
|
|
const queue = createTodayWork({{storage, getLogin: () => login, limit: 3}});
|
|
const issue = number => ({{kind:'issue', repository:'stackchain/dashboard', number, title:'Issue ' + number}});
|
|
const first = queue.add(issue(1));
|
|
const duplicate = queue.add(issue(1));
|
|
queue.add(issue(2));
|
|
queue.add(issue(3));
|
|
const full = queue.add(issue(4));
|
|
queue.move(issue(3), 'up');
|
|
queue.move(issue(3), 'up');
|
|
const timmy = queue.reconcile([issue(1), issue(2), issue(3), issue(4)]).map(item => item.number);
|
|
const persisted = createTodayWork({{storage, getLogin: () => login, limit: 3}})
|
|
.reconcile([issue(1), issue(2), issue(3)]).map(item => item.number);
|
|
login = 'alexander';
|
|
const isolated = queue.reconcile([issue(1), issue(2), issue(3)]).map(item => item.number);
|
|
process.stdout.write(JSON.stringify({{first, duplicate, full, timmy, persisted, isolated}}));
|
|
"""
|
|
|
|
assert json.loads(run_node(script)) == {
|
|
"first": "added",
|
|
"duplicate": "exists",
|
|
"full": "full",
|
|
"timmy": [3, 1, 2],
|
|
"persisted": [3, 1, 2],
|
|
"isolated": [],
|
|
}
|
|
|
|
|
|
def test_today_queue_exposes_reorder_boundaries_for_touch_controls():
|
|
script = f"""
|
|
const createTodayWork = require({json.dumps(str(TODAY_WORK))});
|
|
const values = new Map();
|
|
const storage = {{
|
|
getItem: key => values.get(key) || null,
|
|
setItem: (key, value) => values.set(key, value),
|
|
removeItem: key => values.delete(key),
|
|
}};
|
|
const queue = createTodayWork({{storage, getLogin: () => 'timmy'}});
|
|
const item = number => ({{kind:'issue', repository:'stackchain/dashboard', number}});
|
|
queue.add(item(1)); queue.add(item(2)); queue.add(item(3));
|
|
process.stdout.write(JSON.stringify([
|
|
queue.position(item(1)), queue.position(item(2)), queue.position(item(3)), queue.position(item(9))
|
|
]));
|
|
"""
|
|
|
|
assert json.loads(run_node(script)) == [
|
|
{"can_up": False, "can_down": True},
|
|
{"can_up": True, "can_down": True},
|
|
{"can_up": True, "can_down": False},
|
|
{"can_up": False, "can_down": False},
|
|
]
|
|
|
|
|
|
@pytest.mark.anyio
|
|
async def test_dashboard_runs_the_curated_today_queue_as_a_mobile_work_flow():
|
|
html = await dashboard()
|
|
|
|
assert '<script src="static/today-work.js"></script>' in html
|
|
assert 'data-work-filter="today"' in html
|
|
assert 'data-work-count="today"' in html
|
|
assert "const todayWork = createTodayWork({" in html
|
|
assert "const filter = todayMyWork.length ? 'today' : (counts.attention ? 'attention' : 'all');" in html
|
|
assert "selectedWorkFilter === 'today' ? todayMyWork : activeMyWork" in html
|
|
assert 'data-today-add' in html
|
|
assert 'data-today-remove' in html
|
|
assert 'data-today-move="up"' in html
|
|
assert 'data-today-move="down"' in html
|
|
assert 'Today is limited to 5 items' in html
|
|
assert '.today-actions button' in html and 'min-height:44px' in html
|