Merge lp:~gary/launchpad/bug724609 into lp:launchpad
- bug724609
- Merge into devel
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Gary Poster | ||||
Approved revision: | no longer in the source branch. | ||||
Merged at revision: | 14351 | ||||
Proposed branch: | lp:~gary/launchpad/bug724609 | ||||
Merge into: | lp:launchpad | ||||
Diff against target: |
709 lines (+539/-19) 8 files modified
lib/lp/app/javascript/client.js (+18/-9) lib/lp/app/javascript/server_fixture.js (+67/-1) lib/lp/app/javascript/tests/test_lp_client.js (+48/-0) lib/lp/app/javascript/tests/test_lp_client_integration.js (+333/-0) lib/lp/app/javascript/tests/test_lp_client_integration.py (+62/-0) lib/lp/app/templates/base-layout-macros.pt (+6/-2) lib/lp/registry/browser/tests/test_subscription_links.py (+2/-6) lib/lp/testing/__init__.py (+3/-1) |
||||
To merge this branch: | bzr merge lp:~gary/launchpad/bug724609 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Brad Crittenden (community) | Approve | ||
Review via email: mp+82689@code.launchpad.net |
Commit message
[r=bac][bug=724609] re-add integration tests for the lp.client library, replacing the old Windmill tests with yuixhr tests.
Description of the change
This branch addresses critical bug 724609 by re-adding integration tests for the lp.client library. These had been written for Windmill, and were removed earlier this year because we discarded Windmill.
If you would like to look at the original, you can find them here. http://
The match is usually roughly one-to-one, though I did take some editorial liberties, and a few of the tests were moved to pure JS unit tests when I felt I could do that.
The original tests had tests for the hosted file support. Much of them were commented out because we weren't actually using enough of it in Launchpad to test the integration. I tried to port the existing tests, but I discovered that Chrome makes our hosted files even more problematic. We redirect our hosted files to another domain for the librarian, and Chrome disallows getting files from another domain for security reasons. We could fix this by having librarian files in the same domain, but when I brought this up to him, Francis told me to just remove the support in lp.client for hosted files, because we have not used it for years. Therefore, in a second and separate branch, I will do this.
I made a few changes to help with testing.
- The lp.client itself gained a new argument so that you can instantiate it so that requests are performed synchronously. This makes it easier to write tests, because you don't have to try and figure out how long to wait for the results using the YUI async test API.
- The yuixhr module gained two abilities.
* First, you can add arbitrary cleanup functions to be called on teardown.
* The cleanup function ability is then used by the second ability: you can test a page in an iframe. This is good for integration tests that just need to verify that library code is actually hooked up on a page. You should of course not test the library itself with the iframe.
Lint is happy.
Thank you!
Preview Diff
1 | === added file 'lib/lp/app/javascript/__init__.py' | |||
2 | === modified file 'lib/lp/app/javascript/client.js' | |||
3 | --- lib/lp/app/javascript/client.js 2011-10-20 20:14:57 +0000 | |||
4 | +++ lib/lp/app/javascript/client.js 2011-11-21 14:41:36 +0000 | |||
5 | @@ -473,7 +473,9 @@ | |||
6 | 473 | 'headers': {"Content-Type": hosted_file.content_type, | 473 | 'headers': {"Content-Type": hosted_file.content_type, |
7 | 474 | "Content-Disposition": disposition}, | 474 | "Content-Disposition": disposition}, |
8 | 475 | 'arguments': args, | 475 | 'arguments': args, |
10 | 476 | 'data': hosted_file.contents}; | 476 | 'data': hosted_file.contents, |
11 | 477 | 'sync': this.lp_client.sync | ||
12 | 478 | }; | ||
13 | 477 | this.io_provider.io(module.normalize_uri(hosted_file.uri), y_config); | 479 | this.io_provider.io(module.normalize_uri(hosted_file.uri), y_config); |
14 | 478 | }, | 480 | }, |
15 | 479 | 481 | ||
16 | @@ -483,7 +485,9 @@ | |||
17 | 483 | var args = hosted_file; | 485 | var args = hosted_file; |
18 | 484 | var y_config = { method: "DELETE", | 486 | var y_config = { method: "DELETE", |
19 | 485 | on: on, | 487 | on: on, |
21 | 486 | 'arguments': args }; | 488 | 'arguments': args, |
22 | 489 | sync: this.lp_client.sync | ||
23 | 490 | }; | ||
24 | 487 | this.io_provider.io(hosted_file.uri, y_config); | 491 | this.io_provider.io(hosted_file.uri, y_config); |
25 | 488 | } | 492 | } |
26 | 489 | }; | 493 | }; |
27 | @@ -559,11 +563,13 @@ | |||
28 | 559 | 563 | ||
29 | 560 | 564 | ||
30 | 561 | // The service root resource. | 565 | // The service root resource. |
32 | 562 | module.Root = function(client, representation, uri) { | 566 | Root = function(client, representation, uri) { |
33 | 563 | /* The root of the Launchpad web service. */ | 567 | /* The root of the Launchpad web service. */ |
34 | 564 | this.init(client, representation, uri); | 568 | this.init(client, representation, uri); |
35 | 565 | }; | 569 | }; |
37 | 566 | module.Root.prototype = new Resource(); | 570 | Root.prototype = new Resource(); |
38 | 571 | |||
39 | 572 | module.Root = Root; | ||
40 | 567 | 573 | ||
41 | 568 | 574 | ||
42 | 569 | var Collection = function(client, representation, uri) { | 575 | var Collection = function(client, representation, uri) { |
43 | @@ -622,7 +628,6 @@ | |||
44 | 622 | 628 | ||
45 | 623 | Entry.prototype.lp_save = function(config) { | 629 | Entry.prototype.lp_save = function(config) { |
46 | 624 | /* Write modifications to this entry back to the web service. */ | 630 | /* Write modifications to this entry back to the web service. */ |
47 | 625 | var on = config.on; | ||
48 | 626 | var representation = {}; | 631 | var representation = {}; |
49 | 627 | var entry = this; | 632 | var entry = this; |
50 | 628 | Y.each(this.dirty_attributes, function(attribute, key) { | 633 | Y.each(this.dirty_attributes, function(attribute, key) { |
51 | @@ -667,6 +672,7 @@ | |||
52 | 667 | var Launchpad = function(config) { | 672 | var Launchpad = function(config) { |
53 | 668 | /* A client that makes HTTP requests to Launchpad's web service. */ | 673 | /* A client that makes HTTP requests to Launchpad's web service. */ |
54 | 669 | this.io_provider = module.get_configured_io_provider(config); | 674 | this.io_provider = module.get_configured_io_provider(config); |
55 | 675 | this.sync = (config ? config.sync : false); | ||
56 | 670 | }; | 676 | }; |
57 | 671 | 677 | ||
58 | 672 | Launchpad.prototype = { | 678 | Launchpad.prototype = { |
59 | @@ -693,7 +699,8 @@ | |||
60 | 693 | on: on, | 699 | on: on, |
61 | 694 | 'arguments': [client, uri, old_on_success, update_cache], | 700 | 'arguments': [client, uri, old_on_success, update_cache], |
62 | 695 | 'headers': headers, | 701 | 'headers': headers, |
64 | 696 | data: data | 702 | data: data, |
65 | 703 | sync: this.sync | ||
66 | 697 | }; | 704 | }; |
67 | 698 | return this.io_provider.io(uri, y_config); | 705 | return this.io_provider.io(uri, y_config); |
68 | 699 | }, | 706 | }, |
69 | @@ -745,13 +752,14 @@ | |||
70 | 745 | method: "POST", | 752 | method: "POST", |
71 | 746 | on: on, | 753 | on: on, |
72 | 747 | 'arguments': [client, uri, old_on_success, update_cache], | 754 | 'arguments': [client, uri, old_on_success, update_cache], |
74 | 748 | data: data | 755 | data: data, |
75 | 756 | sync: this.sync | ||
76 | 749 | }; | 757 | }; |
77 | 750 | this.io_provider.io(uri, y_config); | 758 | this.io_provider.io(uri, y_config); |
78 | 751 | }, | 759 | }, |
79 | 752 | 760 | ||
80 | 753 | 'patch': function(uri, representation, config, headers) { | 761 | 'patch': function(uri, representation, config, headers) { |
82 | 754 | var on = config.on; | 762 | var on = Y.merge(config.on); |
83 | 755 | var data = Y.JSON.stringify(representation); | 763 | var data = Y.JSON.stringify(representation); |
84 | 756 | uri = module.normalize_uri(uri); | 764 | uri = module.normalize_uri(uri); |
85 | 757 | 765 | ||
86 | @@ -780,7 +788,8 @@ | |||
87 | 780 | 'on': on, | 788 | 'on': on, |
88 | 781 | 'headers': extra_headers, | 789 | 'headers': extra_headers, |
89 | 782 | 'arguments': args, | 790 | 'arguments': args, |
91 | 783 | 'data': data | 791 | 'data': data, |
92 | 792 | 'sync': this.sync | ||
93 | 784 | }; | 793 | }; |
94 | 785 | this.io_provider.io(uri, y_config); | 794 | this.io_provider.io(uri, y_config); |
95 | 786 | }, | 795 | }, |
96 | 787 | 796 | ||
97 | === modified file 'lib/lp/app/javascript/server_fixture.js' | |||
98 | --- lib/lp/app/javascript/server_fixture.js 2011-11-03 23:19:44 +0000 | |||
99 | +++ lib/lp/app/javascript/server_fixture.js 2011-11-21 14:41:36 +0000 | |||
100 | @@ -42,7 +42,73 @@ | |||
101 | 42 | return data; | 42 | return data; |
102 | 43 | }; | 43 | }; |
103 | 44 | 44 | ||
104 | 45 | module.addCleanup = function(testcase, callable) { | ||
105 | 46 | if (Y.Lang.isUndefined(testcase._lp_fixture_cleanups)) { | ||
106 | 47 | testcase._lp_fixture_cleanups = []; | ||
107 | 48 | } | ||
108 | 49 | testcase._lp_fixture_cleanups.push(callable); | ||
109 | 50 | }; | ||
110 | 51 | |||
111 | 52 | module.runWithIFrame = function(config, test) { | ||
112 | 53 | // Note that the iframe url must be in the same domain as the test page. | ||
113 | 54 | var iframe = Y.Node.create('<iframe/>').set('src', config.uri); | ||
114 | 55 | Y.one('body').append(iframe); | ||
115 | 56 | module.addCleanup( | ||
116 | 57 | config.testcase, | ||
117 | 58 | function() { | ||
118 | 59 | iframe.remove(); | ||
119 | 60 | } | ||
120 | 61 | ); | ||
121 | 62 | var timeout = config.timeout; | ||
122 | 63 | if (!Y.Lang.isValue(timeout)) { | ||
123 | 64 | timeout = 8000; | ||
124 | 65 | } | ||
125 | 66 | var iframe_is_ready = config.iframe_is_ready; | ||
126 | 67 | if (!Y.Lang.isValue(iframe_is_ready)) { | ||
127 | 68 | iframe_is_ready = function() {return true;}; | ||
128 | 69 | } | ||
129 | 70 | var wait = config.wait; | ||
130 | 71 | if (!Y.Lang.isValue(wait)) { | ||
131 | 72 | wait = 100; | ||
132 | 73 | } | ||
133 | 74 | var start; | ||
134 | 75 | var retry_function; | ||
135 | 76 | retry_function = function() { | ||
136 | 77 | var tested = false; | ||
137 | 78 | var win = Y.Node.getDOMNode(iframe.get('contentWindow')); | ||
138 | 79 | if (Y.Lang.isValue(win) && Y.Lang.isValue(win.document)) { | ||
139 | 80 | var IYUI = YUI({ | ||
140 | 81 | base: '/+icing/yui/', | ||
141 | 82 | filter: 'raw', | ||
142 | 83 | combine: false, | ||
143 | 84 | fetchCSS: false, | ||
144 | 85 | win: win}); | ||
145 | 86 | if (iframe_is_ready(IYUI)) { | ||
146 | 87 | test(IYUI); | ||
147 | 88 | tested = true; | ||
148 | 89 | } | ||
149 | 90 | } | ||
150 | 91 | if (!tested) { | ||
151 | 92 | var now = new Date().getTime(); | ||
152 | 93 | if (now-start < timeout) { | ||
153 | 94 | config.testcase.wait(retry_function, wait); | ||
154 | 95 | } else { | ||
155 | 96 | Y.Assert.fail('Page did not load in iframe: ' + url); | ||
156 | 97 | } | ||
157 | 98 | } | ||
158 | 99 | }; | ||
159 | 100 | start = new Date().getTime(); | ||
160 | 101 | config.testcase.wait(retry_function, wait); | ||
161 | 102 | }; | ||
162 | 103 | |||
163 | 45 | module.teardown = function(testcase) { | 104 | module.teardown = function(testcase) { |
164 | 105 | var cleanups = testcase._lp_fixture_cleanups; | ||
165 | 106 | var i; | ||
166 | 107 | if (!Y.Lang.isUndefined(cleanups)) { | ||
167 | 108 | for (i=cleanups.length-1; i>=0 ; i--) { | ||
168 | 109 | cleanups[i](); | ||
169 | 110 | } | ||
170 | 111 | } | ||
171 | 46 | var fixtures = testcase._lp_fixture_setups; | 112 | var fixtures = testcase._lp_fixture_setups; |
172 | 47 | if (Y.Lang.isUndefined(fixtures)) { | 113 | if (Y.Lang.isUndefined(fixtures)) { |
173 | 48 | // Nothing to be done. | 114 | // Nothing to be done. |
174 | @@ -101,4 +167,4 @@ | |||
175 | 101 | }, | 167 | }, |
176 | 102 | "0.1", | 168 | "0.1", |
177 | 103 | {"requires": [ | 169 | {"requires": [ |
179 | 104 | "io", "json", "querystring", "test", "console", "lp.client"]}); | 170 | "io", "json", "querystring", "test", "console", "lp.client", "node"]}); |
180 | 105 | 171 | ||
181 | === added file 'lib/lp/app/javascript/tests/__init__.py' | |||
182 | === modified file 'lib/lp/app/javascript/tests/test_lp_client.js' | |||
183 | --- lib/lp/app/javascript/tests/test_lp_client.js 2011-10-11 17:05:30 +0000 | |||
184 | +++ lib/lp/app/javascript/tests/test_lp_client.js 2011-11-21 14:41:36 +0000 | |||
185 | @@ -140,6 +140,54 @@ | |||
186 | 140 | Assert.isInstanceOf(Y.lp.client.Entry, result.entry); | 140 | Assert.isInstanceOf(Y.lp.client.Entry, result.entry); |
187 | 141 | Assert.areSame('foo', result.entry.get('resource_type_link')); | 141 | Assert.areSame('foo', result.entry.get('resource_type_link')); |
188 | 142 | }, | 142 | }, |
189 | 143 | test_get_success_callback: function() { | ||
190 | 144 | var mockio = new Y.lp.testing.mockio.MockIo(); | ||
191 | 145 | var mylist = []; | ||
192 | 146 | var client = new Y.lp.client.Launchpad({io_provider: mockio}); | ||
193 | 147 | client.get('/people', {on:{success: Y.bind(mylist.push, mylist)}}); | ||
194 | 148 | Assert.areEqual('/api/devel/people', mockio.last_request.url); | ||
195 | 149 | mockio.success({ | ||
196 | 150 | responseText: | ||
197 | 151 | '{"entry": {"resource_type_link": "foo"}}', | ||
198 | 152 | responseHeaders: {'Content-Type': 'application/json'} | ||
199 | 153 | }); | ||
200 | 154 | var result = mylist[0]; | ||
201 | 155 | Assert.isInstanceOf(Y.lp.client.Entry, result.entry); | ||
202 | 156 | Assert.areSame('foo', result.entry.get('resource_type_link')); | ||
203 | 157 | }, | ||
204 | 158 | test_get_failure_callback: function() { | ||
205 | 159 | var mockio = new Y.lp.testing.mockio.MockIo(); | ||
206 | 160 | var mylist = []; | ||
207 | 161 | var client = new Y.lp.client.Launchpad({io_provider: mockio}); | ||
208 | 162 | client.get( | ||
209 | 163 | '/people', | ||
210 | 164 | {on: { | ||
211 | 165 | failure: function(){ | ||
212 | 166 | mylist.push(Array.prototype.slice.call(arguments)); | ||
213 | 167 | }}}); | ||
214 | 168 | mockio.failure({status: 503}); | ||
215 | 169 | var result = mylist[0]; | ||
216 | 170 | Assert.areSame(503, result[1].status); | ||
217 | 171 | Assert.areSame('/api/devel/people', result[2][1]); | ||
218 | 172 | }, | ||
219 | 173 | test_named_post_success_callback: function() { | ||
220 | 174 | var mockio = new Y.lp.testing.mockio.MockIo(); | ||
221 | 175 | var mylist = []; | ||
222 | 176 | var client = new Y.lp.client.Launchpad({io_provider: mockio}); | ||
223 | 177 | client.named_post( | ||
224 | 178 | '/people', 'newTeam', {on:{success: Y.bind(mylist.push, mylist)}}); | ||
225 | 179 | Assert.areEqual('/api/devel/people', mockio.last_request.url); | ||
226 | 180 | Assert.areEqual('ws.op=newTeam', mockio.last_request.config.data); | ||
227 | 181 | Assert.areEqual('POST', mockio.last_request.config.method); | ||
228 | 182 | mockio.success({ | ||
229 | 183 | responseText: | ||
230 | 184 | '{"entry": {"resource_type_link": "foo"}}', | ||
231 | 185 | responseHeaders: {'Content-Type': 'application/json'} | ||
232 | 186 | }); | ||
233 | 187 | var result = mylist[0]; | ||
234 | 188 | Assert.isInstanceOf(Y.lp.client.Entry, result.entry); | ||
235 | 189 | Assert.areSame('foo', result.entry.get('resource_type_link')); | ||
236 | 190 | }, | ||
237 | 143 | test_wrap_resource_nested_mapping: function() { | 191 | test_wrap_resource_nested_mapping: function() { |
238 | 144 | // wrap_resource produces mappings of plain object literals. These can | 192 | // wrap_resource produces mappings of plain object literals. These can |
239 | 145 | // be nested and have Entries in them. | 193 | // be nested and have Entries in them. |
240 | 146 | 194 | ||
241 | === added file 'lib/lp/app/javascript/tests/test_lp_client_integration.js' | |||
242 | --- lib/lp/app/javascript/tests/test_lp_client_integration.js 1970-01-01 00:00:00 +0000 | |||
243 | +++ lib/lp/app/javascript/tests/test_lp_client_integration.js 2011-11-21 14:41:36 +0000 | |||
244 | @@ -0,0 +1,333 @@ | |||
245 | 1 | YUI({ | ||
246 | 2 | base: '/+icing/yui/', | ||
247 | 3 | filter: 'raw', combine: false, fetchCSS: false | ||
248 | 4 | }).use('test', | ||
249 | 5 | 'escape', | ||
250 | 6 | 'node', | ||
251 | 7 | 'console', | ||
252 | 8 | 'json', | ||
253 | 9 | 'cookie', | ||
254 | 10 | 'lp.testing.serverfixture', | ||
255 | 11 | 'lp.client', | ||
256 | 12 | function(Y) { | ||
257 | 13 | |||
258 | 14 | |||
259 | 15 | var suite = new Y.Test.Suite( | ||
260 | 16 | "Integration tests for lp.client and basic JS infrastructure"); | ||
261 | 17 | var serverfixture = Y.lp.testing.serverfixture; | ||
262 | 18 | |||
263 | 19 | var makeTestConfig = function(config) { | ||
264 | 20 | if (Y.Lang.isUndefined(config)) { | ||
265 | 21 | config = {}; | ||
266 | 22 | } | ||
267 | 23 | config.on = Y.merge( | ||
268 | 24 | { | ||
269 | 25 | success: function(result) { | ||
270 | 26 | config.successful = true; | ||
271 | 27 | config.result = result; | ||
272 | 28 | }, | ||
273 | 29 | failure: function(tid, response, args) { | ||
274 | 30 | config.successful = false; | ||
275 | 31 | config.result = {tid: tid, response: response, args: args}; | ||
276 | 32 | } | ||
277 | 33 | }, | ||
278 | 34 | config.on); | ||
279 | 35 | return config; | ||
280 | 36 | }; | ||
281 | 37 | |||
282 | 38 | /** | ||
283 | 39 | * Test cache data in page load. | ||
284 | 40 | */ | ||
285 | 41 | suite.add(new Y.Test.Case({ | ||
286 | 42 | name: 'Cache data', | ||
287 | 43 | |||
288 | 44 | tearDown: function() { | ||
289 | 45 | serverfixture.teardown(this); | ||
290 | 46 | }, | ||
291 | 47 | |||
292 | 48 | test_anonymous_user_has_no_cache_data: function() { | ||
293 | 49 | var data = serverfixture.setup(this, 'create_product'); | ||
294 | 50 | serverfixture.runWithIFrame( | ||
295 | 51 | {testcase: this, | ||
296 | 52 | uri: data.product.web_link, | ||
297 | 53 | iframe_is_ready: function(I) { | ||
298 | 54 | I.use('node'); | ||
299 | 55 | return I.Lang.isValue(I.one('#json-cache-script')); | ||
300 | 56 | } | ||
301 | 57 | }, | ||
302 | 58 | function(I) { | ||
303 | 59 | var iframe_window = I.config.win; | ||
304 | 60 | var LP = iframe_window.LP; | ||
305 | 61 | Y.Assert.isUndefined(LP.links.me); | ||
306 | 62 | Y.Assert.isNotUndefined(LP.cache.context); | ||
307 | 63 | } | ||
308 | 64 | ); | ||
309 | 65 | }, | ||
310 | 66 | |||
311 | 67 | test_logged_in_user_has_cache_data: function() { | ||
312 | 68 | var data = serverfixture.setup(this, 'create_product_and_login'); | ||
313 | 69 | serverfixture.runWithIFrame( | ||
314 | 70 | {testcase: this, | ||
315 | 71 | uri: data.product.web_link, | ||
316 | 72 | iframe_is_ready: function(I) { | ||
317 | 73 | I.use('node'); | ||
318 | 74 | return I.Lang.isValue(I.one('#json-cache-script')); | ||
319 | 75 | } | ||
320 | 76 | }, | ||
321 | 77 | function(I) { | ||
322 | 78 | var iframe_window = I.config.win; | ||
323 | 79 | var LP = iframe_window.LP; | ||
324 | 80 | Y.Assert.areSame( | ||
325 | 81 | '/~' + data.user.name, | ||
326 | 82 | LP.links.me | ||
327 | 83 | ); | ||
328 | 84 | Y.Assert.isNotUndefined(LP.cache.context); | ||
329 | 85 | } | ||
330 | 86 | ); | ||
331 | 87 | }, | ||
332 | 88 | |||
333 | 89 | test_get_relative_url: function() { | ||
334 | 90 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
335 | 91 | var config = makeTestConfig(); | ||
336 | 92 | client.get('/people', config); | ||
337 | 93 | Y.Assert.isTrue(config.successful); | ||
338 | 94 | Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result); | ||
339 | 95 | }, | ||
340 | 96 | |||
341 | 97 | test_get_absolute_url: function() { | ||
342 | 98 | var data = serverfixture.setup(this, 'create_product'); | ||
343 | 99 | var link = data.product.self_link; | ||
344 | 100 | Y.Assert.areSame('http', link.slice(0, 4)); // See, it's absolute. | ||
345 | 101 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
346 | 102 | var config = makeTestConfig(); | ||
347 | 103 | client.get(link, config); | ||
348 | 104 | Y.Assert.isTrue(config.successful); | ||
349 | 105 | Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result); | ||
350 | 106 | }, | ||
351 | 107 | |||
352 | 108 | test_get_collection_with_pagination: function() { | ||
353 | 109 | // We could do this with a fixture setup, but I'll rely on the | ||
354 | 110 | // sampledata for now. If this becomes a problem, write a quick | ||
355 | 111 | // fixture that creates three or four people! | ||
356 | 112 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
357 | 113 | var config = makeTestConfig({start: 2, size: 1}); | ||
358 | 114 | client.get('/people', config); | ||
359 | 115 | Y.Assert.isTrue(config.successful); | ||
360 | 116 | Y.Assert.areSame(2, config.result.start); | ||
361 | 117 | Y.Assert.areSame(1, config.result.entries.length); | ||
362 | 118 | }, | ||
363 | 119 | |||
364 | 120 | test_named_get_integration: function() { | ||
365 | 121 | var data = serverfixture.setup(this, 'create_user'); | ||
366 | 122 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
367 | 123 | var config = makeTestConfig({parameters: {text: data.user.name}}); | ||
368 | 124 | client.named_get('people', 'find', config); | ||
369 | 125 | Y.Assert.isTrue(config.successful); | ||
370 | 126 | Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result); | ||
371 | 127 | Y.Assert.areSame(1, config.result.total_size); | ||
372 | 128 | }, | ||
373 | 129 | |||
374 | 130 | test_named_post_integration: function() { | ||
375 | 131 | var data = serverfixture.setup(this, 'create_bug_and_login'); | ||
376 | 132 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
377 | 133 | var config = makeTestConfig(); | ||
378 | 134 | client.named_post( | ||
379 | 135 | data.bug.self_link, 'mute', config); | ||
380 | 136 | Y.Assert.isTrue(config.successful); | ||
381 | 137 | }, | ||
382 | 138 | |||
383 | 139 | test_follow_link: function() { | ||
384 | 140 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
385 | 141 | var config = makeTestConfig(); | ||
386 | 142 | client.get('', config); | ||
387 | 143 | var root = config.result; | ||
388 | 144 | config = makeTestConfig(); | ||
389 | 145 | root.follow_link('people', config); | ||
390 | 146 | Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result); | ||
391 | 147 | Y.Assert.areSame(4, config.result.total_size); | ||
392 | 148 | }, | ||
393 | 149 | |||
394 | 150 | test_follow_redirected_link: function() { | ||
395 | 151 | var data = serverfixture.setup(this, 'create_user_and_login'); | ||
396 | 152 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
397 | 153 | var config = makeTestConfig(); | ||
398 | 154 | client.get('', config); | ||
399 | 155 | var root = config.result; | ||
400 | 156 | config = makeTestConfig(); | ||
401 | 157 | root.follow_link('me', config); | ||
402 | 158 | Y.Assert.isTrue(config.successful); | ||
403 | 159 | Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result); | ||
404 | 160 | Y.Assert.areSame(data.user.name, config.result.get('name')); | ||
405 | 161 | }, | ||
406 | 162 | |||
407 | 163 | test_get_html_representation: function() { | ||
408 | 164 | var data = serverfixture.setup(this, 'create_user'); | ||
409 | 165 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
410 | 166 | var config = makeTestConfig({accept: Y.lp.client.XHTML}); | ||
411 | 167 | client.get(data.user.self_link, config); | ||
412 | 168 | Y.Assert.isTrue(config.successful); | ||
413 | 169 | Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result)); | ||
414 | 170 | }, | ||
415 | 171 | |||
416 | 172 | test_get_html_representation_escaped: function() { | ||
417 | 173 | var data = serverfixture.setup( | ||
418 | 174 | this, 'create_user_with_html_display_name'); | ||
419 | 175 | var actual_display_name = data.user.display_name; | ||
420 | 176 | Y.Assert.areEqual('<strong>naughty</strong>', actual_display_name); | ||
421 | 177 | var html_escaped_display_name = '<strong>naughty</strong>'; | ||
422 | 178 | var has_actual_display_name = new RegExp( | ||
423 | 179 | Y.Escape.regex(actual_display_name)); | ||
424 | 180 | var has_html_escaped_display_name = new RegExp( | ||
425 | 181 | Y.Escape.regex(html_escaped_display_name)); | ||
426 | 182 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
427 | 183 | var config = makeTestConfig({accept: Y.lp.client.XHTML}); | ||
428 | 184 | client.get(data.user.self_link, config); | ||
429 | 185 | Y.Assert.isTrue(config.successful); | ||
430 | 186 | Y.Assert.isTrue(has_html_escaped_display_name.test(config.result)); | ||
431 | 187 | Y.Assert.isFalse(has_actual_display_name.test(config.result)); | ||
432 | 188 | }, | ||
433 | 189 | |||
434 | 190 | test_lp_save_html_representation: function() { | ||
435 | 191 | var data = serverfixture.setup(this, 'create_user'); | ||
436 | 192 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
437 | 193 | var config = makeTestConfig({accept: Y.lp.client.XHTML}); | ||
438 | 194 | var user = new Y.lp.client.Entry( | ||
439 | 195 | client, data.user, data.user.self_link); | ||
440 | 196 | user.lp_save(config); | ||
441 | 197 | Y.Assert.isTrue(config.successful); | ||
442 | 198 | Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result)); | ||
443 | 199 | }, | ||
444 | 200 | |||
445 | 201 | test_patch_html_representation: function() { | ||
446 | 202 | var data = serverfixture.setup(this, 'create_user'); | ||
447 | 203 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
448 | 204 | var config = makeTestConfig({accept: Y.lp.client.XHTML}); | ||
449 | 205 | client.patch(data.user.self_link, {}, config); | ||
450 | 206 | Y.Assert.isTrue(config.successful); | ||
451 | 207 | Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result)); | ||
452 | 208 | }, | ||
453 | 209 | |||
454 | 210 | test_lp_save: function() { | ||
455 | 211 | var data = serverfixture.setup(this, 'create_user_and_login'); | ||
456 | 212 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
457 | 213 | var config = makeTestConfig(); | ||
458 | 214 | var user = new Y.lp.client.Entry( | ||
459 | 215 | client, data.user, data.user.self_link); | ||
460 | 216 | var original_display_name = user.get('display_name'); | ||
461 | 217 | var new_display_name = original_display_name + '_modified'; | ||
462 | 218 | user.set('display_name', new_display_name); | ||
463 | 219 | user.lp_save(config); | ||
464 | 220 | Y.Assert.isTrue(config.successful); | ||
465 | 221 | Y.Assert.areEqual(new_display_name, config.result.get('display_name')); | ||
466 | 222 | }, | ||
467 | 223 | |||
468 | 224 | test_lp_save_fails_with_mismatched_ETag: function() { | ||
469 | 225 | var data = serverfixture.setup(this, 'create_user_and_login'); | ||
470 | 226 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
471 | 227 | var config = makeTestConfig(); | ||
472 | 228 | var user = new Y.lp.client.Entry( | ||
473 | 229 | client, data.user, data.user.self_link); | ||
474 | 230 | var original_display_name = user.get('display_name'); | ||
475 | 231 | var new_display_name = original_display_name + '_modified'; | ||
476 | 232 | user.set('display_name', new_display_name); | ||
477 | 233 | user.set('http_etag', 'Non-matching ETag.'); | ||
478 | 234 | user.lp_save(config); | ||
479 | 235 | Y.Assert.isFalse(config.successful); | ||
480 | 236 | Y.Assert.areEqual(412, config.result.response.status); | ||
481 | 237 | }, | ||
482 | 238 | |||
483 | 239 | test_patch: function() { | ||
484 | 240 | var data = serverfixture.setup(this, 'create_user_and_login'); | ||
485 | 241 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
486 | 242 | var config = makeTestConfig(); | ||
487 | 243 | var new_display_name = data.user.display_name + '_modified'; | ||
488 | 244 | client.patch( | ||
489 | 245 | data.user.self_link, {display_name: new_display_name}, config); | ||
490 | 246 | Y.Assert.isTrue(config.successful); | ||
491 | 247 | Y.Assert.areEqual(new_display_name, config.result.get('display_name')); | ||
492 | 248 | }, | ||
493 | 249 | |||
494 | 250 | test_collection_entries: function() { | ||
495 | 251 | serverfixture.setup(this, 'login_as_admin'); | ||
496 | 252 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
497 | 253 | var config = makeTestConfig(); | ||
498 | 254 | client.get('/people', config); | ||
499 | 255 | var people = config.result; | ||
500 | 256 | Y.Assert.isInstanceOf(Y.lp.client.Collection, people); | ||
501 | 257 | Y.Assert.areEqual(4, people.total_size); | ||
502 | 258 | Y.Assert.areEqual(people.total_size, people.entries.length); | ||
503 | 259 | var i = 0; | ||
504 | 260 | for (; i < people.entries.length; i++) { | ||
505 | 261 | Y.Assert.isInstanceOf(Y.lp.client.Entry, people.entries[i]); | ||
506 | 262 | } | ||
507 | 263 | var entry = people.entries[0]; | ||
508 | 264 | var new_display_name = entry.get('display_name') + '_modified'; | ||
509 | 265 | entry.set('display_name', new_display_name); | ||
510 | 266 | config = makeTestConfig(); | ||
511 | 267 | entry.lp_save(config); | ||
512 | 268 | Y.Assert.areEqual(new_display_name, config.result.get('display_name')); | ||
513 | 269 | }, | ||
514 | 270 | |||
515 | 271 | test_collection_lp_slice: function() { | ||
516 | 272 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
517 | 273 | var config = makeTestConfig(); | ||
518 | 274 | client.get('/people', config); | ||
519 | 275 | var people = config.result; | ||
520 | 276 | people.lp_slice(config.on, 2, 1); | ||
521 | 277 | var slice = config.result; | ||
522 | 278 | Y.Assert.areEqual(2, slice.start); | ||
523 | 279 | Y.Assert.areEqual(1, slice.entries.length); | ||
524 | 280 | }, | ||
525 | 281 | |||
526 | 282 | test_collection_named_get: function() { | ||
527 | 283 | var data = serverfixture.setup(this, 'create_user'); | ||
528 | 284 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
529 | 285 | var config = makeTestConfig(); | ||
530 | 286 | client.get('/people', config); | ||
531 | 287 | var people = config.result; | ||
532 | 288 | config = makeTestConfig({parameters: {text: data.user.name}}); | ||
533 | 289 | people.named_get('find', config); | ||
534 | 290 | Y.Assert.isTrue(config.successful); | ||
535 | 291 | Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result); | ||
536 | 292 | Y.Assert.areEqual(1, config.result.total_size); | ||
537 | 293 | Y.Assert.areEqual(1, config.result.entries.length); | ||
538 | 294 | }, | ||
539 | 295 | |||
540 | 296 | test_collection_named_post: function() { | ||
541 | 297 | serverfixture.setup(this, 'login_as_admin'); | ||
542 | 298 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
543 | 299 | var config = makeTestConfig(); | ||
544 | 300 | client.get('/people', config); | ||
545 | 301 | var people = config.result; | ||
546 | 302 | config = makeTestConfig( | ||
547 | 303 | {parameters: {display_name: 'My lpclient team', | ||
548 | 304 | name: 'newlpclientteam'}}); | ||
549 | 305 | people.named_post('newTeam', config); | ||
550 | 306 | Y.Assert.isTrue(config.successful); | ||
551 | 307 | var team = config.result; | ||
552 | 308 | Y.Assert.isInstanceOf(Y.lp.client.Entry, team); | ||
553 | 309 | Y.Assert.areEqual('My lpclient team', team.get('display_name')); | ||
554 | 310 | Y.Assert.isTrue(/\~newlpclientteam$/.test(team.lp_original_uri)); | ||
555 | 311 | }, | ||
556 | 312 | |||
557 | 313 | test_collection_paged_named_get: function() { | ||
558 | 314 | var data = serverfixture.setup(this, 'create_user'); | ||
559 | 315 | var client = new Y.lp.client.Launchpad({sync: true}); | ||
560 | 316 | var config = makeTestConfig(); | ||
561 | 317 | client.get('/people', config); | ||
562 | 318 | var people = config.result; | ||
563 | 319 | config = makeTestConfig({parameters: {text: data.user.name}, | ||
564 | 320 | start: 10}); | ||
565 | 321 | people.named_get('find', config); | ||
566 | 322 | Y.Assert.isTrue(config.successful); | ||
567 | 323 | Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result); | ||
568 | 324 | // I believe that the total_size is not correct in this case for | ||
569 | 325 | // server-side efficiency in an edge case. It actually reports "10". | ||
570 | 326 | // Y.Assert.areEqual(1, config.result.total_size); | ||
571 | 327 | Y.Assert.areEqual(0, config.result.entries.length); | ||
572 | 328 | } | ||
573 | 329 | |||
574 | 330 | })); | ||
575 | 331 | |||
576 | 332 | serverfixture.run(suite); | ||
577 | 333 | }); | ||
578 | 0 | 334 | ||
579 | === added file 'lib/lp/app/javascript/tests/test_lp_client_integration.py' | |||
580 | --- lib/lp/app/javascript/tests/test_lp_client_integration.py 1970-01-01 00:00:00 +0000 | |||
581 | +++ lib/lp/app/javascript/tests/test_lp_client_integration.py 2011-11-21 14:41:36 +0000 | |||
582 | @@ -0,0 +1,62 @@ | |||
583 | 1 | # Copyright 2011 Canonical Ltd. This software is licensed under the | ||
584 | 2 | # GNU Affero General Public License version 3 (see the file LICENSE). | ||
585 | 3 | |||
586 | 4 | """Support for the lp.client YUIXHR tests. | ||
587 | 5 | """ | ||
588 | 6 | |||
589 | 7 | __metaclass__ = type | ||
590 | 8 | __all__ = [] | ||
591 | 9 | |||
592 | 10 | from lp.testing import person_logged_in | ||
593 | 11 | from lp.testing.yuixhr import ( | ||
594 | 12 | login_as_person, | ||
595 | 13 | make_suite, | ||
596 | 14 | setup, | ||
597 | 15 | ) | ||
598 | 16 | from lp.testing.factory import LaunchpadObjectFactory | ||
599 | 17 | |||
600 | 18 | factory = LaunchpadObjectFactory() | ||
601 | 19 | |||
602 | 20 | |||
603 | 21 | @setup | ||
604 | 22 | def create_user(request, data): | ||
605 | 23 | data['user'] = factory.makePerson() | ||
606 | 24 | |||
607 | 25 | |||
608 | 26 | @create_user.extend | ||
609 | 27 | def create_user_and_login(request, data): | ||
610 | 28 | login_as_person(data['user']) | ||
611 | 29 | |||
612 | 30 | |||
613 | 31 | @create_user.extend | ||
614 | 32 | def create_product(request, data): | ||
615 | 33 | with person_logged_in(data['user']): | ||
616 | 34 | data['product'] = factory.makeProduct(owner=data['user']) | ||
617 | 35 | |||
618 | 36 | |||
619 | 37 | @create_product.extend | ||
620 | 38 | def create_product_and_login(request, data): | ||
621 | 39 | login_as_person(data['user']) | ||
622 | 40 | |||
623 | 41 | |||
624 | 42 | @create_product_and_login.extend | ||
625 | 43 | def create_bug_and_login(request, data): | ||
626 | 44 | data['bug'] = factory.makeBug( | ||
627 | 45 | product=data['product'], owner=data['user']) | ||
628 | 46 | data['bugtask'] = data['bug'].bugtasks[0] | ||
629 | 47 | |||
630 | 48 | |||
631 | 49 | @setup | ||
632 | 50 | def login_as_admin(request, data): | ||
633 | 51 | data['admin'] = factory.makeAdministrator() | ||
634 | 52 | login_as_person(data['admin']) | ||
635 | 53 | |||
636 | 54 | |||
637 | 55 | @setup | ||
638 | 56 | def create_user_with_html_display_name(request, data): | ||
639 | 57 | data['user'] = factory.makePerson( | ||
640 | 58 | displayname='<strong>naughty</strong>') | ||
641 | 59 | |||
642 | 60 | |||
643 | 61 | def test_suite(): | ||
644 | 62 | return make_suite(__name__) | ||
645 | 0 | 63 | ||
646 | === modified file 'lib/lp/app/templates/base-layout-macros.pt' | |||
647 | --- lib/lp/app/templates/base-layout-macros.pt 2011-11-11 10:20:12 +0000 | |||
648 | +++ lib/lp/app/templates/base-layout-macros.pt 2011-11-21 14:41:36 +0000 | |||
649 | @@ -194,8 +194,12 @@ | |||
650 | 194 | '${links/?key/fmt:api_url}';"> | 194 | '${links/?key/fmt:api_url}';"> |
651 | 195 | </script> | 195 | </script> |
652 | 196 | </tal:cache> | 196 | </tal:cache> |
655 | 197 | 197 | <tal:comment condition="nothing"> | |
656 | 198 | <script tal:content="string:LP.cache = ${view/getCacheJSON};"> | 198 | The id of the script block below is used to determine whether this |
657 | 199 | page is loaded by test_lp_client_integration.js. | ||
658 | 200 | </tal:comment> | ||
659 | 201 | <script id="json-cache-script" | ||
660 | 202 | tal:content="string:LP.cache = ${view/getCacheJSON};"> | ||
661 | 199 | </script> | 203 | </script> |
662 | 200 | </metal:lp-client-cache> | 204 | </metal:lp-client-cache> |
663 | 201 | 205 | ||
664 | 202 | 206 | ||
665 | === modified file 'lib/lp/registry/browser/tests/test_subscription_links.py' | |||
666 | --- lib/lp/registry/browser/tests/test_subscription_links.py 2011-11-04 11:10:23 +0000 | |||
667 | +++ lib/lp/registry/browser/tests/test_subscription_links.py 2011-11-21 14:41:36 +0000 | |||
668 | @@ -6,7 +6,6 @@ | |||
669 | 6 | __metaclass__ = type | 6 | __metaclass__ = type |
670 | 7 | 7 | ||
671 | 8 | import re | 8 | import re |
672 | 9 | import simplejson | ||
673 | 10 | import unittest | 9 | import unittest |
674 | 11 | from zope.component import getUtility | 10 | from zope.component import getUtility |
675 | 12 | from BeautifulSoup import BeautifulSoup | 11 | from BeautifulSoup import BeautifulSoup |
676 | @@ -24,6 +23,7 @@ | |||
677 | 24 | from lp.registry.model.milestone import ProjectMilestone | 23 | from lp.registry.model.milestone import ProjectMilestone |
678 | 25 | from lp.testing import ( | 24 | from lp.testing import ( |
679 | 26 | celebrity_logged_in, | 25 | celebrity_logged_in, |
680 | 26 | extract_lp_cache, | ||
681 | 27 | person_logged_in, | 27 | person_logged_in, |
682 | 28 | BrowserTestCase, | 28 | BrowserTestCase, |
683 | 29 | TestCaseWithFactory, | 29 | TestCaseWithFactory, |
684 | @@ -71,11 +71,7 @@ | |||
685 | 71 | None, self.new_edit_link, | 71 | None, self.new_edit_link, |
686 | 72 | "Expected edit_bug_mail link missing") | 72 | "Expected edit_bug_mail link missing") |
687 | 73 | # Ensure the LP.cache has been populated. | 73 | # Ensure the LP.cache has been populated. |
693 | 74 | mo = re.search( | 74 | cache = extract_lp_cache(self.contents) |
689 | 75 | r'<script>\s*LP.cache\s*=\s*({.*?});\s*</script>', self.contents) | ||
690 | 76 | if mo is None: | ||
691 | 77 | self.fail('No JSON cache found') | ||
692 | 78 | cache = simplejson.loads(mo.group(1)) | ||
694 | 79 | self.assertIn('administratedTeams', cache) | 75 | self.assertIn('administratedTeams', cache) |
695 | 80 | # Ensure the call to setup the subscription is in the HTML. | 76 | # Ensure the call to setup the subscription is in the HTML. |
696 | 81 | # Only check for the presence of setup's configuration step; more | 77 | # Only check for the presence of setup's configuration step; more |
697 | 82 | 78 | ||
698 | === modified file 'lib/lp/testing/__init__.py' | |||
699 | --- lib/lp/testing/__init__.py 2011-11-15 10:39:28 +0000 | |||
700 | +++ lib/lp/testing/__init__.py 2011-11-21 14:41:36 +0000 | |||
701 | @@ -1322,7 +1322,9 @@ | |||
702 | 1322 | 1322 | ||
703 | 1323 | 1323 | ||
704 | 1324 | def extract_lp_cache(text): | 1324 | def extract_lp_cache(text): |
706 | 1325 | match = re.search(r'<script>LP.cache = (\{.*\});</script>', text) | 1325 | match = re.search(r'<script[^>]*>LP.cache = (\{.*\});</script>', text) |
707 | 1326 | if match is None: | ||
708 | 1327 | raise ValueError('No JSON cache found.') | ||
709 | 1326 | return simplejson.loads(match.group(1)) | 1328 | return simplejson.loads(match.group(1)) |
710 | 1327 | 1329 | ||
711 | 1328 | 1330 |
Hi Gary,
Thanks for this branch and helping to kill of windmill.
* As discussed on IRC, I think waitForIFrame is a bit misleading for a name but I've not got a reasonable suggestion for fixing it.
* When calling waitForIFrame it would be helpful to comment the arguments being passed. I needed to go back and forth to understand what was being passed. Trivial point.
* fix: """{Describe your test suite here}."""
Very nice branch.