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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
from __future__ import annotations
from pygitweb.static_tests.js_runtime import JsPage
from pygitweb.static_tests.pages import board_shell, card_html, posted_json, requested
_TASKS = [
{
"ref": "refs/tags/tasks/task_abcd",
"id": "abcd",
"title": "Ship",
"description": "**bold**",
"status": "TODO",
"priority": "CRITICAL",
"points": 2,
"assignee": "Ada",
"comments_count": 1,
"due_date": "2020-01-02",
"updated_at": "2020-01-03",
"tags": "ui",
},
{
"ref": "refs/tags/tasks/task_eeee",
"title": "",
"status": "IN_PROGRESS",
"priority": "HIGH",
"points": 1,
"comments_count": 0,
},
{"ref": "refs/tags/tasks/task_ffff", "status": "DONE", "priority": "LOW", "points": 0},
{"status": "TODO", "title": "Zero", "points": 0, "priority": "LOW"},
]
def test_preload_and_assign_icons(js: JsPage) -> None:
js.set_html(board_shell(cards_todo=card_html(ref="t")))
js.load("board-cards.js")
js.call("board-cards.js", "preloadIcons")
preloaded = js.value("__preloaded")
assert len(preloaded) == 6
assert any(src.endswith("cloud.svg") for src in preloaded)
js.call("board-cards.js", "assignCloudIconToAllCards")
assert js.attr(".board-card-icon", "data-icon") == "cloud"
def test_load_tasks_builds_cards_for_known_columns(js: JsPage) -> None:
js.route("tasks/list", body=_TASKS)
js.route("board-ref-manifest", body={"commits": [], "refs": []})
js.set_html(board_shell())
js.load("board-cards.js")
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert js.count('.board-column[data-status="TODO"] .board-card') == 2
assert js.count('.board-column[data-status="IN_PROGRESS"] .board-card') == 1
assert js.count('.board-column[data-status="DONE"] .board-card') == 0
assert js.text(".board-card-title") == "Ship"
assert "text-danger" in js.classes(".badge")
assert js.text(".board-card-points-badge") == "2 pts"
assert js.text(".board-card-assignee-compact") == "Ada"
assert js.text(".board-card-comments-count-meta") == "1 comment"
assert js.text(".board-card-due-date") == "2020-01-02"
assert js.text(".board-card-points") == "2"
assert js.text(".board-card-tags") == "ui"
assert js.count(".board-card-description-full strong") == 1
assert js.attr(".board-card-icon", "data-icon") == "cloud-filled"
assert js.text(".board-column-task-count") == "2"
assert js.text('.board-card[data-task-points="0"] .board-card-points-badge') == "0 pts"
assert "text-secondary" in js.classes('.board-card[data-task-points="0"] .badge')
assert js.text('.board-column[data-status="IN_PROGRESS"] .board-card-title') == "Untitled"
assert js.text('.board-column[data-status="IN_PROGRESS"] .board-card-points-badge') == "1 pt"
assert "text-warning" in js.classes('.board-column[data-status="IN_PROGRESS"] .badge')
def test_load_tasks_keeps_cards_when_the_response_is_unusable(js: JsPage) -> None:
js.set_html('<div class="board-columns"></div>')
js.set_location("http://127.0.0.1/")
js.load("board-cards.js")
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert requested(js, "tasks/list") is False
js.set_location("http://127.0.0.1/project/demo/board/")
js.set_html(board_shell(cards_todo=card_html(ref="keep", title="Keep")))
js.route("tasks/list", status=500)
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert js.text(".board-card-title") == "Keep"
js.route("tasks/list", body={"nope": True})
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert js.text(".board-card-title") == "Keep"
js.route("tasks/list", error="offline")
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert js.text(".board-card-title") == "Keep"
def test_load_tasks_without_a_template_clears_the_column(js: JsPage) -> None:
js.route("tasks/list", body=[{"ref": "t", "title": "X", "status": "TODO"}])
js.route("board-ref-manifest", body={"commits": [], "refs": []})
js.set_html(board_shell(include_template=False, cards_todo=card_html(ref="old", title="Old")))
js.load("board-cards.js")
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
assert js.count(".board-card") == 0
def test_fetch_and_reorder_follow_the_task_list(js: JsPage) -> None:
js.set_html('<div class="board-columns"></div>')
js.set_location("http://127.0.0.1/")
js.load("board-cards.js")
assert js.settle("__loaded['board-cards.js'].fetchBoardTasksJson()") is None
js.set_html(
board_shell(
cards_todo=card_html(ref="a", title="A") + card_html(ref="b", title="B"),
cards_progress=card_html(ref="c", title="C"),
)
)
js.route("tasks/list", status=404)
assert js.settle("__loaded['board-cards.js'].fetchBoardTasksJson()") is None
js.route(
"tasks/list",
body=[{"ref": "b", "status": "TODO"}, {"ref": "a", "status": "TODO"}, {"ref": "c", "status": "IN_PROGRESS"}],
)
assert js.settle("__loaded['board-cards.js'].fetchBoardTasksJson()")[0]["ref"] == "b"
js.run("__loaded['board-cards.js'].reorderBoardDomToTaskList(null)")
js.run(
"__loaded['board-cards.js'].reorderBoardDomToTaskList(["
"{ref:'b', status:'TODO'}, {status:'TODO'}, {ref:'missing', status:'TODO'}, {ref:'a', status:'IN_PROGRESS'}"
"])"
)
titles = js.texts('.board-column[data-status="TODO"] .board-card-title')
assert titles == ["B"]
progress = js.texts('.board-column[data-status="IN_PROGRESS"] .board-card-title')
assert progress == ["C", "A"]
def test_add_card_posts_and_marks_success_or_failure(js: JsPage) -> None:
js.route("tasks/create", body={"ref": "refs/tags/tasks/task_abcd"})
js.set_html(board_shell())
js.load("board-cards.js")
js.call("board-cards.js", "bindAddCardButtons")
js.click(".board-column-task-count")
assert js.count(".board-card") == 0
js.click(".board-column-add-card")
assert js.text(".board-card-title") == "New task"
assert js.attr(".board-card-icon", "data-icon") == "cloud-filled"
assert js.text(".board-card-task-id") == "abcd"
assert js.text(".board-column-task-count") == "1"
body_url = next(str(item["url"]) for item in js.requests() if "tasks/create" in str(item["url"]))
assert "title=New+task" in body_url or "title=New%20task" in body_url
assert "status=TODO" in body_url
js.route("tasks/create", status=500)
js.click('.board-column[data-status="IN_PROGRESS"] .board-column-add-card')
assert js.attr('.board-column[data-status="IN_PROGRESS"] .board-card-icon', "data-icon") == "cloud-exclamation"
js.route("tasks/create", status=302)
js.click(".board-column-add-card")
pending = js.attrs('.board-column[data-status="TODO"] .board-card-icon', "data-icon")
assert pending[-1] == "cloud-up"
js.route("tasks/create", error="offline")
js.click(".board-column-add-card")
icons = js.attrs('.board-column[data-status="TODO"] .board-card-icon', "data-icon")
assert icons[-1] == "cloud-exclamation"
def test_add_card_does_nothing_without_a_project_or_template(js: JsPage) -> None:
js.set_html(board_shell(project="", include_template=True))
js.set_location("http://127.0.0.1/")
js.load("board-cards.js")
js.call("board-cards.js", "bindAddCardButtons")
js.click(".board-column-add-card")
assert js.count(".board-card") == 0
js.set_html(board_shell(include_template=False))
js.call("board-cards.js", "bindAddCardButtons")
js.click(".board-column-add-card")
assert js.count(".board-card") == 0
def test_compact_assignee_edit_commits_or_restores(js: JsPage) -> None:
js.route("tasks/list", body=[{"ref": "t1", "title": "Ship", "status": "TODO", "assignee": "Ada"}])
js.route("board-ref-manifest", body={"commits": [], "refs": []})
js.route("tasks/update", status=200)
js.set_html(board_shell())
js.load("board-cards.js")
js.settle("__loaded['board-cards.js'].loadTasksForBoard()")
js.click(".board-card-assignee-compact")
assert js.count(".board-card-assignee-compact-edit") == 1
js.dispatch(".board-card-assignee-compact-edit", "mousedown")
js.set_prop(".board-card-assignee-compact-edit", "value", "Bea")
js.dispatch(".board-card-assignee-compact-edit", "keydown", key="Enter")
assert js.text(".board-card-assignee-compact") == "Bea"
assert js.text(".board-card-assignee") == "Bea"
assert posted_json(js, "tasks/update")["assignee"] == "Bea"
assert js.prop(".board-card", "draggable") is True
js.click(".board-card-assignee-compact")
js.set_prop(".board-card-assignee-compact-edit", "value", "nope")
js.dispatch(".board-card-assignee-compact-edit", "keydown", key="Escape")
assert js.text(".board-card-assignee-compact") == "Bea"
js.click(".board-card-assignee-compact")
js.set_prop(".board-card-assignee-compact-edit", "value", " ")
js.dispatch(".board-card-assignee-compact-edit", "blur")
assert js.text(".board-card-assignee-compact") == "None"
assert posted_json(js, "tasks/update")["assignee"] is None