Merge lp:~sinzui/launchpad/launchpad-header-0 into lp:launchpad
- launchpad-header-0
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Paul Hummer |
Approved revision: | no longer in the source branch. |
Merged at revision: | 11229 |
Proposed branch: | lp:~sinzui/launchpad/launchpad-header-0 |
Merge into: | lp:launchpad |
Diff against target: |
1273 lines (+397/-542) 13 files modified
lib/canonical/launchpad/doc/hierarchical-menu.txt (+20/-14) lib/canonical/launchpad/icing/style-3-0.css.in (+28/-13) lib/lp/app/browser/tests/base-layout.txt (+1/-280) lib/lp/app/browser/tests/test_base_layout.py (+163/-0) lib/lp/app/templates/base-layout-macros.pt (+1/-1) lib/lp/app/templates/base-layout.pt (+18/-13) lib/lp/app/templates/launchpad-hierarchy.pt (+4/-4) lib/lp/blueprints/stories/sprints/05-sprint-creation.txt (+99/-117) lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt (+4/-4) lib/lp/bugs/stories/bugs/xx-malone-homepage.txt (+10/-18) lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt (+4/-11) lib/lp/bugs/stories/cve/cve-pages.txt (+40/-65) lib/lp/bugs/templates/bugtask-index.pt (+5/-2) |
To merge this branch: | bzr merge lp:~sinzui/launchpad/launchpad-header-0 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer (community) | ui | Approve | |
Brad Crittenden (community) | code | Approve | |
Robert Collins (community) | Abstain | ||
Review via email: mp+30570@code.launchpad.net |
Commit message
Description of the change
This is my branch to fix the launchpad header so for the title edit widget.
This branch fixes a number of visual issues in the header, but in doing so,
it call attention to the registered slot. Michael and I decided that the
registration slot is out of scope for this branch and the probable solution
is to move it, which requires a reexamination of all its uses.
lp:~sinzui/launchpad/owner-driver-supervisor
Diff size: 643
Launchpad bug: https:/
Test command: ./bin/test -vv -t test_base_layout -t base-layout
Pre-
Target release: 10.08
Fix the launchpad header so for the title edit widget
-------
The floated left and right instructions in the launchpad header break
the layout of the title edit widget; The widget disassembles itself trying
to avoid the floated content.
Rules
-----
* Consider using inline-block CSS and relative positioning to fix the
issue.
Michael suggested:
* ADDENDUM: Fix the leading white space before the watermark.
* ADDENDUM: Align the apps with the bottom of the icon so that there is
no extra white space between the context, apps, heading and bead crumbs.
Curtis:
* Michael's suggestions are very good. They make other problems in the
header more obvious. The registration slot is taking advantage of the
unwanted white space. It will be obvious that it was an after thought
when it creates an extra line. If it is clear that the apps are the
second part of the header, it may be clear to some users they appear
in the wrong place in the bread crumbs.
* ADDENDUM: Move the check for bread crumbs to the list element so that
no part of the block is rendered when there is no bread crumbs.
QA
--
* Visit https:/
* Choose the edit the title
* Verify the widget stays together and remains adjacent to the logo.
* Visit https:/
* Verify the context, apps, title, and bread crumbs are together...
the reported by line does not interrupt.
* Visit https:/
* Verify the context, apps, title, and bread crumbs are together
Lint
----
Linting changed files:
lib/canonical
lib/lp/
lib/lp/
lib/lp/
lib/lp/
lib/lp/
Test
----
* lib/lp/
* Removed big chunks of a very brittle test that broke from the minor
markup changes. The diff shows the tests for common parts were
different, but they where/should be the same. I decided to move
the testing to a unittest. Note that the diff /before/ my decision to
to replace the tests was long and hard to read.
* lib/lp/
* Moved the tests for the parts that base-layout provides to unittests.
I should now be obvious when parts each layout provides.
Implementation
--------------
* lib/canonical/
* Added .flowed-block which allows us to layout horizontal chunks
of block content like we image it in our heads.
* Replaced #locationbar with.login-logout which accidentally solved
Michaels concern about the leading space :)
* Updated watermark-
content so that the title edit widget does not need to avoid other
parts of the header.
* Removed the excess whitespace from .registering and #logincontrol
* lib/lp/
* Added an id to make the content easy to test.
* lib/lp/
* Wrapped the problem code in flowed-block divs.
* Moved the registration slot below the logo so that there was no
floated content near the heading that the title edit widget wraps.
* Added ids to make content testable.
* lib/lp/
* Moved the bread crumb test condition to the block element...the page
was always rendering space for the bread crumbs.
Curtis Hovey (sinzui) wrote : | # |
These are the three headers Michael and I looked at. They represent the best and worst of how the header works. In all cases the space before the logo and the space without bread crumbs is fixed \o/. The title edit widget hold together since it does not need to dodge the left and right content. By aligning the primary context title and apps with the bottom of the logo, view.png shows what we wanted users to see. But the two context images have a registration line that inserts a blank line. Registration always did this, it was not as obvious.
http://
http://
http://
http://
I ponder moving the registation before or after the bread crumbs. We want the registration to be adjacent to the title of the context. This was/is not possible for primary contexts. We rely on the sparceness of the header to imply that the context is the only thing the registration text can pertain to. Placing the registration before the breadcrumbs, a, returns to the orginal design for 3.0, and b, puts the information closer to the primary context logo, and the secondary context title when it exists.
/me hacks to make screen caps.
Curtis Hovey (sinzui) wrote : | # |
I moved the registration slot into context-location area, it it between the context title and bread crumbs. I made the text the same size as the bread crumbs.
http://
http://
http://
http://
These changes did not inflate the diff. I updated a test to verify the content moved.
The bug page did look bad, and that is because it violates the registration slot rules...the
data is immutable; it is not a status. I moved the heat inline and updated the one affected
test. Another was robust. I removed the redundant bug number too (no tests broke).
./bin/test -vvc -t bug-heat-view -t xx-bug-
Brad Crittenden (bac) wrote : | # |
Robert, if you claim a review from the team and then abstain the review disappears from the view of other reviewers. To remedy, select 'Request another review' and assign it to the Launchpad Reviewers team. (Sorry for cluttering the MP with these instructions but it makes sense since it provides context.)
Brad Crittenden (bac) wrote : | # |
Curtis I think these changes really clean up the top of the page. The code looks good. Another round of UI review will be beneficial.
This docstring needs a final period:
"""Test for the ILaunchpadRoot permission"""
Curtis Hovey (sinzui) wrote : | # |
Hi Paul.
In summary. The start of the review says I did not want to fix the registration slot issue, and you can see pictures of how fixing the white space made the problem very obvious. The more recent comments with the better-* set of images shows I decided to move the registration slot anyway. I think this is an improvement, though I would not ever claim it is perfect.
Paul Hummer (rockstar) wrote : | # |
This looks REALLY good. Thanks for dealing with it. The header can sometimes be an eyesore, so anything we can do to make it better is something I'm onboard with.
Preview Diff
1 | === modified file 'lib/canonical/launchpad/doc/hierarchical-menu.txt' |
2 | --- lib/canonical/launchpad/doc/hierarchical-menu.txt 2010-01-22 17:28:32 +0000 |
3 | +++ lib/canonical/launchpad/doc/hierarchical-menu.txt 2010-07-26 13:34:58 +0000 |
4 | @@ -1,15 +1,16 @@ |
5 | -= Hierarchical menus = |
6 | +Hierarchical menus |
7 | +================== |
8 | |
9 | The location bar aids users in navigating the depths of Launchpad. It |
10 | is built from a list of Breadcrumb objects collected during Zope's |
11 | object-traversal step. |
12 | |
13 | -== A simple object hierarchy == |
14 | +A simple object hierarchy |
15 | +------------------------- |
16 | |
17 | First, we need a hierarchy of objects to build upon: |
18 | |
19 | - >>> from zope.component import (getMultiAdapter, provideAdapter, |
20 | - ... queryAdapter) |
21 | + >>> from zope.component import getMultiAdapter, provideAdapter |
22 | >>> from zope.interface import Interface, implements |
23 | |
24 | >>> class ICookbook(Interface): |
25 | @@ -56,7 +57,8 @@ |
26 | >>> recipe = Recipe('spam', cookbook) |
27 | |
28 | |
29 | -== Discovering breadcrumbs == |
30 | +Discovering breadcrumbs |
31 | +----------------------- |
32 | |
33 | The Hierarchy class builds the breadcrumbs by looking at each object in |
34 | the request.traversed_objects attribute. If a traversed object can be |
35 | @@ -124,10 +126,11 @@ |
36 | >>> cooker_hierarchy = getMultiAdapter( |
37 | ... (cooker, cooker_request), name='+hierarchy') |
38 | >>> cooker_hierarchy.items |
39 | - [<TextualBreadcrumb url='http://launchpad.dev/+cooker/jamie' text='Jamie'>] |
40 | - |
41 | - |
42 | -== Displaying breadcrumbs == |
43 | + [<TextualBreadcrumb url='.../+cooker/jamie' text='Jamie'>] |
44 | + |
45 | + |
46 | +Displaying breadcrumbs |
47 | +---------------------- |
48 | |
49 | Breadcrumbs are only displayed if there is more than one breadcrumb, as |
50 | otherwise the breadcrumb will simply replicate the context.title heading |
51 | @@ -163,7 +166,8 @@ |
52 | False |
53 | |
54 | |
55 | -== Building IBreadcrumb objects == |
56 | +Building IBreadcrumb objects |
57 | +---------------------------- |
58 | |
59 | The construction of breadcrumb objects is handled by an IBreadcrumb adapter, |
60 | which adapts a context object and produces an IBreadcrumb object for that |
61 | @@ -195,7 +199,8 @@ |
62 | text='Joy of cooking'> |
63 | |
64 | |
65 | -== Customizing the hierarchy == |
66 | +Customizing the hierarchy |
67 | +------------------------- |
68 | |
69 | We can customize the hierarchy itself by changing the list of objects |
70 | and URLs that it uses to construct the breadcrumbs list. |
71 | @@ -216,7 +221,8 @@ |
72 | text='Spam'>] |
73 | |
74 | |
75 | -== Rendering the list == |
76 | +Rendering the list |
77 | +------------------ |
78 | |
79 | The Hierarchy object is responsible for rendering the HTML for the |
80 | location bar. |
81 | @@ -261,5 +267,5 @@ |
82 | >>> homepage_hierarchy.items |
83 | [] |
84 | |
85 | - >>> print_hierarchy(homepage_hierarchy.render()) |
86 | - Location: |
87 | + >>> homepage_hierarchy.render().strip() |
88 | + '' |
89 | |
90 | === modified file 'lib/canonical/launchpad/icing/style-3-0.css.in' |
91 | --- lib/canonical/launchpad/icing/style-3-0.css.in 2010-07-22 16:27:12 +0000 |
92 | +++ lib/canonical/launchpad/icing/style-3-0.css.in 2010-07-26 13:34:58 +0000 |
93 | @@ -709,6 +709,13 @@ |
94 | .batch-navigation-links .next { |
95 | margin-right: 0.25em; |
96 | } |
97 | +.flowed-block { |
98 | + display: table-cell; |
99 | + display: inline-table; |
100 | + display: inline-block; |
101 | + text-align: left; |
102 | + vertical-align: top; |
103 | + } |
104 | |
105 | |
106 | /* ========================= |
107 | @@ -1111,18 +1118,22 @@ |
108 | .demo { |
109 | background-color: #fee; |
110 | } |
111 | -#locationbar { |
112 | - /* The following style works around the fact that the locationbar, which |
113 | - is only used of the Login/Register links in 3.0 UI, is accessible on |
114 | - "locationless" pages. It adds extra space at the top of every page in |
115 | - Launchpad but that does actually look quite good ... ;-).*/ |
116 | - height: 1em; |
117 | +.login-logout { |
118 | + position: absolute; |
119 | + top: .5em; |
120 | + right: 1.5em; |
121 | } |
122 | div.watermark-apps-portlet { |
123 | - padding-bottom:1em; |
124 | + clear: both; |
125 | + margin-bottom: .5em; |
126 | + white-space: no-wrap; |
127 | + } |
128 | +div.watermark-apps-portlet .wide { |
129 | + width: 75%; |
130 | + vertical-align: bottom; |
131 | + margin-bottom: 0.3em; |
132 | } |
133 | div.watermark-apps-portlet img { |
134 | - float: left; |
135 | margin: 0 1.5em 0 0; |
136 | } |
137 | div.watermark-apps-portlet h1, div.watermark-apps-portlet h2 { |
138 | @@ -1176,19 +1187,20 @@ |
139 | width: 100%; |
140 | white-space: nowrap; |
141 | } |
142 | +.context-publication { |
143 | + margin-bottom: 1em; |
144 | + } |
145 | .registering { |
146 | /* Registered slot */ |
147 | - float: right; |
148 | - font-size: 85%; |
149 | + margin: 0 0 0.2em 0; |
150 | + font-style: italic; |
151 | + font-size: 77%; |
152 | color: #666; |
153 | - position: relative; |
154 | - top: 2em; |
155 | } |
156 | .breadcrumbs { |
157 | margin-left: 0; |
158 | list-style-type: none; |
159 | clear: both; |
160 | - margin-bottom: 2em; |
161 | font-size: 77%; |
162 | } |
163 | .breadcrumbs li { |
164 | @@ -1217,6 +1229,9 @@ |
165 | /* align the image with the text */ |
166 | margin-bottom: -2px; |
167 | } |
168 | +#logincontrol form { |
169 | + margin: 0; |
170 | + } |
171 | #logincontrol input[type='submit'] { |
172 | /* The button lacks the right margin that buttons usually have: */ |
173 | font-size: 77%; |
174 | |
175 | === modified file 'lib/lp/app/browser/tests/base-layout.txt' |
176 | --- lib/lp/app/browser/tests/base-layout.txt 2010-07-08 15:26:09 +0000 |
177 | +++ lib/lp/app/browser/tests/base-layout.txt 2010-07-26 13:34:58 +0000 |
178 | @@ -35,101 +35,12 @@ |
179 | main and side content are positioned using the "yui-t4", "yui-main", |
180 | "yui-b", and "yui-b side" classes. |
181 | |
182 | -Header. |
183 | + >>> from canonical.launchpad.testing.pages import find_tag_by_id |
184 | |
185 | >>> view = MainSideView(user, request) |
186 | >>> html = view.render() |
187 | >>> print html |
188 | <!DOCTYPE html ... |
189 | - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" |
190 | - lang="en" dir="ltr"> |
191 | - <head> |
192 | - <title>Test base-layout: main_side</title> |
193 | - <link rel="shortcut icon" href="/@@/launchpad.png" /> |
194 | - ... |
195 | - |
196 | -Standard Javascript functions. |
197 | - |
198 | - >>> print html |
199 | - <!DOCTYPE html ... |
200 | - ... |
201 | - sortables_init(); |
202 | - initInlineHelp(); |
203 | - Y.lp.activate_collapsibles(); |
204 | - activateFoldables(); |
205 | - activateConstrainBugExpiration(); |
206 | - ... |
207 | - |
208 | -Body preamble. |
209 | - |
210 | - >>> from canonical.launchpad.testing.pages import find_tag_by_id |
211 | - |
212 | - >>> body_tag = find_tag_by_id(html, 'document') |
213 | - >>> body = str(body_tag) |
214 | - >>> print body |
215 | - <body id="document" |
216 | - class="tab-overview |
217 | - main_side |
218 | - public |
219 | - yui-skin-sam"> |
220 | - <div class="yui-d0"> |
221 | - <BLANKLINE> |
222 | - <div id="locationbar"> |
223 | - ... |
224 | - |
225 | -Watermark and breadcrumbs. |
226 | - |
227 | - >>> print body |
228 | - <body ... |
229 | - ... |
230 | - <div class="watermark-apps-portlet top-portlet"> |
231 | - <img alt="" width="64" height="64" src="/@@/person-logo" /> |
232 | - <h2>Waffles</h2> |
233 | - ... |
234 | - <!-- Application Menu --> |
235 | - <ul class="facetmenu"> |
236 | - <BLANKLINE> |
237 | - <li class="overview active" |
238 | - title="General information about Waffles"><a |
239 | - href="http://launchpad.dev/~waffles">Overview</a></li> |
240 | - ... |
241 | - <div class="yui-b" dir="ltr"> |
242 | - <div> |
243 | - <h2>Heading</h2> |
244 | - <ol class="breadcrumbs"> |
245 | - ... |
246 | - |
247 | -Top portlet. |
248 | - |
249 | - >>> print body |
250 | - <body ... |
251 | - <div class="top-portlet"> |
252 | - <p class="registered"> |
253 | - Registered on 2005-09-16 |
254 | - by <a class="sprite team" href="#">Illuminati team</a> |
255 | - </p> |
256 | - <p> |
257 | - Main content of the page. |
258 | - </p> |
259 | - </div> |
260 | - ... |
261 | - |
262 | -Help pane. |
263 | - |
264 | - >>> print str(find_tag_by_id(body_tag, 'help-pane')) |
265 | - <div id="help-pane" class="invisible"> |
266 | - <div id="help-body"> |
267 | - <iframe id="help-pane-content" class="invisible" ... |
268 | - </div> |
269 | - <div id="help-footer"> |
270 | - <span id="help-close"></span> |
271 | - </div> |
272 | - </div> |
273 | - |
274 | -Footer. |
275 | - |
276 | - >>> print html |
277 | - <!DOCTYPE html ... |
278 | <!-- |
279 | Facet name: overview |
280 | Page type: main_side |
281 | @@ -151,72 +62,6 @@ |
282 | |
283 | >>> view = MainOnlyView(user, request) |
284 | >>> html = view.render() |
285 | - |
286 | -Heading. |
287 | - |
288 | - >>> print html |
289 | - <!DOCTYPE html ... |
290 | - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" |
291 | - lang="en" dir="ltr"> |
292 | - <head> |
293 | - <title>Test base-layout: main_only</title> |
294 | - <link rel="shortcut icon" href="/@@/launchpad.png" /> |
295 | - ... |
296 | - |
297 | -Watermark. |
298 | - |
299 | - >>> body_tag = find_tag_by_id(html, 'document') |
300 | - >>> body = str(body_tag) |
301 | - >>> print body |
302 | - <body ... |
303 | - <div class="watermark-apps-portlet top-portlet"> |
304 | - <img alt="" width="64" height="64" src="/@@/person-logo" /> |
305 | - <h2>Waffles</h2> |
306 | - ... |
307 | - |
308 | -Main content. |
309 | - |
310 | - >>> print str(find_tag_by_id(body_tag, 'maincontent')) |
311 | - <div id="maincontent" class="yui-main"> |
312 | - <div class="yui-b" dir="ltr"> |
313 | - <div> |
314 | - <BLANKLINE> |
315 | - <ol class="breadcrumbs"> |
316 | - <BLANKLINE> |
317 | - </ol> |
318 | - <BLANKLINE> |
319 | - </div> |
320 | - <BLANKLINE> |
321 | - <BLANKLINE> |
322 | - <BLANKLINE> |
323 | - <div class="top-portlet"> |
324 | - <h1>Heading</h1> |
325 | - <p class="registered"> |
326 | - Registered on 2005-09-16 |
327 | - by <a class="sprite team" href="#">Illuminati team</a> |
328 | - </p> |
329 | - <p> |
330 | - Main content of the page. |
331 | - </p> |
332 | - </div> |
333 | - </div><!-- yui-b --> |
334 | - </div> |
335 | - |
336 | -Global search. |
337 | - |
338 | - >>> print body |
339 | - <body ... |
340 | - <form id="globalsearch" method="get" |
341 | - accept-charset="UTF-8" |
342 | - action="http://launchpad.dev/+search"> |
343 | - <input type="search" id="search-text" name="field.text" /> |
344 | - <input type="submit" value="" class="icon-only sprite search-icon" /> |
345 | - </form> |
346 | - </div> |
347 | - ... |
348 | - |
349 | -Footer. |
350 | - |
351 | >>> print html |
352 | <!DOCTYPE html ... |
353 | ... |
354 | @@ -240,83 +85,6 @@ |
355 | |
356 | >>> view = SearchlessView(user, request) |
357 | >>> html = view.render() |
358 | - |
359 | -Heading. |
360 | - |
361 | - >>> print html |
362 | - <!DOCTYPE html ... |
363 | - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" |
364 | - lang="en" dir="ltr"> |
365 | - <head> |
366 | - <title>Test base-layout: searchless</title> |
367 | - <link rel="shortcut icon" href="/@@/launchpad.png" /> |
368 | - ... |
369 | - |
370 | -Body class. |
371 | - |
372 | - >>> body_tag = find_tag_by_id(html, 'document') |
373 | - >>> body = str(body_tag) |
374 | - >>> print body |
375 | - <body id="document" |
376 | - class="tab-overview |
377 | - searchless |
378 | - public |
379 | - yui-skin-sam"> |
380 | - <div class="yui-d0"> |
381 | - ... |
382 | - |
383 | -Watermarks. |
384 | - |
385 | - >>> print body |
386 | - <body ... |
387 | - <div class="watermark-apps-portlet top-portlet"> |
388 | - <img alt="" width="64" height="64" src="/@@/person-logo" /> |
389 | - <h2>Waffles</h2> |
390 | - <div id="registration" class="registering"> |
391 | - </div> |
392 | - <BLANKLINE> |
393 | - <!-- Application Menu --> |
394 | - <ul class="facetmenu"> |
395 | - <BLANKLINE> |
396 | - <li class="overview active" |
397 | - title="General information about Waffles"><a |
398 | - href="http://launchpad.dev/~waffles">Overview</a></li> |
399 | - ... |
400 | - |
401 | -Main content. |
402 | - |
403 | - >>> print str(find_tag_by_id(body_tag, 'maincontent')) |
404 | - <div id="maincontent" class="yui-main"> |
405 | - <div class="yui-b" dir="ltr"> |
406 | - <div> |
407 | - <BLANKLINE> |
408 | - <ol class="breadcrumbs"> |
409 | - <BLANKLINE> |
410 | - </ol> |
411 | - ... |
412 | - |
413 | -Top portlet. |
414 | - |
415 | - >>> print body |
416 | - <body ... |
417 | - <div class="top-portlet"> |
418 | - <h1>Heading</h1> |
419 | - <p class="registered"> |
420 | - Registered on 2005-09-16 |
421 | - by <a class="sprite team" href="#">Illuminati team</a> |
422 | - </p> |
423 | - <p> |
424 | - Main content of the page. |
425 | - </p> |
426 | - </div> |
427 | - </div><!-- yui-b --> |
428 | - </div><!-- yui-main --> |
429 | - <!-- yui-b side --> |
430 | - <!-- yui-t4 --> |
431 | - ... |
432 | - |
433 | -Footer. |
434 | - |
435 | >>> print html |
436 | <!DOCTYPE html ... |
437 | ... |
438 | @@ -342,51 +110,6 @@ |
439 | |
440 | >>> view = LocationlessView(user, request) |
441 | >>> html = view.render() |
442 | - |
443 | -Heading. |
444 | - |
445 | - >>> print html |
446 | - <!DOCTYPE html ... |
447 | - <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" |
448 | - lang="en" dir="ltr"> |
449 | - <head> |
450 | - <title>Test base-layout: locationless</title> |
451 | - <link rel="shortcut icon" href="/@@/launchpad.png" /> |
452 | - ... |
453 | - |
454 | -Body. |
455 | - |
456 | - >>> body_tag = find_tag_by_id(html, 'document') |
457 | - >>> body = str(body_tag) |
458 | - >>> print body |
459 | - <body id="document" |
460 | - class="tab-overview |
461 | - locationless |
462 | - public |
463 | - yui-skin-sam"> |
464 | - <div class="yui-d0"> |
465 | - ... |
466 | - |
467 | -Main content. |
468 | - |
469 | - >>> print str(find_tag_by_id(body_tag, 'maincontent')) |
470 | - <div id="maincontent" class="yui-main"> |
471 | - <div class="yui-b" dir="ltr"> |
472 | - <div class="top-portlet"> |
473 | - <h1>Heading</h1> |
474 | - <p class="registered"> |
475 | - Registered on 2005-09-16 |
476 | - by <a class="sprite team" href="#">Illuminati team</a> |
477 | - </p> |
478 | - <p> |
479 | - Main content of the page. |
480 | - </p> |
481 | - </div> |
482 | - </div><!-- yui-b --> |
483 | - </div> |
484 | - |
485 | -Footer. |
486 | - |
487 | >>> print html |
488 | <!DOCTYPE html ... |
489 | ... |
490 | @@ -517,8 +240,6 @@ |
491 | >>> body_tag = find_tag_by_id(view.render(), 'maincontent') |
492 | >>> print str(body_tag) |
493 | <div id="maincontent" ... |
494 | - <ol class="breadcrumbs"> |
495 | - </ol> |
496 | ... |
497 | <div class="informational message">I cannot do that Dave.</div> |
498 | <div class="top-portlet"> |
499 | |
500 | === added file 'lib/lp/app/browser/tests/test_base_layout.py' |
501 | --- lib/lp/app/browser/tests/test_base_layout.py 1970-01-01 00:00:00 +0000 |
502 | +++ lib/lp/app/browser/tests/test_base_layout.py 2010-07-26 13:34:58 +0000 |
503 | @@ -0,0 +1,163 @@ |
504 | +# Copyright 2010 Canonical Ltd. This software is licensed under the |
505 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
506 | + |
507 | +"""Tests for base-layout.pt and its macros. |
508 | + |
509 | +The base-layout master template defines macros that control the layout |
510 | +of the page. Any page can use these layout options by including |
511 | + |
512 | + metal:use-macro='view/macro:page/<layout>" |
513 | + |
514 | +in the root element. The template provides common layout to Launchpad. |
515 | +""" |
516 | + |
517 | +__metaclass__ = type |
518 | + |
519 | +from BeautifulSoup import BeautifulSoup |
520 | + |
521 | +from z3c.ptcompat import ViewPageTemplateFile |
522 | + |
523 | +from canonical.launchpad.webapp.publisher import LaunchpadView |
524 | +from canonical.launchpad.webapp.servers import LaunchpadTestRequest |
525 | +from canonical.testing.layers import DatabaseFunctionalLayer |
526 | +from canonical.launchpad.testing.pages import find_tag_by_id |
527 | + |
528 | +from lp.testing import TestCaseWithFactory |
529 | + |
530 | + |
531 | +class TestBaseLayout(TestCaseWithFactory): |
532 | + """Test the page parts provided by the base-layout.pt.""" |
533 | + layer = DatabaseFunctionalLayer |
534 | + |
535 | + def setUp(self): |
536 | + super(TestBaseLayout, self).setUp() |
537 | + self.user = self.factory.makePerson(name='waffles') |
538 | + self.request = LaunchpadTestRequest( |
539 | + SERVER_URL='http://launchpad.dev', |
540 | + PATH_INFO='/~waffles/+layout') |
541 | + self.request.setPrincipal(self.user) |
542 | + |
543 | + def makeTemplateView(self, layout): |
544 | + """Return a view that uses the specified layout.""" |
545 | + |
546 | + class TemplateView(LaunchpadView): |
547 | + """A simple view to test base-layout.""" |
548 | + __launchpad_facetname__ = 'overview' |
549 | + template = ViewPageTemplateFile( |
550 | + 'testfiles/%s.pt' % layout.replace('_', '-')) |
551 | + page_title = 'Test base-layout: %s' % layout |
552 | + |
553 | + return TemplateView(self.user, self.request) |
554 | + |
555 | + def test_base_layout_doctype(self): |
556 | + # Verify that the document is a html DOCTYPE. |
557 | + view = self.makeTemplateView('main_side') |
558 | + markup = view() |
559 | + self.assertTrue(markup.startswith('<!DOCTYPE html')) |
560 | + |
561 | + def verify_base_layout_html_element(self, content): |
562 | + # The html element states the namespace and language information. |
563 | + self.assertEqual( |
564 | + 'http://www.w3.org/1999/xhtml', content.html['xmlns']) |
565 | + html_tag = content.html |
566 | + self.assertEqual('en', html_tag['xml:lang']) |
567 | + self.assertEqual('en', html_tag['lang']) |
568 | + self.assertEqual('ltr', html_tag['dir']) |
569 | + |
570 | + def verify_base_layout_head_parts(self, view, content): |
571 | + # Verify the common head parts of every layout. |
572 | + head = content.head |
573 | + # The page's title starts with the view's page_title. |
574 | + self.assertTrue(head.title.string.startswith(view.page_title)) |
575 | + # The shortcut icon for the browser chrome is provided. |
576 | + link_tag = head.link |
577 | + self.assertEqual('shortcut icon', link_tag['rel']) |
578 | + self.assertEqual('/@@/launchpad.png', link_tag['href']) |
579 | + # The template loads the common scripts. |
580 | + load_script = find_tag_by_id(head, 'base-layout-load-scripts').name |
581 | + self.assertEqual('script', load_script) |
582 | + |
583 | + def verify_base_layout_body_parts(self, document): |
584 | + # Verify the common body parts of every layout. |
585 | + self.assertEqual('body', document.name) |
586 | + yui_layout = document.find('div', 'yui-d0') |
587 | + self.assertTrue(yui_layout is not None) |
588 | + self.assertEqual( |
589 | + 'login-logout', yui_layout.find(True, id='locationbar')['class']) |
590 | + self.assertEqual( |
591 | + 'yui-main', yui_layout.find(True, id='maincontent')['class']) |
592 | + self.assertEqual( |
593 | + 'invisible', document.find(True, id='help-pane')['class']) |
594 | + self.assertEqual( |
595 | + 'footer', yui_layout.find(True, id='footer')['class']) |
596 | + |
597 | + def verify_watermark(self, document): |
598 | + # Verify the parts of a watermark. |
599 | + yui_layout = document.find('div', 'yui-d0') |
600 | + watermark = yui_layout.find(True, id='watermark') |
601 | + self.assertEqual('watermark-apps-portlet', watermark['class']) |
602 | + self.assertEqual('/@@/person-logo', watermark.img['src']) |
603 | + self.assertEqual('Waffles', watermark.h2.string) |
604 | + self.assertEqual('facetmenu', watermark.ul['class']) |
605 | + |
606 | + def test_main_side(self): |
607 | + # The main_side layout has everything. |
608 | + view = self.makeTemplateView('main_side') |
609 | + content = BeautifulSoup(view()) |
610 | + self.verify_base_layout_html_element(content) |
611 | + self.verify_base_layout_head_parts(view, content) |
612 | + document = find_tag_by_id(content, 'document') |
613 | + self.verify_base_layout_body_parts(document) |
614 | + classes = 'tab-overview main_side public yui-skin-sam'.split() |
615 | + self.assertEqual(classes, document['class'].split()) |
616 | + self.verify_watermark(document) |
617 | + self.assertEqual( |
618 | + 'yui-b side', document.find(True, id='side-portlets')['class']) |
619 | + self.assertEqual('form', document.find(True, id='globalsearch').name) |
620 | + |
621 | + def test_main_only(self): |
622 | + # The main_only layout has everything except side portlets. |
623 | + view = self.makeTemplateView('main_only') |
624 | + content = BeautifulSoup(view()) |
625 | + self.verify_base_layout_html_element(content) |
626 | + self.verify_base_layout_head_parts(view, content) |
627 | + document = find_tag_by_id(content, 'document') |
628 | + self.verify_base_layout_body_parts(document) |
629 | + classes = 'tab-overview main_only public yui-skin-sam'.split() |
630 | + self.assertEqual(classes, document['class'].split()) |
631 | + self.verify_watermark(document) |
632 | + self.assertEqual( |
633 | + 'registering', document.find(True, id='registration')['class']) |
634 | + self.assertEqual(None, document.find(True, id='side-portlets')) |
635 | + self.assertEqual('form', document.find(True, id='globalsearch').name) |
636 | + |
637 | + def test_searchless(self): |
638 | + # The searchless layout is missing side portlets and search. |
639 | + view = self.makeTemplateView('searchless') |
640 | + content = BeautifulSoup(view()) |
641 | + self.verify_base_layout_html_element(content) |
642 | + self.verify_base_layout_head_parts(view, content) |
643 | + document = find_tag_by_id(content, 'document') |
644 | + self.verify_base_layout_body_parts(document) |
645 | + self.verify_watermark(document) |
646 | + classes = 'tab-overview searchless public yui-skin-sam'.split() |
647 | + self.assertEqual(classes, document['class'].split()) |
648 | + self.assertEqual( |
649 | + 'registering', document.find(True, id='registration')['class']) |
650 | + self.assertEqual(None, document.find(True, id='side-portlets')) |
651 | + self.assertEqual(None, document.find(True, id='globalsearch')) |
652 | + |
653 | + def test_locationless(self): |
654 | + # The locationless layout has no optional content. |
655 | + view = self.makeTemplateView('locationless') |
656 | + content = BeautifulSoup(view()) |
657 | + self.verify_base_layout_html_element(content) |
658 | + self.verify_base_layout_head_parts(view, content) |
659 | + document = find_tag_by_id(content, 'document') |
660 | + self.verify_base_layout_body_parts(document) |
661 | + classes = 'tab-overview locationless public yui-skin-sam'.split() |
662 | + self.assertEqual(classes, document['class'].split()) |
663 | + self.assertEqual(None, document.find(True, id='registration')) |
664 | + self.assertEqual(None, document.find(True, id='watermark')) |
665 | + self.assertEqual(None, document.find(True, id='side-portlets')) |
666 | + self.assertEqual(None, document.find(True, id='globalsearch')) |
667 | |
668 | === modified file 'lib/lp/app/templates/base-layout-macros.pt' |
669 | --- lib/lp/app/templates/base-layout-macros.pt 2010-07-14 14:38:07 +0000 |
670 | +++ lib/lp/app/templates/base-layout-macros.pt 2010-07-26 13:34:58 +0000 |
671 | @@ -288,7 +288,7 @@ |
672 | |
673 | <metal:load-lavascript use-macro="context/@@+base-layout-macros/load-javascript" /> |
674 | |
675 | - <script type="text/javascript"> |
676 | + <script id="base-layout-load-scripts" type="text/javascript"> |
677 | LPS.use('node', 'lp', function(Y) { |
678 | Y.on('load', function(e) { |
679 | sortables_init(); |
680 | |
681 | === modified file 'lib/lp/app/templates/base-layout.pt' |
682 | --- lib/lp/app/templates/base-layout.pt 2010-07-02 18:35:03 +0000 |
683 | +++ lib/lp/app/templates/base-layout.pt 2010-07-26 13:34:58 +0000 |
684 | @@ -83,21 +83,22 @@ |
685 | })(); |
686 | </script> |
687 | <div class="yui-d0"> |
688 | - <div id="locationbar"> |
689 | + <div id="locationbar" class="login-logout"> |
690 | <tal:login replace="structure context/@@login_status" /> |
691 | </div><!--id="locationbar"--> |
692 | |
693 | - <div class="watermark-apps-portlet top-portlet" |
694 | + <div id="watermark" class="watermark-apps-portlet" |
695 | tal:condition="view/macro:pagehas/applicationtabs"> |
696 | - <span tal:replace="structure view/watermark:logo"></span> |
697 | - <h2 tal:replace="structure view/watermark:heading"> |
698 | - Celso Providelo |
699 | - </h2> |
700 | - <div id="registration" class="registering"> |
701 | - <metal:registering define-slot="registering" /> |
702 | - </div> |
703 | - <metal:heading_nav |
704 | - use-macro="context/@@+base-layout-macros/application-buttons"/> |
705 | + <div class="flowed-block"> |
706 | + <span tal:replace="structure view/watermark:logo"></span> |
707 | + </div> |
708 | + <div class="flowed-block wide"> |
709 | + <h2 tal:replace="structure view/watermark:heading"> |
710 | + Celso Providelo |
711 | + </h2> |
712 | + <metal:heading_nav |
713 | + use-macro="context/@@+base-layout-macros/application-buttons"/> |
714 | + </div> |
715 | </div> |
716 | |
717 | <div class="yui-t4" |
718 | @@ -108,13 +109,17 @@ |
719 | lang view/lang|default_language|default; |
720 | xml:lang view/lang|default_language|default; |
721 | dir view/dir|string:ltr"> |
722 | - <div tal:condition="view/macro:pagehas/applicationtabs"> |
723 | + <div class="context-publication" |
724 | + tal:condition="view/macro:pagehas/applicationtabs"> |
725 | <h1 |
726 | tal:condition="view/label|nothing" |
727 | tal:content="view/label" |
728 | metal:define-slot="heading" |
729 | >Page Label |
730 | </h1> |
731 | + <div id="registration" class="registering"> |
732 | + <metal:registering define-slot="registering" /> |
733 | + </div> |
734 | <tal:breadcrumbs replace="structure context/@@+hierarchy"> |
735 | ProjectName > Branches > Merge Proposals > fix-for-navigation |
736 | </tal:breadcrumbs> |
737 | @@ -132,7 +137,7 @@ |
738 | </div><!-- yui-b --> |
739 | </div><!-- yui-main --> |
740 | |
741 | - <div class="yui-b side" |
742 | + <div id="side-portlets" class="yui-b side" |
743 | tal:condition="view/macro:pagehas/portlets"> |
744 | <metal:portlets define-slot="side" /> |
745 | </div><!-- yui-b side --> |
746 | |
747 | === modified file 'lib/lp/app/templates/launchpad-hierarchy.pt' |
748 | --- lib/lp/app/templates/launchpad-hierarchy.pt 2010-01-22 17:28:32 +0000 |
749 | +++ lib/lp/app/templates/launchpad-hierarchy.pt 2010-07-26 13:34:58 +0000 |
750 | @@ -3,13 +3,13 @@ |
751 | xmlns:tal="http://xml.zope.org/namespaces/tal" |
752 | xmlns:metal="http://xml.zope.org/namespaces/metal" |
753 | xmlns:i18n="http://xml.zope.org/namespaces/i18n" |
754 | - i18n:domain="launchpad"> |
755 | + i18n:domain="launchpad" |
756 | + tal:condition="view/display_breadcrumbs"> |
757 | |
758 | -<tal:breadcrumbs repeat="breadcrumb view/items" |
759 | - condition="view/display_breadcrumbs"> |
760 | +<tal:breadcrumbs repeat="breadcrumb view/items"> |
761 | <li> |
762 | <a tal:attributes="href breadcrumb/url" |
763 | - tal:omit-tag="repeat/breadcrumb/end"><tal:text |
764 | + tal:omit-tag="repeat/breadcrumb/end"><tal:text |
765 | condition="not: repeat/breadcrumb/end" |
766 | replace="breadcrumb/text"> |
767 | Bugs on redfish</tal:text><tal:text |
768 | |
769 | === modified file 'lib/lp/blueprints/stories/sprints/05-sprint-creation.txt' |
770 | --- lib/lp/blueprints/stories/sprints/05-sprint-creation.txt 2009-11-08 20:09:26 +0000 |
771 | +++ lib/lp/blueprints/stories/sprints/05-sprint-creation.txt 2010-07-26 13:34:58 +0000 |
772 | @@ -1,137 +1,119 @@ |
773 | -= Creating new sprints = |
774 | - |
775 | -We should also be able to create a new sprint. We do this off the Sprints |
776 | -+new page. |
777 | - |
778 | - >>> user_browser.open('http://launchpad.dev/sprints') |
779 | - >>> user_browser.getLink('Register a meeting').click() |
780 | - |
781 | - >>> print user_browser.title |
782 | - Register a meeting... |
783 | +Creating new sprints |
784 | +==================== |
785 | + |
786 | +We should also be able to create a new sprint. We do this off the |
787 | +Sprints +new page. |
788 | + |
789 | + >>> user_browser.open('http://launchpad.dev/sprints') |
790 | + >>> user_browser.getLink('Register a meeting').click() |
791 | + |
792 | + >>> print user_browser.title |
793 | + Register a meeting... |
794 | |
795 | First we'll test the name field validator. |
796 | |
797 | - >>> user_browser.getControl('Name').value = 'ltsp_on_steroids!' |
798 | - >>> user_browser.getControl('Title').value = 'LTSP On Steroids' |
799 | - >>> summary = 'This is a sprint summary. Some words about the sprint' |
800 | - >>> user_browser.getControl('Summary').value = summary |
801 | - >>> user_browser.getControl('Driver').value = 'kamion' |
802 | - >>> user_browser.getControl('Home Page').value = 'http://www.willy.net' |
803 | - >>> user_browser.getControl('Timezone').value = ['UTC'] |
804 | - >>> user_browser.getControl( |
805 | - ... 'Starting Date and Time').value = '10 Oct 2006 09:15' |
806 | - >>> user_browser.getControl( |
807 | - ... 'Finishing Date and Time').value = '13 Oct 2006 16:00' |
808 | - >>> user_browser.getControl('Add Sprint').click() |
809 | - |
810 | - >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
811 | - ... print tag.renderContents() |
812 | - There is 1 error. |
813 | - <BLANKLINE> |
814 | - Invalid name 'ltsp_on_steroids!'. Names must be at least two characters ... |
815 | - |
816 | -Register a sprint with the same name of a existing one also returns a nice |
817 | -error message |
818 | - |
819 | - >>> user_browser.getControl('Name').value = 'ubz' |
820 | - >>> user_browser.getControl('Add Sprint').click() |
821 | - |
822 | - >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
823 | - ... print tag.renderContents() |
824 | - There is 1 error. |
825 | - <BLANKLINE> |
826 | - ubz is already in use by another sprint. |
827 | + >>> user_browser.getControl('Name').value = 'ltsp_on_steroids!' |
828 | + >>> user_browser.getControl('Title').value = 'LTSP On Steroids' |
829 | + >>> summary = 'This is a sprint summary. Some words about the sprint' |
830 | + >>> user_browser.getControl('Summary').value = summary |
831 | + >>> user_browser.getControl('Driver').value = 'kamion' |
832 | + >>> user_browser.getControl('Home Page').value = 'http://www.willy.net' |
833 | + >>> user_browser.getControl('Timezone').value = ['UTC'] |
834 | + >>> user_browser.getControl( |
835 | + ... 'Starting Date and Time').value = '10 Oct 2006 09:15' |
836 | + >>> user_browser.getControl( |
837 | + ... 'Finishing Date and Time').value = '13 Oct 2006 16:00' |
838 | + >>> user_browser.getControl('Add Sprint').click() |
839 | + |
840 | + >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
841 | + ... print tag.renderContents() |
842 | + There is 1 error. |
843 | + <BLANKLINE> |
844 | + Invalid name 'ltsp_on_steroids!'. Names must be at least two characte... |
845 | + |
846 | +Register a sprint with the same name of a existing one also returns a |
847 | +nice error message |
848 | + |
849 | + >>> user_browser.getControl('Name').value = 'ubz' |
850 | + >>> user_browser.getControl('Add Sprint').click() |
851 | + |
852 | + >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
853 | + ... print tag.renderContents() |
854 | + There is 1 error. |
855 | + <BLANKLINE> |
856 | + ubz is already in use by another sprint. |
857 | |
858 | Create a new sprint with a finish date before the starting date returns |
859 | a error message. |
860 | |
861 | - >>> user_browser.getControl('Name').value = 'ltsponsteroids' |
862 | - >>> user_browser.getControl( |
863 | - ... 'Starting Date and Time').value = '13 Oct 2006 09:15 ' |
864 | - >>> user_browser.getControl( |
865 | - ... 'Finishing Date and Time').value = '10 Oct 2006 16:00' |
866 | - >>> user_browser.getControl('Add Sprint').click() |
867 | + >>> user_browser.getControl('Name').value = 'ltsponsteroids' |
868 | + >>> user_browser.getControl( |
869 | + ... 'Starting Date and Time').value = '13 Oct 2006 09:15 ' |
870 | + >>> user_browser.getControl( |
871 | + ... 'Finishing Date and Time').value = '10 Oct 2006 16:00' |
872 | + >>> user_browser.getControl('Add Sprint').click() |
873 | |
874 | - >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
875 | - ... print tag.renderContents() |
876 | - There is 1 error. |
877 | - <BLANKLINE> |
878 | - This event can't start after it ends |
879 | + >>> for tag in find_tags_by_class(user_browser.contents, 'message'): |
880 | + ... print tag.renderContents() |
881 | + There is 1 error. |
882 | + <BLANKLINE> |
883 | + This event can't start after it ends |
884 | |
885 | Also, the date is now presented in a canonicalised format, with time in |
886 | minutes rather than second-level accuracy: |
887 | |
888 | - >>> user_browser.getControl('Starting Date and Time').value |
889 | - '2006-10-13 09:15' |
890 | - >>> user_browser.getControl('Finishing Date and Time').value |
891 | - '2006-10-10 16:00' |
892 | - |
893 | -Fix the date and try again. We're redirected to the sprint home page |
894 | -for the new sprint. |
895 | - |
896 | - >>> user_browser.getControl( |
897 | - ... 'Starting Date and Time').value = '10 Oct 2006 09:15 ' |
898 | - >>> user_browser.getControl( |
899 | - ... 'Finishing Date and Time').value = '13 Oct 2006 16:00' |
900 | - >>> user_browser.getControl('Add Sprint').click() |
901 | - |
902 | - >>> user_browser.url |
903 | - 'http://launchpad.dev/sprints/ltsponsteroids' |
904 | - |
905 | -Because sprints are root contexts, an h1 heading is used to display |
906 | -the main heading above the application buttons) and the breadcrumbs |
907 | -are not displayed. |
908 | - |
909 | - >>> print_location(user_browser.contents) |
910 | - Hierarchy: None displayed |
911 | - Tabs: |
912 | - * Overview (selected) - not linked |
913 | - ... |
914 | - * Bugs - not linked |
915 | - * Blueprints - http://blueprints.launchpad.dev/sprints/ltsponsteroids |
916 | - * Translations - not linked |
917 | - * Answers - not linked |
918 | - Main heading: LTSP On Steroids |
919 | - |
920 | -There is no h1 heading in the main content. |
921 | - |
922 | - >>> print find_main_content(user_browser.contents).find('h1') |
923 | - None |
924 | + >>> user_browser.getControl('Starting Date and Time').value |
925 | + '2006-10-13 09:15' |
926 | + |
927 | + >>> user_browser.getControl('Finishing Date and Time').value |
928 | + '2006-10-10 16:00' |
929 | + |
930 | +Fix the date and try again. We're redirected to the sprint home page for |
931 | +the new sprint. |
932 | + |
933 | + >>> user_browser.getControl( |
934 | + ... 'Starting Date and Time').value = '10 Oct 2006 09:15 ' |
935 | + >>> user_browser.getControl( |
936 | + ... 'Finishing Date and Time').value = '13 Oct 2006 16:00' |
937 | + >>> user_browser.getControl('Add Sprint').click() |
938 | + |
939 | + >>> user_browser.url |
940 | + 'http://launchpad.dev/sprints/ltsponsteroids' |
941 | |
942 | Since the sprint's time zone was set to UTC, the dates are displayed in |
943 | that time zone: |
944 | |
945 | - >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end')) |
946 | - Starts: 09:15 UTC on Tuesday, 2006-10-10 |
947 | - Ends: 16:00 UTC on Friday, 2006-10-13 |
948 | - |
949 | -Because this is a brand new sprint, it will have no specs, and we should see |
950 | -a warning to that effect on the page. |
951 | - |
952 | - >>> message = 'Nobody has yet proposed any blueprints for discussion' |
953 | - >>> message in user_browser.contents |
954 | - True |
955 | - |
956 | + >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end')) |
957 | + Starts: 09:15 UTC on Tuesday, 2006-10-10 |
958 | + Ends: 16:00 UTC on Friday, 2006-10-13 |
959 | + |
960 | +Because this is a brand new sprint, it will have no specs, and we should |
961 | +see a warning to that effect on the page. |
962 | + |
963 | + >>> message = 'Nobody has yet proposed any blueprints for discussion' |
964 | + >>> message in user_browser.contents |
965 | + True |
966 | |
967 | Add a new sprint with a different time zone is also handled correctly. |
968 | |
969 | - >>> user_browser.open('http://launchpad.dev/sprints/+new') |
970 | - >>> user_browser.getControl('Name').value = 'africa-sprint' |
971 | - >>> user_browser.getControl('Title').value = 'Africa Sprint' |
972 | - >>> summary = 'This is a sprint summary. Some words about the sprint' |
973 | - >>> user_browser.getControl('Summary').value = summary |
974 | - >>> user_browser.getControl('Home Page').value = 'http://www.ubuntu.com' |
975 | - >>> user_browser.getControl('Timezone').value = ['Africa/Johannesburg'] |
976 | - >>> user_browser.getControl( |
977 | - ... 'Starting Date and Time').value = '10 Jul 2006 09:15' |
978 | - >>> user_browser.getControl( |
979 | - ... 'Finishing Date and Time').value = '13 Jul 2006 16:00' |
980 | - >>> user_browser.getControl('Add Sprint').click() |
981 | - |
982 | - >>> user_browser.url |
983 | - 'http://launchpad.dev/sprints/africa-sprint' |
984 | - |
985 | - >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end')) |
986 | - Starts: 09:15 SAST on Monday, 2006-07-10 |
987 | - Ends: 16:00 SAST on Thursday, 2006-07-13 |
988 | + >>> user_browser.open('http://launchpad.dev/sprints/+new') |
989 | + >>> user_browser.getControl('Name').value = 'africa-sprint' |
990 | + >>> user_browser.getControl('Title').value = 'Africa Sprint' |
991 | + >>> summary = 'This is a sprint summary. Some words about the sprint' |
992 | + >>> user_browser.getControl('Summary').value = summary |
993 | + >>> user_browser.getControl('Home Page').value = 'http://www.ubuntu.com' |
994 | + >>> user_browser.getControl('Timezone').value = ['Africa/Johannesburg'] |
995 | + >>> user_browser.getControl( |
996 | + ... 'Starting Date and Time').value = '10 Jul 2006 09:15' |
997 | + >>> user_browser.getControl( |
998 | + ... 'Finishing Date and Time').value = '13 Jul 2006 16:00' |
999 | + >>> user_browser.getControl('Add Sprint').click() |
1000 | + |
1001 | + >>> user_browser.url |
1002 | + 'http://launchpad.dev/sprints/africa-sprint' |
1003 | + |
1004 | + >>> print extract_text(find_tag_by_id(user_browser.contents, 'start-end')) |
1005 | + Starts: 09:15 SAST on Monday, 2006-07-10 |
1006 | + Ends: 16:00 SAST on Thursday, 2006-07-13 |
1007 | + |
1008 | |
1009 | |
1010 | === modified file 'lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt' |
1011 | --- lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-03-15 13:22:02 +0000 |
1012 | +++ lib/lp/bugs/stories/bugs/xx-bug-heat-on-bug-page.txt 2010-07-26 13:34:58 +0000 |
1013 | @@ -3,8 +3,8 @@ |
1014 | The bug heat appears on the bug index page as four flames. |
1015 | |
1016 | >>> anon_browser.open('http://bugs.launchpad.dev/firefox/+bug/1') |
1017 | - >>> print find_tag_by_id( |
1018 | - ... anon_browser.contents, 'registration').fetch('img')[0] |
1019 | + >>> print find_main_content( |
1020 | + ... anon_browser.contents).find('img', src='/@@/bug-heat-0.png') |
1021 | <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 0" /> |
1022 | |
1023 | Packages use their distribution as context when generating flame icons |
1024 | @@ -30,6 +30,6 @@ |
1025 | >>> logout() |
1026 | |
1027 | >>> anon_browser.open(canonical_url(dsp_bug)) |
1028 | - >>> print find_tag_by_id( |
1029 | - ... anon_browser.contents, 'registration').fetch('img')[0] |
1030 | + >>> print find_main_content( |
1031 | + ... anon_browser.contents).find('img', src='/@@/bug-heat-0.png') |
1032 | <img src="/@@/bug-heat-0.png" alt="0 out of 4 heat flames" title="Heat: 1250" /> |
1033 | |
1034 | === modified file 'lib/lp/bugs/stories/bugs/xx-malone-homepage.txt' |
1035 | --- lib/lp/bugs/stories/bugs/xx-malone-homepage.txt 2010-05-19 05:47:50 +0000 |
1036 | +++ lib/lp/bugs/stories/bugs/xx-malone-homepage.txt 2010-07-26 13:34:58 +0000 |
1037 | @@ -1,26 +1,18 @@ |
1038 | Say hello to Bugs. :) |
1039 | |
1040 | - >>> browser.open('http://bugs.launchpad.dev/') |
1041 | - >>> browser.url |
1042 | - 'http://bugs.launchpad.dev/' |
1043 | + >>> browser.open('http://bugs.launchpad.dev/') |
1044 | + >>> browser.url |
1045 | + 'http://bugs.launchpad.dev/' |
1046 | |
1047 | - >>> print_location(browser.contents) |
1048 | - Hierarchy: None displayed |
1049 | - Tabs: |
1050 | - * Launchpad Home - http://launchpad.dev/ |
1051 | - * Code - http://code.launchpad.dev/ |
1052 | - * Bugs (selected) - http://bugs.launchpad.dev/ |
1053 | - * Blueprints - http://blueprints.launchpad.dev/ |
1054 | - * Translations - http://translations.launchpad.dev/ |
1055 | - * Answers - http://answers.launchpad.dev/ |
1056 | - Main heading: Bug tracking |
1057 | + >>> print browser.title |
1058 | + Bug tracking |
1059 | |
1060 | There are a few related pages linked in a portlet: |
1061 | |
1062 | - >>> related_pages = find_portlet(browser.contents, 'Related pages') |
1063 | - >>> for link in related_pages.findAll('a'): |
1064 | - ... print "%s\n --> %s" % (extract_text(link), link.get('href')) |
1065 | - Bug trackers |
1066 | + >>> related_pages = find_portlet(browser.contents, 'Related pages') |
1067 | + >>> for link in related_pages.findAll('a'): |
1068 | + ... print "%s\n --> %s" % (extract_text(link), link.get('href')) |
1069 | + Bug trackers |
1070 | --> http://bugs.launchpad.dev/bugs/bugtrackers |
1071 | - CVE tracker |
1072 | + CVE tracker |
1073 | --> http://bugs.launchpad.dev/bugs/cve |
1074 | |
1075 | === modified file 'lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt' |
1076 | --- lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt 2010-06-16 15:28:35 +0000 |
1077 | +++ lib/lp/bugs/stories/bugtracker/bugtrackers-index.txt 2010-07-26 13:34:58 +0000 |
1078 | @@ -1,19 +1,12 @@ |
1079 | -= Bug trackers in Launchpad = |
1080 | +Bug trackers in Launchpad |
1081 | +========================= |
1082 | |
1083 | The bug trackers index page has the same navigation as the main Bugs |
1084 | page, with the addition of a breadcrumb itself. |
1085 | |
1086 | >>> user_browser.open('http://launchpad.dev/bugs/bugtrackers') |
1087 | - >>> print_location(user_browser.contents) |
1088 | - Hierarchy: None displayed |
1089 | - Tabs: |
1090 | - * Launchpad Home - http://launchpad.dev/ |
1091 | - * Code - http://code.launchpad.dev/ |
1092 | - * Bugs (selected) - http://bugs.launchpad.dev/ |
1093 | - * Blueprints - http://blueprints.launchpad.dev/ |
1094 | - * Translations - http://translations.launchpad.dev/ |
1095 | - * Answers - http://answers.launchpad.dev/ |
1096 | - Main heading: Bug trackers registered in Launchpad |
1097 | + >>> print user_browser.title |
1098 | + Bug trackers registered in Launchpad |
1099 | |
1100 | The page presents a table with all bugtrackers currently registered: |
1101 | |
1102 | |
1103 | === modified file 'lib/lp/bugs/stories/cve/cve-pages.txt' |
1104 | --- lib/lp/bugs/stories/cve/cve-pages.txt 2010-05-21 13:49:36 +0000 |
1105 | +++ lib/lp/bugs/stories/cve/cve-pages.txt 2010-07-26 13:34:58 +0000 |
1106 | @@ -1,4 +1,5 @@ |
1107 | -= CVE tracking = |
1108 | +CVE tracking |
1109 | +============ |
1110 | |
1111 | Launchpad supports the CVE database system. |
1112 | |
1113 | @@ -17,16 +18,8 @@ |
1114 | >>> browser.getLink('CVE entries').click() |
1115 | >>> print browser.url |
1116 | http://bugs.launchpad.dev/bugs/cve |
1117 | - >>> print_location(browser.contents) |
1118 | - Hierarchy: None displayed |
1119 | - Tabs: |
1120 | - * Launchpad Home - http://launchpad.dev/ |
1121 | - * Code - http://code.launchpad.dev/ |
1122 | - * Bugs (selected) - http://bugs.launchpad.dev/ |
1123 | - * Blueprints - http://blueprints.launchpad.dev/ |
1124 | - * Translations - http://translations.launchpad.dev/ |
1125 | - * Answers - http://answers.launchpad.dev/ |
1126 | - Main heading: Launchpad CVE tracker |
1127 | + >>> print browser.title |
1128 | + Launchpad CVE tracker |
1129 | >>> print browser.contents |
1130 | <... |
1131 | ...CVE-2005-2737 (Candidate)... |
1132 | @@ -37,76 +30,58 @@ |
1133 | >>> browser.getLink('Show all registered CVEs').click() |
1134 | >>> print browser.url |
1135 | http://bugs.launchpad.dev/bugs/cve/+all |
1136 | - >>> print_location(browser.contents) |
1137 | - Hierarchy: None displayed |
1138 | - Tabs: |
1139 | - * Launchpad Home - http://launchpad.dev/ |
1140 | - * Code - http://code.launchpad.dev/ |
1141 | - * Bugs (selected) - http://bugs.launchpad.dev/ |
1142 | - * Blueprints - http://blueprints.launchpad.dev/ |
1143 | - * Translations - http://translations.launchpad.dev/ |
1144 | - * Answers - http://answers.launchpad.dev/ |
1145 | - Main heading: Launchpad CVE tracker |
1146 | + >>> print browser.title |
1147 | + Launchpad CVE tracker |
1148 | |
1149 | Now, we will test the search functionality of the CVE tracker. First we |
1150 | will search for the word "loss" in the database and see how many show |
1151 | up. We expect to see CVE-1999-2345. |
1152 | |
1153 | - >>> print http(r""" |
1154 | - ... POST /bugs/cve/ HTTP/1.1 |
1155 | - ... Content-Length: 9 |
1156 | - ... Content-Type: application/x-www-form-urlencoded |
1157 | - ... |
1158 | - ... text=loss""") |
1159 | - HTTP/1.1 200 Ok |
1160 | - ...Matches: 1... |
1161 | - ...CVE-1999-2345 (Candidate)... |
1162 | + >>> print http(r""" |
1163 | + ... POST /bugs/cve/ HTTP/1.1 |
1164 | + ... Content-Length: 9 |
1165 | + ... Content-Type: application/x-www-form-urlencoded |
1166 | + ... |
1167 | + ... text=loss""") |
1168 | + HTTP/1.1 200 Ok |
1169 | + ...Matches: 1... |
1170 | + ...CVE-1999-2345 (Candidate)... |
1171 | |
1172 | |
1173 | Finally, let's put a CVE number in there. We expect to jump straight to that |
1174 | CVE. |
1175 | |
1176 | - >>> print http(r""" |
1177 | - ... POST /bugs/cve/ HTTP/1.1 |
1178 | - ... Content-Length: 14 |
1179 | - ... Content-Type: application/x-www-form-urlencoded |
1180 | - ... |
1181 | - ... text=2005-2737""") |
1182 | - HTTP/1.1 303 See Other |
1183 | - ... |
1184 | - Location: http://.../bugs/cve/2005-2737 |
1185 | - ... |
1186 | + >>> print http(r""" |
1187 | + ... POST /bugs/cve/ HTTP/1.1 |
1188 | + ... Content-Length: 14 |
1189 | + ... Content-Type: application/x-www-form-urlencoded |
1190 | + ... |
1191 | + ... text=2005-2737""") |
1192 | + HTTP/1.1 303 See Other |
1193 | + ... |
1194 | + Location: http://.../bugs/cve/2005-2737 |
1195 | + ... |
1196 | |
1197 | -A CVE page has the same navigation as the Bugs front page (because CVEs |
1198 | -don't have their own blueprints, translations, etc), and also includes |
1199 | -a link back to the main CVE page. |
1200 | +A CVE page includes a link back to the main CVE page. |
1201 | |
1202 | >>> anon_browser.open('http://launchpad.dev/bugs/cve/2005-2737') |
1203 | - >>> print_location(anon_browser.contents) |
1204 | - Hierarchy: None displayed |
1205 | - Tabs: |
1206 | - * Launchpad Home - http://launchpad.dev/ |
1207 | - * Code - http://code.launchpad.dev/ |
1208 | - * Bugs (selected) - http://bugs.launchpad.dev/ |
1209 | - * Blueprints - http://blueprints.launchpad.dev/ |
1210 | - * Translations - http://translations.launchpad.dev/ |
1211 | - * Answers - http://answers.launchpad.dev/ |
1212 | - Main heading: CVE 2005-2737 |
1213 | + >>> print anon_browser.title |
1214 | + CVE 2005-2737 : ... |
1215 | >>> back_link = anon_browser.getLink('Launchpad CVE tracker') |
1216 | >>> print back_link.url |
1217 | http://launchpad.dev/bugs/cve |
1218 | |
1219 | The CVE page links to the related bugs. |
1220 | |
1221 | - >>> "Cross-site scripting (XSS) vulnerability" in anon_browser.contents |
1222 | - True |
1223 | - >>> 'No related bugs' in anon_browser.contents |
1224 | - True |
1225 | - >>> for tag in find_tags_by_class( |
1226 | - ... anon_browser.contents, 'menu-link-linkbug'): |
1227 | - ... print tag |
1228 | - <a href="+linkbug" class="menu-link-linkbug sprite add">Link to bug</a> |
1229 | - >>> 'Candidate' in anon_browser.contents |
1230 | - True |
1231 | - >>> 'http://marc.theaimsgroup.com/?l=bugtraq&m=112511025414488&w=2">20050826 Multiple PHP' in anon_browser.contents |
1232 | - True |
1233 | + >>> "Cross-site scripting (XSS) vulnerability" in anon_browser.contents |
1234 | + True |
1235 | + >>> 'No related bugs' in anon_browser.contents |
1236 | + True |
1237 | + >>> for tag in find_tags_by_class( |
1238 | + ... anon_browser.contents, 'menu-link-linkbug'): |
1239 | + ... print tag |
1240 | + <a href="+linkbug" class="menu-link-linkbug sprite add">Link to bug</a> |
1241 | + >>> 'Candidate' in anon_browser.contents |
1242 | + True |
1243 | + >>> '20050826 Multiple PHP' in anon_browser.contents |
1244 | + True |
1245 | |
1246 | === modified file 'lib/lp/bugs/templates/bugtask-index.pt' |
1247 | --- lib/lp/bugs/templates/bugtask-index.pt 2010-07-16 16:14:39 +0000 |
1248 | +++ lib/lp/bugs/templates/bugtask-index.pt 2010-07-26 13:34:58 +0000 |
1249 | @@ -43,12 +43,11 @@ |
1250 | </metal:side> |
1251 | |
1252 | <tal:registering metal:fill-slot="registering"> |
1253 | - Bug #<tal:block content="context/bug/id"/> reported by |
1254 | + Reported by |
1255 | <tal:reporter replace="structure context/bug/owner/fmt:link" /> |
1256 | <span |
1257 | tal:attributes="title context/bug/datecreated/fmt:datetime" |
1258 | tal:content="context/bug/datecreated/fmt:displaydate" /> |
1259 | - <tal:heat replace="structure view/bug_heat_html" /> |
1260 | </tal:registering> |
1261 | |
1262 | <metal:heading fill-slot="heading" tal:define="context_menu context/menu:context"> |
1263 | @@ -92,6 +91,10 @@ |
1264 | </tal:XXX> |
1265 | </p> |
1266 | |
1267 | + <div style="float: right;"> |
1268 | + <tal:heat replace="structure view/bug_heat_html" /> |
1269 | + </div> |
1270 | + |
1271 | <div tal:replace="structure context/bug/@@+bugtasks-and-nominations-table" /> |
1272 | |
1273 | <div id="maincontentsub"> |
This sounds nice, but I'm not comfortable enough with this part of the system yet to be your reviewer - sorry!