launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #05602
[Merge] lp:~gary/launchpad/bug724609 into lp:launchpad
Gary Poster has proposed merging lp:~gary/launchpad/bug724609 into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #724609 in Launchpad itself: "Rewrite lp.client tests using yuitest"
https://bugs.launchpad.net/launchpad/+bug/724609
For more details, see:
https://code.launchpad.net/~gary/launchpad/bug724609/+merge/82689
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!
--
https://code.launchpad.net/~gary/launchpad/bug724609/+merge/82689
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~gary/launchpad/bug724609 into lp:launchpad.
=== 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-18 15:00:41 +0000
@@ -473,7 +473,9 @@
'headers': {"Content-Type": hosted_file.content_type,
"Content-Disposition": disposition},
'arguments': args,
- 'data': hosted_file.contents};
+ 'data': hosted_file.contents,
+ 'sync': this.lp_client.sync
+ };
this.io_provider.io(module.normalize_uri(hosted_file.uri), y_config);
},
@@ -483,7 +485,9 @@
var args = hosted_file;
var y_config = { method: "DELETE",
on: on,
- 'arguments': args };
+ 'arguments': args,
+ sync: this.lp_client.sync
+ };
this.io_provider.io(hosted_file.uri, y_config);
}
};
@@ -559,11 +563,13 @@
// The service root resource.
-module.Root = function(client, representation, uri) {
+Root = function(client, representation, uri) {
/* The root of the Launchpad web service. */
this.init(client, representation, uri);
};
-module.Root.prototype = new Resource();
+Root.prototype = new Resource();
+
+module.Root = Root;
var Collection = function(client, representation, uri) {
@@ -622,7 +628,6 @@
Entry.prototype.lp_save = function(config) {
/* Write modifications to this entry back to the web service. */
- var on = config.on;
var representation = {};
var entry = this;
Y.each(this.dirty_attributes, function(attribute, key) {
@@ -667,6 +672,7 @@
var Launchpad = function(config) {
/* A client that makes HTTP requests to Launchpad's web service. */
this.io_provider = module.get_configured_io_provider(config);
+ this.sync = config.sync;
};
Launchpad.prototype = {
@@ -693,7 +699,8 @@
on: on,
'arguments': [client, uri, old_on_success, update_cache],
'headers': headers,
- data: data
+ data: data,
+ sync: this.sync
};
return this.io_provider.io(uri, y_config);
},
@@ -745,13 +752,14 @@
method: "POST",
on: on,
'arguments': [client, uri, old_on_success, update_cache],
- data: data
+ data: data,
+ sync: this.sync
};
this.io_provider.io(uri, y_config);
},
'patch': function(uri, representation, config, headers) {
- var on = config.on;
+ var on = Y.merge(config.on);
var data = Y.JSON.stringify(representation);
uri = module.normalize_uri(uri);
@@ -780,7 +788,8 @@
'on': on,
'headers': extra_headers,
'arguments': args,
- 'data': data
+ 'data': data,
+ 'sync': this.sync
};
this.io_provider.io(uri, y_config);
},
=== 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-18 15:00:41 +0000
@@ -42,7 +42,64 @@
return data;
};
+module.addCleanup = function(testcase, callable) {
+ if (Y.Lang.isUndefined(testcase._lp_fixture_cleanups)) {
+ testcase._lp_fixture_cleanups = [];
+ }
+ testcase._lp_fixture_cleanups.push(callable);
+};
+
+module.waitForIFrame = function(testcase, url, is_ready, test, timeout) {
+ // Note that the iframe url must be in the same domain as the test page.
+ var iframe = Y.Node.create('<iframe/>').set('src', url);
+ Y.one('body').append(iframe);
+ module.addCleanup(
+ testcase,
+ function() {
+ iframe.remove();
+ }
+ );
+ if (!Y.Lang.isValue(timeout)) {
+ timeout = 5000;
+ }
+ var start;
+ var retry_function;
+ retry_function = function() {
+ var tested = false;
+ var win = Y.Node.getDOMNode(iframe.get('contentWindow'));
+ if (Y.Lang.isValue(win) && Y.Lang.isValue(win.document)) {
+ var IYUI = YUI({
+ base: '/+icing/yui/',
+ filter: 'raw',
+ combine: false,
+ fetchCSS: false,
+ win: win});
+ if (is_ready(IYUI)) {
+ test(IYUI);
+ tested = true;
+ }
+ }
+ if (!tested) {
+ var now = new Date().getTime();
+ if (now-start < timeout) {
+ testcase.wait(retry_function, 100);
+ } else {
+ Y.Assert.fail('Page did not load in iframe: ' + url);
+ }
+ }
+ };
+ start = new Date().getTime();
+ testcase.wait(retry_function, 100);
+};
+
module.teardown = function(testcase) {
+ var cleanups = testcase._lp_fixture_cleanups;
+ var i;
+ if (!Y.Lang.isUndefined(cleanups)) {
+ for (i=cleanups.length-1; i>=0 ; i--) {
+ cleanups[i]();
+ }
+ }
var fixtures = testcase._lp_fixture_setups;
if (Y.Lang.isUndefined(fixtures)) {
// Nothing to be done.
@@ -101,4 +158,4 @@
},
"0.1",
{"requires": [
- "io", "json", "querystring", "test", "console", "lp.client"]});
+ "io", "json", "querystring", "test", "console", "lp.client", "node"]});
=== 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-18 15:00:41 +0000
@@ -140,6 +140,54 @@
Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
Assert.areSame('foo', result.entry.get('resource_type_link'));
},
+ test_get_success_callback: function() {
+ var mockio = new Y.lp.testing.mockio.MockIo();
+ var mylist = [];
+ var client = new Y.lp.client.Launchpad({io_provider: mockio});
+ client.get('/people', {on:{success: Y.bind(mylist.push, mylist)}});
+ Assert.areEqual('/api/devel/people', mockio.last_request.url);
+ mockio.success({
+ responseText:
+ '{"entry": {"resource_type_link": "foo"}}',
+ responseHeaders: {'Content-Type': 'application/json'}
+ });
+ var result = mylist[0];
+ Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
+ Assert.areSame('foo', result.entry.get('resource_type_link'));
+ },
+ test_get_failure_callback: function() {
+ var mockio = new Y.lp.testing.mockio.MockIo();
+ var mylist = [];
+ var client = new Y.lp.client.Launchpad({io_provider: mockio});
+ client.get(
+ '/people',
+ {on: {
+ failure: function(){
+ mylist.push(Array.prototype.slice.call(arguments));
+ }}});
+ mockio.failure({status: 503});
+ var result = mylist[0];
+ Assert.areSame(503, result[1].status);
+ Assert.areSame('/api/devel/people', result[2][1]);
+ },
+ test_named_post_success_callback: function() {
+ var mockio = new Y.lp.testing.mockio.MockIo();
+ var mylist = [];
+ var client = new Y.lp.client.Launchpad({io_provider: mockio});
+ client.named_post(
+ '/people', 'newTeam', {on:{success: Y.bind(mylist.push, mylist)}});
+ Assert.areEqual('/api/devel/people', mockio.last_request.url);
+ Assert.areEqual('ws.op=newTeam', mockio.last_request.config.data);
+ Assert.areEqual('POST', mockio.last_request.config.method);
+ mockio.success({
+ responseText:
+ '{"entry": {"resource_type_link": "foo"}}',
+ responseHeaders: {'Content-Type': 'application/json'}
+ });
+ var result = mylist[0];
+ Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
+ Assert.areSame('foo', result.entry.get('resource_type_link'));
+ },
test_wrap_resource_nested_mapping: function() {
// wrap_resource produces mappings of plain object literals. These can
// be nested and have Entries in them.
=== 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-18 15:00:41 +0000
@@ -0,0 +1,331 @@
+YUI({
+ base: '/+icing/yui/',
+ filter: 'raw', combine: false, fetchCSS: false
+}).use('test',
+ 'escape',
+ 'node',
+ 'console',
+ 'json',
+ 'cookie',
+ 'lp.testing.serverfixture',
+ 'lp.client',
+ function(Y) {
+
+
+var suite = new Y.Test.Suite(
+ "Integration tests for lp.client and basic JS infrastructure");
+var serverfixture = Y.lp.testing.serverfixture;
+
+var makeTestConfig = function(config) {
+ if (Y.Lang.isUndefined(config)) {
+ config = {};
+ }
+ config.on = Y.merge(
+ {
+ success: function(result) {
+ config.successful = true;
+ config.result = result;
+ },
+ failure: function(tid, response, args) {
+ config.successful = false;
+ config.result = {tid: tid, response: response, args: args};
+ }
+ },
+ config.on);
+ return config;
+};
+
+/**
+ * Test cache data in page load.
+ */
+suite.add(new Y.Test.Case({
+ name: 'Cache data',
+
+ tearDown: function() {
+ serverfixture.teardown(this);
+ },
+
+ test_anonymous_user_has_no_cache_data: function() {
+ var data = serverfixture.setup(this, 'create_product');
+ serverfixture.waitForIFrame(
+ this,
+ data.product.web_link,
+ function(I) {
+ I.use('node');
+ return I.Lang.isValue(I.one('#json-cache-script'));
+ },
+ function(I) {
+ var iframe_window = I.config.win;
+ var LP = iframe_window.LP;
+ Y.Assert.isUndefined(LP.links.me);
+ Y.Assert.isNotUndefined(LP.cache.context);
+ }
+ );
+ },
+
+ test_logged_in_user_has_cache_data: function() {
+ var data = serverfixture.setup(this, 'create_product_and_login');
+ serverfixture.waitForIFrame(
+ this,
+ data.product.web_link,
+ function(I) {
+ I.use('node');
+ return Y.Lang.isValue(I.one('#json-cache-script'));
+ },
+ function(I) {
+ var iframe_window = I.config.win;
+ var LP = iframe_window.LP;
+ Y.Assert.areSame(
+ '/~' + data.user.name,
+ LP.links.me
+ );
+ Y.Assert.isNotUndefined(LP.cache.context);
+ }
+ );
+ },
+
+ test_get_relative_url: function() {
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
+ },
+
+ test_get_absolute_url: function() {
+ var data = serverfixture.setup(this, 'create_product');
+ var link = data.product.self_link;
+ Y.Assert.areSame('http', link.slice(0, 4)); // See, it's absolute.
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get(link, config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result);
+ },
+
+ test_get_collection_with_pagination: function() {
+ // We could do this with a fixture setup, but I'll rely on the
+ // sampledata for now. If this becomes a problem, write a quick
+ // fixture that creates three or four people!
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({start: 2, size: 1});
+ client.get('/people', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.areSame(2, config.result.start);
+ Y.Assert.areSame(1, config.result.entries.length);
+ },
+
+ test_named_get_integration: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({parameters: {text: data.user.name}});
+ client.named_get('people', 'find', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
+ Y.Assert.areSame(1, config.result.total_size);
+ },
+
+ test_named_post_integration: function() {
+ var data = serverfixture.setup(this, 'create_bug_and_login');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.named_post(
+ data.bug.self_link, 'mute', config);
+ Y.Assert.isTrue(config.successful);
+ },
+
+ test_follow_link: function() {
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('', config);
+ var root = config.result;
+ config = makeTestConfig();
+ root.follow_link('people', config);
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
+ Y.Assert.areSame(4, config.result.total_size);
+ },
+
+ test_follow_redirected_link: function() {
+ var data = serverfixture.setup(this, 'create_user_and_login');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('', config);
+ var root = config.result;
+ config = makeTestConfig();
+ root.follow_link('me', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, config.result);
+ Y.Assert.areSame(data.user.name, config.result.get('name'));
+ },
+
+ test_get_html_representation: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({accept: Y.lp.client.XHTML});
+ client.get(data.user.self_link, config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
+ },
+
+ test_get_html_representation_escaped: function() {
+ var data = serverfixture.setup(
+ this, 'create_user_with_html_display_name');
+ var actual_display_name = data.user.display_name;
+ Y.Assert.areEqual('<strong>naughty</strong>', actual_display_name);
+ var html_escaped_display_name = '<strong>naughty</strong>';
+ var has_actual_display_name = new RegExp(
+ Y.Escape.regex(actual_display_name));
+ var has_html_escaped_display_name = new RegExp(
+ Y.Escape.regex(html_escaped_display_name));
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({accept: Y.lp.client.XHTML});
+ client.get(data.user.self_link, config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isTrue(has_html_escaped_display_name.test(config.result));
+ Y.Assert.isFalse(has_actual_display_name.test(config.result));
+ },
+
+ test_lp_save_html_representation: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({accept: Y.lp.client.XHTML});
+ var user = new Y.lp.client.Entry(
+ client, data.user, data.user.self_link);
+ user.lp_save(config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
+ },
+
+ test_patch_html_representation: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig({accept: Y.lp.client.XHTML});
+ client.patch(data.user.self_link, {}, config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isTrue(/<a href=\"\/\~/.test(config.result));
+ },
+
+ test_lp_save: function() {
+ var data = serverfixture.setup(this, 'create_user_and_login');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ var user = new Y.lp.client.Entry(
+ client, data.user, data.user.self_link);
+ var original_display_name = user.get('display_name');
+ var new_display_name = original_display_name + '_modified';
+ user.set('display_name', new_display_name);
+ user.lp_save(config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
+ },
+
+ test_lp_save_fails_with_mismatched_ETag: function() {
+ var data = serverfixture.setup(this, 'create_user_and_login');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ var user = new Y.lp.client.Entry(
+ client, data.user, data.user.self_link);
+ var original_display_name = user.get('display_name');
+ var new_display_name = original_display_name + '_modified';
+ user.set('display_name', new_display_name);
+ user.set('http_etag', 'Non-matching ETag.');
+ user.lp_save(config);
+ Y.Assert.isFalse(config.successful);
+ Y.Assert.areEqual(412, config.result.response.status);
+ },
+
+ test_patch: function() {
+ var data = serverfixture.setup(this, 'create_user_and_login');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ var new_display_name = data.user.display_name + '_modified';
+ client.patch(
+ data.user.self_link, {display_name: new_display_name}, config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
+ },
+
+ test_collection_entries: function() {
+ serverfixture.setup(this, 'login_as_admin');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ var people = config.result;
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, people);
+ Y.Assert.areEqual(4, people.total_size);
+ Y.Assert.areEqual(people.total_size, people.entries.length);
+ var i = 0;
+ for (; i < people.entries.length; i++) {
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, people.entries[i]);
+ }
+ var entry = people.entries[0];
+ var new_display_name = entry.get('display_name') + '_modified';
+ entry.set('display_name', new_display_name);
+ config = makeTestConfig();
+ entry.lp_save(config);
+ Y.Assert.areEqual(new_display_name, config.result.get('display_name'));
+ },
+
+ test_collection_lp_slice: function() {
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ var people = config.result;
+ people.lp_slice(config.on, 2, 1);
+ var slice = config.result;
+ Y.Assert.areEqual(2, slice.start);
+ Y.Assert.areEqual(1, slice.entries.length);
+ },
+
+ test_collection_named_get: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ var people = config.result;
+ config = makeTestConfig({parameters: {text: data.user.name}});
+ people.named_get('find', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
+ Y.Assert.areEqual(1, config.result.total_size);
+ Y.Assert.areEqual(1, config.result.entries.length);
+ },
+
+ test_collection_named_post: function() {
+ serverfixture.setup(this, 'login_as_admin');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ var people = config.result;
+ config = makeTestConfig(
+ {parameters: {display_name: 'My lpclient team',
+ name: 'newlpclientteam'}});
+ people.named_post('newTeam', config);
+ Y.Assert.isTrue(config.successful);
+ var team = config.result;
+ Y.Assert.isInstanceOf(Y.lp.client.Entry, team);
+ Y.Assert.areEqual('My lpclient team', team.get('display_name'));
+ Y.Assert.isTrue(/\~newlpclientteam$/.test(team.lp_original_uri));
+ },
+
+ test_collection_paged_named_get: function() {
+ var data = serverfixture.setup(this, 'create_user');
+ var client = new Y.lp.client.Launchpad({sync: true});
+ var config = makeTestConfig();
+ client.get('/people', config);
+ var people = config.result;
+ config = makeTestConfig({parameters: {text: data.user.name},
+ start: 10});
+ people.named_get('find', config);
+ Y.Assert.isTrue(config.successful);
+ Y.Assert.isInstanceOf(Y.lp.client.Collection, config.result);
+ // I believe that the total_size is not correct in this case for
+ // server-side efficiency in an edge case. It actually reports "10".
+ // Y.Assert.areEqual(1, config.result.total_size);
+ Y.Assert.areEqual(0, config.result.entries.length);
+ }
+
+}));
+
+serverfixture.run(suite);
+});
=== 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-18 15:00:41 +0000
@@ -0,0 +1,62 @@
+# Copyright 2011 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""{Describe your test suite here}.
+"""
+
+__metaclass__ = type
+__all__ = []
+
+from lp.testing import person_logged_in
+from lp.testing.yuixhr import (
+ login_as_person,
+ make_suite,
+ setup,
+ )
+from lp.testing.factory import LaunchpadObjectFactory
+
+factory = LaunchpadObjectFactory()
+
+
+@setup
+def create_user(request, data):
+ data['user'] = factory.makePerson()
+
+
+@create_user.extend
+def create_user_and_login(request, data):
+ login_as_person(data['user'])
+
+
+@create_user.extend
+def create_product(request, data):
+ with person_logged_in(data['user']):
+ data['product'] = factory.makeProduct(owner=data['user'])
+
+
+@create_product.extend
+def create_product_and_login(request, data):
+ login_as_person(data['user'])
+
+
+@create_product_and_login.extend
+def create_bug_and_login(request, data):
+ data['bug'] = factory.makeBug(
+ product=data['product'], owner=data['user'])
+ data['bugtask'] = data['bug'].bugtasks[0]
+
+
+@setup
+def login_as_admin(request, data):
+ data['admin'] = factory.makeAdministrator()
+ login_as_person(data['admin'])
+
+
+@setup
+def create_user_with_html_display_name(request, data):
+ data['user'] = factory.makePerson(
+ displayname='<strong>naughty</strong>')
+
+
+def test_suite():
+ return make_suite(__name__)
=== 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-18 15:00:41 +0000
@@ -194,8 +194,12 @@
'${links/?key/fmt:api_url}';">
</script>
</tal:cache>
-
- <script tal:content="string:LP.cache = ${view/getCacheJSON};">
+ <tal:comment condition="nothing">
+ The id of the script block below is used to determine whether this
+ page is loaded by test_lp_client_integration.js.
+ </tal:comment>
+ <script id="json-cache-script"
+ tal:content="string:LP.cache = ${view/getCacheJSON};">
</script>
</metal:lp-client-cache>