Merge lp:~rharding/juju-gui/routing-issues into lp:juju-gui/experimental

Proposed by Richard Harding
Status: Merged
Merged at revision: 905
Proposed branch: lp:~rharding/juju-gui/routing-issues
Merge into: lp:juju-gui/experimental
Diff against target: 350 lines (+121/-41)
8 files modified
app/app.js (+5/-0)
app/assets/javascripts/ns-routing-app-extension.js (+19/-16)
app/subapps/browser/browser.js (+21/-17)
app/subapps/browser/views/charm.js (+2/-1)
app/subapps/browser/views/view.js (+9/-0)
app/widgets/charm-search.js (+1/-1)
test/test_browser_app.js (+44/-0)
test/test_routing.js (+20/-6)
To merge this branch: bzr merge lp:~rharding/juju-gui/routing-issues
Reviewer Review Type Date Requested Status
Juju GUI Hackers Pending
Review via email: mp+177401@code.launchpad.net

Description of the change

Fixes routing issues around search and charm tabs

- See bug #1205468
- See bug #1200743

Summary
-------
There are two problems. The first is that when doing some view state changes
we need to also clear the hash so that it doesn't carry around. We also clear
the charm id when doing searches in fullscreen mode. This helps the browser
app ack that there were changes in state and show the correct view.

The second issue was that clicking a tab on the fullscreen charm details after
a search causes two #bws-readme (for instance) to be added. One is before the
query string and one is after the querysting. This is caused by our double
dispatch and the fact that our routing code builds urls with query strings
after the hash of the url. This is not proper. The Y.App adds it to the end of
the url. In this way we ended up with it in both places.

Our routing code would then assume the whole #bws-readme?text=apache2 was the
hash of the url and that there was no query string. All kinds of trouble came
out of this.

Tests are added to verify the changes work as expected given our sample bad
urls.

QA
---
To QA simpler go through the steps in the two bugs and it should work as
expected. Other QA would be to verify that other usage is not adversely
effected by moving the hash to be at the end of the url while the querystring
is immediately after the path.

https://codereview.appspot.com/12036043/

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) wrote :

Reviewers: mp+177401_code.launchpad.net,

Message:
Please take a look.

Description:
Fixes routing issues around search and charm tabs

- See bug #1205468
- See bug #1200743

Summary
-------
There are two problems. The first is that when doing some view state
changes
we need to also clear the hash so that it doesn't carry around. We also
clear
the charm id when doing searches in fullscreen mode. This helps the
browser
app ack that there were changes in state and show the correct view.

The second issue was that clicking a tab on the fullscreen charm details
after
a search causes two #bws-readme (for instance) to be added. One is
before the
query string and one is after the querysting. This is caused by our
double
dispatch and the fact that our routing code builds urls with query
strings
after the hash of the url. This is not proper. The Y.App adds it to the
end of
the url. In this way we ended up with it in both places.

Our routing code would then assume the whole #bws-readme?text=apache2
was the
hash of the url and that there was no query string. All kinds of trouble
came
out of this.

Tests are added to verify the changes work as expected given our sample
bad
urls.

QA
---
To QA simpler go through the steps in the two bugs and it should work as
expected. Other QA would be to verify that other usage is not adversely
effected by moving the hash to be at the end of the url while the
querystring
is immediately after the path.

https://code.launchpad.net/~rharding/juju-gui/routing-issues/+merge/177401

(do not edit description out of merge proposal)

Please review this at https://codereview.appspot.com/12036043/

Affected files:
   A [revision details]
   M app/assets/javascripts/ns-routing-app-extension.js
   M app/subapps/browser/browser.js
   M app/subapps/browser/views/view.js
   M app/widgets/charm-search.js
   M test/test_browser_app.js
   M test/test_routing.js

Revision history for this message
j.c.sackett (jcsackett) wrote :

LGTM, thanks.

https://codereview.appspot.com/12036043/diff/1/app/assets/javascripts/ns-routing-app-extension.js
File app/assets/javascripts/ns-routing-app-extension.js (right):

https://codereview.appspot.com/12036043/diff/1/app/assets/javascripts/ns-routing-app-extension.js#newcode104
app/assets/javascripts/ns-routing-app-extension.js:104: var hashIndex =
qs.indexOf('#');
Thanks for using hashIndex instead of hashIdx. Deryck is, I'm sure,
proud.

https://codereview.appspot.com/12036043/

Revision history for this message
Jeff Pihach (hatch) wrote :

QA No Go

Go to http://localhost:8888/fullscreen/precise/cinder-8/
Click, Readme, Interfaces
Click JuJu (top left)
Click Browse
Url will now be http://localhost:8888/fullscree/##bws-interfaces

Can't repro on jujucharms.com because I can't get past the readme step,
so I'm not sure if this was pre-existing or a side effect of this
branch.

https://codereview.appspot.com/12036043/

lp:~rharding/juju-gui/routing-issues updated
906. By Richard Harding

Make sure to reset the subapp state when showing root view

907. By Richard Harding

Make that safe

908. By Richard Harding

lint

909. By Richard Harding

The # is part of the url so it's part of the hash

Revision history for this message
Richard Harding (rharding) wrote :
lp:~rharding/juju-gui/routing-issues updated
910. By Richard Harding

Update the back to also clear the hash

911. By Richard Harding

Remove the unneeded code

Revision history for this message
Richard Harding (rharding) wrote :
Revision history for this message
Jeff Pihach (hatch) wrote :

LGTM Thanks for getting these blockers/criticals fixed!
QA OK

This branch exposed an issue where the router is not dispatching when
the hash changes. https://bugs.launchpad.net/juju-gui/+bug/1206239

https://codereview.appspot.com/12036043/

Revision history for this message
Richard Harding (rharding) wrote :

*** Submitted:

Fixes routing issues around search and charm tabs

- See bug #1205468
- See bug #1200743

Summary
-------
There are two problems. The first is that when doing some view state
changes
we need to also clear the hash so that it doesn't carry around. We also
clear
the charm id when doing searches in fullscreen mode. This helps the
browser
app ack that there were changes in state and show the correct view.

The second issue was that clicking a tab on the fullscreen charm details
after
a search causes two #bws-readme (for instance) to be added. One is
before the
query string and one is after the querysting. This is caused by our
double
dispatch and the fact that our routing code builds urls with query
strings
after the hash of the url. This is not proper. The Y.App adds it to the
end of
the url. In this way we ended up with it in both places.

Our routing code would then assume the whole #bws-readme?text=apache2
was the
hash of the url and that there was no query string. All kinds of trouble
came
out of this.

Tests are added to verify the changes work as expected given our sample
bad
urls.

QA
---
To QA simpler go through the steps in the two bugs and it should work as
expected. Other QA would be to verify that other usage is not adversely
effected by moving the hash to be at the end of the url while the
querystring
is immediately after the path.

R=j.c.sackett, jeff.pihach
CC=
https://codereview.appspot.com/12036043

https://codereview.appspot.com/12036043/

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'app/app.js'
2--- app/app.js 2013-07-25 16:41:41 +0000
3+++ app/app.js 2013-07-29 18:11:23 +0000
4@@ -1144,6 +1144,11 @@
5 */
6 showRootView: function() {
7 this._navigate('/', { overrideAllNamespaces: true });
8+ // Reset the view state of the subapps.
9+ var subapps = this.get('subApps');
10+ if (subapps.charmstore) {
11+ subapps.charmstore.initState();
12+ }
13 },
14
15 /**
16
17=== modified file 'app/assets/javascripts/ns-routing-app-extension.js'
18--- app/assets/javascripts/ns-routing-app-extension.js 2013-06-11 17:55:57 +0000
19+++ app/assets/javascripts/ns-routing-app-extension.js 2013-07-29 18:11:23 +0000
20@@ -97,7 +97,16 @@
21 * @method getQS
22 **/
23 getQS: function(url) {
24- return url.split('?')[1];
25+ var qs = url.split('?')[1];
26+ if (qs) {
27+ // The query string picks up the hash due to the simple nature of this
28+ // generation. Remove it.
29+ var hashIndex = qs.indexOf('#');
30+ if (hashIndex !== -1) {
31+ qs = qs.substr(0, hashIndex);
32+ }
33+ }
34+ return qs;
35 },
36
37 /**
38@@ -131,8 +140,8 @@
39 href: url
40 };
41 var origin = this._regexUrlOrigin.exec(url);
42+ result.hash = this.getHash(url);
43 result.search = this.getQS(url);
44- result.hash = this.getHash(url);
45
46 if (origin) {
47 // Take the match.
48@@ -144,11 +153,11 @@
49 }
50
51 if (result.search) {
52- result.pathname = result.pathname.substr(
53- 0,
54- (result.pathname.length - result.search.length) - 1);
55- }
56-
57+ result.pathname = result.pathname.replace('?' + result.search, '');
58+ }
59+ if (result.hash) {
60+ result.pathname = result.pathname.replace('#' + result.hash, '');
61+ }
62 return result;
63 },
64
65@@ -173,10 +182,6 @@
66 result.hash = parts.hash;
67 result.search = parts.search;
68
69- // If there was a hash we need to split it off url
70- if (result.hash) {
71- url = url.slice(0, url.indexOf('#'));
72- }
73 parts = url.split(this._fragment);
74 // Example output
75 // '/foo/bar'.split(this._fragment)
76@@ -268,14 +273,12 @@
77 });
78
79 url = slash(url);
80-
81+ if (components.search) {
82+ url += '?' + components.search;
83+ }
84 if (components.hash) {
85 url += '#' + components.hash;
86 }
87-
88- if (components.search) {
89- url += '?' + components.search;
90- }
91 return url;
92 },
93
94
95=== modified file 'app/subapps/browser/browser.js'
96--- app/subapps/browser/browser.js 2013-07-19 18:56:15 +0000
97+++ app/subapps/browser/browser.js 2013-07-29 18:11:23 +0000
98@@ -120,6 +120,10 @@
99 qs: this._viewState.querystring
100 });
101 }
102+
103+ if (this._viewState.hash) {
104+ url = url + this._viewState.hash;
105+ }
106 return url;
107 },
108
109@@ -144,22 +148,6 @@
110 },
111
112 /**
113- Create an initial subapp state for later url generation.
114-
115- @method _initState
116- */
117- _initState: function() {
118- this._oldState = {
119- charmID: null,
120- hash: null,
121- querystring: null,
122- search: null,
123- viewmode: null
124- };
125- this._viewState = Y.merge(this._oldState, {});
126- },
127-
128- /**
129 Registers Handlebars helpers that need access to subapp data like the
130 store instance.
131
132@@ -430,7 +418,7 @@
133 search: null,
134 interesting: null
135 };
136- this._initState();
137+ this.initState();
138 this._filter = new models.browser.Filter();
139
140 this._registerSubappHelpers();
141@@ -449,6 +437,22 @@
142 },
143
144 /**
145+ Create an initial subapp state for later url generation.
146+
147+ @method initState
148+ */
149+ initState: function() {
150+ this._oldState = {
151+ charmID: null,
152+ querystring: null,
153+ hash: null,
154+ search: null,
155+ viewmode: null
156+ };
157+ this._viewState = Y.merge(this._oldState, {});
158+ },
159+
160+ /**
161 Render the charm details view
162
163 @method renderCharmDetails
164
165=== modified file 'app/subapps/browser/views/charm.js'
166--- app/subapps/browser/views/charm.js 2013-07-24 17:10:03 +0000
167+++ app/subapps/browser/views/charm.js 2013-07-29 18:11:23 +0000
168@@ -269,7 +269,8 @@
169 ev.halt();
170 this.fire('viewNavigate', {
171 change: {
172- charmID: null
173+ charmID: null,
174+ hash: null
175 }
176 });
177 },
178
179=== modified file 'app/subapps/browser/views/view.js'
180--- app/subapps/browser/views/view.js 2013-07-24 17:17:25 +0000
181+++ app/subapps/browser/views/view.js 2013-07-29 18:11:23 +0000
182@@ -149,6 +149,7 @@
183 _goHome: function(ev) {
184 var change = {
185 charmID: undefined,
186+ hash: undefined,
187 search: false,
188 filter: {
189 clear: true
190@@ -204,6 +205,14 @@
191 }
192 };
193
194+ // If we're in fullscreen and you did a search we clear the charmID to
195+ // help make sure that we show you the search results you just asked for
196+ // properly.
197+ if (this.isFullscreen()) {
198+ change.charmID = undefined;
199+ change.hash = undefined;
200+ }
201+
202 // Perhaps there's more to this change than just a search change. This
203 // might come from places, such as autocomplete, which are a search
204 // change, but also want to select a charm id as well.
205
206=== modified file 'app/widgets/charm-search.js'
207--- app/widgets/charm-search.js 2013-07-29 16:01:59 +0000
208+++ app/widgets/charm-search.js 2013-07-29 18:11:23 +0000
209@@ -271,7 +271,7 @@
210 );
211
212 // Make sure the UI around the autocomplete search input is setup.
213- if (window.flags.ac) {
214+ if (window.flags && window.flags.ac) {
215 this._setupAutocomplete();
216
217 // Override a couple of autocomplete events to help perform our
218
219=== modified file 'test/test_browser_app.js'
220--- test/test_browser_app.js 2013-07-24 15:23:57 +0000
221+++ test/test_browser_app.js 2013-07-29 18:11:23 +0000
222@@ -157,6 +157,7 @@
223 view.on('viewNavigate', function(ev) {
224 assert.equal(ev.change.search, false);
225 assert.equal(ev.change.filter.clear, true);
226+ assert.equal(ev.change.hash, undefined);
227 done();
228 });
229
230@@ -166,6 +167,26 @@
231 });
232 });
233
234+ it('resets charmid and hash on search', function(done) {
235+ var container = Y.one('#subapp-browser'),
236+ fakeStore = new Y.juju.Charmworld2({});
237+ view = new FullScreen({
238+ charmID: 'precise/jenkins-13'
239+ });
240+
241+ view.on('viewNavigate', function(ev) {
242+ assert.equal(ev.change.filter.text, 'test search');
243+ assert.equal(ev.change.charmID, undefined);
244+ assert.equal(ev.change.hash, undefined);
245+ done();
246+ });
247+
248+ view.render(container);
249+ view._searchChanged({
250+ newVal: 'test search'
251+ });
252+ });
253+
254 });
255 })();
256
257@@ -517,6 +538,28 @@
258 filter: undefined
259 });
260 assert.equal(url, 'fullscreen');
261+
262+ // It takes into account hash.
263+ url = app._getStateUrl({
264+ viewmode: 'fullscreen',
265+ charmID: undefined,
266+ hash: '#bws-readme',
267+ search: undefined,
268+ filter: undefined
269+ });
270+ assert.equal(url, 'fullscreen#bws-readme');
271+
272+ // It always puts the hash last.
273+ url = app._getStateUrl({
274+ viewmode: 'fullscreen',
275+ charmID: 'precise/jenkins-2',
276+ hash: '#bws-readme',
277+ search: true,
278+ querystring: 'text=jenkins'
279+ });
280+ assert.equal(
281+ url,
282+ 'fullscreen/search/precise/jenkins-2?text=jenkins#bws-readme');
283 });
284
285 it('/charm/id router ignores other urls', function() {
286@@ -1147,5 +1190,6 @@
287 });
288 assert.equal(url, '/search?text=mysql');
289 });
290+
291 });
292 })();
293
294=== modified file 'test/test_routing.js'
295--- test/test_routing.js 2013-06-19 15:44:25 +0000
296+++ test/test_routing.js 2013-07-29 18:11:23 +0000
297@@ -137,10 +137,8 @@
298 // argument to combine, values from the original url
299 // are discarded.
300 url = router.combine('/foo/bar?world=gone+away',
301- '/:inspector:/foo/#hello?world=beater');
302- url.should.equal('/foo/bar/:inspector:/foo/#hello?world=beater');
303-
304-
305+ '/:inspector:/foo/?world=beater#hello');
306+ url.should.equal('/foo/bar/:inspector:/foo/?world=beater#hello');
307 });
308
309 it('should be able to split qualified urls', function() {
310@@ -200,7 +198,6 @@
311 assert.equal(u, '/:gui:/services/mysql/:gui:/services/mediawiki/');
312 });
313
314-
315 it('should allow combining routes in a given namespace', function() {
316 var router = juju.Router('charmstore');
317 var match = router.parse('/');
318@@ -225,7 +222,6 @@
319 var u = router.url(match);
320 assert.equal(u, '/:gui:/services/mysql/:gui:/services/mediawiki/');
321
322-
323 // Combine works as well (note the flag, like with parse this can be an
324 // object).
325 u = router.combine('/:gui:/services/mysql/', ':gui:/services/mediawiki/',
326@@ -234,6 +230,24 @@
327
328 });
329
330+ it('should properly parse the QS of a given url', function() {
331+ var router = juju.Router('charmstore');
332+ var qs = router.getQS(
333+ '/fullscreen/search/precise/jenkins-5/?text=jenkins#bws-readme');
334+ assert.equal(qs, 'text=jenkins');
335+ });
336+
337+ it('should split a url into components properly', function() {
338+ var router = juju.Router('charmstore');
339+ var components = router.split(
340+ '/fullscreen/search/precise/jenkins-5/?text=jenkins#bws-readme');
341+ assert.equal(
342+ components.pathname,
343+ '/fullscreen/search/precise/jenkins-5/');
344+ assert.equal(components.hash, 'bws-readme');
345+ assert.equal(components.search, 'text=jenkins');
346+ });
347+
348 });
349
350 describe('Juju Gui Routing', function() {

Subscribers

People subscribed via source and target branches