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

Proposed by Richard Harding on 2012-06-27
Status: Merged
Approved by: Richard Harding on 2012-06-27
Approved revision: no longer in the source branch.
Merged at revision: 15507
Proposed branch: lp:~rharding/launchpad/listingnav_yui35
Merge into: lp:launchpad
Diff against target: 2143 lines (+1050/-1040)
3 files modified
lib/lp/app/javascript/listing_navigator.js (+16/-16)
lib/lp/app/javascript/tests/test_listing_navigator.html (+5/-2)
lib/lp/app/javascript/tests/test_listing_navigator.js (+1029/-1022)
To merge this branch: bzr merge lp:~rharding/launchpad/listingnav_yui35
Reviewer Review Type Date Requested Status
Raphaël Badin (community) 2012-06-27 Approve on 2012-06-27
Francesco Banconi (community) code* 2012-06-27 Approve on 2012-06-27
Review via email: mp+112334@code.launchpad.net

Commit Message

Update the test_listing_navigator tests to common testrunner, update for YUI 3.5 as well.

Description of the Change

= Summary =

This updates the test_listing_navigator to work in YUI 3.5. It also updates it to use the current test runner to avoid code duplication.

== Implementation Notes ==

This module was running on its own test runner setup which required duplicated effort to clean up. This reformats the tests to use the standard setup from the testrunner module.

This also updates for YUI 3.5 by fixing some cleanup of the browser history state in the teardown methods of the various test suites. This is required because of the updated History module pulling from a global history state which polluted test runs afterwards.

== Tests ==

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

== Lint ==

Linting changed files:
  lib/lp/app/javascript/listing_navigator.js
  lib/lp/app/javascript/tests/test_listing_navigator.js

== LoC Qualification ==

There are two qualifications:

1. Fixing these tests reduces tech debt and eases maintenance.
2. Getting to YUI 3.5 will allow us to use the build in calendar widget and
will remove all of the YUI2 code from the code base which is aroud 12K LoC
(non-minified) and over 27K lines of total files.

To post a comment you must log in.
Francesco Banconi (frankban) wrote :

This branch looks good Richard, thank you.
As we discussed on IRC, you added 'node' as a requirement because in the code a NodeList is used at one point.
The tests pass.

review: Approve (code*)
Raphaël Badin (rvb) wrote :

Looks good.

[0]

1436 + setUp: function() {
1437 + this.target = Y.Node.create('<div></div>').set(
1438 + 'id', 'client-listing');
1439 + Y.one('body').appendChild(this.target);
1440 + },
1441 +
1442 + tearDown: function() {
1443 + this.target.remove();
1444 + delete this.target;
1445 + reset_history();
1446 + },

I see a lot of identical setUp/tearDown methods. Maybe you could put them in a test case class and use that class instead of having that code duplicated.

[1]

pocketlint thinks some of the lines are too long: http://paste.ubuntu.com/1062555/.

[2]

285 + * gets pulled into the next text automatically.

s/text/test/ ?

[3]

>This also updates for YUI 3.5 by fixing some cleanup of the browser history state in the teardown methods of the
> various test suites

> 1191 + tests.suite.add(new Y.Test.Case({

Too bad we're not deriving our tests from a custom test case as this seems like the kind of cleanup that should always be done… don't you think?

review: Approve
Richard Harding (rharding) wrote :

> Looks good.
>
> [0]
>
> 1436 + setUp: function() {
> 1437 + this.target = Y.Node.create('<div></div>').set(
> 1438 + 'id', 'client-listing');
> 1439 + Y.one('body').appendChild(this.target);
> 1440 + },
> 1441 +
> 1442 + tearDown: function() {
> 1443 + this.target.remove();
> 1444 + delete this.target;
> 1445 + reset_history();
> 1446 + },
>
> I see a lot of identical setUp/tearDown methods. Maybe you could put them in
> a test case class and use that class instead of having that code duplicated.

Yes, it's a debate on how far to touch things in the process of just trying to get tests passing under YUI3.5. I'll probably hold off on this at the moment.

>
> [1]
>
> pocketlint thinks some of the lines are too long:
> http://paste.ubuntu.com/1062555/.

Sorry, forgot to repush after I ran lint. The branch should be updated. It was mainly comment blocks that were too long.

> [2]
>
> 285 + * gets pulled into the next text automatically.
>
> s/text/test/ ?

ty

> [3]
>
> >This also updates for YUI 3.5 by fixing some cleanup of the browser history
> state in the teardown methods of the
> > various test suites
>
> > 1191 + tests.suite.add(new Y.Test.Case({
>
> Too bad we're not deriving our tests from a custom test case as this seems
> like the kind of cleanup that should always be done… don't you think?

I had the thought, but here's the interesting thing. If I were to reload the page, the previous state was causing the pollution from the browser itself. Now, if a user were to load the page, and do something, and reload the page, it would do the same. So I don't know that I'd want to blindly clear this in all tests. I'd want to make sure people were aware of how this works.

I'm blindly adding here to these tests because I'm documenting the issues as they come up and I've got a list of issues to look into going forward. If there is still an issue in production code, it'll be part of the tests coming out of this. If every test blanked it then tests would always pass, but we'd have a strange history state bug in production.

I guess I lean a bit toward explicit in the case of test setup/teardowns so that I can see expected changes and cleanup that my code does. I hate when people just do things like Y.one('body').setHTML('') because it doesn't say much about what they were expecting in there. There could have been extra garbage no one noticed because it wasn't left dangling for all to see.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/javascript/listing_navigator.js'
2--- lib/lp/app/javascript/listing_navigator.js 2012-03-20 12:11:48 +0000
3+++ lib/lp/app/javascript/listing_navigator.js 2012-06-27 14:13:23 +0000
4@@ -40,20 +40,8 @@
5 * - batch_key: A string representing the position and ordering of the
6 * current batch, as returned by listing_navigator.get_batch_key
7 */
8-module.SimpleListingModel = function () {
9- module.SimpleListingModel.superclass.constructor.apply(this, arguments);
10-};
11-
12-
13-module.SimpleListingModel.NAME = 'simple-listing-model';
14-
15-module.SimpleListingModel.ATTRS = {
16- total: {
17- value: null
18- }
19-};
20-
21-Y.extend(module.SimpleListingModel, Y.Base, {
22+module.SimpleListingModel = Y.Base.create('simple-listing-model', Y.Base, [], {
23+
24 /**
25 * Initializer sets up the History object that stores most of the
26 * model data.
27@@ -81,6 +69,18 @@
28 var url = '?' + Y.QueryString.stringify(query);
29 this.get('history').addValue('batch_key', batch_key, {url: url});
30 }
31+}, {
32+ ATTRS: {
33+ batch_key: {
34+ value: null
35+ },
36+ history: {
37+ value: null
38+ },
39+ total: {
40+ value: null
41+ }
42+ }
43 });
44
45
46@@ -640,7 +640,7 @@
47
48 }, "0.1", {
49 "requires": [
50- "node", 'history', 'lp.client', 'lp.app.errors', 'lp.app.indicator',
51- 'lp.mustache'
52+ "base", "node", 'history', 'lp.client', 'lp.app.errors',
53+ 'lp.app.indicator', 'lp.mustache'
54 ]
55 });
56
57=== modified file 'lib/lp/app/javascript/tests/test_listing_navigator.html'
58--- lib/lp/app/javascript/tests/test_listing_navigator.html 2012-03-14 04:41:36 +0000
59+++ lib/lp/app/javascript/tests/test_listing_navigator.html 2012-06-27 14:13:23 +0000
60@@ -30,6 +30,10 @@
61 <script type="text/javascript"
62 src="../../../../../build/js/lp/app/testing/mockio.js"></script>
63 <script type="text/javascript"
64+ src="../../../../../build/js/lp/app/extras/extras.js"></script>
65+ <script type="text/javascript"
66+ src="../../../../../build/js/lp/app/anim/anim.js"></script>
67+ <script type="text/javascript"
68 src="../../../../../build/js/lp/app/client.js"></script>
69 <script type="text/javascript"
70 src="../../../../../build/js/lp/app/effects/effects.js"></script>
71@@ -65,8 +69,7 @@
72 </head>
73 <body class="yui3-skin-sam">
74 <ul id="suites">
75- <!-- <li>lp.large_indicator.test</li> -->
76- <li>lp.listing_navigator.test</li>
77+ <li>lp.app.listing_navigator.test</li>
78 </ul>
79
80 <div id="fixture"></div>
81
82=== modified file 'lib/lp/app/javascript/tests/test_listing_navigator.js'
83--- lib/lp/app/javascript/tests/test_listing_navigator.js 2012-03-20 06:11:07 +0000
84+++ lib/lp/app/javascript/tests/test_listing_navigator.js 2012-06-27 14:13:23 +0000
85@@ -1,1029 +1,1036 @@
86-YUI({
87- base: '../../../../canonical/launchpad/icing/yui/',
88- filter: 'raw', combine: false, fetchCSS: false
89- }).use('test', 'console', 'lp.app.listing_navigator', 'lp.testing.mockio',
90- 'lp.testing.assert', 'history',
91- function(Y) {
92-
93-var suite = new Y.Test.Suite("lp.app.listing_navigator Tests");
94-var module = Y.lp.app.listing_navigator;
95-
96-var TestModel = function() {
97- this.constructor.superclass.constructor.apply(this, arguments);
98-};
99-
100-TestModel.ATTRS = {
101- batch_key: {value: null}
102-};
103-
104-var TestListingNavigator = function() {
105- this.constructor.superclass.constructor.apply(this, arguments);
106-};
107-
108-Y.extend(TestListingNavigator, Y.lp.app.listing_navigator.ListingNavigator, {
109- update_from_cache: function() {
110- this.constructor.superclass.update_from_cache.apply(this, arguments);
111- this.pre_fetch_batches();
112- this.render();
113- },
114- get_search_params: function(config) {
115- return config.search_params;
116- }
117-});
118-
119-
120-suite.add(new Y.Test.Case({
121- name: 'render',
122-
123- setUp: function() {
124- this.target = Y.Node.create('<div></div>').set(
125- 'id', 'client-listing');
126- Y.one('body').appendChild(this.target);
127- },
128-
129- tearDown: function() {
130- Y.one('#fixture').setContent('');
131- this.target.remove();
132- delete this.target;
133- },
134-
135- get_render_navigator: function() {
136- var lp_cache = {
137- mustache_model: {
138- items: [{foo: 'bar', show_foo: true}]
139- },
140- next: null,
141- prev: null,
142- start: 5,
143- total: 256,
144- field_visibility: {show_foo: true},
145- field_visibility_defaults: {show_foo: false}
146- };
147- var template = "{{#items}}{{#show_foo}}{{foo}}{{/show_foo}}" +
148- "{{/items}}";
149- var navigator = new TestListingNavigator({
150- cache: lp_cache,
151- template: template,
152- target: this.target
153- });
154- var index = Y.Node.create(
155- '<div><strong>3</strong> &rarr; <strong>4</strong>' +
156- ' of 512 results</div>');
157- navigator.get('navigation_indices').push(index);
158- navigator.get('backwards_navigation').push(
159- Y.Node.create('<div></div>'));
160- navigator.get('forwards_navigation').push(
161- Y.Node.create('<div></div>'));
162- return navigator;
163- },
164- test_render: function() {
165- // Rendering should work with #client-listing supplied.
166- var navigator = this.get_render_navigator();
167- navigator.render();
168- Y.Assert.areEqual('bar', navigator.get('target').getContent());
169- },
170- /**
171- * update_navigation_links should disable "previous" and "first" if there
172- * is no previous batch (i.e. we're at the beginning.)
173- */
174- test_update_navigation_links_disables_backwards_navigation_if_no_prev:
175- function() {
176- var navigator = this.get_render_navigator();
177- var action = navigator.get('backwards_navigation').item(0);
178- navigator.update_navigation_links();
179- Y.Assert.isTrue(action.hasClass('inactive'));
180- },
181- /**
182- * update_navigation_links should enable "previous" and "first" if there is
183- * a previous batch (i.e. we're not at the beginning.)
184- */
185- test_update_navigation_links_enables_backwards_navigation_if_prev:
186- function() {
187- var navigator = this.get_render_navigator();
188- var action = navigator.get('backwards_navigation').item(0);
189- action.addClass('inactive');
190- navigator.get_current_batch().prev = {
191- start: 1, memo: 'pi'
192- };
193- navigator.update_navigation_links();
194- Y.Assert.isFalse(action.hasClass('inactive'));
195- },
196- /**
197- * update_navigation_links should disable "next" and "last" if there is
198- * no next batch (i.e. we're at the end.)
199- */
200- test_update_navigation_links_disables_forwards_navigation_if_no_next:
201- function() {
202- var navigator = this.get_render_navigator();
203- var action = navigator.get('forwards_navigation').item(0);
204- navigator.update_navigation_links();
205- Y.Assert.isTrue(action.hasClass('inactive'));
206- },
207- /**
208- * update_navigation_links should enable "next" and "last" if there is a
209- * next batch (i.e. we're not at the end.)
210- */
211- test_update_navigation_links_enables_forwards_navigation_if_next:
212- function() {
213- var navigator = this.get_render_navigator();
214- var action = navigator.get('forwards_navigation').item(0);
215- action.addClass('inactive');
216- navigator.get_current_batch().next = {
217- start: 1, memo: 'pi'
218- };
219- navigator.update_navigation_links();
220- Y.Assert.isFalse(action.hasClass('inactive'));
221- },
222- /**
223- * linkify_navigation should convert previous, next, first last into
224- * hyperlinks, while retaining the original content.
225- */
226- test_linkify_navigation: function() {
227- Y.one('#fixture').setContent(
228- '<span class="previous">PreVious</span>' +
229- '<span class="next">NeXt</span>' +
230- '<span class="first">FiRST</span>' +
231- '<span class="last">lAst</span>');
232- module.linkify_navigation();
233- function checkNav(selector, content) {
234- var node = Y.one(selector);
235- Y.Assert.areEqual('a', node.get('tagName').toLowerCase());
236- Y.Assert.areEqual(content, node.getContent());
237- Y.Assert.areEqual('#', node.get('href').substr(-1, 1));
238- }
239- checkNav('.previous', 'PreVious');
240- checkNav('.next', 'NeXt');
241- checkNav('.first', 'FiRST');
242- checkNav('.last', 'lAst');
243- },
244- /**
245- * Render should update the navigation_indices with the result info.
246- */
247- test_update_navigation_links_indices: function() {
248- var navigator = this.get_render_navigator();
249- var index = navigator.get('navigation_indices').item(0);
250- Y.Assert.areEqual(
251- '<strong>3</strong> \u2192 <strong>4</strong> of 512 results',
252- index.getContent());
253- navigator.render();
254- Y.Assert.areEqual(
255- '<strong>6</strong> \u2192 <strong>6</strong> of 256 results',
256- index.getContent());
257- }
258-}));
259-
260-suite.add(new Y.Test.Case({
261- name: 'first_batch',
262-
263- setUp: function() {
264- this.target = Y.Node.create('<div></div>').set(
265- 'id', 'client-listing');
266- Y.one('body').appendChild(this.target);
267- },
268-
269- tearDown: function() {
270- this.target.remove();
271- delete this.target;
272- },
273-
274- /**
275- * Return a ListingNavigator ordered by 'intensity'
276- */
277- get_intensity_listing: function() {
278- mock_io = new Y.lp.testing.mockio.MockIo();
279+/* Copyright (c) 2012, Canonical Ltd. All rights reserved. */
280+
281+YUI.add('lp.app.listing_navigator.test', function (Y) {
282+ var module = Y.lp.app.listing_navigator;
283+
284+ /**
285+ * This is required in teardown to blank out the browser history which
286+ * gets pulled into the next text automatically.
287+ */
288+ var reset_history = function () {
289+ var win = Y.config.win;
290+ var originalURL = (win && win.location.toString()) || '';
291+ win.history.replaceState(null, null, originalURL);
292+ };
293+
294+ var TestListingNavigator = Y.Base.create('test-listing-navigator',
295+ module.ListingNavigator, [], {
296+ update_from_cache: function() {
297+ this.constructor.superclass.update_from_cache.apply(this,
298+ arguments);
299+ this.pre_fetch_batches();
300+ this.render();
301+ },
302+ get_search_params: function(config) {
303+ return config.search_params;
304+ }
305+ }, {});
306+
307+ var get_navigator = function(url, config) {
308+ var mock_io = new Y.lp.testing.mockio.MockIo();
309+ if (Y.Lang.isUndefined(url)){
310+ url = '';
311+ }
312+ if (Y.Lang.isUndefined(config)){
313+ config = {};
314+ }
315+ var target = config.target;
316+ if (!Y.Lang.isValue(target)){
317+ var target_parent = Y.Node.create('<div></div>');
318+ target = Y.Node.create('<div "id=#client-listing"></div>');
319+ target_parent.appendChild(target);
320+ }
321 lp_cache = {
322 context: {
323 resource_type_link: 'http://foo_type',
324 web_link: 'http://foo/bar'
325 },
326 view_name: '+items',
327- mustache_model: {
328- foo: 'bar',
329- items: []
330- },
331- next: null,
332- prev: null,
333- field_visibility: {},
334- field_visibility_defaults: {}
335- };
336- var navigator = new TestListingNavigator({
337- current_url:
338- "http://yahoo.com?start=5&memo=6&direction=backwards",
339- cache: lp_cache,
340- template: "<ol>" + "{{#item}}<li>{{name}}</li>{{/item}}</ol>",
341- target: this.target,
342- io_provider: mock_io
343- });
344- navigator.first_batch('intensity');
345- Y.Assert.areEqual('', navigator.get('target').getContent());
346- mock_io.last_request.successJSON({
347- context: {
348- resource_type_link: 'http://foo_type',
349- web_link: 'http://foo/bar'
350- },
351- mustache_model:
352- {
353- item: [
354- {name: 'first'},
355- {name: 'second'}],
356- items: []
357- },
358- order_by: 'intensity',
359- start: 0,
360- forwards: true,
361- memo: null,
362- next: null,
363- prev: null
364- });
365- return navigator;
366- },
367- test_first_batch: function() {
368- /* first_batch retrieves a listing for the new ordering and
369- * displays it */
370- var navigator = this.get_intensity_listing();
371- var mock_io = navigator.get('io_provider');
372- Y.Assert.areEqual('<ol><li>first</li><li>second</li></ol>',
373- navigator.get('target').getContent());
374- Y.Assert.areEqual('/bar/+items/++model++?orderby=intensity&start=0',
375- mock_io.last_request.url);
376- },
377- test_first_batch_uses_cache: function() {
378- /* first_batch will use the cached value instead of making a
379- * second AJAX request. */
380- var navigator = this.get_intensity_listing();
381- Y.Assert.areEqual(1, navigator.get('io_provider').requests.length);
382- navigator.first_batch('intensity');
383- Y.Assert.areEqual(1, navigator.get('io_provider').requests.length);
384- },
385- test_io_error: function() {
386- var overlay_node;
387- var navigator = this.get_intensity_listing();
388- navigator.first_batch('failure');
389- navigator.get('io_provider').failure();
390- overlay_node = Y.one('.yui3-lazr-formoverlay-errors');
391- Y.Assert.isTrue(Y.Lang.isValue(overlay_node));
392- }
393-}));
394-
395-suite.add(new Y.Test.Case({
396- name: 'Batch caching',
397-
398- setUp: function() {
399- this.target = Y.Node.create('<div></div>').set(
400- 'id', 'client-listing');
401- Y.one('body').appendChild(this.target);
402- },
403-
404- tearDown: function() {
405- this.target.remove();
406- delete this.target;
407- },
408-
409- test_update_from_new_model_caches: function() {
410- /* update_from_new_model caches the settings in the module.batches. */
411- var lp_cache = {
412- context: {
413- resource_type_link: 'http://foo_type',
414- web_link: 'http://foo/bar'
415- },
416- mustache_model: {
417- foo: 'bar'
418- },
419- next: null,
420- prev: null,
421- field_visibility: {},
422- field_visibility_defaults: {}
423- };
424- var template = "<ol>" +
425- "{{#item}}<li>{{name}}</li>{{/item}}</ol>";
426- var navigator = new TestListingNavigator({
427- current_url: window.location,
428- cache: lp_cache,
429- template: template,
430- target: this.target
431- });
432- var key = module.get_batch_key({
433- order_by: "intensity",
434- memo: 'memo1',
435- forwards: true,
436- start: 5,
437- target: this.target
438- });
439- var batch = {
440- order_by: 'intensity',
441- memo: 'memo1',
442- forwards: true,
443- start: 5,
444- next: null,
445- prev: null,
446- mustache_model: {
447- item: [
448- {name: 'first'},
449- {name: 'second'}
450- ],
451- items: ['a', 'b', 'c']
452- }};
453- var query = navigator.get_batch_query(batch);
454- navigator.update_from_new_model(query, true, batch);
455- Y.lp.testing.assert.assert_equal_structure(
456- batch, navigator.get('batches')[key]);
457- },
458- /**
459- * get_batch_key returns a JSON-serialized list.
460- */
461- test_get_batch_key: function() {
462- var key = module.get_batch_key({
463- order_by: 'order_by1',
464- memo: 'memo1',
465- forwards: true,
466- target: this.target,
467- start: 5});
468- Y.Assert.areSame('["order_by1","memo1",true,5]', key);
469- }
470-}));
471-
472-suite.add(new Y.Test.Case({
473- name: 'get_query',
474-
475- test_get_query: function() {
476- // get_query returns the query portion of a URL in structured form.
477- var query = module.get_query('http://yahoo.com?me=you&a=b&a=c');
478- Y.lp.testing.assert.assert_equal_structure(
479- {me: 'you', a: ['b', 'c']}, query);
480- }
481-}));
482-
483-
484-suite.add(new Y.Test.Case({
485- name: 'get_batch_url',
486-
487- setUp: function() {
488- this.target = Y.Node.create('<div></div>').set(
489- 'id', 'client-listing');
490- Y.one('body').appendChild(this.target);
491- },
492-
493- tearDown: function() {
494- this.target.remove();
495- delete this.target;
496- },
497-
498- /**
499- * get_batch_query accepts the order_by param.
500- */
501- test_get_batch_query_orderby: function() {
502- var navigator = new TestListingNavigator({
503- search_params: {
504- param: 1
505- },
506- target: this.target,
507- cache: {next: null, prev: null}
508- });
509- var query = navigator.get_batch_query({order_by: 'importance'});
510- Y.Assert.areSame('importance', query.orderby);
511- Y.Assert.areSame(1, query.param);
512- },
513- /**
514- * get_batch_query accepts the memo param.
515- */
516- test_get_batch_query_memo: function() {
517- var navigator = new TestListingNavigator({
518- search_params: {
519- param: 'foo'
520- },
521- target: this.target,
522- cache: {next: null, prev: null}
523- });
524- var query = navigator.get_batch_query({memo: 'pi'});
525- Y.Assert.areSame('pi', query.memo);
526- Y.Assert.areSame('foo', query.param);
527- },
528- /**
529- * When memo is null, query.memo is undefined.
530- */
531- test_get_batch_null_memo: function() {
532- var navigator = new TestListingNavigator({
533- search_params: {},
534- cache: {next: null, prev: null},
535- target: this.target
536- });
537- var query = navigator.get_batch_query({memo: null});
538- Y.Assert.areSame(undefined, query.memo);
539- },
540- /**
541- * If 'forwards' is true, direction does not appear.
542- */
543- test_get_batch_query_forwards: function() {
544- var navigator = new TestListingNavigator({
545- search_params: {
546- param: 'pi'
547- },
548- cache: {next: null, prev: null},
549- target: this.target
550- });
551- var query = navigator.get_batch_query({forwards: true});
552- Y.Assert.areSame('pi', query.param);
553- Y.Assert.areSame(undefined, query.direction);
554- },
555- /**
556- * If 'forwards' is false, direction is set to backwards.
557- */
558- test_get_batch_query_backwards: function() {
559- var navigator = new TestListingNavigator({
560- search_params: {
561- param: 'pi'
562- },
563- cache: {next: null, prev: null},
564- target: this.target
565- });
566- var query = navigator.get_batch_query({forwards: false});
567- Y.Assert.areSame('pi', query.param);
568- Y.Assert.areSame('backwards', query.direction);
569- },
570- /**
571- * If start is provided, it overrides existing values.
572- */
573- test_get_batch_query_start: function() {
574- var navigator = new TestListingNavigator({
575- search_params: {},
576- cache: {next: null, prev:null},
577- target: this.target
578- });
579- var query = navigator.get_batch_query({});
580- Y.Assert.areSame(undefined, query.start);
581- query = navigator.get_batch_query({start: 1});
582- Y.Assert.areSame(1, query.start);
583- query = navigator.get_batch_query({start: null});
584- Y.lp.testing.assert.assert_equal_structure({}, query);
585- }
586-}));
587-
588-var get_navigator = function(url, config) {
589- var mock_io = new Y.lp.testing.mockio.MockIo();
590- if (Y.Lang.isUndefined(url)){
591- url = '';
592- }
593- if (Y.Lang.isUndefined(config)){
594- config = {};
595- }
596- var target = config.target;
597- if (!Y.Lang.isValue(target)){
598- var target_parent = Y.Node.create('<div></div>');
599- target = Y.Node.create('<div "id=#client-listing"></div>');
600- target_parent.appendChild(target);
601- }
602- lp_cache = {
603- context: {
604- resource_type_link: 'http://foo_type',
605- web_link: 'http://foo/bar'
606- },
607- view_name: '+items',
608- next: {
609- memo: 467,
610- start: 500
611- },
612- prev: {
613- memo: 457,
614- start: 400
615- },
616- forwards: true,
617- order_by: 'foo',
618- memo: 457,
619- start: 450,
620- last_start: 23,
621- field_visibility: {},
622- field_visibility_defaults: {}
623- };
624- if (config.no_next){
625- lp_cache.next = null;
626- }
627- if (config.no_prev){
628- lp_cache.prev = null;
629- }
630- var navigator_config = {
631- current_url: url,
632- cache: lp_cache,
633- io_provider: mock_io,
634- pre_fetch: config.pre_fetch,
635- target: target,
636- template: ''
637- };
638- return new TestListingNavigator(navigator_config);
639-};
640-
641-suite.add(new Y.Test.Case({
642- name: 'navigation',
643-
644- setUp: function() {
645- this.target = Y.Node.create('<div></div>').set(
646- 'id', 'client-listing');
647- Y.one('body').appendChild(this.target);
648- },
649-
650- tearDown: function() {
651- this.target.remove();
652- delete this.target;
653- },
654-
655- test_model_uses_view_name: function() {
656- var navigator = get_navigator('', {target: this.target});
657- navigator.get_current_batch().view_name = '+funitems';
658- navigator.load_model({});
659- Y.Assert.areSame(
660- '/bar/+funitems/++model++',
661- navigator.get('io_provider').last_request.url);
662- },
663-
664- /**
665- * last_batch uses memo="", start=navigator.current_batch.last_start,
666- * direction=backwards, orderby=navigator.current_batch.order_by.
667- */
668- test_last_batch: function() {
669- var navigator = get_navigator(
670- '?memo=pi&direction=backwards&start=57', {target: this.target});
671- navigator.last_batch();
672- Y.Assert.areSame(
673- '/bar/+items/++model++?orderby=foo&memo=&start=23&' +
674- 'direction=backwards',
675- navigator.get('io_provider').last_request.url);
676- },
677-
678- /**
679- * first_batch omits memo and direction, start=0,
680- * orderby=navigator.current_batch.order_by.
681- */
682- test_first_batch: function() {
683- var navigator = get_navigator(
684- '?memo=pi&start=26', {target: this.target});
685- navigator.first_batch();
686- Y.Assert.areSame(
687- '/bar/+items/++model++?orderby=foo&start=0',
688- navigator.get('io_provider').last_request.url);
689- },
690-
691- /**
692- * next_batch uses values from current_batch.next +
693- * current_batch.ordering.
694- */
695- test_next_batch: function() {
696- var navigator = get_navigator(
697- '?memo=pi&start=26', {target: this.target});
698- navigator.next_batch();
699- Y.Assert.areSame(
700- '/bar/+items/++model++?orderby=foo&memo=467&start=500',
701- navigator.get('io_provider').last_request.url);
702- },
703-
704- /**
705- * Calling next_batch when there is none is a no-op.
706- */
707- test_next_batch_missing: function() {
708- var navigator = get_navigator(
709- '?memo=pi&start=26', {no_next: true, target: this.target});
710- navigator.next_batch();
711- Y.Assert.areSame(
712- null, navigator.get('io_provider').last_request);
713- },
714-
715- /**
716- * prev_batch uses values from current_batch.prev + direction=backwards
717- * and ordering=current_batch.ordering.
718- */
719- test_prev_batch: function() {
720- var navigator = get_navigator(
721- '?memo=pi&start=26', {target: this.target});
722- navigator.prev_batch();
723- Y.Assert.areSame(
724- '/bar/+items/++model++?orderby=foo&memo=457&start=400&' +
725- 'direction=backwards',
726- navigator.get('io_provider').last_request.url);
727- },
728-
729- /**
730- * Calling prev_batch when there is none is a no-op.
731- */
732- test_prev_batch_missing: function() {
733- var navigator = get_navigator(
734- '?memo=pi&start=26',
735- {no_prev: true, no_next: true, target: this.target});
736- navigator.prev_batch();
737- Y.Assert.areSame(
738- null, navigator.get('io_provider').last_request);
739- },
740-
741- /**
742- * Verify we get a reasonable default context if there is no context
743- * available as is the case with the BugsBugTaskSearchListingView.
744- */
745- test_default_context: function () {
746- var navigator = get_navigator('', {target: this.target});
747- // now remove the context
748- var batch = navigator.get_current_batch();
749- delete batch.context;
750-
751- navigator.get_current_batch().view_name = '+funitems';
752- navigator.load_model({});
753-
754- // the start of the url used will be whatever the current
755- // location.href is for the window object. We can make sure we did get
756- // a nicely generated url though by checking they end built correctly.
757- var generated_url = navigator.get('io_provider').last_request.url;
758-
759- Y.Assert.areSame(
760- '+funitems/++model++',
761- generated_url.substr(generated_url.indexOf('+')));
762- }
763-
764-}));
765-
766-suite.add(new Y.Test.Case({
767- name: "pre-fetching batches",
768- setUp: function() {
769- this.target = Y.Node.create('<div></div>').set(
770- 'id', 'client-listing');
771- Y.one('body').appendChild(this.target);
772- },
773- tearDown: function() {
774- this.target.remove();
775- delete this.target;
776- },
777- /**
778- * get_pre_fetch_configs should return a config for the next batch.
779- */
780- test_get_pre_fetch_configs: function() {
781- var navigator = get_navigator('', {target: this.target});
782- var configs = navigator.get_pre_fetch_configs();
783- var batch_keys = [];
784- Y.each(configs, function(value) {
785- batch_keys.push(module.get_batch_key(value));
786- });
787- Y.Assert.areSame('["foo",467,true,500]', batch_keys[0]);
788- Y.Assert.areSame(1, batch_keys.length);
789- },
790-
791- /**
792- * get_pre_fetch_configs should return an empty list if no next batch.
793- */
794- test_get_pre_fetch_configs_no_next: function() {
795- var navigator = get_navigator(
796- '', {no_next: true, target: this.target});
797- var configs = navigator.get_pre_fetch_configs();
798- var batch_keys = [];
799- Y.each(configs, function(value) {
800- batch_keys.push(module.get_batch_key(value));
801- });
802- Y.Assert.areSame(0, batch_keys.length);
803- },
804-
805- get_pre_fetch_navigator: function(config) {
806- var navigator = get_navigator('', config);
807- var batch = navigator.get_current_batch();
808- batch.next = {memo: 57, start: 56};
809- batch.order_by = '';
810- return navigator;
811- },
812-
813- /**
814- * Calling pre_fetch_batches should produce a request for the next batch.
815- */
816- test_pre_fetch_batches: function() {
817- var navigator = this.get_pre_fetch_navigator({target: this.target});
818- var io_provider = navigator.get('io_provider');
819- navigator.set('pre_fetch', true);
820- Y.Assert.isNull(io_provider.last_request);
821- navigator.pre_fetch_batches();
822- Y.Assert.areSame(
823- io_provider.last_request.url,
824- '/bar/+items/++model++?orderby=&memo=57&start=56');
825- },
826-
827- /**
828- * Calling pre_fetch_batches should not produce a request for the next
829- * batch if Navigator.get('pre_fetch') is false.
830- */
831- test_pre_fetch_disabled: function() {
832- var last_url;
833- var navigator = this.get_pre_fetch_navigator({target: this.target});
834- navigator.pre_fetch_batches();
835- Y.Assert.areSame(null, navigator.get('io_provider').last_request);
836- },
837-
838- /**
839- * Initialization does a pre-fetch.
840- */
841- test_pre_fetch_on_init: function() {
842- var navigator = get_navigator(
843- '', {pre_fetch: true, target:this.target});
844- var last_url = navigator.get('io_provider').last_request.url;
845- Y.Assert.areSame(
846- last_url,
847- '/bar/+items/++model++?orderby=foo&memo=467&start=500');
848- },
849- /**
850- * update_from_new_model does a pre-fetch.
851- */
852- test_pre_fetch_on_update_from_new_model: function() {
853- var navigator = get_navigator('', {target: this.target});
854- var io_provider = navigator.get('io_provider');
855- var lp_client = new Y.lp.client.Launchpad();
856- var batch = lp_client.wrap_resource(null, {
857- context: {
858- resource_type_link: 'http://foo_type',
859- web_link: 'http://foo/bar'
860- },
861- view_name: '+items',
862- order_by: 'baz',
863- memo: 'memo1',
864- next: {
865- memo: "pi",
866- start: 314
867- },
868- prev: null,
869- forwards: true,
870- start: 5,
871- mustache_model: {
872- item: [
873- {name: 'first'},
874- {name: 'second'}
875- ],
876- items: ['a', 'b', 'c']
877- }});
878- Y.Assert.isNull(io_provider.last_request);
879- navigator.set('pre_fetch', true);
880- navigator.update_from_new_model({}, false, batch);
881- Y.Assert.areSame(
882- io_provider.last_request.url,
883- '/bar/+items/++model++?orderby=baz&memo=pi&start=314');
884- }
885-}));
886-
887-
888-suite.add(new Y.Test.Case({
889- name: "Test indicators",
890-
891- /**
892- * Update starts showing the pending indicator
893- */
894- test_show_on_update: function() {
895- var navigator = get_navigator();
896- navigator.update({});
897- Y.Assert.isTrue(navigator.indicator.get('visible'));
898- },
899- /**
900- * A fetch-only update starts ignores the pending indicator
901- */
902- test_ignore_on_fetch_only_update: function() {
903- var navigator = get_navigator();
904- navigator.update({fetch_only: true});
905- Y.Assert.isFalse(navigator.indicator.get('visible'));
906- },
907- /**
908- * A successful IO operation clears the pending indicator.
909- */
910- test_hide_on_success: function() {
911- var navigator = get_navigator();
912- navigator.update({});
913- navigator.get('io_provider').last_request.successJSON({
914- mustache_model: {items: []},
915- next: null,
916- prev: null
917- });
918- Y.Assert.isFalse(navigator.indicator.get('visible'));
919- },
920- /**
921- * A successful fetch-only IO operation ignores the pending indicator.
922- */
923- test_no_hide_on_fetch_only_success: function() {
924- var navigator = get_navigator();
925- navigator.indicator.setBusy();
926- navigator.update({fetch_only: true});
927- navigator.get('io_provider').last_request.successJSON({
928- mustache_model: {items: []},
929- next: null,
930- prev: null
931- });
932- Y.Assert.isTrue(navigator.indicator.get('visible'));
933- },
934- /**
935- * A failed IO operation hides the pending indicator.
936- */
937- test_hide_on_failure: function() {
938- var navigator = get_navigator();
939- navigator.update({});
940- navigator.get('io_provider').failure();
941- Y.Assert.isFalse(navigator.indicator.get('visible'));
942- },
943- /**
944- * A failed fetch-only IO operation does not hide the pending indicator.
945- */
946- test_no_hide_on_fetch_only_failure: function() {
947- var navigator = get_navigator();
948- navigator.indicator.setBusy();
949- navigator.update({fetch_only: true});
950- navigator.get('io_provider').failure();
951- Y.Assert.isTrue(navigator.indicator.get('visible'));
952- }
953-}));
954-
955-
956-suite.add(new Y.Test.Case({
957- name: "Find batch aliases",
958-
959- test_get_batch_key_list: function() {
960- var keys = module.get_batch_key_list({
961- prev: null,
962- next:null,
963- memo: 'pi',
964- start: -1,
965- forwards: true,
966- order_by: 'ordering'
967- });
968- Y.ArrayAssert.itemsAreSame(
969- [null, '["ordering","pi",true,-1]', null], keys);
970- keys = module.get_batch_key_list({
971- prev: {
972- memo: "pi",
973- start: -2
974- },
975- next: {
976- memo: "e",
977- start: 0
978- },
979- memo: 'pi',
980- start: -1,
981- forwards: true,
982- order_by: 'ordering'
983- });
984- Y.ArrayAssert.itemsAreSame([
985- '["ordering","pi",false,-2]',
986- '["ordering","pi",true,-1]',
987- '["ordering","e",true,0]'], keys);
988- },
989-
990- /* Detect batch aliases for forward movement (next). */
991- test_find_batch_alias_moving_forward: function() {
992- var prev_batch = ['a', 'b', 'c'];
993- var next_batch = ["b'", 'c', 'd'];
994- var result = module.find_batch_alias(prev_batch, next_batch);
995- Y.Assert.areSame(result[0], 'b');
996- Y.Assert.areSame(result[1], "b'");
997- result = module.find_batch_alias(next_batch, prev_batch);
998- Y.Assert.areSame(result[0], 'b');
999- Y.Assert.areSame(result[1], "b'");
1000- },
1001-
1002- /* Detect batch aliases for backward movement (prev). */
1003- test_find_batch_alias_moving_backward: function() {
1004- var prev_batch = ['a', 'b', 'c'];
1005- var next_batch = ['b', "c'", 'd'];
1006- var result = module.find_batch_alias(prev_batch, next_batch);
1007- Y.Assert.areSame(result[0], 'c');
1008- Y.Assert.areSame(result[1], "c'");
1009- result = module.find_batch_alias(next_batch, prev_batch);
1010- Y.Assert.areSame(result[0], 'c');
1011- Y.Assert.areSame(result[1], "c'");
1012- },
1013-
1014- /* Do not detect aliases if batches are unrelated */
1015- test_find_batch_alias_unrelated: function() {
1016- var prev_batch = ['a', 'b', 'c'];
1017- var next_batch = ['d', 'e', 'f'];
1018- var result = module.find_batch_alias(next_batch, prev_batch);
1019- Y.Assert.isNull(result);
1020- },
1021-
1022- /**
1023- * When dealias_batches is called on the next batch, the current batch is
1024- * re-added to the batches mapping, under its alias from the next batch.
1025- */
1026- test_dealias_batches_next: function() {
1027- var navigator = get_navigator();
1028- var next_batch = {
1029- memo: 467,
1030- start: 500,
1031- order_by: 'foo',
1032- forwards: true,
1033- prev: {
1034- memo: 467,
1035- start: 450
1036- },
1037- next: null
1038- };
1039- var prev_batch_config = module.prev_batch_config(next_batch);
1040- var prev_batch_key = module.get_batch_key(
1041- prev_batch_config);
1042- navigator.dealias_batches(next_batch);
1043- Y.Assert.areSame(
1044- navigator.get('batches')[prev_batch_key],
1045- navigator.get_current_batch()
1046- );
1047- Y.Assert.areNotSame(
1048- prev_batch_key, navigator.get('model').get_batch_key());
1049- },
1050- /**
1051- * When dealias_batches is called on the previous batch, the current batch
1052- * is re-added to the batches mapping, under its alias from the previous
1053- * batch.
1054- */
1055- test_dealias_batches_prev: function() {
1056- var navigator = get_navigator();
1057- var prev_batch = {
1058- memo: 457,
1059- start: 400,
1060- order_by: 'foo',
1061- forwards: false,
1062- next: {
1063- memo: 467,
1064- start: 450
1065- },
1066- prev: null
1067- };
1068- var next_batch_config = module.next_batch_config(prev_batch);
1069- var next_batch_key = module.get_batch_key(
1070- next_batch_config);
1071- navigator.dealias_batches(prev_batch);
1072- Y.Assert.areSame(
1073- navigator.get('batches')[next_batch_key],
1074- navigator.get_current_batch()
1075- );
1076- Y.Assert.areNotSame(
1077- next_batch_key, navigator.get('model').get_batch_key());
1078- }
1079-}));
1080-
1081-var TestListingNavigatorWithHistory = function() {
1082- this.constructor.superclass.constructor.apply(this, arguments);
1083-};
1084-
1085-suite.add(new Y.Test.Case({
1086- name: 'browser history',
1087-
1088- setUp: function() {
1089- this.target = Y.Node.create('<div></div>').set(
1090- 'id', 'client-listing');
1091- Y.one('body').appendChild(this.target);
1092- },
1093-
1094- tearDown: function() {
1095- this.target.remove();
1096- delete this.target;
1097- },
1098-
1099- /**
1100- * Update from cache generates a change event for the specified batch.
1101- */
1102- test_update_from_cache_generates_event: function() {
1103- var navigator = get_navigator('', {target: this.target});
1104- var e = null;
1105- navigator.get('model').get('history').on('change', function(inner_e) {
1106- e = inner_e;
1107- });
1108- navigator.get('batches')['some-batch-key'] = {
1109- mustache_model: {
1110- items: []
1111- },
1112- next: null,
1113- prev: null
1114- };
1115- navigator.update_from_cache({foo: 'bar'}, 'some-batch-key');
1116- Y.Assert.areEqual('some-batch-key', e.newVal.batch_key);
1117- Y.Assert.areEqual('?foo=bar', e._options.url);
1118- },
1119-
1120- /**
1121- * When a change event is emitted, the relevant batch becomes the current
1122- * batch and is rendered.
1123- */
1124- test_change_event_renders_cache: function() {
1125- var navigator = get_navigator('', {target: this.target});
1126- var batch = {
1127- mustache_model: {
1128- items: [],
1129- foo: 'bar'
1130- },
1131- next: null,
1132- prev: null
1133- };
1134- navigator.set('template', '{{foo}}');
1135- navigator.get('batches')['some-batch-key'] = batch;
1136- navigator.get('model').get('history').addValue(
1137- 'batch_key', 'some-batch-key');
1138- Y.Assert.areEqual(batch, navigator.get_current_batch());
1139- Y.Assert.areEqual('bar', navigator.get('target').getContent());
1140- }
1141-}));
1142-
1143-
1144-var handle_complete = function(data) {
1145- window.status = '::::' + JSON.stringify(data);
1146- };
1147-Y.Test.Runner.on('complete', handle_complete);
1148-Y.Test.Runner.add(suite);
1149-
1150-var console = new Y.Console({newestOnTop: false});
1151-console.render('#log');
1152-
1153-Y.on('domready', function() {
1154- Y.Test.Runner.run();
1155-});
1156+ next: {
1157+ memo: 467,
1158+ start: 500
1159+ },
1160+ prev: {
1161+ memo: 457,
1162+ start: 400
1163+ },
1164+ forwards: true,
1165+ order_by: 'foo',
1166+ memo: 457,
1167+ start: 450,
1168+ last_start: 23,
1169+ field_visibility: {},
1170+ field_visibility_defaults: {}
1171+ };
1172+ if (config.no_next){
1173+ lp_cache.next = null;
1174+ }
1175+ if (config.no_prev){
1176+ lp_cache.prev = null;
1177+ }
1178+ var navigator_config = {
1179+ current_url: url,
1180+ cache: lp_cache,
1181+ io_provider: mock_io,
1182+ pre_fetch: config.pre_fetch,
1183+ target: target,
1184+ template: ''
1185+ };
1186+ return new TestListingNavigator(navigator_config);
1187+ };
1188+
1189+
1190+ var tests = Y.namespace('lp.app.listing_navigator.test');
1191+ tests.suite = new Y.Test.Suite('Listing Navigator Tests');
1192+
1193+ tests.suite.add(new Y.Test.Case({
1194+ name: 'render',
1195+
1196+ setUp: function() {
1197+ this.target = Y.Node.create('<div></div>').set(
1198+ 'id', 'client-listing');
1199+ Y.one('body').appendChild(this.target);
1200+ },
1201+
1202+ tearDown: function() {
1203+ Y.one('#fixture').setContent('');
1204+ this.target.remove();
1205+ delete this.target;
1206+ reset_history();
1207+ },
1208+
1209+ get_render_navigator: function() {
1210+ var lp_cache = {
1211+ mustache_model: {
1212+ items: [{foo: 'bar', show_foo: true}]
1213+ },
1214+ next: null,
1215+ prev: null,
1216+ start: 5,
1217+ total: 256,
1218+ field_visibility: {show_foo: true},
1219+ field_visibility_defaults: {show_foo: false}
1220+ };
1221+ var template = "{{#items}}{{#show_foo}}{{foo}}{{/show_foo}}" +
1222+ "{{/items}}";
1223+ var navigator = new TestListingNavigator({
1224+ cache: lp_cache,
1225+ template: template,
1226+ target: this.target
1227+ });
1228+ var index = Y.Node.create(
1229+ '<div><strong>3</strong> &rarr; <strong>4</strong>' +
1230+ ' of 512 results</div>');
1231+ navigator.get('navigation_indices').push(index);
1232+ navigator.get('backwards_navigation').push(
1233+ Y.Node.create('<div></div>'));
1234+ navigator.get('forwards_navigation').push(
1235+ Y.Node.create('<div></div>'));
1236+ return navigator;
1237+ },
1238+ test_render: function() {
1239+ // Rendering should work with #client-listing supplied.
1240+ var navigator = this.get_render_navigator();
1241+ navigator.render();
1242+ Y.Assert.areEqual('bar', navigator.get('target').getContent());
1243+ },
1244+ /**
1245+ * update_navigation_links should disable "previous" and "first" if
1246+ * there is no previous batch (i.e. we're at the beginning.)
1247+ */
1248+ test_update_navigation_links_disables_backwards_navigation_if_no_prev:
1249+ function() {
1250+ var navigator = this.get_render_navigator();
1251+ var action = navigator.get('backwards_navigation').item(0);
1252+ navigator.update_navigation_links();
1253+ Y.Assert.isTrue(action.hasClass('inactive'));
1254+ },
1255+ /**
1256+ * update_navigation_links should enable "previous" and "first" if
1257+ * there is a previous batch (i.e. we're not at the beginning.)
1258+ */
1259+ test_update_navigation_links_enables_backwards_navigation_if_prev:
1260+ function() {
1261+ var navigator = this.get_render_navigator();
1262+ var action = navigator.get('backwards_navigation').item(0);
1263+ action.addClass('inactive');
1264+ navigator.get_current_batch().prev = {
1265+ start: 1, memo: 'pi'
1266+ };
1267+ navigator.update_navigation_links();
1268+ Y.Assert.isFalse(action.hasClass('inactive'));
1269+ },
1270+ /**
1271+ * update_navigation_links should disable "next" and "last" if there is
1272+ * no next batch (i.e. we're at the end.)
1273+ */
1274+ test_update_navigation_links_disables_forwards_navigation_if_no_next:
1275+ function() {
1276+ var navigator = this.get_render_navigator();
1277+ var action = navigator.get('forwards_navigation').item(0);
1278+ navigator.update_navigation_links();
1279+ Y.Assert.isTrue(action.hasClass('inactive'));
1280+ },
1281+ /**
1282+ * update_navigation_links should enable "next" and "last" if there is a
1283+ * next batch (i.e. we're not at the end.)
1284+ */
1285+ test_update_navigation_links_enables_forwards_navigation_if_next:
1286+ function() {
1287+ var navigator = this.get_render_navigator();
1288+ var action = navigator.get('forwards_navigation').item(0);
1289+ action.addClass('inactive');
1290+ navigator.get_current_batch().next = {
1291+ start: 1, memo: 'pi'
1292+ };
1293+ navigator.update_navigation_links();
1294+ Y.Assert.isFalse(action.hasClass('inactive'));
1295+ },
1296+ /**
1297+ * linkify_navigation should convert previous, next, first last into
1298+ * hyperlinks, while retaining the original content.
1299+ */
1300+ test_linkify_navigation: function() {
1301+ Y.one('#fixture').setContent(
1302+ '<span class="previous">PreVious</span>' +
1303+ '<span class="next">NeXt</span>' +
1304+ '<span class="first">FiRST</span>' +
1305+ '<span class="last">lAst</span>');
1306+ module.linkify_navigation();
1307+ function checkNav(selector, content) {
1308+ var node = Y.one(selector);
1309+ Y.Assert.areEqual('a', node.get('tagName').toLowerCase());
1310+ Y.Assert.areEqual(content, node.getContent());
1311+ Y.Assert.areEqual('#', node.get('href').substr(-1, 1));
1312+ }
1313+ checkNav('.previous', 'PreVious');
1314+ checkNav('.next', 'NeXt');
1315+ checkNav('.first', 'FiRST');
1316+ checkNav('.last', 'lAst');
1317+ },
1318+ /**
1319+ * Render should update the navigation_indices with the result info.
1320+ */
1321+ test_update_navigation_links_indices: function() {
1322+ var navigator = this.get_render_navigator();
1323+ var index = navigator.get('navigation_indices').item(0);
1324+ Y.Assert.areEqual(
1325+ '<strong>3</strong> \u2192 <strong>4</strong> of 512 results',
1326+ index.getContent());
1327+ navigator.render();
1328+ Y.Assert.areEqual(
1329+ '<strong>6</strong> \u2192 <strong>6</strong> of 256 results',
1330+ index.getContent());
1331+ }
1332+ }));
1333+
1334+ tests.suite.add(new Y.Test.Case({
1335+ name: 'first_batch',
1336+
1337+ setUp: function() {
1338+ this.target = Y.Node.create('<div></div>').set(
1339+ 'id', 'client-listing');
1340+ Y.one('body').appendChild(this.target);
1341+ console.log('setup', Y.config.win.history.state);
1342+ Y.config.win.history.state = {};
1343+ },
1344+
1345+ tearDown: function() {
1346+ this.target.remove();
1347+ delete this.target;
1348+ reset_history();
1349+ },
1350+
1351+ /**
1352+ * Return a ListingNavigator ordered by 'intensity'
1353+ */
1354+ get_intensity_listing: function() {
1355+ mock_io = new Y.lp.testing.mockio.MockIo();
1356+ lp_cache = {
1357+ context: {
1358+ resource_type_link: 'http://foo_type',
1359+ web_link: 'http://foo/bar'
1360+ },
1361+ view_name: '+items',
1362+ mustache_model: {
1363+ foo: 'bar',
1364+ items: []
1365+ },
1366+ next: null,
1367+ prev: null,
1368+ field_visibility: {},
1369+ field_visibility_defaults: {}
1370+ };
1371+
1372+ var navigator = new TestListingNavigator({
1373+ current_url:
1374+ "http://yahoo.com?start=5&memo=6&direction=backwards",
1375+ cache: lp_cache,
1376+ template: "<ol>" + "{{#item}}<li>{{name}}</li>{{/item}}</ol>",
1377+ target: this.target,
1378+ io_provider: mock_io
1379+ });
1380+ navigator.first_batch('intensity');
1381+ Y.Assert.areEqual('', navigator.get('target').getContent());
1382+ mock_io.last_request.successJSON({
1383+ context: {
1384+ resource_type_link: 'http://foo_type',
1385+ web_link: 'http://foo/bar'
1386+ },
1387+ mustache_model:
1388+ {
1389+ item: [
1390+ {name: 'first'},
1391+ {name: 'second'}],
1392+ items: []
1393+ },
1394+ order_by: 'intensity',
1395+ start: 0,
1396+ forwards: true,
1397+ memo: null,
1398+ next: null,
1399+ prev: null
1400+ });
1401+ return navigator;
1402+ },
1403+
1404+ test_first_batch: function() {
1405+ /* first_batch retrieves a listing for the new ordering and
1406+ * displays it */
1407+ var navigator = this.get_intensity_listing();
1408+ var mock_io = navigator.get('io_provider');
1409+ Y.Assert.areEqual('<ol><li>first</li><li>second</li></ol>',
1410+ navigator.get('target').getContent());
1411+ Y.Assert.areEqual('/bar/+items/++model++?orderby=intensity&start=0',
1412+ mock_io.last_request.url);
1413+ },
1414+
1415+ test_first_batch_uses_cache: function() {
1416+ /* first_batch will use the cached value instead of making a
1417+ * second AJAX request. */
1418+ var navigator = this.get_intensity_listing();
1419+ Y.Assert.areEqual(1, navigator.get('io_provider').requests.length);
1420+ navigator.first_batch('intensity');
1421+ Y.Assert.areEqual(1, navigator.get('io_provider').requests.length);
1422+ },
1423+
1424+ test_io_error: function() {
1425+ var overlay_node;
1426+ var navigator = this.get_intensity_listing();
1427+ navigator.first_batch('failure');
1428+ navigator.get('io_provider').failure();
1429+ overlay_node = Y.one('.yui3-lazr-formoverlay-errors');
1430+ Y.Assert.isTrue(Y.Lang.isValue(overlay_node));
1431+ }
1432+ }));
1433+
1434+
1435+ tests.suite.add(new Y.Test.Case({
1436+ name: 'Batch caching',
1437+
1438+ setUp: function() {
1439+ this.target = Y.Node.create('<div></div>').set(
1440+ 'id', 'client-listing');
1441+ Y.one('body').appendChild(this.target);
1442+ },
1443+
1444+ tearDown: function() {
1445+ this.target.remove();
1446+ delete this.target;
1447+ reset_history();
1448+ },
1449+
1450+ test_update_from_new_model_caches: function() {
1451+ /*
1452+ * update_from_new_model caches the settings in the
1453+ * module.batches.
1454+ */
1455+ var lp_cache = {
1456+ context: {
1457+ resource_type_link: 'http://foo_type',
1458+ web_link: 'http://foo/bar'
1459+ },
1460+ mustache_model: {
1461+ foo: 'bar'
1462+ },
1463+ next: null,
1464+ prev: null,
1465+ field_visibility: {},
1466+ field_visibility_defaults: {}
1467+ };
1468+ var template = "<ol>" +
1469+ "{{#item}}<li>{{name}}</li>{{/item}}</ol>";
1470+ var navigator = new TestListingNavigator({
1471+ current_url: window.location,
1472+ cache: lp_cache,
1473+ template: template,
1474+ target: this.target
1475+ });
1476+ var key = module.get_batch_key({
1477+ order_by: "intensity",
1478+ memo: 'memo1',
1479+ forwards: true,
1480+ start: 5,
1481+ target: this.target
1482+ });
1483+ var batch = {
1484+ order_by: 'intensity',
1485+ memo: 'memo1',
1486+ forwards: true,
1487+ start: 5,
1488+ next: null,
1489+ prev: null,
1490+ mustache_model: {
1491+ item: [
1492+ {name: 'first'},
1493+ {name: 'second'}
1494+ ],
1495+ items: ['a', 'b', 'c']
1496+ }};
1497+ var query = navigator.get_batch_query(batch);
1498+ navigator.update_from_new_model(query, true, batch);
1499+ Y.lp.testing.assert.assert_equal_structure(
1500+ batch, navigator.get('batches')[key]);
1501+ },
1502+ /**
1503+ * get_batch_key returns a JSON-serialized list.
1504+ */
1505+ test_get_batch_key: function() {
1506+ var key = module.get_batch_key({
1507+ order_by: 'order_by1',
1508+ memo: 'memo1',
1509+ forwards: true,
1510+ target: this.target,
1511+ start: 5});
1512+ Y.Assert.areSame('["order_by1","memo1",true,5]', key);
1513+ }
1514+ }));
1515+
1516+ tests.suite.add(new Y.Test.Case({
1517+ name: 'get_query',
1518+
1519+ test_get_query: function() {
1520+ // get_query returns the query portion of a URL in structured form.
1521+ var query = module.get_query('http://yahoo.com?me=you&a=b&a=c');
1522+ Y.lp.testing.assert.assert_equal_structure(
1523+ {me: 'you', a: ['b', 'c']}, query);
1524+ }
1525+ }));
1526+
1527+ tests.suite.add(new Y.Test.Case({
1528+ name: 'get_batch_url',
1529+
1530+ setUp: function() {
1531+ this.target = Y.Node.create('<div></div>').set(
1532+ 'id', 'client-listing');
1533+ Y.one('body').appendChild(this.target);
1534+ },
1535+
1536+ tearDown: function() {
1537+ this.target.remove();
1538+ delete this.target;
1539+ reset_history();
1540+ },
1541+
1542+ /**
1543+ * get_batch_query accepts the order_by param.
1544+ */
1545+ test_get_batch_query_orderby: function() {
1546+ var navigator = new TestListingNavigator({
1547+ search_params: {
1548+ param: 1
1549+ },
1550+ target: this.target,
1551+ cache: {next: null, prev: null}
1552+ });
1553+ var query = navigator.get_batch_query({order_by: 'importance'});
1554+ Y.Assert.areSame('importance', query.orderby);
1555+ Y.Assert.areSame(1, query.param);
1556+ },
1557+ /**
1558+ * get_batch_query accepts the memo param.
1559+ */
1560+ test_get_batch_query_memo: function() {
1561+ var navigator = new TestListingNavigator({
1562+ search_params: {
1563+ param: 'foo'
1564+ },
1565+ target: this.target,
1566+ cache: {next: null, prev: null}
1567+ });
1568+ var query = navigator.get_batch_query({memo: 'pi'});
1569+ Y.Assert.areSame('pi', query.memo);
1570+ Y.Assert.areSame('foo', query.param);
1571+ },
1572+ /**
1573+ * When memo is null, query.memo is undefined.
1574+ */
1575+ test_get_batch_null_memo: function() {
1576+ var navigator = new TestListingNavigator({
1577+ search_params: {},
1578+ cache: {next: null, prev: null},
1579+ target: this.target
1580+ });
1581+ var query = navigator.get_batch_query({memo: null});
1582+ Y.Assert.areSame(undefined, query.memo);
1583+ },
1584+ /**
1585+ * If 'forwards' is true, direction does not appear.
1586+ */
1587+ test_get_batch_query_forwards: function() {
1588+ var navigator = new TestListingNavigator({
1589+ search_params: {
1590+ param: 'pi'
1591+ },
1592+ cache: {next: null, prev: null},
1593+ target: this.target
1594+ });
1595+ var query = navigator.get_batch_query({forwards: true});
1596+ Y.Assert.areSame('pi', query.param);
1597+ Y.Assert.areSame(undefined, query.direction);
1598+ },
1599+ /**
1600+ * If 'forwards' is false, direction is set to backwards.
1601+ */
1602+ test_get_batch_query_backwards: function() {
1603+ var navigator = new TestListingNavigator({
1604+ search_params: {
1605+ param: 'pi'
1606+ },
1607+ cache: {next: null, prev: null},
1608+ target: this.target
1609+ });
1610+ var query = navigator.get_batch_query({forwards: false});
1611+ Y.Assert.areSame('pi', query.param);
1612+ Y.Assert.areSame('backwards', query.direction);
1613+ },
1614+ /**
1615+ * If start is provided, it overrides existing values.
1616+ */
1617+ test_get_batch_query_start: function() {
1618+ var navigator = new TestListingNavigator({
1619+ search_params: {},
1620+ cache: {next: null, prev:null},
1621+ target: this.target
1622+ });
1623+ var query = navigator.get_batch_query({});
1624+ Y.Assert.areSame(undefined, query.start);
1625+ query = navigator.get_batch_query({start: 1});
1626+ Y.Assert.areSame(1, query.start);
1627+ query = navigator.get_batch_query({start: null});
1628+ Y.lp.testing.assert.assert_equal_structure({}, query);
1629+ }
1630+ }));
1631+
1632+
1633+ tests.suite.add(new Y.Test.Case({
1634+ name: 'navigation',
1635+
1636+ setUp: function() {
1637+ this.target = Y.Node.create('<div></div>').set(
1638+ 'id', 'client-listing');
1639+ Y.one('body').appendChild(this.target);
1640+ },
1641+
1642+ tearDown: function() {
1643+ this.target.remove();
1644+ delete this.target;
1645+ reset_history();
1646+ },
1647+
1648+ test_model_uses_view_name: function() {
1649+ var navigator = get_navigator('', {target: this.target});
1650+ navigator.get_current_batch().view_name = '+funitems';
1651+ navigator.load_model({});
1652+ Y.Assert.areSame(
1653+ '/bar/+funitems/++model++',
1654+ navigator.get('io_provider').last_request.url);
1655+ },
1656+
1657+ /**
1658+ * last_batch uses memo="", start=navigator.current_batch.last_start,
1659+ * direction=backwards, orderby=navigator.current_batch.order_by.
1660+ */
1661+ test_last_batch: function() {
1662+ var navigator = get_navigator(
1663+ '?memo=pi&direction=backwards&start=57', {target: this.target});
1664+ navigator.last_batch();
1665+ Y.Assert.areSame(
1666+ '/bar/+items/++model++?orderby=foo&memo=&start=23&' +
1667+ 'direction=backwards',
1668+ navigator.get('io_provider').last_request.url);
1669+ },
1670+
1671+ /**
1672+ * first_batch omits memo and direction, start=0,
1673+ * orderby=navigator.current_batch.order_by.
1674+ */
1675+ test_first_batch: function() {
1676+ var navigator = get_navigator(
1677+ '?memo=pi&start=26', {target: this.target});
1678+ navigator.first_batch();
1679+ Y.Assert.areSame(
1680+ '/bar/+items/++model++?orderby=foo&start=0',
1681+ navigator.get('io_provider').last_request.url);
1682+ },
1683+
1684+ /**
1685+ * next_batch uses values from current_batch.next +
1686+ * current_batch.ordering.
1687+ */
1688+ test_next_batch: function() {
1689+ var navigator = get_navigator(
1690+ '?memo=pi&start=26', {target: this.target});
1691+ navigator.next_batch();
1692+ Y.Assert.areSame(
1693+ '/bar/+items/++model++?orderby=foo&memo=467&start=500',
1694+ navigator.get('io_provider').last_request.url);
1695+ },
1696+
1697+ /**
1698+ * Calling next_batch when there is none is a no-op.
1699+ */
1700+ test_next_batch_missing: function() {
1701+ var navigator = get_navigator(
1702+ '?memo=pi&start=26', {no_next: true, target: this.target});
1703+ navigator.next_batch();
1704+ Y.Assert.areSame(
1705+ null, navigator.get('io_provider').last_request);
1706+ },
1707+
1708+ /**
1709+ * prev_batch uses values from current_batch.prev + direction=backwards
1710+ * and ordering=current_batch.ordering.
1711+ */
1712+ test_prev_batch: function() {
1713+ var navigator = get_navigator(
1714+ '?memo=pi&start=26', {target: this.target});
1715+ navigator.prev_batch();
1716+ Y.Assert.areSame(
1717+ '/bar/+items/++model++?orderby=foo&memo=457&start=400&' +
1718+ 'direction=backwards',
1719+ navigator.get('io_provider').last_request.url);
1720+ },
1721+
1722+ /**
1723+ * Calling prev_batch when there is none is a no-op.
1724+ */
1725+ test_prev_batch_missing: function() {
1726+ var navigator = get_navigator(
1727+ '?memo=pi&start=26',
1728+ {no_prev: true, no_next: true, target: this.target});
1729+ navigator.prev_batch();
1730+ Y.Assert.areSame(
1731+ null, navigator.get('io_provider').last_request);
1732+ },
1733+
1734+ /**
1735+ * Verify we get a reasonable default context if there is no context
1736+ * available as is the case with the BugsBugTaskSearchListingView.
1737+ */
1738+ test_default_context: function () {
1739+ var navigator = get_navigator('', {target: this.target});
1740+ // now remove the context
1741+ var batch = navigator.get_current_batch();
1742+ delete batch.context;
1743+
1744+ navigator.get_current_batch().view_name = '+funitems';
1745+ navigator.load_model({});
1746+
1747+ // the start of the url used will be whatever the current
1748+ // location.href is for the window object. We can make sure we did
1749+ // get a nicely generated url though by checking they end built
1750+ // correctly.
1751+ var generated_url = navigator.get('io_provider').last_request.url;
1752+
1753+ Y.Assert.areSame(
1754+ '+funitems/++model++',
1755+ generated_url.substr(generated_url.indexOf('+')));
1756+ }
1757+
1758+ }));
1759+
1760+ tests.suite.add(new Y.Test.Case({
1761+ name: "pre-fetching batches",
1762+ setUp: function() {
1763+ this.target = Y.Node.create('<div></div>').set(
1764+ 'id', 'client-listing');
1765+ Y.one('body').appendChild(this.target);
1766+ },
1767+ tearDown: function() {
1768+ this.target.remove();
1769+ delete this.target;
1770+ reset_history();
1771+ },
1772+ /**
1773+ * get_pre_fetch_configs should return a config for the next batch.
1774+ */
1775+ test_get_pre_fetch_configs: function() {
1776+ var navigator = get_navigator('', {target: this.target});
1777+ var configs = navigator.get_pre_fetch_configs();
1778+ var batch_keys = [];
1779+ Y.each(configs, function(value) {
1780+ batch_keys.push(module.get_batch_key(value));
1781+ });
1782+ Y.Assert.areSame('["foo",467,true,500]', batch_keys[0]);
1783+ Y.Assert.areSame(1, batch_keys.length);
1784+ },
1785+
1786+ /**
1787+ * get_pre_fetch_configs should return an empty list if no next batch.
1788+ */
1789+ test_get_pre_fetch_configs_no_next: function() {
1790+ var navigator = get_navigator(
1791+ '', {no_next: true, target: this.target});
1792+ var configs = navigator.get_pre_fetch_configs();
1793+ var batch_keys = [];
1794+ Y.each(configs, function(value) {
1795+ batch_keys.push(module.get_batch_key(value));
1796+ });
1797+ Y.Assert.areSame(0, batch_keys.length);
1798+ },
1799+
1800+ get_pre_fetch_navigator: function(config) {
1801+ var navigator = get_navigator('', config);
1802+ var batch = navigator.get_current_batch();
1803+ batch.next = {memo: 57, start: 56};
1804+ batch.order_by = '';
1805+ return navigator;
1806+ },
1807+
1808+ /**
1809+ * Calling pre_fetch_batches should produce a request for the next
1810+ * batch.
1811+ */
1812+ test_pre_fetch_batches: function() {
1813+ var navigator = this.get_pre_fetch_navigator({target: this.target});
1814+ var io_provider = navigator.get('io_provider');
1815+ navigator.set('pre_fetch', true);
1816+ Y.Assert.isNull(io_provider.last_request);
1817+ navigator.pre_fetch_batches();
1818+ Y.Assert.areSame(
1819+ io_provider.last_request.url,
1820+ '/bar/+items/++model++?orderby=&memo=57&start=56');
1821+ },
1822+
1823+ /**
1824+ * Calling pre_fetch_batches should not produce a request for the next
1825+ * batch if Navigator.get('pre_fetch') is false.
1826+ */
1827+ test_pre_fetch_disabled: function() {
1828+ var last_url;
1829+ var navigator = this.get_pre_fetch_navigator({target: this.target});
1830+ navigator.pre_fetch_batches();
1831+ Y.Assert.areSame(null, navigator.get('io_provider').last_request);
1832+ },
1833+
1834+ /**
1835+ * Initialization does a pre-fetch.
1836+ */
1837+ test_pre_fetch_on_init: function() {
1838+ var navigator = get_navigator(
1839+ '', {pre_fetch: true, target:this.target});
1840+ var last_url = navigator.get('io_provider').last_request.url;
1841+ Y.Assert.areSame(
1842+ last_url,
1843+ '/bar/+items/++model++?orderby=foo&memo=467&start=500');
1844+ },
1845+ /**
1846+ * update_from_new_model does a pre-fetch.
1847+ */
1848+ test_pre_fetch_on_update_from_new_model: function() {
1849+ var navigator = get_navigator('', {target: this.target});
1850+ var io_provider = navigator.get('io_provider');
1851+ var lp_client = new Y.lp.client.Launchpad();
1852+ var batch = lp_client.wrap_resource(null, {
1853+ context: {
1854+ resource_type_link: 'http://foo_type',
1855+ web_link: 'http://foo/bar'
1856+ },
1857+ view_name: '+items',
1858+ order_by: 'baz',
1859+ memo: 'memo1',
1860+ next: {
1861+ memo: "pi",
1862+ start: 314
1863+ },
1864+ prev: null,
1865+ forwards: true,
1866+ start: 5,
1867+ mustache_model: {
1868+ item: [
1869+ {name: 'first'},
1870+ {name: 'second'}
1871+ ],
1872+ items: ['a', 'b', 'c']
1873+ }});
1874+ Y.Assert.isNull(io_provider.last_request);
1875+ navigator.set('pre_fetch', true);
1876+ navigator.update_from_new_model({}, false, batch);
1877+ Y.Assert.areSame(
1878+ io_provider.last_request.url,
1879+ '/bar/+items/++model++?orderby=baz&memo=pi&start=314');
1880+ }
1881+ }));
1882+
1883+ tests.suite.add(new Y.Test.Case({
1884+ name: "Test indicators",
1885+
1886+ tearDown: function () {
1887+ reset_history();
1888+ },
1889+
1890+ /**
1891+ * Update starts showing the pending indicator
1892+ */
1893+ test_show_on_update: function() {
1894+ var navigator = get_navigator();
1895+ navigator.update({});
1896+ Y.Assert.isTrue(navigator.indicator.get('visible'));
1897+ },
1898+ /**
1899+ * A fetch-only update starts ignores the pending indicator
1900+ */
1901+ test_ignore_on_fetch_only_update: function() {
1902+ var navigator = get_navigator();
1903+ navigator.update({fetch_only: true});
1904+ Y.Assert.isFalse(navigator.indicator.get('visible'));
1905+ },
1906+ /**
1907+ * A successful IO operation clears the pending indicator.
1908+ */
1909+ test_hide_on_success: function() {
1910+ var navigator = get_navigator();
1911+ navigator.update({});
1912+ navigator.get('io_provider').last_request.successJSON({
1913+ mustache_model: {items: []},
1914+ next: null,
1915+ prev: null
1916+ });
1917+ Y.Assert.isFalse(navigator.indicator.get('visible'));
1918+ },
1919+ /**
1920+ * A successful fetch-only IO operation ignores the pending indicator.
1921+ */
1922+ test_no_hide_on_fetch_only_success: function() {
1923+ var navigator = get_navigator();
1924+ navigator.indicator.setBusy();
1925+ navigator.update({fetch_only: true});
1926+ navigator.get('io_provider').last_request.successJSON({
1927+ mustache_model: {items: []},
1928+ next: null,
1929+ prev: null
1930+ });
1931+ Y.Assert.isTrue(navigator.indicator.get('visible'));
1932+ },
1933+ /**
1934+ * A failed IO operation hides the pending indicator.
1935+ */
1936+ test_hide_on_failure: function() {
1937+ var navigator = get_navigator();
1938+ navigator.update({});
1939+ navigator.get('io_provider').failure();
1940+ Y.Assert.isFalse(navigator.indicator.get('visible'));
1941+ },
1942+ /**
1943+ * A failed fetch-only IO operation does not hide the pending indicator.
1944+ */
1945+ test_no_hide_on_fetch_only_failure: function() {
1946+ var navigator = get_navigator();
1947+ navigator.indicator.setBusy();
1948+ navigator.update({fetch_only: true});
1949+ navigator.get('io_provider').failure();
1950+ Y.Assert.isTrue(navigator.indicator.get('visible'));
1951+ }
1952+ }));
1953+
1954+ tests.suite.add(new Y.Test.Case({
1955+ name: "Find batch aliases",
1956+
1957+ test_get_batch_key_list: function() {
1958+ var keys = module.get_batch_key_list({
1959+ prev: null,
1960+ next:null,
1961+ memo: 'pi',
1962+ start: -1,
1963+ forwards: true,
1964+ order_by: 'ordering'
1965+ });
1966+ Y.ArrayAssert.itemsAreSame(
1967+ [null, '["ordering","pi",true,-1]', null], keys);
1968+ keys = module.get_batch_key_list({
1969+ prev: {
1970+ memo: "pi",
1971+ start: -2
1972+ },
1973+ next: {
1974+ memo: "e",
1975+ start: 0
1976+ },
1977+ memo: 'pi',
1978+ start: -1,
1979+ forwards: true,
1980+ order_by: 'ordering'
1981+ });
1982+ Y.ArrayAssert.itemsAreSame([
1983+ '["ordering","pi",false,-2]',
1984+ '["ordering","pi",true,-1]',
1985+ '["ordering","e",true,0]'], keys);
1986+ },
1987+
1988+ /* Detect batch aliases for forward movement (next). */
1989+ test_find_batch_alias_moving_forward: function() {
1990+ var prev_batch = ['a', 'b', 'c'];
1991+ var next_batch = ["b'", 'c', 'd'];
1992+ var result = module.find_batch_alias(prev_batch, next_batch);
1993+ Y.Assert.areSame(result[0], 'b');
1994+ Y.Assert.areSame(result[1], "b'");
1995+ result = module.find_batch_alias(next_batch, prev_batch);
1996+ Y.Assert.areSame(result[0], 'b');
1997+ Y.Assert.areSame(result[1], "b'");
1998+ },
1999+
2000+ /* Detect batch aliases for backward movement (prev). */
2001+ test_find_batch_alias_moving_backward: function() {
2002+ var prev_batch = ['a', 'b', 'c'];
2003+ var next_batch = ['b', "c'", 'd'];
2004+ var result = module.find_batch_alias(prev_batch, next_batch);
2005+ Y.Assert.areSame(result[0], 'c');
2006+ Y.Assert.areSame(result[1], "c'");
2007+ result = module.find_batch_alias(next_batch, prev_batch);
2008+ Y.Assert.areSame(result[0], 'c');
2009+ Y.Assert.areSame(result[1], "c'");
2010+ },
2011+
2012+ /* Do not detect aliases if batches are unrelated */
2013+ test_find_batch_alias_unrelated: function() {
2014+ var prev_batch = ['a', 'b', 'c'];
2015+ var next_batch = ['d', 'e', 'f'];
2016+ var result = module.find_batch_alias(next_batch, prev_batch);
2017+ Y.Assert.isNull(result);
2018+ },
2019+
2020+ /**
2021+ * When dealias_batches is called on the next batch, the current batch
2022+ * is re-added to the batches mapping, under its alias from the next
2023+ * batch.
2024+ */
2025+ test_dealias_batches_next: function() {
2026+ var navigator = get_navigator();
2027+ var next_batch = {
2028+ memo: 467,
2029+ start: 500,
2030+ order_by: 'foo',
2031+ forwards: true,
2032+ prev: {
2033+ memo: 467,
2034+ start: 450
2035+ },
2036+ next: null
2037+ };
2038+ var prev_batch_config = module.prev_batch_config(next_batch);
2039+ var prev_batch_key = module.get_batch_key(
2040+ prev_batch_config);
2041+ navigator.dealias_batches(next_batch);
2042+ Y.Assert.areSame(
2043+ navigator.get('batches')[prev_batch_key],
2044+ navigator.get_current_batch()
2045+ );
2046+ Y.Assert.areNotSame(
2047+ prev_batch_key, navigator.get('model').get_batch_key());
2048+ },
2049+ /**
2050+ * When dealias_batches is called on the previous batch, the current
2051+ * batch is re-added to the batches mapping, under its alias from the
2052+ * previous batch.
2053+ */
2054+ test_dealias_batches_prev: function() {
2055+ var navigator = get_navigator();
2056+ var prev_batch = {
2057+ memo: 457,
2058+ start: 400,
2059+ order_by: 'foo',
2060+ forwards: false,
2061+ next: {
2062+ memo: 467,
2063+ start: 450
2064+ },
2065+ prev: null
2066+ };
2067+ var next_batch_config = module.next_batch_config(prev_batch);
2068+ var next_batch_key = module.get_batch_key(
2069+ next_batch_config);
2070+ navigator.dealias_batches(prev_batch);
2071+ Y.Assert.areSame(
2072+ navigator.get('batches')[next_batch_key],
2073+ navigator.get_current_batch()
2074+ );
2075+ Y.Assert.areNotSame(
2076+ next_batch_key, navigator.get('model').get_batch_key());
2077+ }
2078+ }));
2079+
2080+ tests.suite.add(new Y.Test.Case({
2081+ name: 'browser history',
2082+
2083+ setUp: function() {
2084+ this.target = Y.Node.create('<div></div>').set(
2085+ 'id', 'client-listing');
2086+ Y.one('body').appendChild(this.target);
2087+ },
2088+
2089+ tearDown: function() {
2090+ this.target.remove();
2091+ delete this.target;
2092+ reset_history();
2093+ },
2094+
2095+ /**
2096+ * Update from cache generates a change event for the specified batch.
2097+ */
2098+ test_update_from_cache_generates_event: function() {
2099+ var navigator = get_navigator('', {target: this.target});
2100+ var e = null;
2101+ navigator.get('model').get('history').on('change',
2102+ function(inner_e) {
2103+ e = inner_e;
2104+ });
2105+ navigator.get('batches')['some-batch-key'] = {
2106+ mustache_model: {
2107+ items: []
2108+ },
2109+ next: null,
2110+ prev: null
2111+ };
2112+ navigator.update_from_cache({foo: 'bar'}, 'some-batch-key');
2113+ Y.Assert.areEqual('some-batch-key', e.newVal.batch_key);
2114+ Y.Assert.areEqual('?foo=bar', e._options.url);
2115+ },
2116+
2117+ /**
2118+ * When a change event is emitted, the relevant batch becomes the
2119+ * current batch and is rendered.
2120+ */
2121+ test_change_event_renders_cache: function() {
2122+ var navigator = get_navigator('', {target: this.target});
2123+ var batch = {
2124+ mustache_model: {
2125+ items: [],
2126+ foo: 'bar'
2127+ },
2128+ next: null,
2129+ prev: null
2130+ };
2131+ navigator.set('template', '{{foo}}');
2132+ navigator.get('batches')['some-batch-key'] = batch;
2133+ navigator.get('model').get('history').addValue(
2134+ 'batch_key', 'some-batch-key');
2135+ Y.Assert.areEqual(batch, navigator.get_current_batch());
2136+ Y.Assert.areEqual('bar', navigator.get('target').getContent());
2137+ }
2138+ }));
2139+
2140+}, '0.1', {
2141+ 'requires': ['base', 'test', 'console', 'lp.app.listing_navigator',
2142+ 'lp.testing.mockio', 'lp.testing.assert', 'history']
2143 });