Merge lp:~rharding/launchpad/lpclient_fix into lp:launchpad

Proposed by Richard Harding
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 15685
Proposed branch: lp:~rharding/launchpad/lpclient_fix
Merge into: lp:launchpad
Prerequisite: lp:~rharding/launchpad/garden_client
Diff against target: 1471 lines (+651/-597)
6 files modified
lib/lp/app/javascript/client.js (+63/-55)
lib/lp/app/javascript/tests/test_lp_client.html (+0/-6)
lib/lp/app/javascript/tests/test_lp_client.js (+583/-518)
lib/lp/app/javascript/tests/test_lp_client_integration.js (+2/-15)
lib/lp/registry/javascript/tests/test_structural_subscription.js (+1/-1)
lib/lp/soyuz/javascript/lp_dynamic_dom_updater.js (+2/-2)
To merge this branch: bzr merge lp:~rharding/launchpad/lpclient_fix
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+116338@code.launchpad.net

Commit message

Fix client.js to maintain uri on PATCH requests so that web_link:changed event fires off LP.cache updates.

Description of the change

= Summary =

Fixes bug #1004248 introduced by attempting to fix bug #911973. That fix only
considered named_post/get methods and not patch methods.

== Pre Implementation ==

Talked a bunch with Aaron and Deryck while working on issue and best way to
fix.

== Implementation Notes ==

The big fix is to only reassign the uri when the request method is not a
patch. #89 of the diff.

This required letting the wrapper method know what the method was so we pass
that along in the args.

Tests were added to verify that this is not altered during a patch, but is
during a named_get/post. See #776.

The test suite was updated to the new standard format and thus was indented
for the big line change there. No other tests were modified or adjusted.

Since the entry/resource lp_original_uri no longer makes sense, it's just
changed to uri. It's used in other methods that the instance makes so they
were updated to match.

There are a few other small drive by lints to fix spacing and such. See
#8/25/etc.

== Tests ==

xvfb-run ./bin/test -x -cvv --layer=YUITestLayer

== LoC Qualification ==
Need 54 LoC

Worked on cleaning up the client.js, removing the original bug test that isn't
required and isn't run.

See:
https://code.launchpad.net/~rharding/launchpad/garden_client -33
https://code.launchpad.net/~rharding/launchpad/reportbug/+merge/115562 -8
https://code.launchpad.net/~rharding/launchpad/lpnames_yui35 -17

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/app/javascript/client.js'
--- lib/lp/app/javascript/client.js 2012-07-23 18:06:49 +0000
+++ lib/lp/app/javascript/client.js 2012-07-24 16:58:23 +0000
@@ -43,11 +43,11 @@
43 'lp:' + cache_name + ':' + name + ':changed';43 'lp:' + cache_name + ':' + name + ':changed';
44 var new_value_html = entry.getHTML(name);44 var new_value_html = entry.getHTML(name);
45 var event = {45 var event = {
46 'name': name,46 name: name,
47 'old_value': old_value,47 old_value: old_value,
48 'new_value': new_value,48 new_value: new_value,
49 'new_value_html': new_value_html,49 new_value_html: new_value_html,
50 'entry': entry50 entry: entry
51 };51 };
52 Y.fire(field_updated_event_name, event);52 Y.fire(field_updated_event_name, event);
53 }53 }
@@ -66,17 +66,16 @@
66 }66 }
6767
68 if (fields_changed.length > 0) {68 if (fields_changed.length > 0) {
69 var event_name = 'lp:' + cache_name + ':changed';69 var event_name = 'lp:' + cache_name + ':changed';
70 var event_ = {70 var event_ = {
71 'fields_changed': fields_changed,71 fields_changed: fields_changed,
72 'entry': entry72 entry: entry
73 };73 };
74 Y.fire(event_name, event_);74 Y.fire(event_name, event_);
75 }75 }
76 };76 };
7777
7878
79
80 var module = Y.namespace('lp.client');79 var module = Y.namespace('lp.client');
8180
82 module.HTTP_CREATED = 201;81 module.HTTP_CREATED = 201;
@@ -84,6 +83,9 @@
84 module.HTTP_NOT_FOUND = 404;83 module.HTTP_NOT_FOUND = 404;
8584
86 module.XHTML = 'application/xhtml+xml';85 module.XHTML = 'application/xhtml+xml';
86 module.GET = 'get';
87 module.POST = 'post';
88 module.PATCH = 'patch';
8789
88 /* Log the normal attributes accessible via o[key], and if it is a90 /* Log the normal attributes accessible via o[key], and if it is a
89 * YUI node, log all of the attributes accessible via o.get(key).91 * YUI node, log all of the attributes accessible via o.get(key).
@@ -328,7 +330,7 @@
328 if (!entry) {330 if (!entry) {
329 return;331 return;
330 }332 }
331 var original_uri = entry.lp_original_uri;333 var original_uri = entry.uri;
332 var full_uri = module.get_absolute_uri(original_uri);334 var full_uri = module.get_absolute_uri(original_uri);
333 var name;335 var name;
334 var cached_object;336 var cached_object;
@@ -349,14 +351,29 @@
349351
350 module.wrap_resource_on_success = function(ignore, response, args) {352 module.wrap_resource_on_success = function(ignore, response, args) {
351 var client = args[0];353 var client = args[0];
354 // The original uri of the caller.
352 var uri = args[1];355 var uri = args[1];
353 var old_on_success = args[2];356 var callback = args[2];
354 var update_cache = args[3];357 var update_cache = args[3];
358 var method = args[4];
355 var representation, wrapped;359 var representation, wrapped;
356 if (old_on_success) {360
361 if (callback) {
357 var media_type = response.getResponseHeader('Content-Type');362 var media_type = response.getResponseHeader('Content-Type');
358 if (media_type.substring(0,16) === 'application/json') {363 if (media_type.substring(0,16) === 'application/json') {
359 representation = Y.JSON.parse(response.responseText);364 representation = Y.JSON.parse(response.responseText);
365
366 // If the object fetched has a self_link, make that the object's
367 // uri for use in other api methods off of that object.
368 // During a PATCH request the caller is the object. Leave the
369 // original_uri alone. Otherwise make the uri the object
370 // coming back.
371 if (Y.Lang.isValue(representation) &&
372 Y.Lang.isValue(representation.self_link) &&
373 method !== module.PATCH) {
374 uri = representation.self_link;
375 }
376
360 // If the response contains a notification header, display the377 // If the response contains a notification header, display the
361 // notifications.378 // notifications.
362 var notifications = response.getResponseHeader(379 var notifications = response.getResponseHeader(
@@ -364,18 +381,14 @@
364 if (notifications !== null && notifications !== "") {381 if (notifications !== null && notifications !== "") {
365 module.display_notifications(notifications);382 module.display_notifications(notifications);
366 }383 }
367 if (Y.Lang.isValue(representation) &&
368 Y.Lang.isValue(representation.self_link)) {
369 uri = representation.self_link;
370 }
371 wrapped = client.wrap_resource(uri, representation);384 wrapped = client.wrap_resource(uri, representation);
372 var result = old_on_success(wrapped);385 var result = callback(wrapped);
373 if (update_cache) {386 if (update_cache) {
374 module.update_cache(wrapped);387 module.update_cache(wrapped);
375 }388 }
376 return result;389 return result;
377 } else {390 } else {
378 return old_on_success(response.responseText);391 return callback(response.responseText);
379 }392 }
380 }393 }
381 };394 };
@@ -501,7 +514,7 @@
501 'init': function(client, representation, uri) {514 'init': function(client, representation, uri) {
502 /* Initialize a resource with its representation and URI. */515 /* Initialize a resource with its representation and URI. */
503 this.lp_client = client;516 this.lp_client = client;
504 this.lp_original_uri = uri;517 this.uri = uri;
505 var key;518 var key;
506 for (key in representation) {519 for (key in representation) {
507 if (representation.hasOwnProperty(key)) {520 if (representation.hasOwnProperty(key)) {
@@ -548,15 +561,13 @@
548561
549 'named_get': function(operation_name, config) {562 'named_get': function(operation_name, config) {
550 /* Get the result of a named GET operation on this resource. */563 /* Get the result of a named GET operation on this resource. */
551 return this.lp_client.named_get(this.lp_original_uri,564 return this.lp_client.named_get(this.uri, operation_name,
552 operation_name,
553 config);565 config);
554 },566 },
555567
556 'named_post': function(operation_name, config) {568 'named_post': function(operation_name, config) {
557 /* Trigger a named POST operation on this resource. */569 /* Trigger a named POST operation on this resource. */
558 return this.lp_client.named_post(this.lp_original_uri,570 return this.lp_client.named_post(this.uri, operation_name,
559 operation_name,
560 config);571 config);
561 }572 }
562 };573 };
@@ -588,14 +599,14 @@
588 :param start: Where in the collection to start serving entries.599 :param start: Where in the collection to start serving entries.
589 :param size: How many entries to serve.600 :param size: How many entries to serve.
590 */601 */
591 return this.lp_client.get(this.lp_original_uri,602 return this.lp_client.get(this.uri,
592 {on: on, start: start, size: size});603 {on: on, start: start, size: size});
593 };604 };
594605
595 module.Entry = function(client, representation, uri) {606 module.Entry = function(client, representation, uri) {
596 /* A single object from the Launchpad web service. */607 /* A single object from the Launchpad web service. */
597 this.lp_client = client;608 this.lp_client = client;
598 this.lp_original_uri = uri;609 this.uri = uri;
599 this.dirty_attributes = [];610 this.dirty_attributes = [];
600 var entry = this;611 var entry = this;
601612
@@ -644,21 +655,21 @@
644 };655 };
645656
646 module.Entry.prototype.getHTML = function(key) {657 module.Entry.prototype.getHTML = function(key) {
647 var lp_html = this.get('lp_html');658 var lp_html = this.get('lp_html');
648 if (lp_html) {659 if (lp_html) {
649 // First look for the key.660 // First look for the key.
650 var value = lp_html[key];661 var value = lp_html[key];
651 if (value === undefined) {662 if (value === undefined) {
652 // now look for key_link663 // now look for key_link
653 value = lp_html[key + '_link'];664 value = lp_html[key + '_link'];
654 }665 }
655 if (value !== undefined) {666 if (value !== undefined) {
656 var result = Y.Node.create("<span/>");667 var result = Y.Node.create("<span/>");
657 result.setContent(value);668 result.setContent(value);
658 return result;669 return result;
659 }670 }
660 }671 }
661 return null;672 return null;
662 };673 };
663674
664 // The Launchpad client itself.675 // The Launchpad client itself.
@@ -690,7 +701,8 @@
690 var client = this;701 var client = this;
691 var y_config = {702 var y_config = {
692 on: on,703 on: on,
693 'arguments': [client, uri, old_on_success, update_cache],704 'arguments': [
705 client, uri, old_on_success, update_cache, module.GET],
694 'headers': headers,706 'headers': headers,
695 data: data,707 data: data,
696 sync: this.sync708 sync: this.sync
@@ -737,9 +749,8 @@
737 { on: { success: old_on_success,749 { on: { success: old_on_success,
738 failure: on.failure } });750 failure: on.failure } });
739 }751 }
740 return module.wrap_resource_on_success(undefined,752 return module.wrap_resource_on_success(
741 response,753 undefined, response, args, module.POST);
742 args);
743 };754 };
744 var client = this;755 var client = this;
745 var update_cache = false;756 var update_cache = false;
@@ -761,12 +772,12 @@
761 var old_on_success = on.success;772 var old_on_success = on.success;
762 var update_cache = true;773 var update_cache = true;
763 on.success = module.wrap_resource_on_success;774 on.success = module.wrap_resource_on_success;
764 args = [this, uri, old_on_success, update_cache];775 var args = [this, uri, old_on_success, update_cache, module.PATCH];
765776
766 var extra_headers = {777 var extra_headers = {
767 "X-HTTP-Method-Override": "PATCH",778 "X-HTTP-Method-Override": "PATCH",
768 "Content-Type": "application/json",779 "Content-Type": "application/json",
769 "X-Content-Type-Override": "application/json"780 "X-Content-Type-Override": "application/json"
770 };781 };
771 var name;782 var name;
772 if (headers !== undefined) {783 if (headers !== undefined) {
@@ -1027,14 +1038,12 @@
1027 }1038 }
1028 });1039 });
10291040
1030
1031}, "0.1", {1041}, "0.1", {
1032 requires: ["attribute", "base", "io", "querystring", "json-parse",1042 requires: ["attribute", "base", "io", "querystring", "json-parse",
1033 "json-stringify", "lp"]1043 "json-stringify", "lp"]
1034});1044});
10351045
1036YUI.add('lp.client.plugins', function (Y) {1046YUI.add('lp.client.plugins', function (Y) {
1037
1038 /**1047 /**
1039 * A collection of plugins to hook lp.client into widgets.1048 * A collection of plugins to hook lp.client into widgets.
1040 *1049 *
@@ -1245,7 +1254,6 @@
1245 }1254 }
1246 }1255 }
1247 });1256 });
1248
1249}, "0.1", {1257}, "0.1", {
1250 requires: ["base", "plugin", "dump", "lazr.editor", "lp.client"]1258 requires: ["base", "plugin", "dump", "lazr.editor", "lp.client"]
1251});1259});
12521260
=== modified file 'lib/lp/app/javascript/tests/test_lp_client.html'
--- lib/lp/app/javascript/tests/test_lp_client.html 2012-03-27 04:36:24 +0000
+++ lib/lp/app/javascript/tests/test_lp_client.html 2012-07-24 16:58:23 +0000
@@ -3,7 +3,6 @@
3Copyright 2012 Canonical Ltd. This software is licensed under the3Copyright 2012 Canonical Ltd. This software is licensed under the
4GNU Affero General Public License version 3 (see the file LICENSE).4GNU Affero General Public License version 3 (see the file LICENSE).
5-->5-->
6
7<html>6<html>
8 <head>7 <head>
9 <title>Test client</title>8 <title>Test client</title>
@@ -30,20 +29,15 @@
30 <script type="text/javascript"29 <script type="text/javascript"
31 src="../../../../../build/js/lp/app/lp.js"></script>30 src="../../../../../build/js/lp/app/lp.js"></script>
3231
33
34 <!-- The module under test. -->32 <!-- The module under test. -->
35 <script type="text/javascript" src="../client.js"></script>33 <script type="text/javascript" src="../client.js"></script>
3634
37 <!-- Placeholder for any css asset for this module. -->
38 <!-- <link rel="stylesheet" href="../assets/client-core.css" /> -->
39
40 <!-- The test suite. -->35 <!-- The test suite. -->
41 <script type="text/javascript" src="test_lp_client.js"></script>36 <script type="text/javascript" src="test_lp_client.js"></script>
4237
43 </head>38 </head>
44 <body class="yui3-skin-sam">39 <body class="yui3-skin-sam">
45 <ul id="suites">40 <ul id="suites">
46 <!-- <li>lp.large_indicator.test</li> -->
47 <li>lp.client.test</li>41 <li>lp.client.test</li>
48 </ul>42 </ul>
49 <div class="context-publication">43 <div class="context-publication">
5044
=== modified file 'lib/lp/app/javascript/tests/test_lp_client.js'
--- lib/lp/app/javascript/tests/test_lp_client.js 2012-06-26 17:16:34 +0000
+++ lib/lp/app/javascript/tests/test_lp_client.js 2012-07-24 16:58:23 +0000
@@ -1,522 +1,587 @@
1/* Copyright (c) 2011, Canonical Ltd. All rights reserved. */1/* Copyright (c) 2011-2012 Canonical Ltd. All rights reserved. */
22YUI.add('lp.client.test', function (Y) {
3YUI().use('lp.testing.runner', 'lp.testing.mockio', 'test', 'console',3 var Assert = Y.Assert;
4 'lp.client', 'escape', function(Y) {4 var tests = Y.namespace('lp.client.test');
55 tests.suite = new Y.Test.Suite('client Tests');
6var Assert = Y.Assert; // For easy access to isTrue(), etc.6 tests.suite.add(new Y.Test.Case({
77 name: "lp.client",
8var suite = new Y.Test.Suite("lp.client Tests");8
99 setUp: function() {
10suite.add(new Y.Test.Case({10 },
11 name: "lp.client",11
1212 test_normalize_uri: function() {
13 setUp: function() {13 var normalize = Y.lp.client.normalize_uri;
14 },14 Assert.areEqual(
1515 normalize("http://www.example.com/api/devel/foo"),
16 test_normalize_uri: function() {16 "/api/devel/foo");
17 var normalize = Y.lp.client.normalize_uri;17 Assert.areEqual(
18 Assert.areEqual(18 normalize("http://www.example.com/foo/bar"), "/foo/bar");
19 normalize("http://www.example.com/api/devel/foo"),19 Assert.areEqual(
20 "/api/devel/foo");20 normalize("/foo/bar"), "/api/devel/foo/bar");
21 Assert.areEqual(21 Assert.areEqual(
22 normalize("http://www.example.com/foo/bar"), "/foo/bar");22 normalize("/api/devel/foo/bar"), "/api/devel/foo/bar");
23 Assert.areEqual(23 Assert.areEqual(
24 normalize("/foo/bar"), "/api/devel/foo/bar");24 normalize("foo/bar"), "/api/devel/foo/bar");
25 Assert.areEqual(25 Assert.areEqual(
26 normalize("/api/devel/foo/bar"), "/api/devel/foo/bar");26 normalize("api/devel/foo/bar"), "/api/devel/foo/bar");
27 Assert.areEqual(27 },
28 normalize("foo/bar"), "/api/devel/foo/bar");28
29 Assert.areEqual(29 test_get_io_provider__default: function() {
30 normalize("api/devel/foo/bar"), "/api/devel/foo/bar");30 var undefined_provider,
31 },31 io_provider = Y.lp.client.get_io_provider(undefined_provider);
3232 Assert.areSame(Y, io_provider);
33 test_get_io_provider__default: function() {33 },
34 var undefined_provider,34
35 io_provider = Y.lp.client.get_io_provider(undefined_provider);35 test_get_io_provider__mockio: function() {
36 Assert.areSame(Y, io_provider);36 // If a mock provider is provided, it is picked as the io_provider.
37 },37 var mockio = new Y.lp.testing.mockio.MockIo(),
3838 io_provider = Y.lp.client.get_io_provider(mockio);
39 test_get_io_provider__mockio: function() {39 Assert.areSame(mockio, io_provider);
40 // If a mock provider is provided, it is picked as the io_provider.40 },
41 var mockio = new Y.lp.testing.mockio.MockIo(),41
42 io_provider = Y.lp.client.get_io_provider(mockio);42 test_get_configured_io_provider__default: function() {
43 Assert.areSame(mockio, io_provider);43 // If no io_provider is configured, Y is the io_provider.
44 },44 var io_provider = Y.lp.client.get_configured_io_provider({});
4545 Assert.areSame(Y, io_provider);
46 test_get_configured_io_provider__default: function() {46 },
47 // If no io_provider is configured, Y is the io_provider.47
48 var io_provider = Y.lp.client.get_configured_io_provider({});48 test_get_configured_io_provider__default_undefined: function() {
49 Assert.areSame(Y, io_provider);49 // If no configuration is provided, Y is the io_provider.
50 },50 var io_provider = Y.lp.client.get_configured_io_provider();
5151 Assert.areSame(Y, io_provider);
52 test_get_configured_io_provider__default_undefined: function() {52 },
53 // If no configuration is provided, Y is the io_provider.53
54 var io_provider = Y.lp.client.get_configured_io_provider();54 test_get_configured_io_provider__mockio: function() {
55 Assert.areSame(Y, io_provider);55 // If an io_provider is configured, it is picked as the
56 },56 // io_provider.
5757 var mockio = new Y.lp.testing.mockio.MockIo(),
58 test_get_configured_io_provider__mockio: function() {58 io_provider = Y.lp.client.get_configured_io_provider(
59 // If an io_provider is configured, it is picked as the io_provider.59 {io_provider: mockio});
60 var mockio = new Y.lp.testing.mockio.MockIo(),60 Assert.areSame(mockio, io_provider);
61 io_provider = Y.lp.client.get_configured_io_provider(61 },
62 {io_provider: mockio});62
63 Assert.areSame(mockio, io_provider);63 test_get_configured_io_provider__different_key: function() {
64 },64 // The io_provider can be stored with a different key.
6565 var mockio = new Y.lp.testing.mockio.MockIo(),
66 test_get_configured_io_provider__different_key: function() {66 io_provider = Y.lp.client.get_configured_io_provider(
67 // The io_provider can be stored with a different key.67 {my_io: mockio}, 'my_io');
68 var mockio = new Y.lp.testing.mockio.MockIo(),68 Assert.areSame(mockio, io_provider);
69 io_provider = Y.lp.client.get_configured_io_provider(69 },
70 {my_io: mockio}, 'my_io');70
71 Assert.areSame(mockio, io_provider);71 test_append_qs: function() {
72 },72 var qs = "";
7373 qs = Y.lp.client.append_qs(qs, "Pöllä", "Perelló");
74 test_append_qs: function() {74 Assert.areEqual(
75 var qs = "";75 "P%C3%83%C2%B6ll%C3%83%C2%A4=Perell%C3%83%C2%B3", qs,
76 qs = Y.lp.client.append_qs(qs, "Pöllä", "Perelló");76 'This tests is known to fail in Chrome.');
77 Assert.areEqual(77 },
78 "P%C3%83%C2%B6ll%C3%83%C2%A4=Perell%C3%83%C2%B3", qs,78
79 'This tests is known to fail in Chrome, it is browser specific.');79 test_append_qs_with_array: function() {
80 },80 // append_qs() appends multiple arguments to the query string
8181 // when a parameter value is an array.
82 test_append_qs_with_array: function() {82 var qs = "";
83 // append_qs() appends multiple arguments to the query string83 qs = Y.lp.client.append_qs(qs, "foo", ["bar", "baz"]);
84 // when a parameter value is an array.84 Assert.areEqual("foo=bar&foo=baz", qs);
85 var qs = "";85 // All values in the array are encoded correctly too.
86 qs = Y.lp.client.append_qs(qs, "foo", ["bar", "baz"]);86 qs = Y.lp.client.append_qs(qs, "a&b", ["a+b"]);
87 Assert.areEqual("foo=bar&foo=baz", qs);87 Assert.areEqual("foo=bar&foo=baz&a%26b=a%2Bb", qs);
88 // All values in the array are encoded correctly too.88 },
89 qs = Y.lp.client.append_qs(qs, "a&b", ["a+b"]);89
90 Assert.areEqual("foo=bar&foo=baz&a%26b=a%2Bb", qs);90 test_field_uri: function() {
91 },91 var get_field_uri = Y.lp.client.get_field_uri;
9292 Assert.areEqual(
93 test_field_uri: function() {93 get_field_uri("http://www.example.com/api/devel/foo", "field"),
94 var get_field_uri = Y.lp.client.get_field_uri;94 "/api/devel/foo/field");
95 Assert.areEqual(95 Assert.areEqual(
96 get_field_uri("http://www.example.com/api/devel/foo", "field"),96 get_field_uri("/no/slash", "field"),
97 "/api/devel/foo/field");97 "/api/devel/no/slash/field");
98 Assert.areEqual(98 Assert.areEqual(
99 get_field_uri("/no/slash", "field"),99 get_field_uri("/has/slash/", "field"),
100 "/api/devel/no/slash/field");100 "/api/devel/has/slash/field");
101 Assert.areEqual(101 },
102 get_field_uri("/has/slash/", "field"),102 test_view_url: function() {
103 "/api/devel/has/slash/field");103 entry_repr = {web_link: 'http://example.com/context'};
104 },104 var context = new Y.lp.client.Entry(null, entry_repr, null);
105 test_view_url: function() {105 expected = '/context/+myview/++mynamespace++';
106 entry_repr = {web_link: 'http://example.com/context'};106 actual = Y.lp.client.get_view_url(
107 var context = new Y.lp.client.Entry(null, entry_repr, null);107 context, '+myview', 'mynamespace');
108 expected = '/context/+myview/++mynamespace++';108 Assert.areEqual(expected, actual);
109 actual = Y.lp.client.get_view_url(context, '+myview', 'mynamespace');109 },
110 Assert.areEqual(expected, actual);110 test_get_form_url: function() {
111 },111 entry_repr = {web_link: 'http://example.com/context'};
112 test_get_form_url: function() {112 var context = new Y.lp.client.Entry(null, entry_repr, null);
113 entry_repr = {web_link: 'http://example.com/context'};113 expected = '/context/+myview/++form++';
114 var context = new Y.lp.client.Entry(null, entry_repr, null);114 actual = Y.lp.client.get_form_url(context, '+myview');
115 expected = '/context/+myview/++form++';115 Assert.areEqual(expected, actual);
116 actual = Y.lp.client.get_form_url(context, '+myview');116 },
117 Assert.areEqual(expected, actual);117 test_load_model: function(){
118 },118 var mockio = new Y.lp.testing.mockio.MockIo();
119 test_load_model: function(){119 Assert.areEqual(0, mockio.requests.length);
120 var mockio = new Y.lp.testing.mockio.MockIo();120 var mylist = [];
121 Assert.areEqual(0, mockio.requests.length);121 var config = {
122 var mylist = [];122 io_provider: mockio,
123 var config = {123 on: {
124 io_provider: mockio,124 success: Y.bind(mylist.push, mylist)
125 on: {125 }
126 success: Y.bind(mylist.push, mylist)126 };
127 }127 var entry_repr = {web_link: 'http://example.com/context'};
128 };128 var client = new Y.lp.client.Launchpad();
129 var entry_repr = {web_link: 'http://example.com/context'};129 var context = new Y.lp.client.Entry(client, entry_repr, null);
130 var client = new Y.lp.client.Launchpad();130 Y.lp.client.load_model(context, '+myview', config);
131 var context = new Y.lp.client.Entry(client, entry_repr, null);131 Assert.areEqual(
132 Y.lp.client.load_model(context, '+myview', config);132 '/context/+myview/++model++', mockio.last_request.url);
133 Assert.areEqual(133 mockio.success({
134 '/context/+myview/++model++', mockio.last_request.url);134 responseText:
135 mockio.success({135 '{"boolean": true, "entry": {"resource_type_link": "foo"}}',
136 responseText:136 responseHeaders: {'Content-Type': 'application/json'}
137 '{"boolean": true, "entry": {"resource_type_link": "foo"}}',137 });
138 responseHeaders: {'Content-Type': 'application/json'}138 var result = mylist[0];
139 });139 Assert.areSame(true, result.boolean);
140 var result = mylist[0];140 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
141 Assert.areSame(true, result.boolean);141 Assert.areSame('foo', result.entry.get('resource_type_link'));
142 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);142 },
143 Assert.areSame('foo', result.entry.get('resource_type_link'));143 test_get_success_callback: function() {
144 },144 var mockio = new Y.lp.testing.mockio.MockIo();
145 test_get_success_callback: function() {145 var mylist = [];
146 var mockio = new Y.lp.testing.mockio.MockIo();146 var client = new Y.lp.client.Launchpad({io_provider: mockio});
147 var mylist = [];147 client.get('/people', {on:{success: Y.bind(mylist.push, mylist)}});
148 var client = new Y.lp.client.Launchpad({io_provider: mockio});148 Assert.areEqual('/api/devel/people', mockio.last_request.url);
149 client.get('/people', {on:{success: Y.bind(mylist.push, mylist)}});149 mockio.success({
150 Assert.areEqual('/api/devel/people', mockio.last_request.url);150 responseText:
151 mockio.success({151 '{"entry": {"resource_type_link": "foo"}}',
152 responseText:152 responseHeaders: {'Content-Type': 'application/json'}
153 '{"entry": {"resource_type_link": "foo"}}',153 });
154 responseHeaders: {'Content-Type': 'application/json'}154 var result = mylist[0];
155 });155 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
156 var result = mylist[0];156 Assert.areSame('foo', result.entry.get('resource_type_link'));
157 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);157 },
158 Assert.areSame('foo', result.entry.get('resource_type_link'));158 test_get_failure_callback: function() {
159 },159 var mockio = new Y.lp.testing.mockio.MockIo();
160 test_get_failure_callback: function() {160 var mylist = [];
161 var mockio = new Y.lp.testing.mockio.MockIo();161 var client = new Y.lp.client.Launchpad({io_provider: mockio});
162 var mylist = [];162 client.get(
163 var client = new Y.lp.client.Launchpad({io_provider: mockio});163 '/people',
164 client.get(164 {on: {
165 '/people',165 failure: function(){
166 {on: {166 mylist.push(Array.prototype.slice.call(arguments));
167 failure: function(){167 }}});
168 mylist.push(Array.prototype.slice.call(arguments));168 mockio.failure({status: 503});
169 }}});169 var result = mylist[0];
170 mockio.failure({status: 503});170 Assert.areSame(503, result[1].status);
171 var result = mylist[0];171 Assert.areSame('/api/devel/people', result[2][1]);
172 Assert.areSame(503, result[1].status);172 },
173 Assert.areSame('/api/devel/people', result[2][1]);173 test_named_post_success_callback: function() {
174 },174 var mockio = new Y.lp.testing.mockio.MockIo();
175 test_named_post_success_callback: function() {175 var mylist = [];
176 var mockio = new Y.lp.testing.mockio.MockIo();176 var client = new Y.lp.client.Launchpad({io_provider: mockio});
177 var mylist = [];177 client.named_post(
178 var client = new Y.lp.client.Launchpad({io_provider: mockio});178 '/people', 'newTeam', {on:{success: Y.bind(mylist.push, mylist)}});
179 client.named_post(179 Assert.areEqual('/api/devel/people', mockio.last_request.url);
180 '/people', 'newTeam', {on:{success: Y.bind(mylist.push, mylist)}});180 Assert.areEqual('ws.op=newTeam', mockio.last_request.config.data);
181 Assert.areEqual('/api/devel/people', mockio.last_request.url);181 Assert.areEqual('POST', mockio.last_request.config.method);
182 Assert.areEqual('ws.op=newTeam', mockio.last_request.config.data);182 mockio.success({
183 Assert.areEqual('POST', mockio.last_request.config.method);183 responseText:
184 mockio.success({184 '{"entry": {"resource_type_link": "foo"}}',
185 responseText:185 responseHeaders: {'Content-Type': 'application/json'}
186 '{"entry": {"resource_type_link": "foo"}}',186 });
187 responseHeaders: {'Content-Type': 'application/json'}187 var result = mylist[0];
188 });188 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);
189 var result = mylist[0];189 Assert.areSame('foo', result.entry.get('resource_type_link'));
190 Assert.isInstanceOf(Y.lp.client.Entry, result.entry);190 },
191 Assert.areSame('foo', result.entry.get('resource_type_link'));191 test_wrap_resource_nested_mapping: function() {
192 },192 // wrap_resource produces mappings of plain object literals. These
193 test_wrap_resource_nested_mapping: function() {193 // can be nested and have Entries in them.
194 // wrap_resource produces mappings of plain object literals. These can194 var foo = {
195 // be nested and have Entries in them.195 baa: {},
196 var foo = {196 bar: {
197 baa: {},197 baz: {
198 bar: {198 resource_type_link: 'qux'}
199 baz: {199 }
200 resource_type_link: 'qux'}200 };
201 }201 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);
202 };202 Assert.isInstanceOf(Y.lp.client.Entry, foo.bar.baz);
203 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);203 },
204 Y.Assert.isInstanceOf(Y.lp.client.Entry, foo.bar.baz);204 test_wrap_resource_nested_array: function() {
205 },205 // wrap_resource produces arrays of array literals. These can
206 test_wrap_resource_nested_array: function() {206 // be nested and have Entries in them.
207 // wrap_resource produces arrays of array literals. These can207 var foo = [[{resource_type_link: 'qux'}]];
208 // be nested and have Entries in them.208 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);
209 var foo = [[{resource_type_link: 'qux'}]];209 Assert.isInstanceOf(Y.lp.client.Entry, foo[0][0]);
210 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);210 },
211 Y.Assert.isInstanceOf(Y.lp.client.Entry, foo[0][0]);211 test_wrap_resource_creates_array: function() {
212 },212 // wrap_resource creates new arrays, rather than reusing the
213 test_wrap_resource_creates_array: function() {213 // existing one.
214 // wrap_resource creates new arrays, rather than reusing the existing214 var foo = ['a'];
215 // one.215 var bar = new Y.lp.client.Launchpad().wrap_resource(null, foo);
216 var foo = ['a'];216 Assert.areNotSame(foo, bar);
217 var bar = new Y.lp.client.Launchpad().wrap_resource(null, foo);217 },
218 Y.Assert.areNotSame(foo, bar);218 test_wrap_resource_creates_mapping: function() {
219 },219 // wrap_resource creates new mappings, rather than reusing the
220 test_wrap_resource_creates_mapping: function() {220 // existing one.
221 // wrap_resource creates new mappings, rather than reusing the221 var foo = {a: 'b'};
222 // existing one.222 var bar = new Y.lp.client.Launchpad().wrap_resource(null, foo);
223 var foo = {a: 'b'};223 Assert.areNotSame(foo, bar);
224 var bar = new Y.lp.client.Launchpad().wrap_resource(null, foo);224 },
225 Y.Assert.areNotSame(foo, bar);225 test_wrap_resource_null: function() {
226 },226 // wrap_resource handles null correctly.
227 test_wrap_resource_null: function() {227 var foo = {
228 // wrap_resource handles null correctly.228 bar: null
229 var foo = {229 };
230 bar: null230 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);
231 };231 Assert.isNull(foo.bar);
232 foo = new Y.lp.client.Launchpad().wrap_resource(null, foo);232 }
233 Y.Assert.isNull(foo.bar);233 }));
234 }234
235}));235 tests.suite.add(new Y.Test.Case({
236236 name: "lp.wrap_on_success",
237suite.add(new Y.Test.Case({237 original_uri: 'http://launchpad.net/original_uri',
238 name: "update cache",238 updated_uri: 'http://launchpad.net/object_uri',
239239
240 setUp: function() {240 setUp: function() {
241 window.LP = {241 window.LP = {};
242 cache: {242 this.called = false;
243 context: {243 },
244
245 tearDown: function () {
246 delete window.LP;
247 delete this.called;
248 },
249
250 _gen_callback: function (uri) {
251 var that = this;
252 return function (wrapped) {
253 that.called = true;
254 Assert.areEqual(wrapped.uri, uri);
255 };
256 },
257
258 _fake_response: function () {
259 return {
260 responseText: Y.JSON.stringify({
261 self_link: this.updated_uri,
262 resource_type_link: 'object'
263 }),
264 getResponseHeader: function (key) {
265 var headers = {'Content-Type': 'application/json'};
266 return headers[key];
267 }
268 };
269 },
270
271 test_wrap_resource_patch_link: function () {
272 // wrap_resource_on_success will not modify the uri on a patch
273 // request and keep the original value.
274 var callback = this._gen_callback(this.original_uri);
275 Y.lp.client.wrap_resource_on_success(10, this._fake_response(), [
276 new Y.lp.client.Launchpad(), this.original_uri, callback,
277 true, 'patch']
278 );
279 Assert.isTrue(this.called);
280 },
281
282 test_wrap_resource_patch_named: function () {
283 // wrap_resource_on_success will modify the uri on a
284 // named_get/post methods to the value that came back in
285 // self_link.
286 var callback = this._gen_callback(this.updated_uri);
287 Y.lp.client.wrap_resource_on_success(10, this._fake_response(), [
288 new Y.lp.client.Launchpad(), this.original_uri, callback,
289 true, 'post']
290 );
291 Assert.isTrue(this.called);
292 }
293 }));
294
295 tests.suite.add(new Y.Test.Case({
296 name: "update cache",
297
298 setUp: function() {
299 window.LP = {
300 cache: {
301 context: {
302 'first': "Hello",
303 'second': true,
304 'third': 42,
305 'fourth': "Unaltered",
306 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
307 }
308 }};
309 },
310
311 tearDown: function() {
312 delete window.LP;
313 },
314
315 test_update_cache: function() {
316 // Make sure that the cached objects are in fact updated.
317 var entry_repr = {
318 'first': "World",
319 'second': false,
320 'third': 24,
321 'fourth': "Unaltered",
322 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
323 };
324 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
325 Y.lp.client.update_cache(entry);
326 Assert.areEqual("World", LP.cache.context.first);
327 Assert.areEqual(false, LP.cache.context.second);
328 Assert.areEqual(24, LP.cache.context.third);
329 Assert.areEqual("Unaltered", LP.cache.context.fourth);
330 },
331
332 test_getHTML: function() {
333 // Make sure that the getHTML method works as expected.
334 var entry_repr = {
244 'first': "Hello",335 'first': "Hello",
245 'second': true,336 'second': "World",
246 'third': 42,337 'self_link': Y.lp.client.get_absolute_uri("a_self_link"),
338 'lp_html': {'first': "<p>Hello</p><p>World</p>"}
339 };
340 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
341 Assert.areEqual(
342 "<p>Hello</p><p>World</p>",
343 entry.getHTML('first').get('innerHTML'));
344 // If there is no html representation, null is returned.
345 Assert.areEqual(null, entry.getHTML('second'));
346 },
347
348 test_update_cache_raises_events: function() {
349 // Check that the object changed event is raised.
350 var raised_event = null;
351 var handle = Y.on('lp:context:changed', function(e) {
352 raised_event = e;
353 });
354 var entry_repr = {
355 'first': "World",
356 'second': false,
357 'third': 24,
247 'fourth': "Unaltered",358 'fourth': "Unaltered",
248 'self_link': Y.lp.client.get_absolute_uri("a_self_link")359 'self_link': Y.lp.client.get_absolute_uri("a_self_link")
249 }360 };
250 }};361 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
251 },362 Y.lp.client.update_cache(entry);
252363 handle.detach();
253 tearDown: function() {364 Y.ArrayAssert.itemsAreEqual(
254 delete window.LP;365 ['first','second','third'], raised_event.fields_changed);
255 },366 Assert.areEqual(entry, raised_event.entry);
256367 },
257 test_update_cache: function() {368
258 // Make sure that the cached objects are in fact updated.369 test_update_cache_raises_attribute_events: function() {
259 var entry_repr = {370 // Check that the object attribute changed events are raised.
260 'first': "World",371 var first_event = null;
261 'second': false,372 var second_event = null;
262 'third': 24,373 var third_event = null;
263 'fourth': "Unaltered",374 var fourth_event = null;
264 'self_link': Y.lp.client.get_absolute_uri("a_self_link")375 var first_handle = Y.on('lp:context:first:changed', function(e) {
265 };376 first_event = e;
266 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");377 });
267 Y.lp.client.update_cache(entry);378 var second_handle = Y.on('lp:context:second:changed', function(e) {
268 Assert.areEqual("World", LP.cache.context.first);379 second_event = e;
269 Assert.areEqual(false, LP.cache.context.second);380 });
270 Assert.areEqual(24, LP.cache.context.third);381 var third_handle = Y.on('lp:context:third:changed', function(e) {
271 Assert.areEqual("Unaltered", LP.cache.context.fourth);382 third_event = e;
272 },383 });
273384 var fourth_handle = Y.on('lp:context:fourth:changed', function(e) {
274 test_getHTML: function() {385 fourth_event = e;
275 // Make sure that the getHTML method works as expected.386 });
276 var entry_repr = {387 var entry_repr = {
277 'first': "Hello",388 'first': "World<boo/>",
278 'second': "World",389 'second': false,
279 'self_link': Y.lp.client.get_absolute_uri("a_self_link"),390 'third': 24,
280 'lp_html': {'first': "<p>Hello</p><p>World</p>"}391 'fourth': "Unaltered",
281 };392 'self_link': Y.lp.client.get_absolute_uri("a_self_link"),
282 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");393 'lp_html': {'first': "<p>World html<boo/></p>"}
283 Assert.areEqual(394 };
284 "<p>Hello</p><p>World</p>",395 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");
285 entry.getHTML('first').get('innerHTML'));396 Y.lp.client.update_cache(entry);
286 // If there is no html representation, null is returned.397 first_handle.detach();
287 Assert.areEqual(null, entry.getHTML('second'));398 second_handle.detach();
288 },399 third_handle.detach();
289400 fourth_handle.detach();
290 test_update_cache_raises_events: function() {401
291 // Check that the object changed event is raised.402 Assert.areEqual('first', first_event.name);
292 var raised_event = null;403 Assert.areEqual('Hello', first_event.old_value);
293 var handle = Y.on('lp:context:changed', function(e) {404 Assert.areEqual('World<boo/>', first_event.new_value);
294 raised_event = e;405 Assert.areEqual(
295 });406 '<p>World html<boo></boo></p>',
296 var entry_repr = {407 first_event.new_value_html.get('innerHTML'));
297 'first': "World",408 Assert.areEqual(entry, first_event.entry);
298 'second': false,409
299 'third': 24,410 Assert.areEqual('second', second_event.name);
300 'fourth': "Unaltered",411 Assert.areEqual(true, second_event.old_value);
301 'self_link': Y.lp.client.get_absolute_uri("a_self_link")412 Assert.areEqual(false, second_event.new_value);
302 };413 Assert.areEqual(entry, second_event.entry);
303 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");414
304 Y.lp.client.update_cache(entry);415 Assert.areEqual('third', third_event.name);
305 handle.detach();416 Assert.areEqual(42, third_event.old_value);
306 Y.ArrayAssert.itemsAreEqual(417 Assert.areEqual(24, third_event.new_value);
307 ['first','second','third'], raised_event.fields_changed);418 Assert.areEqual(entry, third_event.entry);
308 Assert.areEqual(entry, raised_event.entry);419
309 },420 Assert.isNull(fourth_event);
310421 },
311 test_update_cache_raises_attribute_events: function() {422
312 // Check that the object attribute changed events are raised.423 test_update_cache_different_object: function() {
313 var first_event = null;424 // Check that the object is not modified if the entry has a
314 var second_event = null;425 // different link.
315 var third_event = null;426 var entry_repr = {
316 var fourth_event = null;427 'first': "World",
317 var first_handle = Y.on('lp:context:first:changed', function(e) {428 'second': false,
318 first_event = e;429 'third': 24,
319 });430 'fourth': "Unaltered",
320 var second_handle = Y.on('lp:context:second:changed', function(e) {431 'self_link': Y.lp.client.get_absolute_uri("different_link")
321 second_event = e;432 };
322 });433 var entry = new Y.lp.client.Entry(
323 var third_handle = Y.on('lp:context:third:changed', function(e) {434 null, entry_repr, "different_link");
324 third_event = e;435 Y.lp.client.update_cache(entry);
325 });436 Assert.areEqual("Hello", LP.cache.context.first);
326 var fourth_handle = Y.on('lp:context:fourth:changed', function(e) {437 Assert.areEqual(true, LP.cache.context.second);
327 fourth_event = e;438 Assert.areEqual(42, LP.cache.context.third);
328 });439 Assert.areEqual("Unaltered", LP.cache.context.fourth);
329 var entry_repr = {440 }
330 'first': "World<boo/>",441 }));
331 'second': false,442
332 'third': 24,443 tests.suite.add(new Y.Test.Case({
333 'fourth': "Unaltered",444 name: "lp.client.notifications",
334 'self_link': Y.lp.client.get_absolute_uri("a_self_link"),445
335 'lp_html': {'first': "<p>World html<boo/></p>"}446 setUp: function() {
336 };447 this.client = new Y.lp.client.Launchpad();
337 var entry = new Y.lp.client.Entry(null, entry_repr, "a_self_link");448 this.args=[this.client, null, this._on_success, false];
338 Y.lp.client.update_cache(entry);449 this.response = new Y.lp.testing.mockio.MockHttpResponse();
339 first_handle.detach();450 this.response.setResponseHeader('Content-Type', 'application/json');
340 second_handle.detach();451 },
341 third_handle.detach();452
342 fourth_handle.detach();453 _on_success: function(entry) {
343454 },
344 Assert.areEqual('first', first_event.name);455
345 Assert.areEqual('Hello', first_event.old_value);456 _checkNotificationNode: function(node_class, node_text) {
346 Assert.areEqual('World<boo/>', first_event.new_value);457 var node = Y.one('div#request-notifications div'+node_class);
347 Assert.areEqual(458 Assert.areEqual(node_text, node.get("innerHTML"));
348 '<p>World html<boo></boo></p>',459 },
349 first_event.new_value_html.get('innerHTML'));460
350 Assert.areEqual(entry, first_event.entry);461 _checkNoNotificationNode: function(node_class) {
351462 var node = Y.one('div#request-notifications div'+node_class);
352 Assert.areEqual('second', second_event.name);463 Assert.isNull(node);
353 Assert.areEqual(true, second_event.old_value);464 },
354 Assert.areEqual(false, second_event.new_value);465
355 Assert.areEqual(entry, second_event.entry);466 test_display_notifications: function() {
356467 var notifications = '[ [10, "A debug"], [20, "An info"] ]';
357 Assert.areEqual('third', third_event.name);468 this.response.setResponseHeader(
358 Assert.areEqual(42, third_event.old_value);469 'X-Lazr-Notifications', notifications);
359 Assert.areEqual(24, third_event.new_value);470 Y.lp.client.wrap_resource_on_success(
360 Assert.areEqual(entry, third_event.entry);471 null, this.response, this.args);
361472 this._checkNotificationNode('.debug.message', 'A debug');
362 Assert.isNull(fourth_event);473 this._checkNotificationNode('.informational.message', 'An info');
363 },474
364475 // Any subsequent request should preserve existing notifications.
365 test_update_cache_different_object: function() {476 var new_notifications = '[ [30, "A warning"], [40, "An error"] ]';
366 // Check that the object is not modified if the entry has a different477 this.response.setResponseHeader(
367 // link.478 'X-Lazr-Notifications', new_notifications);
368 var entry_repr = {479 Y.lp.client.wrap_resource_on_success(
369 'first': "World",480 null, this.response, this.args);
370 'second': false,481 this._checkNotificationNode('.debug.message', 'A debug');
371 'third': 24,482 this._checkNotificationNode('.informational.message', 'An info');
372 'fourth': "Unaltered",483 this._checkNotificationNode('.warning.message', 'A warning');
373 'self_link': Y.lp.client.get_absolute_uri("different_link")484 this._checkNotificationNode('.error.message', 'An error');
374 };485 },
375 var entry = new Y.lp.client.Entry(null, entry_repr, "different_link");486
376 Y.lp.client.update_cache(entry);487 test_remove_notifications: function() {
377 Assert.areEqual("Hello", LP.cache.context.first);488 // Make some notifications that will be removed.
378 Assert.areEqual(true, LP.cache.context.second);489 var notifications = '[ [10, "A debug"], [20, "An info"] ]';
379 Assert.areEqual(42, LP.cache.context.third);490 this.response.setResponseHeader(
380 Assert.areEqual("Unaltered", LP.cache.context.fourth);491 'X-Lazr-Notifications', notifications);
381 }492 Y.lp.client.wrap_resource_on_success(
382}));493 null, this.response, this.args);
383494
384suite.add(new Y.Test.Case({495 // If the notifications header is just the string "null", then the
385 name: "lp.client.notifications",496 // current notifications are removed.
386497 this.response.setResponseHeader('X-Lazr-Notifications', "null");
387 setUp: function() {498 Y.lp.client.wrap_resource_on_success(
388 this.client = new Y.lp.client.Launchpad();499 null, this.response, this.args);
389 this.args=[this.client, null, this._on_success, false];500 this._checkNoNotificationNode('.debug.message');
390 this.response = new Y.lp.testing.mockio.MockHttpResponse();501 this._checkNoNotificationNode('.informational.message');
391 this.response.setResponseHeader('Content-Type', 'application/json');502 },
392 },503
393504 test_notifications_not_removed: function() {
394 _on_success: function(entry) {505 // Make some notifications that will be removed.
395 },506 var notifications = '[ [10, "A debug"], [20, "An info"] ]';
396507 this.response.setResponseHeader(
397 _checkNotificationNode: function(node_class, node_text) {508 'X-Lazr-Notifications', notifications);
398 var node = Y.one('div#request-notifications div'+node_class);509 Y.lp.client.wrap_resource_on_success(
399 Assert.areEqual(node_text, node.get("innerHTML"));510 null, this.response, this.args);
400 },511
401512 // If the response does not include a notifications header, then
402 _checkNoNotificationNode: function(node_class) {513 // any pre-existing notifiactions are not removed.
403 var node = Y.one('div#request-notifications div'+node_class);514 this.response.setResponseHeader('X-Lazr-Notifications', null);
404 Assert.isNull(node);515 Y.lp.client.wrap_resource_on_success(
405 },516 null, this.response, this.args);
406517 this._checkNotificationNode('.debug.message', 'A debug');
407 test_display_notifications: function() {518 this._checkNotificationNode('.informational.message', 'An info');
408 var notifications = '[ [10, "A debug"], [20, "An info"] ]';519 }
409 this.response.setResponseHeader(520 }));
410 'X-Lazr-Notifications', notifications);521
411 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);522 tests.suite.add(new Y.Test.Case({
412 this._checkNotificationNode('.debug.message', 'A debug');523 name: "lp.client.forms",
413 this._checkNotificationNode('.informational.message', 'An info');524
414525 setUp: function() {
415 // Any subsequent request should preserve existing notifications.526 var form = Y.one("#testform");
416 var new_notifications = '[ [30, "A warning"], [40, "An error"] ]';527 this.error_handler = new Y.lp.client.FormErrorHandler({
417 this.response.setResponseHeader(528 form: form
418 'X-Lazr-Notifications', new_notifications);529 });
419 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);530 },
420 this._checkNotificationNode('.debug.message', 'A debug');531
421 this._checkNotificationNode('.informational.message', 'An info');532 tearDown: function() {
422 this._checkNotificationNode('.warning.message', 'A warning');533 this.error_handler.clearFormErrors();
423 this._checkNotificationNode('.error.message', 'An error');534 },
424 },535
425536 test_form_error_handler_ignores_other_responses: function() {
426 test_remove_notifications: function() {537 // Only XHR responses not containing validation data are ignored.
427 // Make some notifications that will be removed.538 var result = this.error_handler.handleError(0, {
428 var notifications = '[ [10, "A debug"], [20, "An info"] ]';539 status: 400,
429 this.response.setResponseHeader(540 statusText: 'Not Validation'
430 'X-Lazr-Notifications', notifications);541 });
431 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);542 Assert.isFalse(result);
432543 },
433 // If the notifications header is just the string "null", then the544
434 // current notifications are removed.545 test_form_error_handler_handles_responses: function() {
435 this.response.setResponseHeader('X-Lazr-Notifications', "null");546 // XHR responses containing validation data are processed.
436 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);547 var error_data = {
437 this._checkNoNotificationNode('.debug.message');548 'error_summary': 'Some errors',
438 this._checkNoNotificationNode('.informational.message');549 'form_wide_errors': ['Form error'],
439 },550 errors: {'field.test': 'Field error'}
440551 };
441 test_notifications_not_removed: function() {552 var result = this.error_handler.handleError(0, {
442 // Make some notifications that will be removed.553 status: 400,
443 var notifications = '[ [10, "A debug"], [20, "An info"] ]';554 statusText: 'Validation',
444 this.response.setResponseHeader(555 responseText: Y.JSON.stringify(error_data)
445 'X-Lazr-Notifications', notifications);556 });
446 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);557 Assert.isTrue(result);
447558 this._assert_error_rendering();
448 // If the response does not include a notifications header, then any559 },
449 // pre-existing notifiactions are not removed.560
450 this.response.setResponseHeader('X-Lazr-Notifications', null);561 _assert_error_rendering: function() {
451 Y.lp.client.wrap_resource_on_success(null, this.response, this.args);562 var label = Y.one('label[for="field.test"]');
452 this._checkNotificationNode('.debug.message', 'A debug');563 var field_error = label.next('div').next('.message');
453 this._checkNotificationNode('.informational.message', 'An info');564 Assert.isTrue(Y.one('#field_div').hasClass('error'),
454 }565 'Field div has class error');
455}));566 Assert.areEqual('Field error', field_error.getContent());
456567 Y.all('.error.message').each(function(error_node) {
457suite.add(new Y.Test.Case({568 var error_message = error_node.getContent();
458 name: "lp.client.forms",569 Assert.isTrue(
459570 error_message === '<p>Form error</p>' ||
460 setUp: function() {571 error_message === 'Some errors',
461 var form = Y.one("#testform");572 'Each error message has the correct content.');
462 this.error_handler = new Y.lp.client.FormErrorHandler({573 });
463 form: form574 },
464 });575
465 },576 test_form_error_handler_renders_errors: function() {
466577 // Form errors are rendered correctly.
467 tearDown: function() {578 this.error_handler.handleFormValidationError(
468 this.error_handler.clearFormErrors();579 "Some errors", ["Form error"],
469 },580 {'field.test': "Field error"});
470581 this._assert_error_rendering();
471 test_form_error_handler_ignores_other_responses: function() {582 }
472 // Only XHR responses not containing validation data are ignored.583 }));
473 var result = this.error_handler.handleError(0, {584}, '0.1', {
474 status: 400,585 requires: ['test', 'lp.testing.helpers', 'console', 'lp.client',
475 statusText: 'Not Validation'586 'lp.testing.mockio', 'lp.client', 'escape']
476 });
477 Y.Assert.isFalse(result);
478 },
479
480 test_form_error_handler_handles_responses: function() {
481 // XHR responses containing validation data are processed.
482 var error_data = {
483 'error_summary': 'Some errors',
484 'form_wide_errors': ['Form error'],
485 errors: {'field.test': 'Field error'}
486 };
487 var result = this.error_handler.handleError(0, {
488 status: 400,
489 statusText: 'Validation',
490 responseText: Y.JSON.stringify(error_data)
491 });
492 Y.Assert.isTrue(result);
493 this._assert_error_rendering();
494 },
495
496 _assert_error_rendering: function() {
497 var label = Y.one('label[for="field.test"]');
498 var field_error = label.next('div').next('.message');
499 Y.Assert.isTrue(Y.one('#field_div').hasClass('error'),
500 'Field div has class error');
501 Y.Assert.areEqual('Field error', field_error.getContent());
502 Y.all('.error.message').each(function(error_node) {
503 var error_message = error_node.getContent();
504 Y.Assert.isTrue(
505 error_message === '<p>Form error</p>' ||
506 error_message === 'Some errors',
507 'Each error message has the correct content.');
508 });
509 },
510
511 test_form_error_handler_renders_errors: function() {
512 // Form errors are rendered correctly.
513 this.error_handler.handleFormValidationError(
514 "Some errors", ["Form error"],
515 {'field.test': "Field error"});
516 this._assert_error_rendering();
517 }
518}));
519
520Y.lp.testing.Runner.run(suite);
521
522});587});
523588
=== modified file 'lib/lp/app/javascript/tests/test_lp_client_integration.js'
--- lib/lp/app/javascript/tests/test_lp_client_integration.js 2012-01-10 14:24:19 +0000
+++ lib/lp/app/javascript/tests/test_lp_client_integration.js 2012-07-24 16:58:23 +0000
@@ -1,4 +1,5 @@
1YUI({1YUI({
2
2 base: '/+icing/yui/',3 base: '/+icing/yui/',
3 filter: 'raw', combine: false, fetchCSS: false4 filter: 'raw', combine: false, fetchCSS: false
4}).use('test',5}).use('test',
@@ -127,20 +128,6 @@
127 Y.Assert.areSame(1, config.result.total_size);128 Y.Assert.areSame(1, config.result.total_size);
128 },129 },
129130
130 test_named_get_uri: function() {
131 var data = serverfixture.setup(
132 this, 'create_product_with_milestone_and_login');
133 var client = new Y.lp.client.Launchpad({sync: true});
134 var config = makeTestConfig({parameters: {name: data.milestone.name}});
135 var product = new Y.lp.client.Entry(
136 client, data.product, data.product.self_link);
137 product.named_get('getMilestone', config);
138 Y.Assert.isTrue(config.successful, 'Getting milestone failed');
139 var milestone = config.result;
140 Y.Assert.isInstanceOf(Y.lp.client.Entry, milestone);
141 Y.Assert.areSame(data.milestone_self_link, milestone.lp_original_uri);
142 },
143
144 test_named_post_integration: function() {131 test_named_post_integration: function() {
145 var data = serverfixture.setup(this, 'create_bug_and_login');132 var data = serverfixture.setup(this, 'create_bug_and_login');
146 var client = new Y.lp.client.Launchpad({sync: true});133 var client = new Y.lp.client.Launchpad({sync: true});
@@ -321,7 +308,7 @@
321 var team = config.result;308 var team = config.result;
322 Y.Assert.isInstanceOf(Y.lp.client.Entry, team);309 Y.Assert.isInstanceOf(Y.lp.client.Entry, team);
323 Y.Assert.areEqual('My lpclient team', team.get('display_name'));310 Y.Assert.areEqual('My lpclient team', team.get('display_name'));
324 Y.Assert.isTrue(/\~newlpclientteam$/.test(team.lp_original_uri));311 Y.Assert.isTrue(/\~newlpclientteam$/.test(team.uri));
325 },312 },
326313
327 test_collection_paged_named_get: function() {314 test_collection_paged_named_get: function() {
328315
=== modified file 'lib/lp/registry/javascript/tests/test_structural_subscription.js'
--- lib/lp/registry/javascript/tests/test_structural_subscription.js 2012-07-20 22:48:23 +0000
+++ lib/lp/registry/javascript/tests/test_structural_subscription.js 2012-07-24 16:58:23 +0000
@@ -234,7 +234,7 @@
234 Y.one('body').appendChild(this.content_node);234 Y.one('body').appendChild(this.content_node);
235235
236 this.bug_filter = {236 this.bug_filter = {
237 lp_original_uri:237 uri:
238 '/api/devel/firefox/+subscription/mark/+filter/28'238 '/api/devel/firefox/+subscription/mark/+filter/28'
239 };239 };
240 this.form_data = {240 this.form_data = {
241241
=== modified file 'lib/lp/soyuz/javascript/lp_dynamic_dom_updater.js'
--- lib/lp/soyuz/javascript/lp_dynamic_dom_updater.js 2012-07-05 14:28:57 +0000
+++ lib/lp/soyuz/javascript/lp_dynamic_dom_updater.js 2012-07-24 16:58:23 +0000
@@ -266,8 +266,8 @@
266 }266 }
267267
268 // Finally, call the LP api method as required...268 // Finally, call the LP api method as required...
269 if (uri){269 if (uri) {
270 if (api_method_name){270 if (api_method_name) {
271 this.get("lp_client").named_get(uri,271 this.get("lp_client").named_get(uri,
272 api_method_name, this._lp_api_config);272 api_method_name, this._lp_api_config);
273 }273 }