From 3b3e4a221fa47d0109c6ab99be65b5ba48bae254 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sat, 26 Sep 2026 11:23:52 +0100 Subject: Count the open tasks in the todo example The left field said how many tasks there were; it now says how many are still open, "2 of 3", with aup's count(t in tasks where !t.done), which Pure runs as a fold over the list on every surface. The reference model counts the same, and in the browser the field follows a task marked done. --- examples/todo.aup | 2 +- tests/browser.mjs | 3 ++- tests/check.py | 14 +++++++++----- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/examples/todo.aup b/examples/todo.aup index a5ee3b0..f1e3825 100644 --- a/examples/todo.aup +++ b/examples/todo.aup @@ -29,7 +29,7 @@ on urgent.down { at.urgent = !at.urgent } on drop.down { tasks.remove(at) } view title = "To do" -view left = "{tasks.count} tasks" +view left = "{count(t in tasks where !t.done)} of {tasks.count}" # A list: label, and optionally tone, badge and detail per row. view tasks_list = each task in tasks { diff --git a/tests/browser.mjs b/tests/browser.mjs index 87ad584..32cf65f 100644 --- a/tests/browser.mjs +++ b/tests/browser.mjs @@ -71,7 +71,7 @@ try { }; await until('three tasks, the last focused', `document.querySelectorAll('li').length === 3 && ${selected} === 2`); - await until('title and field', `document.querySelector('h1').textContent === 'To do' && document.querySelector('dd').textContent === '3 tasks'`); + await until('title and field', `document.querySelector('h1').textContent === 'To do' && document.querySelector('dd').textContent === '3 of 3'`); await key('ArrowUp', 'ArrowUp', 38); await until('ArrowUp moves the focus', `${selected} === 1`); const box = await evaluate(`(() => { const r = document.querySelectorAll('li')[0].getBoundingClientRect(); return [r.x + r.width / 2, r.y + r.height / 2]; })()`); @@ -80,6 +80,7 @@ try { await until('a click focuses its row', `${selected} === 0`); await key('Space', ' ', 32, ' '); await until('Space marks it done, muted', `document.querySelectorAll('li')[0].classList.contains('muted') && document.querySelector('li .badge')?.textContent === 'done'`); + await until('the count of open tasks follows', `document.querySelector('dd').textContent === '2 of 3'`); await evaluate(`document.getElementById('entry').focus()`); await send('Input.insertText', { text: '브라우저' }); await key('Enter', 'Enter', 13, '\r'); diff --git a/tests/check.py b/tests/check.py index 51fbf7f..1e1289f 100644 --- a/tests/check.py +++ b/tests/check.py @@ -88,6 +88,10 @@ class Todo: self.at = min(self.at, max(len(self.tasks) - 1, 0)) return True + def left(self): + """The left field: open tasks of all, a count over the list.""" + return f"{sum(1 for t in self.tasks if not t['done'])} of {len(self.tasks)}" + @staticmethod def badge(task): return 'done' if task['done'] else 'urgent' if task['urgent'] else '' @@ -97,7 +101,7 @@ class Todo: return 'muted' if task['done'] else 'danger' if task['urgent'] else 'normal' def cli(self): - lines = ['To do', f'left: {len(self.tasks)} tasks', f'tasks_list ({len(self.tasks)})'] + lines = ['To do', f'left: {self.left()}', f'tasks_list ({len(self.tasks)})'] for i, task in enumerate(self.tasks): line = ('> ' if i == self.at else ' ') + pictures(task['title']) if self.badge(task): @@ -122,7 +126,7 @@ class Todo: def say(self): n = len(self.tasks) - lines = ['To do.', f'left: {n} tasks.', f'tasks_list, {n} item{"" if n == 1 else "s"}.'] + lines = ['To do.', f'left: {self.left()}.', f'tasks_list, {n} item{"" if n == 1 else "s"}.'] return lines + ([self.focus()] if n else []) @@ -426,7 +430,7 @@ def web(vega, module, work, seeds, steps=150, visible=4): if model.at < first: first = model.at if model.at >= first + visible: first = model.at - visible + 1 if first + visible > n: first = max(n - visible, 0) - assert screen['title'] == 'To do' and screen['fields'] == [{'name': 'left', 'text': f'{n} tasks'}], screen + assert screen['title'] == 'To do' and screen['fields'] == [{'name': 'left', 'text': model.left()}], screen assert (listed['rows'], listed['focus'], listed['first']) == (n, model.at, first), (seed, listed, model.at) window = tasks[first:first + visible] assert [(item['row'], item['label'], item['badge'], item['tone']) for item in listed['items']] == \ @@ -481,7 +485,7 @@ def browser(vega, module, work): outcome = json.loads(result.stdout) assert result.returncode == 0 and outcome['ok'], outcome after = run(vega, 'cli', module, '--store', store, stdin='show\n').stdout.splitlines() - assert after == ['To do', 'left: 4 tasks', 'tasks_list (4)', ' a [done] (muted)', ' b', ' c', '> 브라우저'], after + assert after == ['To do', 'left: 3 of 4', 'tasks_list (4)', ' a [done] (muted)', ' b', ' c', '> 브라우저'], after return len(outcome['steps']) @@ -559,7 +563,7 @@ def interactive(vega, module, store=None): assert '\x1b[?1000h' in text and '\x1b[?1000l' in text, 'mouse reports on, then off' if store: # what was typed is still there in the next session again = run(vega, 'cli', module, '--store', store, stdin='show\n').stdout.splitlines() - assert again == ['To do', 'left: 2 tasks', 'tasks_list (2)', ' milk [done] (muted)', '> béton'], again + assert again == ['To do', 'left: 1 of 2', 'tasks_list (2)', ' milk [done] (muted)', '> béton'], again return 1 -- cgit v1.2.3