1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
from __future__ import annotations
from pygitweb.static_tests.js_runtime import JsPage
from pygitweb.static_tests.pages import MANIFEST, board_shell, card_html, posted_json, requested
def _open(js: JsPage, *, cards: str | None = None, project: str = "demo") -> None:
js.route("board/list", body=[{"name": "Tasks"}, {"name": "Other"}])
js.route("comments/list", body=[])
js.route("board-ref-manifest", body=MANIFEST)
js.route("tasks/list", body=[])
js.route("tasks/update", status=200)
html = board_shell(
project=project,
cards_todo=cards if cards is not None else card_html(ref="t1", title="Ship", description="see #abcd"),
)
js.set_html(html)
js.load("board-popout.js")
js.settle("__loaded['board-context.js'].refreshBoardRefManifest()")
js.run("__loaded['board-popout.js'].showCardPopout(document.querySelector('.board-card'))")
def test_popout_edits_post_the_card_fields(js: JsPage) -> None:
_open(js)
assert "d-none" not in js.classes(".board-card-details")
assert js.count(".board-card-popout-overlay") == 1
assert js.count(".board-ref-commit") == 1
js.click(".board-card-title")
js.click(".board-card-title")
js.set_prop(".board-card-title-edit", "value", " ")
js.dispatch(".board-card-title-edit", "keydown", key="Enter")
assert js.text(".board-card-title") == "Untitled"
js.click(".board-card-description-full")
js.set_prop(".board-card-description-edit", "value", "updated")
js.dispatch(".board-card-description-edit", "blur")
assert js.text(".board-card-description-preview") == "updated"
js.run("document.querySelector('.board-card-due-date').textContent = '2020-01-02 extra';")
js.click(".board-card-due-date")
assert js.prop(".board-card-due-date-edit", "value") == "2020-01-02"
js.set_prop(".board-card-due-date-edit", "value", "")
js.dispatch(".board-card-due-date-edit", "blur")
assert js.text(".board-card-due-date") == "None"
js.click(".board-card-status")
js.run("document.querySelector('.board-card-status-edit option').textContent = 'In Progress';")
js.dispatch(".board-card-status-edit", "change")
assert js.text(".board-card-status") == "In Progress"
assert js.attr(".board-card", "data-task-status") == "IN_PROGRESS"
js.click(".board-card-priority")
js.run("document.querySelector('.board-card-priority-edit option').textContent = 'High';")
js.dispatch(".board-card-priority-edit", "change")
assert js.text(".board-card-priority") == "High"
js.click(".board-card-points")
assert js.count(".board-card-points-edit option") == 9
js.set_prop(".board-card-points-edit", "value", "5")
js.dispatch(".board-card-points-edit", "change")
assert js.text(".board-card-points") == "5"
assert js.attr(".board-card", "data-task-points") == "5"
js.click(".board-card-assignee")
js.set_prop(".board-card-assignee-edit", "value", "Ada")
js.dispatch(".board-card-assignee-edit", "keydown", key="Enter")
assert js.text(".board-card-assignee-compact") == "Ada"
js.run("document.querySelector('.board-card-tags').textContent = 'ui';")
js.dispatch(".board-card-tags", "blur")
body = posted_json(js, "tasks/update")
assert body["title"] == "Untitled"
assert body["description"] == "updated"
assert body["status"] == "IN_PROGRESS"
assert body["priority"] == "HIGH"
assert body["points"] == 5
assert body["assignee"] == "Ada"
assert body["due_date"] is None
assert body["tags"] == "ui"
def test_due_date_keeps_a_typed_value_and_rejects_junk(js: JsPage) -> None:
_open(js)
js.run("document.querySelector('.board-card-due-date').textContent = 'soon';")
js.click(".board-card-due-date")
assert js.prop(".board-card-due-date-edit", "value") == ""
js.set_prop(".board-card-due-date-edit", "value", "2024-05-06")
js.dispatch(".board-card-due-date-edit", "blur")
assert posted_json(js, "tasks/update")["due_date"] == "2024-05-06"
def test_backdrop_restores_the_card_ahead_of_its_sibling(js: JsPage) -> None:
_open(js, cards=card_html(ref="t1", title="A") + card_html(ref="t2", title="B"))
assert js.count(".board-card-popout-overlay .board-card") == 1
js.click(".board-card-popout-overlay")
assert js.count(".board-card-popout-overlay") == 0
titles = js.texts(".board-column-cards .board-card-title")
assert titles == ["A", "B"]
assert "d-none" in js.classes(".board-card-details")
assert js.attr(".board-card", "draggable") == "true"
def test_closing_the_last_card_appends_it(js: JsPage) -> None:
_open(js, cards=card_html(ref="t1", title="Only"))
js.click(".board-card-popout-overlay")
assert js.text(".board-column-cards .board-card-title") == "Only"
def test_delete_and_archive_ask_before_removing_the_card(js: JsPage) -> None:
js.route("tasks/delete", status=200)
js.route("tasks/archive", status=200)
_open(js)
js.run("__confirmYes = false;")
js.click(".board-card-delete-btn")
assert requested(js, "tasks/delete") is False
assert "permanently" in js.value("__confirms")[0]
js.run("__confirmYes = true;")
js.click(".board-card-delete-btn")
assert js.count(".board-card") == 0
_open(js)
js.route("tasks/delete", status=500)
js.click(".board-card-delete-btn")
assert js.count(".board-card") == 1
js.click(".board-card-archive-btn")
assert js.count(".board-card") == 0
assert "Archive this task" in js.value("__confirms")[-1]
def test_archive_failure_leaves_the_card(js: JsPage) -> None:
js.route("tasks/archive", error="offline")
_open(js)
js.click(".board-card-archive-btn")
assert js.count(".board-card") == 1
def test_move_removes_the_card_or_shows_an_error(js: JsPage) -> None:
js.route("tasks/move", status=200)
_open(js)
labels = js.texts(".board-card-move-select option")
assert "Tasks" not in labels
assert "Other" in labels
js.dispatch(".board-card-move-select", "change")
assert requested(js, "tasks/move") is False
js.set_prop(".board-card-move-select", "value", "Other")
js.dispatch(".board-card-move-select", "change")
assert js.count(".board-card-popout-overlay") == 0
assert any("target=Other" in str(item["url"]) for item in js.requests())
js.route("tasks/move", status=401)
_open(js)
js.set_prop(".board-card-move-select", "value", "Other")
js.dispatch(".board-card-move-select", "change")
assert "Not authorized" in js.text(".board-task-error-hint")
assert js.prop(".board-card-move-select", "value") == ""
js.route("tasks/move", status=500)
js.set_prop(".board-card-move-select", "value", "Other")
js.dispatch(".board-card-move-select", "change")
assert "Could not move task" in js.text(".board-task-error-hint")
js.route("tasks/move", error="offline")
js.set_prop(".board-card-move-select", "value", "Other")
js.dispatch(".board-card-move-select", "change")
assert "Could not move task" in js.text(".board-task-error-hint")
def test_actions_without_a_ref_or_project_are_not_bound(js: JsPage) -> None:
_open(js, cards=card_html(title="Draft"), project="")
js.set_location("http://127.0.0.1/")
js.click(".board-card-delete-btn")
assert js.value("__confirms") == []
assert requested(js, "tasks/delete") is False
def test_assignee_cleared_to_none_posts_null(js: JsPage) -> None:
_open(js)
js.run("document.querySelector('.board-card-assignee').textContent = 'Ada';")
js.click(".board-card-assignee")
js.set_prop(".board-card-assignee-edit", "value", "")
js.dispatch(".board-card-assignee-edit", "blur")
assert js.text(".board-card-assignee") == "None"
assert posted_json(js, "tasks/update")["assignee"] is None