Merge lp:~jkakar/kanban/fix-header-links into lp:kanban
- fix-header-links
- Merge into trunk
Proposed by
Jamu Kakar
Status: | Merged |
---|---|
Merged at revision: | 23 |
Proposed branch: | lp:~jkakar/kanban/fix-header-links |
Merge into: | lp:kanban |
Diff against target: |
588 lines (+203/-154) 7 files modified
kanban/board.py (+37/-20) kanban/commands.py (+9/-14) kanban/html.py (+5/-2) kanban/launchpad.py (+5/-4) kanban/templates/kanban.html (+15/-9) kanban/tests/test_board.py (+131/-104) kanban/tests/test_html.py (+1/-1) |
To merge this branch: | bzr merge lp:~jkakar/kanban/fix-header-links |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pool | Approve | ||
Review via email: mp+50118@code.launchpad.net |
Commit message
Description of the change
This branch introduces the following changes:
- The kanban.milestone module has been renamed to kanban.board and the
Milestone class has been refactored into MilestoneBoard and
PersonBoard.
- Rendering logic is special cased to behave correctly for each type
of board.
- A stray LPNET_SERVICE_ROOT has been replaced by the SERVICE_ROOT
defined in kanban.launchpad. Changing the service root defined
there will change the service root used throughout the system.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === renamed file 'kanban/milestone.py' => 'kanban/board.py' |
2 | --- kanban/milestone.py 2011-02-15 11:27:45 +0000 |
3 | +++ kanban/board.py 2011-02-17 10:14:45 +0000 |
4 | @@ -155,21 +155,21 @@ |
5 | self.queued.append(bug) |
6 | |
7 | |
8 | -class Milestone(BugCollectionMixin): |
9 | - """ |
10 | - An milestone is a collection of L{Bug}s targetted as part of an |
11 | - incremental development process. |
12 | - |
13 | - @param project: The name of the project in Launchpad this milestone is |
14 | - part of. |
15 | - @param name: The name of this milestone. |
16 | - @param warning_limit: Optionally, the number of days to wait before |
17 | - showing a warning icon. |
18 | - """ |
19 | - |
20 | - def __init__(self, project, name, warning_limit=None): |
21 | - super(Milestone, self).__init__(name) |
22 | - self.project = project |
23 | +class Story(BugCollectionMixin): |
24 | + """A story is a collection of L{Bug}s related to a particular feature. |
25 | + |
26 | + @param name: The name of this story. |
27 | + """ |
28 | + |
29 | + |
30 | +class StoryCollectionMixin(BugCollectionMixin): |
31 | + """A collection of L{Bug}s grouped into L{Story}s. |
32 | + |
33 | + @param name: The name of this collection. |
34 | + """ |
35 | + |
36 | + def __init__(self, name): |
37 | + super(StoryCollectionMixin, self).__init__(name) |
38 | self._stories = {} |
39 | |
40 | @property |
41 | @@ -179,7 +179,7 @@ |
42 | |
43 | def add(self, bug): |
44 | """Add C{bug} to this milestone.""" |
45 | - super(Milestone, self).add(bug) |
46 | + super(StoryCollectionMixin, self).add(bug) |
47 | stories = self._get_stories(bug) |
48 | for story in stories: |
49 | story.add(bug) |
50 | @@ -205,10 +205,27 @@ |
51 | return stories |
52 | |
53 | |
54 | -class Story(BugCollectionMixin): |
55 | - """A story is a collection of L{Bug}s related to a particular feature. |
56 | - |
57 | - @param name: The name of this story. |
58 | +class MilestoneBoard(StoryCollectionMixin): |
59 | + """ |
60 | + A milestone board contains a collection of L{Bug}s targetted to a |
61 | + milestone in Launchpad. |
62 | + |
63 | + @param project_name: The name of the project or project group in Launchpad |
64 | + this milestone is part of. |
65 | + @param milestone_name: The name of this milestone. |
66 | + """ |
67 | + |
68 | + def __init__(self, project_name, milestone_name): |
69 | + super(MilestoneBoard, self).__init__(milestone_name) |
70 | + self.project_name = project_name |
71 | + |
72 | + |
73 | +class PersonBoard(StoryCollectionMixin): |
74 | + """ |
75 | + A person board contains a collection of L{Bug}s targetted to a particular |
76 | + person or team. |
77 | + |
78 | + @param name: The name of the person or team. |
79 | """ |
80 | |
81 | |
82 | |
83 | === modified file 'kanban/commands.py' |
84 | --- kanban/commands.py 2011-02-17 07:07:56 +0000 |
85 | +++ kanban/commands.py 2011-02-17 10:14:45 +0000 |
86 | @@ -5,11 +5,11 @@ |
87 | |
88 | from launchpadlib.launchpad import Launchpad |
89 | |
90 | +from kanban.board import MilestoneBoard, PersonBoard, compare_bugs |
91 | from kanban.html import generate_html, generate_roadmap_html |
92 | from kanban.launchpad import ( |
93 | get_config_path, get_cache_path, get_launchpad, get_milestone_bugs, |
94 | get_person_assigned_bugs, SERVICE_ROOT) |
95 | -from kanban.milestone import Milestone, compare_bugs |
96 | from kanban.roadmap import load_roadmap |
97 | |
98 | |
99 | @@ -62,11 +62,11 @@ |
100 | |
101 | def run(self, person_name, output_file=None): |
102 | launchpad = get_launchpad() |
103 | - milestone = Milestone(person_name, "assigned") |
104 | + person_board = PersonBoard(person_name) |
105 | bugs = get_person_assigned_bugs(launchpad, person_name) |
106 | for bug in sorted(bugs, compare_bugs): |
107 | - milestone.add(bug) |
108 | - self.write_output(generate_html(milestone), output_file) |
109 | + person_board.add(bug) |
110 | + self.write_output(generate_html(person_board), output_file) |
111 | |
112 | |
113 | class cmd_generate_milestone_kanban(HTMLOutputMixin, Command): |
114 | @@ -74,21 +74,16 @@ |
115 | |
116 | takes_args = ["project_group", "milestone_name"] |
117 | takes_options = [Option("output-file", short_name="o", type=str, |
118 | - help="Write HTML to file."), |
119 | - Option("warning-limit", short_name="w", type=int, |
120 | - help="The number of days to wait before showing " |
121 | - "a warning signal.")] |
122 | + help="Write HTML to file.")] |
123 | _see_also = ["launchpad-login"] |
124 | |
125 | - def run(self, project_group, milestone_name, output_file=None, |
126 | - warning_limit=None): |
127 | + def run(self, project_group, milestone_name, output_file=None): |
128 | launchpad = get_launchpad() |
129 | - milestone = Milestone(project_group, milestone_name, |
130 | - warning_limit=warning_limit) |
131 | + milestone_board = MilestoneBoard(project_group, milestone_name) |
132 | bugs = get_milestone_bugs(launchpad, project_group, milestone_name) |
133 | for bug in sorted(bugs, compare_bugs): |
134 | - milestone.add(bug) |
135 | - self.write_output(generate_html(milestone), output_file) |
136 | + milestone_board.add(bug) |
137 | + self.write_output(generate_html(milestone_board), output_file) |
138 | |
139 | |
140 | class cmd_generate_roadmap(HTMLOutputMixin, Command): |
141 | |
142 | === modified file 'kanban/html.py' |
143 | --- kanban/html.py 2011-02-15 11:27:45 +0000 |
144 | +++ kanban/html.py 2011-02-17 10:14:45 +0000 |
145 | @@ -2,6 +2,8 @@ |
146 | |
147 | from jinja2 import Environment, PackageLoader |
148 | |
149 | +from kanban.board import MilestoneBoard |
150 | + |
151 | |
152 | def branch_name(branch_url): |
153 | """Extract and return the branch name from a branch URL.""" |
154 | @@ -55,7 +57,7 @@ |
155 | return False |
156 | |
157 | |
158 | -def generate_html(milestone): |
159 | +def generate_html(kanban_board): |
160 | """Generate an HTML kanban board to represent L{Bug}s in C{milestone}.""" |
161 | environment = Environment(loader=PackageLoader("kanban", "templates")) |
162 | environment.filters["branch_name"] = branch_name |
163 | @@ -64,7 +66,8 @@ |
164 | environment.filters["warn"] = lambda bug: warn(bug, 3, 1) |
165 | environment.filters["danger"] = lambda bug: warn(bug, 7, 3) |
166 | template = environment.get_template("kanban.html") |
167 | - data = {"milestone": milestone, |
168 | + data = {"kanban_board": kanban_board, |
169 | + "is_milestone": isinstance(kanban_board, MilestoneBoard), |
170 | "now": datetime.utcnow().strftime("%a %e %b at %H:%M UTC")} |
171 | return template.render(**data) |
172 | |
173 | |
174 | === modified file 'kanban/launchpad.py' |
175 | --- kanban/launchpad.py 2011-02-17 07:07:39 +0000 |
176 | +++ kanban/launchpad.py 2011-02-17 10:14:45 +0000 |
177 | @@ -12,12 +12,13 @@ |
178 | |
179 | from launchpadlib.credentials import Credentials |
180 | from launchpadlib.launchpad import Launchpad |
181 | - |
182 | from launchpadlib.uris import LPNET_SERVICE_ROOT |
183 | + |
184 | +from kanban.board import Bug |
185 | + |
186 | + |
187 | SERVICE_ROOT = LPNET_SERVICE_ROOT |
188 | |
189 | -from kanban.milestone import Bug |
190 | - |
191 | |
192 | def _make_path(path): |
193 | """Ensure that C{path} exists in the current user's home directory.""" |
194 | @@ -49,7 +50,7 @@ |
195 | "Run the launchpad-login command to create OAuth credentials.") |
196 | credentials = Credentials() |
197 | credentials.load(open(credentials_path, "r")) |
198 | - return Launchpad(credentials, LPNET_SERVICE_ROOT, get_cache_path()) |
199 | + return Launchpad(credentials, SERVICE_ROOT, get_cache_path()) |
200 | |
201 | |
202 | def get_project_group(launchpad, name): |
203 | |
204 | === modified file 'kanban/templates/kanban.html' |
205 | --- kanban/templates/kanban.html 2011-02-15 11:27:45 +0000 |
206 | +++ kanban/templates/kanban.html 2011-02-17 10:14:45 +0000 |
207 | @@ -1,6 +1,6 @@ |
208 | <html> |
209 | <head> |
210 | - <title>{{ milestone.project }} {{ milestone.name }}</title> |
211 | + <title>{{ kanban_board.project_name }} {{ kanban_board.name }}</title> |
212 | <link rel="stylesheet" type="text/css" href="media/decogrids-12.css" /> |
213 | <link rel="stylesheet" type="text/css" href="media/kanban.css" /> |
214 | </head> |
215 | @@ -8,19 +8,25 @@ |
216 | <body> |
217 | <div id="container"> |
218 | <div id="heading" class="row"> |
219 | - <div class="position-0 width-12 cell"><h1><a href="https://launchpad.net/{{ milestone.project }}">{{ milestone.project }}</a> <a href="https://launchpad.net/{{ milestone.project }}/+milestone/{{ milestone.name}}">{{ milestone.name }}</a> <span class="bug-count">{{ milestone.bugs|length }} bugs</span></h1></div> |
220 | + <div class="position-0 width-12 cell"> |
221 | + {% if is_milestone %} |
222 | + <h1><a href="https://launchpad.net/{{ kanban_board.project_name }}">{{ kanban_board.project_name }}</a> <a href="https://launchpad.net/{{ kanban_board.project_name }}/+milestone/{{ kanban_board.name}}">{{ kanban_board.name }}</a> <span class="bug-count">{{ kanban_board.bugs|length }} bugs</span></h1> |
223 | + {% else %} |
224 | + <h1><a href="https://launchpad.net/~{{ kanban_board.name }}">{{ kanban_board.name }}</a> <span class="bug-count">{{ kanban_board.bugs|length }} bugs</span></h1> |
225 | + {% endif %} |
226 | + </div> |
227 | </div> |
228 | |
229 | <div id="header" class="row"> |
230 | - <div class="position-0 width-2 cell"><h2>Queued <span class="bug-count">{{ milestone.queued|length }} bugs</span></h2></div> |
231 | - <div class="position-2 width-2 cell"><h2>In progress <span class="bug-count">{{ milestone.in_progress|length }} bugs</span></h2></div> |
232 | - <div class="position-4 width-2 cell"><h2>Needs review <span class="bug-count">{{ milestone.needs_review|length }} bugs</span></h2></div> |
233 | - <div class="position-6 width-2 cell"><h2>Needs testing <span class="bug-count">{{ milestone.needs_testing|length }} bugs</span></h2></div> |
234 | - <div class="position-8 width-2 cell"><h2>Needs release <span class="bug-count">{{ milestone.needs_release|length }} bugs</span></h2></div> |
235 | - <div class="position-10 width-2 cell"><h2>Released <span class="bug-count">{{ milestone.released|length }} bugs</span></h2></div> |
236 | + <div class="position-0 width-2 cell"><h2>Queued <span class="bug-count">{{ kanban_board.queued|length }} bugs</span></h2></div> |
237 | + <div class="position-2 width-2 cell"><h2>In progress <span class="bug-count">{{ kanban_board.in_progress|length }} bugs</span></h2></div> |
238 | + <div class="position-4 width-2 cell"><h2>Needs review <span class="bug-count">{{ kanban_board.needs_review|length }} bugs</span></h2></div> |
239 | + <div class="position-6 width-2 cell"><h2>Needs testing <span class="bug-count">{{ kanban_board.needs_testing|length }} bugs</span></h2></div> |
240 | + <div class="position-8 width-2 cell"><h2>Needs release <span class="bug-count">{{ kanban_board.needs_release|length }} bugs</span></h2></div> |
241 | + <div class="position-10 width-2 cell"><h2>Released <span class="bug-count">{{ kanban_board.released|length }} bugs</span></h2></div> |
242 | </div> |
243 | |
244 | - {% for story in milestone.stories %} |
245 | + {% for story in kanban_board.stories %} |
246 | <div class="tiles row story"> |
247 | {% if story.name %} |
248 | <div class="position-0 width-12 cell">{{ story.name }}</div> |
249 | |
250 | === renamed file 'kanban/tests/test_milestone.py' => 'kanban/tests/test_board.py' |
251 | --- kanban/tests/test_milestone.py 2011-02-15 11:27:45 +0000 |
252 | +++ kanban/tests/test_board.py 2011-02-17 10:14:45 +0000 |
253 | @@ -2,11 +2,11 @@ |
254 | |
255 | from testtools import TestCase |
256 | |
257 | -from kanban.milestone import ( |
258 | - Bug, Milestone, Story, compare_bugs, compare_stories, NEW, CONFIRMED, |
259 | - TRIAGED, IN_PROGRESS, FIX_COMMITTED, FIX_RELEASED, UNDECIDED, WISHLIST, |
260 | - LOW, MEDIUM, HIGH, CRITICAL, WORK_IN_PROGRESS, NEEDS_REVIEW, APPROVED, |
261 | - MERGED) |
262 | +from kanban.board import ( |
263 | + Bug, MilestoneBoard, PersonBoard, Story, compare_bugs, compare_stories, |
264 | + NEW, CONFIRMED, TRIAGED, IN_PROGRESS, FIX_COMMITTED, FIX_RELEASED, |
265 | + UNDECIDED, WISHLIST, LOW, MEDIUM, HIGH, CRITICAL, WORK_IN_PROGRESS, |
266 | + NEEDS_REVIEW, APPROVED, MERGED) |
267 | |
268 | |
269 | class BugTest(TestCase): |
270 | @@ -282,15 +282,15 @@ |
271 | story. |
272 | """ |
273 | bug = Bug("1", "kanban", MEDIUM, NEW, "A title") |
274 | - milestone = self.create_test_class() |
275 | - milestone.add(bug) |
276 | - self.assertEqual([bug], milestone.bugs) |
277 | - self.assertEqual([bug], milestone.queued) |
278 | - self.assertEqual([], milestone.in_progress) |
279 | - self.assertEqual([], milestone.needs_review) |
280 | - self.assertEqual([], milestone.needs_testing) |
281 | - self.assertEqual([], milestone.needs_release) |
282 | - self.assertEqual([], milestone.released) |
283 | + kanban_board = self.create_test_class() |
284 | + kanban_board.add(bug) |
285 | + self.assertEqual([bug], kanban_board.bugs) |
286 | + self.assertEqual([bug], kanban_board.queued) |
287 | + self.assertEqual([], kanban_board.in_progress) |
288 | + self.assertEqual([], kanban_board.needs_review) |
289 | + self.assertEqual([], kanban_board.needs_testing) |
290 | + self.assertEqual([], kanban_board.needs_release) |
291 | + self.assertEqual([], kanban_board.released) |
292 | |
293 | def test_add_in_progress(self): |
294 | """ |
295 | @@ -299,15 +299,15 @@ |
296 | default story. |
297 | """ |
298 | bug = Bug("1", "kanban", MEDIUM, IN_PROGRESS, "A title") |
299 | - milestone = self.create_test_class() |
300 | - milestone.add(bug) |
301 | - self.assertEqual([bug], milestone.bugs) |
302 | - self.assertEqual([], milestone.queued) |
303 | - self.assertEqual([bug], milestone.in_progress) |
304 | - self.assertEqual([], milestone.needs_review) |
305 | - self.assertEqual([], milestone.needs_testing) |
306 | - self.assertEqual([], milestone.needs_release) |
307 | - self.assertEqual([], milestone.released) |
308 | + kanban_board = self.create_test_class() |
309 | + kanban_board.add(bug) |
310 | + self.assertEqual([bug], kanban_board.bugs) |
311 | + self.assertEqual([], kanban_board.queued) |
312 | + self.assertEqual([bug], kanban_board.in_progress) |
313 | + self.assertEqual([], kanban_board.needs_review) |
314 | + self.assertEqual([], kanban_board.needs_testing) |
315 | + self.assertEqual([], kanban_board.needs_release) |
316 | + self.assertEqual([], kanban_board.released) |
317 | |
318 | def test_add_needs_review(self): |
319 | """ |
320 | @@ -317,15 +317,15 @@ |
321 | """ |
322 | bug = Bug("1", "kanban", MEDIUM, IN_PROGRESS, "A title", |
323 | merge_proposal="url", merge_proposal_status=NEEDS_REVIEW) |
324 | - milestone = self.create_test_class() |
325 | - milestone.add(bug) |
326 | - self.assertEqual([bug], milestone.bugs) |
327 | - self.assertEqual([], milestone.queued) |
328 | - self.assertEqual([], milestone.in_progress) |
329 | - self.assertEqual([bug], milestone.needs_review) |
330 | - self.assertEqual([], milestone.needs_testing) |
331 | - self.assertEqual([], milestone.needs_release) |
332 | - self.assertEqual([], milestone.released) |
333 | + kanban_board = self.create_test_class() |
334 | + kanban_board.add(bug) |
335 | + self.assertEqual([bug], kanban_board.bugs) |
336 | + self.assertEqual([], kanban_board.queued) |
337 | + self.assertEqual([], kanban_board.in_progress) |
338 | + self.assertEqual([bug], kanban_board.needs_review) |
339 | + self.assertEqual([], kanban_board.needs_testing) |
340 | + self.assertEqual([], kanban_board.needs_release) |
341 | + self.assertEqual([], kanban_board.released) |
342 | |
343 | def test_add_needs_testing(self): |
344 | """ |
345 | @@ -335,15 +335,15 @@ |
346 | """ |
347 | bug = Bug("1", "kanban", MEDIUM, IN_PROGRESS, "A title", |
348 | merge_proposal="url", merge_proposal_status=APPROVED) |
349 | - milestone = self.create_test_class() |
350 | - milestone.add(bug) |
351 | - self.assertEqual([bug], milestone.bugs) |
352 | - self.assertEqual([], milestone.queued) |
353 | - self.assertEqual([], milestone.in_progress) |
354 | - self.assertEqual([], milestone.needs_review) |
355 | - self.assertEqual([bug], milestone.needs_testing) |
356 | - self.assertEqual([], milestone.needs_release) |
357 | - self.assertEqual([], milestone.released) |
358 | + kanban_board = self.create_test_class() |
359 | + kanban_board.add(bug) |
360 | + self.assertEqual([bug], kanban_board.bugs) |
361 | + self.assertEqual([], kanban_board.queued) |
362 | + self.assertEqual([], kanban_board.in_progress) |
363 | + self.assertEqual([], kanban_board.needs_review) |
364 | + self.assertEqual([bug], kanban_board.needs_testing) |
365 | + self.assertEqual([], kanban_board.needs_release) |
366 | + self.assertEqual([], kanban_board.released) |
367 | |
368 | def test_add_needs_release(self): |
369 | """ |
370 | @@ -354,15 +354,15 @@ |
371 | bug = Bug("1", "kanban", MEDIUM, FIX_COMMITTED, "A title", |
372 | merge_proposal="url", merge_proposal_status=MERGED, |
373 | tags=["verified"]) |
374 | - milestone = self.create_test_class() |
375 | - milestone.add(bug) |
376 | - self.assertEqual([bug], milestone.bugs) |
377 | - self.assertEqual([], milestone.queued) |
378 | - self.assertEqual([], milestone.in_progress) |
379 | - self.assertEqual([], milestone.needs_review) |
380 | - self.assertEqual([], milestone.needs_testing) |
381 | - self.assertEqual([bug], milestone.needs_release) |
382 | - self.assertEqual([], milestone.released) |
383 | + kanban_board = self.create_test_class() |
384 | + kanban_board.add(bug) |
385 | + self.assertEqual([bug], kanban_board.bugs) |
386 | + self.assertEqual([], kanban_board.queued) |
387 | + self.assertEqual([], kanban_board.in_progress) |
388 | + self.assertEqual([], kanban_board.needs_review) |
389 | + self.assertEqual([], kanban_board.needs_testing) |
390 | + self.assertEqual([bug], kanban_board.needs_release) |
391 | + self.assertEqual([], kanban_board.released) |
392 | |
393 | def test_add_released(self): |
394 | """ |
395 | @@ -371,47 +371,31 @@ |
396 | default story. |
397 | """ |
398 | bug = Bug("1", "kanban", MEDIUM, FIX_RELEASED, "A title") |
399 | - milestone = self.create_test_class() |
400 | - milestone.add(bug) |
401 | - self.assertEqual([bug], milestone.bugs) |
402 | - self.assertEqual([], milestone.queued) |
403 | - self.assertEqual([], milestone.in_progress) |
404 | - self.assertEqual([], milestone.needs_review) |
405 | - self.assertEqual([], milestone.needs_testing) |
406 | - self.assertEqual([], milestone.needs_release) |
407 | - self.assertEqual([bug], milestone.released) |
408 | - |
409 | - |
410 | -class MilestoneTest(BugCollectionMixinTestBase, TestCase): |
411 | - |
412 | - def create_test_class(self): |
413 | - """ |
414 | - Create an L{Milestone} for use in L{BugCollectionMixinTestBase} tests. |
415 | - """ |
416 | - return Milestone("project", "name") |
417 | - |
418 | - def test_instantiate(self): |
419 | - """ |
420 | - An L{Milestone} needs the name of the project and its own name when |
421 | - its instantiated. |
422 | - """ |
423 | - milestone = Milestone("project", "name") |
424 | - self.assertEqual("project", milestone.project) |
425 | - self.assertEqual("name", milestone.name) |
426 | - self.assertEqual([], milestone.stories) |
427 | + kanban_board = self.create_test_class() |
428 | + kanban_board.add(bug) |
429 | + self.assertEqual([bug], kanban_board.bugs) |
430 | + self.assertEqual([], kanban_board.queued) |
431 | + self.assertEqual([], kanban_board.in_progress) |
432 | + self.assertEqual([], kanban_board.needs_review) |
433 | + self.assertEqual([], kanban_board.needs_testing) |
434 | + self.assertEqual([], kanban_board.needs_release) |
435 | + self.assertEqual([bug], kanban_board.released) |
436 | + |
437 | + |
438 | +class StoryCollectionMixinTestBase(object): |
439 | |
440 | def test_add_bug_with_story_tag_creates_story(self): |
441 | """ |
442 | - If a L{Bug} with a C{story-<name>} tag is added to an L{Milestone}, |
443 | - and a L{Story} with a matching name doesn't exist, one will be created |
444 | - and the bug will be added to it. |
445 | + If a L{Bug} with a C{story-<name>} tag is added to a |
446 | + L{StoryCollectionMixin}, and a L{Story} with a matching name doesn't |
447 | + exist, one will be created and the bug will be added to it. |
448 | """ |
449 | bug = Bug("1", "kanban", MEDIUM, NEW, "A title", tags=["story-test"]) |
450 | - milestone = Milestone("project", "name") |
451 | - milestone.add(bug) |
452 | - self.assertEqual([bug], milestone.bugs) |
453 | - self.assertEqual(1, len(milestone.stories)) |
454 | - story = milestone.stories[0] |
455 | + kanban_board = self.create_test_class() |
456 | + kanban_board.add(bug) |
457 | + self.assertEqual([bug], kanban_board.bugs) |
458 | + self.assertEqual(1, len(kanban_board.stories)) |
459 | + story = kanban_board.stories[0] |
460 | self.assertIs("story-test", story.name) |
461 | self.assertEqual([bug], story.queued) |
462 | self.assertEqual([], story.in_progress) |
463 | @@ -423,15 +407,16 @@ |
464 | def test_add_bug_with_story_tag_adds_to_existing_story(self): |
465 | """ |
466 | If a L{Story} already exists, and a L{Bug} with a matching tag is |
467 | - added to an L{Milestone}, it will be added to the existing story. |
468 | + added to a L{StoryCollectionMixin}, it will be added to the existing |
469 | + story. |
470 | """ |
471 | bug1 = Bug("1", "kanban", MEDIUM, NEW, "A title", tags=["story-test"]) |
472 | bug2 = Bug("2", "kanban", MEDIUM, NEW, "A title", tags=["story-test"]) |
473 | - milestone = Milestone("project", "name") |
474 | - milestone.add(bug1) |
475 | - milestone.add(bug2) |
476 | - self.assertEqual(1, len(milestone.stories)) |
477 | - story = milestone.stories[0] |
478 | + kanban_board = self.create_test_class() |
479 | + kanban_board.add(bug1) |
480 | + kanban_board.add(bug2) |
481 | + self.assertEqual(1, len(kanban_board.stories)) |
482 | + story = kanban_board.stories[0] |
483 | self.assertIs("story-test", story.name) |
484 | self.assertEqual([bug1, bug2], story.queued) |
485 | self.assertEqual([], story.in_progress) |
486 | @@ -443,35 +428,77 @@ |
487 | def test_add_bug_with_multiple_story_tags(self): |
488 | """ |
489 | If a L{Bug} has more than one C{story-<name>} tag it will be added to |
490 | - multiple L{Story}s when its added to an L{Milestone}. |
491 | + multiple L{Story}s when its added to a L{StoryCollectionMixin}. |
492 | """ |
493 | bug = Bug("1", "kanban", MEDIUM, NEW, "A title", |
494 | tags=["story-test1", "story-test2"]) |
495 | - milestone = Milestone("project", "name") |
496 | - milestone.add(bug) |
497 | - self.assertEqual(2, len(milestone.stories)) |
498 | + kanban_board = self.create_test_class() |
499 | + kanban_board.add(bug) |
500 | + self.assertEqual(2, len(kanban_board.stories)) |
501 | |
502 | - story1 = milestone.stories[0] |
503 | + story1 = kanban_board.stories[0] |
504 | self.assertIs("story-test1", story1.name) |
505 | self.assertEqual([bug], story1.queued) |
506 | |
507 | - story2 = milestone.stories[1] |
508 | + story2 = kanban_board.stories[1] |
509 | self.assertIs("story-test2", story2.name) |
510 | self.assertEqual([bug], story2.queued) |
511 | |
512 | def test_stories_sorting(self): |
513 | """ |
514 | - The L{Milestone.stories} property sorts L{Story}s alphabetically. The |
515 | - default L{Story} is always at the end of the list. |
516 | + The L{StoryCollectionMixin.stories} property sorts L{Story}s |
517 | + alphabetically. The default L{Story} is always at the end of the |
518 | + list. |
519 | """ |
520 | bug1 = Bug("1", "kanban", MEDIUM, NEW, "A title") |
521 | bug2 = Bug("2", "kanban", MEDIUM, NEW, "A title", |
522 | tags=["story-test1", "story-test2"]) |
523 | - milestone = Milestone("project", "name") |
524 | - milestone.add(bug1) |
525 | - milestone.add(bug2) |
526 | + kanban_board = self.create_test_class() |
527 | + kanban_board.add(bug1) |
528 | + kanban_board.add(bug2) |
529 | self.assertEqual(["story-test1", "story-test2", None], |
530 | - [story.name for story in milestone.stories]) |
531 | + [story.name for story in kanban_board.stories]) |
532 | + |
533 | + |
534 | +class MilestoneBoardTest(BugCollectionMixinTestBase, |
535 | + StoryCollectionMixinTestBase, TestCase): |
536 | + |
537 | + def create_test_class(self): |
538 | + """ |
539 | + Create a L{MilestoneBoard} for use in L{BugCollectionMixinTestBase} |
540 | + and L{StoryCollectionMixinTestBase} tests. |
541 | + """ |
542 | + return MilestoneBoard("project", "milestone") |
543 | + |
544 | + def test_instantiate(self): |
545 | + """ |
546 | + A L{MilestoneBoard} needs the name of the project and its own name |
547 | + when its instantiated. |
548 | + """ |
549 | + kanban_board = MilestoneBoard("project", "milestone") |
550 | + self.assertEqual("project", kanban_board.project_name) |
551 | + self.assertEqual("milestone", kanban_board.name) |
552 | + self.assertEqual([], kanban_board.stories) |
553 | + |
554 | + |
555 | +class PersonBoardTest(BugCollectionMixinTestBase, StoryCollectionMixinTestBase, |
556 | + TestCase): |
557 | + |
558 | + def create_test_class(self): |
559 | + """ |
560 | + Create a L{PersonBoard} for use in L{BugCollectionMixinTestBase} and |
561 | + L{StoryCollectionMixinTestBase} tests. |
562 | + """ |
563 | + return PersonBoard("team") |
564 | + |
565 | + def test_instantiate(self): |
566 | + """ |
567 | + A L{PersonBoard} needs the name of the person or team when its |
568 | + instantiated. |
569 | + """ |
570 | + kanban_board = PersonBoard("person") |
571 | + self.assertEqual("person", kanban_board.name) |
572 | + self.assertEqual([], kanban_board.stories) |
573 | |
574 | |
575 | class StoryTest(BugCollectionMixinTestBase, TestCase): |
576 | |
577 | === modified file 'kanban/tests/test_html.py' |
578 | --- kanban/tests/test_html.py 2011-02-08 10:28:49 +0000 |
579 | +++ kanban/tests/test_html.py 2011-02-17 10:14:45 +0000 |
580 | @@ -2,9 +2,9 @@ |
581 | |
582 | from testtools import TestCase |
583 | |
584 | +from kanban.board import Bug, MEDIUM, CRITICAL, IN_PROGRESS, NEEDS_REVIEW |
585 | from kanban.html import ( |
586 | branch_name, branch_url, importance_css_class, status_css_class, warn) |
587 | -from kanban.milestone import Bug, MEDIUM, CRITICAL, IN_PROGRESS, NEEDS_REVIEW |
588 | from kanban.roadmap import QUEUED |
589 | |
590 |
Much better, thanks.