Merge lp:~gary/launchpad/bug724609 into lp:launchpad

Proposed by Gary Poster
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
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://bazaar.launchpad.net/~launchpad-pqm/launchpad/devel/view/13343/lib/canonical/launchpad/windmill/jstests/launchpad_ajax.js

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!

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

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.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added file 'lib/lp/app/javascript/__init__.py'
=== modified file 'lib/lp/app/javascript/client.js'
--- lib/lp/app/javascript/client.js 2011-10-20 20:14:57 +0000
+++ lib/lp/app/javascript/client.js 2011-11-21 14:41:36 +0000
@@ -473,7 +473,9 @@
473 'headers': {"Content-Type": hosted_file.content_type,473 'headers': {"Content-Type": hosted_file.content_type,
474 "Content-Disposition": disposition},474 "Content-Disposition": disposition},
475 'arguments': args,475 'arguments': args,
476 'data': hosted_file.contents};476 'data': hosted_file.contents,
477 'sync': this.lp_client.sync
478 };
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);
478 },480 },
479481
@@ -483,7 +485,9 @@
483 var args = hosted_file;485 var args = hosted_file;
484 var y_config = { method: "DELETE",486 var y_config = { method: "DELETE",
485 on: on,487 on: on,
486 'arguments': args };488 'arguments': args,
489 sync: this.lp_client.sync
490 };
487 this.io_provider.io(hosted_file.uri, y_config);491 this.io_provider.io(hosted_file.uri, y_config);
488 }492 }
489};493};
@@ -559,11 +563,13 @@
559563
560564
561// The service root resource.565// The service root resource.
562module.Root = function(client, representation, uri) {566Root = function(client, representation, uri) {
563 /* The root of the Launchpad web service. */567 /* The root of the Launchpad web service. */
564 this.init(client, representation, uri);568 this.init(client, representation, uri);
565};569};
566module.Root.prototype = new Resource();570Root.prototype = new Resource();
571
572module.Root = Root;
567573
568574
569var Collection = function(client, representation, uri) {575var Collection = function(client, representation, uri) {
@@ -622,7 +628,6 @@
622628
623Entry.prototype.lp_save = function(config) {629Entry.prototype.lp_save = function(config) {
624 /* Write modifications to this entry back to the web service. */630 /* Write modifications to this entry back to the web service. */
625 var on = config.on;
626 var representation = {};631 var representation = {};
627 var entry = this;632 var entry = this;
628 Y.each(this.dirty_attributes, function(attribute, key) {633 Y.each(this.dirty_attributes, function(attribute, key) {
@@ -667,6 +672,7 @@
667var Launchpad = function(config) {672var Launchpad = function(config) {
668 /* A client that makes HTTP requests to Launchpad's web service. */673 /* A client that makes HTTP requests to Launchpad's web service. */
669 this.io_provider = module.get_configured_io_provider(config);674 this.io_provider = module.get_configured_io_provider(config);
675 this.sync = (config ? config.sync : false);
670};676};
671677
672Launchpad.prototype = {678Launchpad.prototype = {
@@ -693,7 +699,8 @@
693 on: on,699 on: on,
694 'arguments': [client, uri, old_on_success, update_cache],700 'arguments': [client, uri, old_on_success, update_cache],
695 'headers': headers,701 'headers': headers,
696 data: data702 data: data,
703 sync: this.sync
697 };704 };
698 return this.io_provider.io(uri, y_config);705 return this.io_provider.io(uri, y_config);
699 },706 },
@@ -745,13 +752,14 @@
745 method: "POST",752 method: "POST",
746 on: on,753 on: on,
747 'arguments': [client, uri, old_on_success, update_cache],754 'arguments': [client, uri, old_on_success, update_cache],
748 data: data755 data: data,
756 sync: this.sync
749 };757 };
750 this.io_provider.io(uri, y_config);758 this.io_provider.io(uri, y_config);
751 },759 },
752760
753 'patch': function(uri, representation, config, headers) {761 'patch': function(uri, representation, config, headers) {
754 var on = config.on;762 var on = Y.merge(config.on);
755 var data = Y.JSON.stringify(representation);763 var data = Y.JSON.stringify(representation);
756 uri = module.normalize_uri(uri);764 uri = module.normalize_uri(uri);
757765
@@ -780,7 +788,8 @@
780 'on': on,788 'on': on,
781 'headers': extra_headers,789 'headers': extra_headers,
782 'arguments': args,790 'arguments': args,
783 'data': data791 'data': data,
792 'sync': this.sync
784 };793 };
785 this.io_provider.io(uri, y_config);794 this.io_provider.io(uri, y_config);
786 },795 },
787796
=== modified file 'lib/lp/app/javascript/server_fixture.js'
--- lib/lp/app/javascript/server_fixture.js 2011-11-03 23:19:44 +0000
+++ lib/lp/app/javascript/server_fixture.js 2011-11-21 14:41:36 +0000
@@ -42,7 +42,73 @@
42 return data;42 return data;
43};43};
4444
45module.addCleanup = function(testcase, callable) {
46 if (Y.Lang.isUndefined(testcase._lp_fixture_cleanups)) {
47 testcase._lp_fixture_cleanups = [];
48 }
49 testcase._lp_fixture_cleanups.push(callable);
50};
51
52module.runWithIFrame = function(config, test) {
53 // Note that the iframe url must be in the same domain as the test page.
54 var iframe = Y.Node.create('<iframe/>').set('src', config.uri);
55 Y.one('body').append(iframe);
56 module.addCleanup(
57 config.testcase,
58 function() {
59 iframe.remove();
60 }
61 );
62 var timeout = config.timeout;
63 if (!Y.Lang.isValue(timeout)) {
64 timeout = 8000;
65 }
66 var iframe_is_ready = config.iframe_is_ready;
67 if (!Y.Lang.isValue(iframe_is_ready)) {
68 iframe_is_ready = function() {return true;};
69 }
70 var wait = config.wait;
71 if (!Y.Lang.isValue(wait)) {
72 wait = 100;
73 }
74 var start;
75 var retry_function;
76 retry_function = function() {
77 var tested = false;
78 var win = Y.Node.getDOMNode(iframe.get('contentWindow'));
79 if (Y.Lang.isValue(win) && Y.Lang.isValue(win.document)) {
80 var IYUI = YUI({
81 base: '/+icing/yui/',
82 filter: 'raw',
83 combine: false,
84 fetchCSS: false,
85 win: win});
86 if (iframe_is_ready(IYUI)) {
87 test(IYUI);
88 tested = true;
89 }
90 }
91 if (!tested) {
92 var now = new Date().getTime();
93 if (now-start < timeout) {
94 config.testcase.wait(retry_function, wait);
95 } else {
96 Y.Assert.fail('Page did not load in iframe: ' + url);
97 }
98 }
99 };
100 start = new Date().getTime();
101 config.testcase.wait(retry_function, wait);
102};
103
45module.teardown = function(testcase) {104module.teardown = function(testcase) {
105 var cleanups = testcase._lp_fixture_cleanups;
106 var i;
107 if (!Y.Lang.isUndefined(cleanups)) {
108 for (i=cleanups.length-1; i>=0 ; i--) {
109 cleanups[i]();
110 }
111 }
46 var fixtures = testcase._lp_fixture_setups;112 var fixtures = testcase._lp_fixture_setups;
47 if (Y.Lang.isUndefined(fixtures)) {113 if (Y.Lang.isUndefined(fixtures)) {
48 // Nothing to be done.114 // Nothing to be done.
@@ -101,4 +167,4 @@
101 },167 },
102 "0.1",168 "0.1",
103 {"requires": [169 {"requires": [
104 "io", "json", "querystring", "test", "console", "lp.client"]});170 "io", "json", "querystring", "test", "console", "lp.client", "node"]});
105171
=== added file 'lib/lp/app/javascript/tests/__init__.py'
=== modified file 'lib/lp/app/javascript/tests/test_lp_client.js'
--- lib/lp/app/javascript/tests/test_lp_client.js 2011-10-11 17:05:30 +0000
+++ lib/lp/app/javascript/tests/test_lp_client.js 2011-11-21 14:41:36 +0000
@@ -140,6 +140,54 @@
140 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);140 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
141 Assert.areSame('foo', result.entry.get('resource_type_link'));141 Assert.areSame('foo', result.entry.get('resource_type_link'));
142 },142 },
143 test_get_success_callback: function() {
144 var mockio = new Y.lp.testing.mockio.MockIo();
145 var mylist = [];
146 var client = new Y.lp.client.Launchpad({io_provider: mockio});
147 client.get('/people', {on:{success: Y.bind(mylist.push, mylist)}});
148 Assert.areEqual('/api/devel/people', mockio.last_request.url);
149 mockio.success({
150 responseText:
151 '{"entry": {"resource_type_link": "foo"}}',
152 responseHeaders: {'Content-Type': 'application/json'}
153 });
154 var result = mylist[0];
155 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
156 Assert.areSame('foo', result.entry.get('resource_type_link'));
157 },
158 test_get_failure_callback: function() {
159 var mockio = new Y.lp.testing.mockio.MockIo();
160 var mylist = [];
161 var client = new Y.lp.client.Launchpad({io_provider: mockio});
162 client.get(
163 '/people',
164 {on: {
165 failure: function(){
166 mylist.push(Array.prototype.slice.call(arguments));
167 }}});
168 mockio.failure({status: 503});
169 var result = mylist[0];
170 Assert.areSame(503, result[1].status);
171 Assert.areSame('/api/devel/people', result[2][1]);
172 },
173 test_named_post_success_callback: function() {
174 var mockio = new Y.lp.testing.mockio.MockIo();
175 var mylist = [];
176 var client = new Y.lp.client.Launchpad({io_provider: mockio});
177 client.named_post(
178 '/people', 'newTeam', {on:{success: Y.bind(mylist.push, mylist)}});
179 Assert.areEqual('/api/devel/people', mockio.last_request.url);
180 Assert.areEqual('ws.op=newTeam', mockio.last_request.config.data);
181 Assert.areEqual('POST', mockio.last_request.config.method);
182 mockio.success({
183 responseText:
184 '{"entry": {"resource_type_link": "foo"}}',
185 responseHeaders: {'Content-Type': 'application/json'}
186 });
187 var result = mylist[0];
188 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
189 Assert.areSame('foo', result.entry.get('resource_type_link'));
190 },
143 test_wrap_resource_nested_mapping: function() {191 test_wrap_resource_nested_mapping: function() {
144 // wrap_resource produces mappings of plain object literals. These can192 // wrap_resource produces mappings of plain object literals. These can
145 // be nested and have Entries in them.193 // be nested and have Entries in them.
146194
=== added file 'lib/lp/app/javascript/tests/test_lp_client_integration.js'
--- lib/lp/app/javascript/tests/test_lp_client_integration.js 1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/tests/test_lp_client_integration.js 2011-11-21 14:41:36 +0000
@@ -0,0 +1,333 @@
1YUI({
2 base: '/+icing/yui/',
3 filter: 'raw', combine: false, fetchCSS: false
4}).use('test',
5 'escape',
6 'node',
7 'console',
8 'json',
9 'cookie',
10 'lp.testing.serverfixture',
11 'lp.client',
12 function(Y) {
13
14
15var suite = new Y.Test.Suite(
16 "Integration tests for lp.client and basic JS infrastructure");
17var serverfixture = Y.lp.testing.serverfixture;
18
19var makeTestConfig = function(config) {
20 if (Y.Lang.isUndefined(config)) {
21 config = {};
22 }
23 config.on = Y.merge(
24 {
25 success: function(result) {
26 config.successful = true;
27 config.result = result;
28 },
29 failure: function(tid, response, args) {
30 config.successful = false;
31 config.result = {tid: tid, response: response, args: args};
32 }
33 },
34 config.on);
35 return config;
36};
37
38/**
39 * Test cache data in page load.
40 */
41suite.add(new Y.Test.Case({
42 name: 'Cache data',
43
44 tearDown: function() {
45 serverfixture.teardown(this);
46 },
47
48 test_anonymous_user_has_no_cache_data: function() {
49 var data = serverfixture.setup(this, 'create_product');
50 serverfixture.runWithIFrame(
51 {testcase: this,
52 uri: data.product.web_link,
53 iframe_is_ready: function(I) {
54 I.use('node');
55 return I.Lang.isValue(I.one('#json-cache-script'));
56 }
57 },
58 function(I) {
59 var iframe_window = I.config.win;
60 var LP = iframe_window.LP;
61 Y.Assert.isUndefined(LP.links.me);
62 Y.Assert.isNotUndefined(LP.cache.context);
63 }
64 );
65 },
66
67 test_logged_in_user_has_cache_data: function() {
68 var data = serverfixture.setup(this, 'create_product_and_login');
69 serverfixture.runWithIFrame(
70 {testcase: this,
71 uri: data.product.web_link,
72 iframe_is_ready: function(I) {
73 I.use('node');
74 return I.Lang.isValue(I.one('#json-cache-script'));
75 }
76 },
77 function(I) {
78 var iframe_window = I.config.win;
79 var LP = iframe_window.LP;
80 Y.Assert.areSame(
81 '/~' + data.user.name,
82 LP.links.me
83 );
84 Y.Assert.isNotUndefined(LP.cache.context);
85 }
86 );
87 },
88
89 test_get_relative_url: function() {
90 var client = new Y.lp.client.Launchpad({sync: true});
91 var config = makeTestConfig();
92 client.get('/people', config);
93 Y.Assert.isTrue(config.successful);
94 Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
95 },
96
97 test_get_absolute_url: function() {
98 var data = serverfixture.setup(this, 'create_product');
99 var link = data.product.self_link;
100 Y.Assert.areSame('http', link.slice(0, 4)); // See, it's absolute.
101 var client = new Y.lp.client.Launchpad({sync: true});
102 var config = makeTestConfig();
103 client.get(link, config);
104 Y.Assert.isTrue(config.successful);
105 Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result);
106 },
107
108 test_get_collection_with_pagination: function() {
109 // We could do this with a fixture setup, but I'll rely on the
110 // sampledata for now. If this becomes a problem, write a quick
111 // fixture that creates three or four people!
112 var client = new Y.lp.client.Launchpad({sync: true});
113 var config = makeTestConfig({start: 2, size: 1});
114 client.get('/people', config);
115 Y.Assert.isTrue(config.successful);
116 Y.Assert.areSame(2, config.result.start);
117 Y.Assert.areSame(1, config.result.entries.length);
118 },
119
120 test_named_get_integration: function() {
121 var data = serverfixture.setup(this, 'create_user');
122 var client = new Y.lp.client.Launchpad({sync: true});
123 var config = makeTestConfig({parameters: {text: data.user.name}});
124 client.named_get('people', 'find', config);
125 Y.Assert.isTrue(config.successful);
126 Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
127 Y.Assert.areSame(1, config.result.total_size);
128 },
129
130 test_named_post_integration: function() {
131 var data = serverfixture.setup(this, 'create_bug_and_login');
132 var client = new Y.lp.client.Launchpad({sync: true});
133 var config = makeTestConfig();
134 client.named_post(
135 data.bug.self_link, 'mute', config);
136 Y.Assert.isTrue(config.successful);
137 },
138
139 test_follow_link: function() {
140 var client = new Y.lp.client.Launchpad({sync: true});
141 var config = makeTestConfig();
142 client.get('', config);
143 var root = config.result;
144 config = makeTestConfig();
145 root.follow_link('people', config);
146 Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
147 Y.Assert.areSame(4, config.result.total_size);
148 },
149
150 test_follow_redirected_link: function() {
151 var data = serverfixture.setup(this, 'create_user_and_login');
152 var client = new Y.lp.client.Launchpad({sync: true});
153 var config = makeTestConfig();
154 client.get('', config);
155 var root = config.result;
156 config = makeTestConfig();
157 root.follow_link('me', config);
158 Y.Assert.isTrue(config.successful);
159 Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result);
160 Y.Assert.areSame(data.user.name, config.result.get('name'));
161 },
162
163 test_get_html_representation: function() {
164 var data = serverfixture.setup(this, 'create_user');
165 var client = new Y.lp.client.Launchpad({sync: true});
166 var config = makeTestConfig({accept: Y.lp.client.XHTML});
167 client.get(data.user.self_link, config);
168 Y.Assert.isTrue(config.successful);
169 Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
170 },
171
172 test_get_html_representation_escaped: function() {
173 var data = serverfixture.setup(
174 this, 'create_user_with_html_display_name');
175 var actual_display_name = data.user.display_name;
176 Y.Assert.areEqual('<strong>naughty</strong>', actual_display_name);
177 var html_escaped_display_name = '&lt;strong&gt;naughty&lt;/strong&gt;';
178 var has_actual_display_name = new RegExp(
179 Y.Escape.regex(actual_display_name));
180 var has_html_escaped_display_name = new RegExp(
181 Y.Escape.regex(html_escaped_display_name));
182 var client = new Y.lp.client.Launchpad({sync: true});
183 var config = makeTestConfig({accept: Y.lp.client.XHTML});
184 client.get(data.user.self_link, config);
185 Y.Assert.isTrue(config.successful);
186 Y.Assert.isTrue(has_html_escaped_display_name.test(config.result));
187 Y.Assert.isFalse(has_actual_display_name.test(config.result));
188 },
189
190 test_lp_save_html_representation: function() {
191 var data = serverfixture.setup(this, 'create_user');
192 var client = new Y.lp.client.Launchpad({sync: true});
193 var config = makeTestConfig({accept: Y.lp.client.XHTML});
194 var user = new Y.lp.client.Entry(
195 client, data.user, data.user.self_link);
196 user.lp_save(config);
197 Y.Assert.isTrue(config.successful);
198 Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
199 },
200
201 test_patch_html_representation: function() {
202 var data = serverfixture.setup(this, 'create_user');
203 var client = new Y.lp.client.Launchpad({sync: true});
204 var config = makeTestConfig({accept: Y.lp.client.XHTML});
205 client.patch(data.user.self_link, {}, config);
206 Y.Assert.isTrue(config.successful);
207 Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
208 },
209
210 test_lp_save: function() {
211 var data = serverfixture.setup(this, 'create_user_and_login');
212 var client = new Y.lp.client.Launchpad({sync: true});
213 var config = makeTestConfig();
214 var user = new Y.lp.client.Entry(
215 client, data.user, data.user.self_link);
216 var original_display_name = user.get('display_name');
217 var new_display_name = original_display_name + '_modified';
218 user.set('display_name', new_display_name);
219 user.lp_save(config);
220 Y.Assert.isTrue(config.successful);
221 Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
222 },
223
224 test_lp_save_fails_with_mismatched_ETag: function() {
225 var data = serverfixture.setup(this, 'create_user_and_login');
226 var client = new Y.lp.client.Launchpad({sync: true});
227 var config = makeTestConfig();
228 var user = new Y.lp.client.Entry(
229 client, data.user, data.user.self_link);
230 var original_display_name = user.get('display_name');
231 var new_display_name = original_display_name + '_modified';
232 user.set('display_name', new_display_name);
233 user.set('http_etag', 'Non-matching ETag.');
234 user.lp_save(config);
235 Y.Assert.isFalse(config.successful);
236 Y.Assert.areEqual(412, config.result.response.status);
237 },
238
239 test_patch: function() {
240 var data = serverfixture.setup(this, 'create_user_and_login');
241 var client = new Y.lp.client.Launchpad({sync: true});
242 var config = makeTestConfig();
243 var new_display_name = data.user.display_name + '_modified';
244 client.patch(
245 data.user.self_link, {display_name: new_display_name}, config);
246 Y.Assert.isTrue(config.successful);
247 Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
248 },
249
250 test_collection_entries: function() {
251 serverfixture.setup(this, 'login_as_admin');
252 var client = new Y.lp.client.Launchpad({sync: true});
253 var config = makeTestConfig();
254 client.get('/people', config);
255 var people = config.result;
256 Y.Assert.isInstanceOf(Y.lp.client.Collection, people);
257 Y.Assert.areEqual(4, people.total_size);
258 Y.Assert.areEqual(people.total_size, people.entries.length);
259 var i = 0;
260 for (; i < people.entries.length; i++) {
261 Y.Assert.isInstanceOf(Y.lp.client.Entry, people.entries[i]);
262 }
263 var entry = people.entries[0];
264 var new_display_name = entry.get('display_name') + '_modified';
265 entry.set('display_name', new_display_name);
266 config = makeTestConfig();
267 entry.lp_save(config);
268 Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
269 },
270
271 test_collection_lp_slice: function() {
272 var client = new Y.lp.client.Launchpad({sync: true});
273 var config = makeTestConfig();
274 client.get('/people', config);
275 var people = config.result;
276 people.lp_slice(config.on, 2, 1);
277 var slice = config.result;
278 Y.Assert.areEqual(2, slice.start);
279 Y.Assert.areEqual(1, slice.entries.length);
280 },
281
282 test_collection_named_get: function() {
283 var data = serverfixture.setup(this, 'create_user');
284 var client = new Y.lp.client.Launchpad({sync: true});
285 var config = makeTestConfig();
286 client.get('/people', config);
287 var people = config.result;
288 config = makeTestConfig({parameters: {text: data.user.name}});
289 people.named_get('find', config);
290 Y.Assert.isTrue(config.successful);
291 Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
292 Y.Assert.areEqual(1, config.result.total_size);
293 Y.Assert.areEqual(1, config.result.entries.length);
294 },
295
296 test_collection_named_post: function() {
297 serverfixture.setup(this, 'login_as_admin');
298 var client = new Y.lp.client.Launchpad({sync: true});
299 var config = makeTestConfig();
300 client.get('/people', config);
301 var people = config.result;
302 config = makeTestConfig(
303 {parameters: {display_name: 'My lpclient team',
304 name: 'newlpclientteam'}});
305 people.named_post('newTeam', config);
306 Y.Assert.isTrue(config.successful);
307 var team = config.result;
308 Y.Assert.isInstanceOf(Y.lp.client.Entry, team);
309 Y.Assert.areEqual('My lpclient team', team.get('display_name'));
310 Y.Assert.isTrue(/\~newlpclientteam$/.test(team.lp_original_uri));
311 },
312
313 test_collection_paged_named_get: function() {
314 var data = serverfixture.setup(this, 'create_user');
315 var client = new Y.lp.client.Launchpad({sync: true});
316 var config = makeTestConfig();
317 client.get('/people', config);
318 var people = config.result;
319 config = makeTestConfig({parameters: {text: data.user.name},
320 start: 10});
321 people.named_get('find', config);
322 Y.Assert.isTrue(config.successful);
323 Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
324 // I believe that the total_size is not correct in this case for
325 // server-side efficiency in an edge case. It actually reports "10".
326 // Y.Assert.areEqual(1, config.result.total_size);
327 Y.Assert.areEqual(0, config.result.entries.length);
328 }
329
330}));
331
332serverfixture.run(suite);
333});
0334
=== added file 'lib/lp/app/javascript/tests/test_lp_client_integration.py'
--- lib/lp/app/javascript/tests/test_lp_client_integration.py 1970-01-01 00:00:00 +0000
+++ lib/lp/app/javascript/tests/test_lp_client_integration.py 2011-11-21 14:41:36 +0000
@@ -0,0 +1,62 @@
1# Copyright 2011 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Support for the lp.client YUIXHR tests.
5"""
6
7__metaclass__ = type
8__all__ = []
9
10from lp.testing import person_logged_in
11from lp.testing.yuixhr import (
12 login_as_person,
13 make_suite,
14 setup,
15 )
16from lp.testing.factory import LaunchpadObjectFactory
17
18factory = LaunchpadObjectFactory()
19
20
21@setup
22def create_user(request, data):
23 data['user'] = factory.makePerson()
24
25
26@create_user.extend
27def create_user_and_login(request, data):
28 login_as_person(data['user'])
29
30
31@create_user.extend
32def create_product(request, data):
33 with person_logged_in(data['user']):
34 data['product'] = factory.makeProduct(owner=data['user'])
35
36
37@create_product.extend
38def create_product_and_login(request, data):
39 login_as_person(data['user'])
40
41
42@create_product_and_login.extend
43def create_bug_and_login(request, data):
44 data['bug'] = factory.makeBug(
45 product=data['product'], owner=data['user'])
46 data['bugtask'] = data['bug'].bugtasks[0]
47
48
49@setup
50def login_as_admin(request, data):
51 data['admin'] = factory.makeAdministrator()
52 login_as_person(data['admin'])
53
54
55@setup
56def create_user_with_html_display_name(request, data):
57 data['user'] = factory.makePerson(
58 displayname='<strong>naughty</strong>')
59
60
61def test_suite():
62 return make_suite(__name__)
063
=== modified file 'lib/lp/app/templates/base-layout-macros.pt'
--- lib/lp/app/templates/base-layout-macros.pt 2011-11-11 10:20:12 +0000
+++ lib/lp/app/templates/base-layout-macros.pt 2011-11-21 14:41:36 +0000
@@ -194,8 +194,12 @@
194 '${links/?key/fmt:api_url}';">194 '${links/?key/fmt:api_url}';">
195 </script>195 </script>
196 </tal:cache>196 </tal:cache>
197197 <tal:comment condition="nothing">
198 <script tal:content="string:LP.cache = ${view/getCacheJSON};">198 The id of the script block below is used to determine whether this
199 page is loaded by test_lp_client_integration.js.
200 </tal:comment>
201 <script id="json-cache-script"
202 tal:content="string:LP.cache = ${view/getCacheJSON};">
199 </script>203 </script>
200</metal:lp-client-cache>204</metal:lp-client-cache>
201205
202206
=== modified file 'lib/lp/registry/browser/tests/test_subscription_links.py'
--- lib/lp/registry/browser/tests/test_subscription_links.py 2011-11-04 11:10:23 +0000
+++ lib/lp/registry/browser/tests/test_subscription_links.py 2011-11-21 14:41:36 +0000
@@ -6,7 +6,6 @@
6__metaclass__ = type6__metaclass__ = type
77
8import re8import re
9import simplejson
10import unittest9import unittest
11from zope.component import getUtility10from zope.component import getUtility
12from BeautifulSoup import BeautifulSoup11from BeautifulSoup import BeautifulSoup
@@ -24,6 +23,7 @@
24from lp.registry.model.milestone import ProjectMilestone23from lp.registry.model.milestone import ProjectMilestone
25from lp.testing import (24from lp.testing import (
26 celebrity_logged_in,25 celebrity_logged_in,
26 extract_lp_cache,
27 person_logged_in,27 person_logged_in,
28 BrowserTestCase,28 BrowserTestCase,
29 TestCaseWithFactory,29 TestCaseWithFactory,
@@ -71,11 +71,7 @@
71 None, self.new_edit_link,71 None, self.new_edit_link,
72 "Expected edit_bug_mail link missing")72 "Expected edit_bug_mail link missing")
73 # Ensure the LP.cache has been populated.73 # Ensure the LP.cache has been populated.
74 mo = re.search(74 cache = extract_lp_cache(self.contents)
75 r'<script>\s*LP.cache\s*=\s*({.*?});\s*</script>', self.contents)
76 if mo is None:
77 self.fail('No JSON cache found')
78 cache = simplejson.loads(mo.group(1))
79 self.assertIn('administratedTeams', cache)75 self.assertIn('administratedTeams', cache)
80 # Ensure the call to setup the subscription is in the HTML.76 # Ensure the call to setup the subscription is in the HTML.
81 # Only check for the presence of setup's configuration step; more77 # Only check for the presence of setup's configuration step; more
8278
=== modified file 'lib/lp/testing/__init__.py'
--- lib/lp/testing/__init__.py 2011-11-15 10:39:28 +0000
+++ lib/lp/testing/__init__.py 2011-11-21 14:41:36 +0000
@@ -1322,7 +1322,9 @@
13221322
13231323
1324def extract_lp_cache(text):1324def extract_lp_cache(text):
1325 match = re.search(r'<script>LP.cache = (\{.*\});</script>', text)1325 match = re.search(r'<script[^>]*>LP.cache = (\{.*\});</script>', text)
1326 if match is None:
1327 raise ValueError('No JSON cache found.')
1326 return simplejson.loads(match.group(1))1328 return simplejson.loads(match.group(1))
13271329
13281330