Merge lp:~cjwatson/launchpad/snap-fix-js-status into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18501
Proposed branch: lp:~cjwatson/launchpad/snap-fix-js-status
Merge into: lp:launchpad
Diff against target: 343 lines (+183/-60)
7 files modified
lib/lp/app/browser/tales.py (+2/-1)
lib/lp/buildmaster/javascript/buildstatus.js (+54/-0)
lib/lp/snappy/javascript/snap.update_build_statuses.js (+5/-34)
lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html (+2/-0)
lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js (+104/-22)
lib/lp/snappy/model/snap.py (+1/-1)
lib/lp/snappy/tests/test_snap.py (+15/-2)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-fix-js-status
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+330015@code.launchpad.net

Commit message

Fix AJAX update of snap builds table to handle all build statuses.

Description of the change

I suspect the original confusion was because it was cloned-and-hacked from lib/lp/soyuz/javascript/update_archive_build_statuses.js, which is dealing with BuildSetStatus values rather than BuildStatus values.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== renamed file 'lib/canonical/launchpad/images/build-failed.gif' => 'lib/canonical/launchpad/images/build-failed.png'
2=== modified file 'lib/lp/app/browser/tales.py'
3--- lib/lp/app/browser/tales.py 2016-12-02 12:04:11 +0000
4+++ lib/lp/app/browser/tales.py 2017-08-31 14:38:35 +0000
5@@ -1097,6 +1097,7 @@
6 return '<img height="14" width="14" alt="" src="/@@/milestone" />'
7
8
9+# Keep this in sync with lib/lp/buildmaster/javascript/build_statuses.js.
10 class BuildImageDisplayAPI(ObjectImageDisplayAPI):
11 """Adapter for IBuild objects to an image.
12
13@@ -1119,8 +1120,8 @@
14 BuildStatus.CHROOTWAIT: {'src': "/@@/build-chrootwait"},
15 BuildStatus.SUPERSEDED: {'src': "/@@/build-superseded"},
16 BuildStatus.BUILDING: {'src': "/@@/processing"},
17+ BuildStatus.FAILEDTOUPLOAD: {'src': "/@@/build-failedtoupload"},
18 BuildStatus.UPLOADING: {'src': "/@@/processing"},
19- BuildStatus.FAILEDTOUPLOAD: {'src': "/@@/build-failedtoupload"},
20 BuildStatus.CANCELLING: {'src': "/@@/processing"},
21 BuildStatus.CANCELLED: {'src': "/@@/build-failed"},
22 }
23
24=== added directory 'lib/lp/buildmaster/javascript'
25=== added file 'lib/lp/buildmaster/javascript/buildstatus.js'
26--- lib/lp/buildmaster/javascript/buildstatus.js 1970-01-01 00:00:00 +0000
27+++ lib/lp/buildmaster/javascript/buildstatus.js 2017-08-31 14:38:35 +0000
28@@ -0,0 +1,54 @@
29+/* Copyright 2017 Canonical Ltd. This software is licensed under the
30+ * GNU Affero General Public License version 3 (see the file LICENSE).
31+ *
32+ * Build status handling.
33+ *
34+ * @module Y.lp.buildmaster.buildstatus
35+ */
36+YUI.add('lp.buildmaster.buildstatus', function(Y) {
37+ var module = Y.namespace('lp.buildmaster.buildstatus');
38+
39+ // Keep this in sync with
40+ // lib/lp/app/browser/tales.py:BuildImageDisplayAPI.
41+ var icon_map = {
42+ NEEDSBUILD: { src: '/@@/build-needed' },
43+ FULLYBUILT: { src: '/@@/build-success' },
44+ FAILEDTOBUILD: {
45+ src: '/@@/build-failed',
46+ width: 16
47+ },
48+ MANUALDEPWAIT: { src: '/@@/build-depwait' },
49+ CHROOTWAIT: { src: '/@@/build-chrootwait' },
50+ SUPERSEDED: { src: '/@@/build-superseded' },
51+ BUILDING: { src: '/@@/processing' },
52+ FAILEDTOUPLOAD: { src: '/@@/build-failedtoupload' },
53+ UPLOADING: { src: '/@@/processing' },
54+ CANCELLING: { src: '/@@/processing' },
55+ CANCELLED: { src: '/@@/build-failed' }
56+ };
57+
58+ module.update_build_status = function(node, build_summary) {
59+ var link_node = node.one('a');
60+ var img_node = node.one('img');
61+
62+ if (link_node === null || img_node === null) {
63+ return false;
64+ }
65+
66+ if (node.hasClass(build_summary.status)) {
67+ return false;
68+ }
69+ if (!(build_summary.status in icon_map)) {
70+ return false;
71+ }
72+ icon_props = icon_map[build_summary.status];
73+
74+ node.setAttribute('class', 'build_status');
75+ node.addClass(build_summary.status);
76+ link_node.set('innerHTML', build_summary.buildstate);
77+ img_node.setAttribute('alt', '[' + build_summary.status + ']');
78+ img_node.setAttribute('title', build_summary.buildstate);
79+ img_node.setAttribute('src', icon_props.src);
80+ img_node.setAttribute('width', icon_props.width || 14);
81+ };
82+}, '0.1', {'requires': []});
83
84=== modified file 'lib/lp/snappy/javascript/snap.update_build_statuses.js'
85--- lib/lp/snappy/javascript/snap.update_build_statuses.js 2016-05-14 00:25:07 +0000
86+++ lib/lp/snappy/javascript/snap.update_build_statuses.js 2017-08-31 14:38:35 +0000
87@@ -5,7 +5,8 @@
88 * LP DynamicDomUpdater plugin for updating the latest builds table of a snap.
89 *
90 * @module Y.lp.snappy.snap.update_build_statuses
91- * @requires anim, node, lp.anim, lp.soyuz.dynamic_dom_updater
92+ * @requires anim, node, lp.anim, lp.buildmaster.buildstatus,
93+ * lp.soyuz.dynamic_dom_updater
94 */
95 YUI.add('lp.snappy.snap.update_build_statuses', function(Y) {
96 Y.log('loading lp.snappy.snap.update_build_statuses');
97@@ -30,40 +31,9 @@
98 return;
99 }
100
101- var link_node = td_build_status.one("a");
102- var img_node = td_build_status.one("img");
103-
104- if (link_node === null || img_node === null) {
105- return;
106- }
107-
108- if (!td_build_status.hasClass(build_summary.status)) {
109+ if (Y.lp.buildmaster.buildstatus.update_build_status(
110+ td_build_status, build_summary)) {
111 ui_changed = true;
112- var new_src = null;
113- switch(build_summary.status) {
114- case 'BUILDING':
115- case 'UPLOADING':
116- new_src = '/@@/processing';
117- break;
118- case 'NEEDSBUILD':
119- new_src = '/@@/build-needed';
120- break;
121- case 'FAILEDTOBUILD':
122- new_src = '/@@/build-failed';
123- break;
124- case 'FULLYBUILT_PENDING':
125- new_src = '/@@/build-success-publishing';
126- break;
127- default:
128- new_src = '/@@/build-success';
129- }
130-
131- td_build_status.setAttribute("class", "build_status");
132- td_build_status.addClass(build_summary.status);
133- link_node.set("innerHTML", build_summary.buildstate);
134- img_node.setAttribute("src", new_src);
135- img_node.setAttribute("title", build_summary.buildstate);
136- img_node.setAttribute("alt", "[" + build_summary.status + "]");
137 }
138
139 if (build_summary.when_complete !== null) {
140@@ -137,4 +107,5 @@
141 }, "0.1", {"requires":["anim",
142 "node",
143 "lp.anim",
144+ "lp.buildmaster.buildstatus",
145 "lp.soyuz.dynamic_dom_updater"]});
146
147=== modified file 'lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html'
148--- lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html 2016-05-14 00:25:07 +0000
149+++ lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.html 2017-08-31 14:38:35 +0000
150@@ -34,6 +34,8 @@
151 <script type="text/javascript"
152 src="../../../../../build/js/lp/app/extras/extras.js"></script>
153 <script type="text/javascript"
154+ src="../../../../../build/js/lp/buildmaster/buildstatus.js"></script>
155+ <script type="text/javascript"
156 src="../../../../../build/js/lp/soyuz/lp_dynamic_dom_updater.js"></script>
157 <script type="text/javascript"
158 src="../../../../../build/js/lp/app/testing/assert.js"></script>
159
160=== modified file 'lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js'
161--- lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js 2017-07-21 17:35:14 +0000
162+++ lib/lp/snappy/javascript/tests/test_snap.update_build_statuses.js 2017-08-31 14:38:35 +0000
163@@ -63,28 +63,110 @@
164 this.td_status.setAttribute("class", this.td_status_class);
165 },
166
167- test_update_build_status_dom: function() {
168- var original_a_href = this.td_status_a.get("href");
169- var data = {
170- "1": {
171- "status": "BUILDING",
172- "build_log_url": null,
173- "when_complete_estimate": true,
174- "buildstate": "Currently building",
175- "build_log_size": null,
176- "when_complete": "in 1 minute"
177- }
178- };
179- module.domUpdate(this.table, data);
180- Y.Assert.areEqual(
181- "build_status BUILDING", this.td_status.getAttribute("class"));
182- Y.Assert.areEqual(
183- "Currently building", this.td_status.get("text").trim());
184- Y.Assert.areEqual("[BUILDING]", this.td_status_img.get("alt"));
185- Y.Assert.areEqual(
186- "Currently building", this.td_status_img.get("title"));
187- Y.Assert.areEqual(
188- "file:///@@/processing", this.td_status_img.get("src"));
189+ test_update_build_status_dom_building: function() {
190+ var original_a_href = this.td_status_a.get("href");
191+ var data = {
192+ "1": {
193+ "status": "BUILDING",
194+ "build_log_url": null,
195+ "when_complete_estimate": true,
196+ "buildstate": "Currently building",
197+ "build_log_size": null,
198+ "when_complete": "in 1 minute"
199+ }
200+ };
201+ module.domUpdate(this.table, data);
202+ Y.Assert.areEqual(
203+ "build_status BUILDING", this.td_status.getAttribute("class"));
204+ Y.Assert.areEqual(
205+ "Currently building", this.td_status.get("text").trim());
206+ Y.Assert.areEqual("[BUILDING]", this.td_status_img.get("alt"));
207+ Y.Assert.areEqual(
208+ "Currently building", this.td_status_img.get("title"));
209+ Y.Assert.areEqual(
210+ "file:///@@/processing", this.td_status_img.get("src"));
211+ Y.Assert.areEqual("14", this.td_status_img.get("width"));
212+ Y.Assert.areEqual(original_a_href, this.td_status_a.get("href"));
213+ },
214+
215+ test_update_build_status_dom_building: function() {
216+ var original_a_href = this.td_status_a.get("href");
217+ var data = {
218+ "1": {
219+ "status": "BUILDING",
220+ "build_log_url": null,
221+ "when_complete_estimate": true,
222+ "buildstate": "Currently building",
223+ "build_log_size": null,
224+ "when_complete": "in 1 minute"
225+ }
226+ };
227+ module.domUpdate(this.table, data);
228+ Y.Assert.areEqual(
229+ "build_status BUILDING", this.td_status.getAttribute("class"));
230+ Y.Assert.areEqual(
231+ "Currently building", this.td_status.get("text").trim());
232+ Y.Assert.areEqual("[BUILDING]", this.td_status_img.get("alt"));
233+ Y.Assert.areEqual(
234+ "Currently building", this.td_status_img.get("title"));
235+ Y.Assert.areEqual(
236+ "file:///@@/processing", this.td_status_img.get("src"));
237+ Y.Assert.areEqual("14", this.td_status_img.get("width"));
238+ Y.Assert.areEqual(original_a_href, this.td_status_a.get("href"));
239+ },
240+
241+ test_update_build_status_dom_failedtobuild: function() {
242+ var original_a_href = this.td_status_a.get("href");
243+ var data = {
244+ "1": {
245+ "status": "FAILEDTOBUILD",
246+ "build_log_url": null,
247+ "when_complete_estimate": false,
248+ "buildstate": "Failed to build",
249+ "build_log_size": null,
250+ "when_complete": "1 minute ago"
251+ }
252+ };
253+ module.domUpdate(this.table, data);
254+ Y.Assert.areEqual(
255+ "build_status FAILEDTOBUILD",
256+ this.td_status.getAttribute("class"));
257+ Y.Assert.areEqual(
258+ "Failed to build", this.td_status.get("text").trim());
259+ Y.Assert.areEqual(
260+ "[FAILEDTOBUILD]", this.td_status_img.get("alt"));
261+ Y.Assert.areEqual(
262+ "Failed to build", this.td_status_img.get("title"));
263+ Y.Assert.areEqual(
264+ "file:///@@/build-failed", this.td_status_img.get("src"));
265+ Y.Assert.areEqual("16", this.td_status_img.get("width"));
266+ Y.Assert.areEqual(original_a_href, this.td_status_a.get("href"));
267+ },
268+
269+ test_update_build_status_dom_chrootwait: function() {
270+ var original_a_href = this.td_status_a.get("href");
271+ var data = {
272+ "1": {
273+ "status": "CHROOTWAIT",
274+ "build_log_url": null,
275+ "when_complete_estimate": false,
276+ "buildstate": "Chroot problem",
277+ "build_log_size": null,
278+ "when_complete": "1 minute ago"
279+ }
280+ };
281+ module.domUpdate(this.table, data);
282+ Y.Assert.areEqual(
283+ "build_status CHROOTWAIT",
284+ this.td_status.getAttribute("class"));
285+ Y.Assert.areEqual(
286+ "Chroot problem", this.td_status.get("text").trim());
287+ Y.Assert.areEqual("[CHROOTWAIT]", this.td_status_img.get("alt"));
288+ Y.Assert.areEqual(
289+ "Chroot problem", this.td_status_img.get("title"));
290+ Y.Assert.areEqual(
291+ "file:///@@/build-chrootwait", this.td_status_img.get("src"));
292+ Y.Assert.areEqual("14", this.td_status_img.get("width"));
293 Y.Assert.areEqual(original_a_href, this.td_status_a.get("href"));
294 },
295
296
297=== modified file 'lib/lp/snappy/model/snap.py'
298--- lib/lp/snappy/model/snap.py 2017-06-29 12:02:11 +0000
299+++ lib/lp/snappy/model/snap.py 2017-08-31 14:38:35 +0000
300@@ -537,7 +537,7 @@
301
302 result[build.id] = {
303 "status": build.status.name,
304- "buildstate": build.status,
305+ "buildstate": build.status.title,
306 "when_complete": when_complete,
307 "when_complete_estimate": build.estimate,
308 "build_log_url": build.log_url,
309
310=== modified file 'lib/lp/snappy/tests/test_snap.py'
311--- lib/lp/snappy/tests/test_snap.py 2017-04-24 13:38:04 +0000
312+++ lib/lp/snappy/tests/test_snap.py 2017-08-31 14:38:35 +0000
313@@ -23,6 +23,8 @@
314 from storm.locals import Store
315 from testtools.matchers import (
316 Equals,
317+ Is,
318+ MatchesDict,
319 MatchesSetwise,
320 MatchesStructure,
321 )
322@@ -420,8 +422,19 @@
323 summary1 = snap1.getBuildSummariesForSnapBuildIds(
324 [build11.id, build12.id])
325 summary2 = snap2.getBuildSummariesForSnapBuildIds([build2.id])
326- self.assertContentEqual([build11.id, build12.id], summary1.keys())
327- self.assertContentEqual([build2.id], summary2.keys())
328+ summary_matcher = MatchesDict({
329+ "status": Equals("NEEDSBUILD"),
330+ "buildstate": Equals("Needs building"),
331+ "when_complete": Is(None),
332+ "when_complete_estimate": Is(False),
333+ "build_log_url": Is(None),
334+ "build_log_size": Is(None),
335+ })
336+ self.assertThat(summary1, MatchesDict({
337+ build11.id: summary_matcher,
338+ build12.id: summary_matcher,
339+ }))
340+ self.assertThat(summary2, MatchesDict({build2.id: summary_matcher}))
341
342 def test_getBuildSummariesForSnapBuildIds_empty_input(self):
343 snap = self.factory.makeSnap()