Package trac :: Package ticket :: Package tests :: Module wikisyntax

Source Code for Module trac.ticket.tests.wikisyntax

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (C) 2006-2020 Edgewall Software 
  4  # All rights reserved. 
  5  # 
  6  # This software is licensed as described in the file COPYING, which 
  7  # you should have received as part of this distribution. The terms 
  8  # are also available at https://trac.edgewall.org/wiki/TracLicense. 
  9  # 
 10  # This software consists of voluntary contributions made by many 
 11  # individuals. For the exact contribution history, see the revision 
 12  # history and logs, available at https://trac.edgewall.org/log/. 
 13   
 14  import unittest 
 15  from datetime import timedelta 
 16   
 17  from trac.test import locale_en 
 18  from trac.ticket.query import QueryModule 
 19  from trac.ticket.report import ReportModule 
 20  from trac.ticket.roadmap import RoadmapModule 
 21  from trac.ticket.model import Milestone, Ticket 
 22  from trac.util.datefmt import (datetime_now, format_datetime, pretty_timedelta, 
 23                                 utc) 
 24  from trac.wiki.tests import formatter 
 25   
 26   
 27  TICKET_TEST_CASES = u""" 
 28  ============================== ticket: link resolver 
 29  ticket:1 
 30  ticket:12 
 31  ticket:abc 
 32  ------------------------------ 
 33  <p> 
 34  <a class="new ticket" href="/ticket/1" title="This is the summary (new)">ticket:1</a> 
 35  <a class="missing ticket">ticket:12</a> 
 36  <a class="missing ticket">ticket:abc</a> 
 37  </p> 
 38  ------------------------------ 
 39  ============================== ticket: link resolver + arguments 
 40  ticket:1?format=csv 
 41  ticket:1#comment:3 
 42  ------------------------------ 
 43  <p> 
 44  <a class="new ticket" href="/ticket/1?format=csv" title="This is the summary (new)">ticket:1?format=csv</a> 
 45  <a class="new ticket" href="/ticket/1#comment:3" title="This is the summary (new)">ticket:1#comment:3</a> 
 46  </p> 
 47  ------------------------------ 
 48  ============================== ticket: link resolver with ranges 
 49  ticket:12-14,33 
 50  ticket:12,33?order=created 
 51  ------------------------------ 
 52  <p> 
 53  <a href="/query?id=12-14%2C33" title="Tickets 12-14, 33">ticket:12-14,33</a> 
 54  <a href="/query?id=12%2C33&amp;order=created" title="Tickets 12, 33">ticket:12,33?order=created</a> 
 55  </p> 
 56  ------------------------------ 
 57  ============================== ticket link shorthand form 
 58  #1, #2 
 59  #12, #abc 
 60  ------------------------------ 
 61  <p> 
 62  <a class="new ticket" href="/ticket/1" title="This is the summary (new)">#1</a>, <a class="missing ticket">#2</a> 
 63  <a class="missing ticket">#12</a>, #abc 
 64  </p> 
 65  ------------------------------ 
 66  ============================== ticket link shorthand form with ranges 
 67  #1-5,42 
 68  #1,3,5,7 
 69  ------------------------------ 
 70  <p> 
 71  <a href="/query?id=1-5%2C42" title="Tickets 1-5, 42">#1-5,42</a> 
 72  <a href="/query?id=1%2C3%2C5%2C7" title="Tickets 1, 3, 5, 7">#1,3,5,7</a> 
 73  </p> 
 74  ------------------------------ 
 75  ============================== ticket link shorthand form with long ranges (#10111 regression) 
 76  #1-123456789012345678901234 
 77  ------------------------------ 
 78  <p> 
 79  <a href="/query?id=1-123456789012345678901234" title="Tickets 1-123456789012345678901234">#1-123456789012345678901234</a> 
 80  </p> 
 81  ------------------------------ 
 82  ============================== escaping the above 
 83  !#1 
 84  ------------------------------ 
 85  <p> 
 86  #1 
 87  </p> 
 88  ------------------------------ 
 89  #1 
 90  ============================== InterTrac for tickets 
 91  trac:ticket:2041 
 92  [trac:ticket:2041 Trac #2041] 
 93  #T2041 
 94  #trac2041 
 95  ------------------------------ 
 96  <p> 
 97  <a class="ext-link" href="https://trac.edgewall.org/intertrac/ticket%3A2041" title="ticket:2041 in The Trac Project"><span class="icon"></span>trac:ticket:2041</a> 
 98  <a class="ext-link" href="https://trac.edgewall.org/intertrac/ticket%3A2041" title="ticket:2041 in The Trac Project"><span class="icon"></span>Trac #2041</a> 
 99  <a class="ext-link" href="https://trac.edgewall.org/intertrac/ticket%3A2041" title="ticket:2041 in The Trac Project"><span class="icon"></span>#T2041</a> 
100  <a class="ext-link" href="https://trac.edgewall.org/intertrac/ticket%3A2041" title="ticket:2041 in The Trac Project"><span class="icon"></span>#trac2041</a> 
101  </p> 
102  ------------------------------ 
103  ============================== Ticket InterTrac shorthands 
104  T:#2041 
105  trac:#2041 
106  ------------------------------ 
107  <p> 
108  <a class="ext-link" href="https://trac.edgewall.org/intertrac/%232041" title="#2041 in The Trac Project"><span class="icon"></span>T:#2041</a> 
109  <a class="ext-link" href="https://trac.edgewall.org/intertrac/%232041" title="#2041 in The Trac Project"><span class="icon"></span>trac:#2041</a> 
110  </p> 
111  ------------------------------ 
112  ============================== ticket syntax with unicode digits 
113  #⁴² 
114  #1-⁵,42 
115  #1,³,5,7 
116  #T²⁰⁴¹ 
117  #trac²⁰⁴¹ 
118  ------------------------------ 
119  <p> 
120  #⁴² 
121  <a class="new ticket" href="/ticket/1" title="This is the summary (new)">#1</a>-⁵,42 
122  <a class="new ticket" href="/ticket/1" title="This is the summary (new)">#1</a>,³,5,7 
123  #T²⁰⁴¹ 
124  #trac²⁰⁴¹ 
125  </p> 
126  ------------------------------ 
127  """ # " 
128   
129 -def ticket_setup(tc):
130 config = tc.env.config 131 config.set('ticket-custom', 'custom1', 'text') 132 config.save() 133 ticket = Ticket(tc.env) 134 ticket.values.update({'reporter': 'santa', 135 'summary': 'This is the summary', 136 'status': 'new'}) 137 ticket.insert()
138
139 -def ticket_teardown(tc):
140 config = tc.env.config 141 config.remove('ticket-custom', 'custom1') 142 config.save() 143 tc.env.reset_db()
144 145 146 147 REPORT_TEST_CASES = u""" 148 ============================== report link shorthand form 149 {1}, {2} 150 {12}, {abc} 151 ------------------------------ 152 <p> 153 <a class="report" href="/report/1">{1}</a>, <a class="report" href="/report/2">{2}</a> 154 <a class="missing report" title="report does not exist">{12}</a>, {abc} 155 </p> 156 ------------------------------ 157 ============================== escaping the above 158 !{1} 159 ------------------------------ 160 <p> 161 {1} 162 </p> 163 ------------------------------ 164 {1} 165 ============================== ticket shorthands, not numerical HTML entities 166 &#1; &#23; 167 ------------------------------ 168 <p> 169 &amp;#1; &amp;#23; 170 </p> 171 ------------------------------ 172 &amp;#1; &amp;#23; 173 ============================== report link with non-digits 174 report:blah 175 ------------------------------ 176 <p> 177 <a class="missing report" title="report does not exist">report:blah</a> 178 </p> 179 ------------------------------ 180 <a class="missing report" title="report does not exist">report:blah</a> 181 ============================== InterTrac for reports 182 trac:report:1 183 [trac:report:1 Trac r1] 184 {T1} 185 {trac1} 186 {trac 1} 187 ------------------------------ 188 <p> 189 <a class="ext-link" href="https://trac.edgewall.org/intertrac/report%3A1" title="report:1 in The Trac Project"><span class="icon"></span>trac:report:1</a> 190 <a class="ext-link" href="https://trac.edgewall.org/intertrac/report%3A1" title="report:1 in The Trac Project"><span class="icon"></span>Trac r1</a> 191 <a class="ext-link" href="https://trac.edgewall.org/intertrac/report%3A1" title="report:1 in The Trac Project"><span class="icon"></span>{T1}</a> 192 <a class="ext-link" href="https://trac.edgewall.org/intertrac/report%3A1" title="report:1 in The Trac Project"><span class="icon"></span>{trac1}</a> 193 <a class="ext-link" href="https://trac.edgewall.org/intertrac/report%3A1" title="report:1 in The Trac Project"><span class="icon"></span>{trac 1}</a> 194 </p> 195 ------------------------------ 196 ============================== report syntax with unicode digits 197 {⁴²} !{⁴²} 198 {T⁴²} 199 {trac⁴²} 200 ------------------------------ 201 <p> 202 {⁴²} !{⁴²} 203 {T⁴²} 204 {trac⁴²} 205 </p> 206 ------------------------------ 207 """ # ' 208
209 -def report_setup(tc):
210 def create_report(tc, id): 211 tc.env.db_transaction(""" 212 INSERT INTO report (id,title,query,description) 213 VALUES (%s,%s,'SELECT 1','')""", (id, 'Report %s' % id))
214 create_report(tc, 1) 215 create_report(tc, 2) 216 217 218 dt_past = datetime_now(utc) - timedelta(days=1) 219 dt_future = datetime_now(utc) + timedelta(days=1) 220 221 222 MILESTONE_TEST_CASES = u""" 223 ============================== milestone: link resolver 224 milestone:foo 225 [milestone:boo Milestone Boo] 226 [milestone:roo Milestone Roo] 227 [milestone:woo Milestone Woo] 228 [milestone:zoo Milestone Zoo] 229 ------------------------------ 230 <p> 231 <a class="missing milestone" href="/milestone/foo" rel="nofollow">milestone:foo</a> 232 <a class="milestone" href="/milestone/boo" title="No date set">Milestone Boo</a> 233 <a class="closed milestone" href="/milestone/roo" title="Completed %(dt_past)s ago (%(datestr_past)s)">Milestone Roo</a> 234 <a class="milestone" href="/milestone/woo" title="Due in %(dt_future)s (%(datestr_future)s)">Milestone Woo</a> 235 <a class="milestone" href="/milestone/zoo" title="%(dt_past)s late (%(datestr_past)s)">Milestone Zoo</a> 236 </p> 237 ------------------------------ 238 ============================== milestone: link resolver + arguments 239 milestone:?action=new 240 [milestone:boo#KnownIssues Known Issues for 1.0] 241 ------------------------------ 242 <p> 243 <a class="missing milestone" href="/milestone/?action=new" rel="nofollow">milestone:?action=new</a> 244 <a class="milestone" href="/milestone/boo#KnownIssues" title="No date set">Known Issues for 1.0</a> 245 </p> 246 ------------------------------ 247 """ % {'dt_past': pretty_timedelta(dt_past), 248 'dt_future': pretty_timedelta(dt_future), 249 'datestr_past': format_datetime(dt_past, locale=locale_en, tzinfo=utc), 250 'datestr_future': format_datetime(dt_future, locale=locale_en, 251 tzinfo=utc)} #" 252
253 -def milestone_setup(tc):
254 boo = Milestone(tc.env) 255 boo.name = 'boo' 256 boo.completed = boo.due = None 257 boo.insert() 258 roo = Milestone(tc.env) 259 roo.name = 'roo' 260 roo.completed = dt_past 261 roo.due = None 262 roo.insert() 263 woo = Milestone(tc.env) 264 woo.name = 'woo' 265 woo.completed = None 266 woo.due = dt_future 267 woo.insert() 268 zoo = Milestone(tc.env) 269 zoo.name = 'zoo' 270 zoo.completed = None 271 zoo.due = dt_past 272 zoo.insert()
273
274 -def milestone_teardown(tc):
275 tc.env.reset_db()
276 277 278 279 QUERY_TEST_CASES = u""" 280 ============================== query: link resolver 281 query:?order=priority 282 283 query:?order=priority&owner=me 284 285 query:?type=résumé 286 287 query:status=new|reopened 288 289 query:reporter!= 290 291 query:reporter=joe|jack&owner=me 292 293 query:group=owner 294 295 query:verbose=1 296 297 query:summary=résumé 298 ------------------------------ 299 <p> 300 <a class="query" href="/query?order=priority">query:?order=priority</a> 301 </p> 302 <p> 303 <a class="query" href="/query?order=priority&amp;owner=me">query:?order=priority&amp;owner=me</a> 304 </p> 305 <p> 306 <a class="query" href="/query?type=r%C3%A9sum%C3%A9">query:?type=résumé</a> 307 </p> 308 <p> 309 <a class="query" href="/query?status=new&amp;status=reopened&amp;order=priority">query:status=new|reopened</a> 310 </p> 311 <p> 312 <a class="query" href="/query?reporter=!&amp;order=priority">query:reporter!=</a> 313 </p> 314 <p> 315 <a class="query" href="/query?owner=me&amp;reporter=joe&amp;reporter=jack&amp;order=priority">query:reporter=joe|jack&amp;owner=me</a> 316 </p> 317 <p> 318 <a class="query" href="/query?group=owner&amp;order=priority">query:group=owner</a> 319 </p> 320 <p> 321 <a class="query" href="/query?order=priority&amp;row=description">query:verbose=1</a> 322 </p> 323 <p> 324 <a class="query" href="/query?summary=r%C3%A9sum%C3%A9&amp;order=priority">query:summary=résumé</a> 325 </p> 326 ------------------------------ 327 ============================== TicketQuery macro: no results, list form 328 Reopened tickets: [[TicketQuery(status=reopened)]] 329 ------------------------------ 330 <p> 331 Reopened tickets: <span class="query_no_results">No results</span> 332 </p> 333 ------------------------------ 334 ============================== TicketQuery macro: no results, count 0 335 Reopened tickets: [[TicketQuery(status=reopened, format=count)]] 336 ------------------------------ 337 <p> 338 Reopened tickets: <span class="query_count" title="0 tickets for which status=reopened&amp;max=0&amp;order=id">0</span> 339 </p> 340 ------------------------------ 341 ============================== TicketQuery macro: no results, compact form 342 Reopened tickets: [[TicketQuery(status=reopened, format=compact)]] 343 ------------------------------ 344 <p> 345 Reopened tickets: <span class="query_no_results">No results</span> 346 </p> 347 ------------------------------ 348 ============================== TicketQuery macro: one result, list form 349 New tickets: [[TicketQuery(status=new)]] 350 ------------------------------ 351 <p> 352 New tickets: </p><div><dl class="wiki compact"><dt><a class="new" href="/ticket/1" title="This is the summary">#1</a></dt><dd>This is the summary</dd></dl></div><p> 353 </p> 354 ------------------------------ 355 ============================== TicketQuery macro: one result, count 1 356 New tickets: [[TicketQuery(status=new, format=count)]] 357 ------------------------------ 358 <p> 359 New tickets: <span class="query_count" title="1 ticket for which status=new&amp;max=0&amp;order=id">1</span> 360 </p> 361 ------------------------------ 362 ============================== TicketQuery macro: one result, compact form 363 New tickets: [[TicketQuery(status=new, format=compact)]] 364 ------------------------------ 365 <p> 366 New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span> 367 </p> 368 ------------------------------ 369 ============================== TicketQuery macro: duplicated fields 370 New tickets: [[TicketQuery(status=new, format=compact, col=summary|status|status)]] 371 ------------------------------ 372 <p> 373 New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span> 374 </p> 375 ------------------------------ 376 ============================== TicketQuery macro: duplicated custom fields 377 New tickets: [[TicketQuery(status=new, format=compact, col=summary|custom1|custom1)]] 378 ------------------------------ 379 <p> 380 New tickets: <span><a class="new" href="/ticket/1" title="This is the summary">#1</a></span> 381 </p> 382 ------------------------------ 383 """ 384 385 QUERY2_TEST_CASES = u""" 386 ============================== TicketQuery macro: two results, list form 387 New tickets: [[TicketQuery(status=new, order=reporter)]] 388 ------------------------------ 389 <p> 390 New tickets: </p><div><dl class="wiki compact"><dt><a class="new" href="/ticket/2" title="This is another summary">#2</a></dt><dd>This is another summary</dd><dt><a class="new" href="/ticket/1" title="This is the summary">#1</a></dt><dd>This is the summary</dd></dl></div><p> 391 </p> 392 ------------------------------ 393 ============================== TicketQuery macro: two results, count 2 394 New tickets: [[TicketQuery(status=new, order=reporter, format=count)]] 395 ------------------------------ 396 <p> 397 New tickets: <span class="query_count" title="2 tickets for which status=new&amp;max=0&amp;order=reporter">2</span> 398 </p> 399 ------------------------------ 400 ============================== TicketQuery macro: two results, compact form 401 New tickets: [[TicketQuery(status=new, order=reporter, format=compact)]] 402 ------------------------------ 403 <p> 404 New tickets: <span><a class="new" href="/ticket/2" title="This is another summary">#2</a>, <a class="new" href="/ticket/1" title="This is the summary">#1</a></span> 405 </p> 406 ------------------------------ 407 """ 408
409 -def query2_setup(tc):
410 ticket = Ticket(tc.env) 411 ticket.values.update({'reporter': 'santa', 412 'summary': 'This is the summary', 413 'status': 'new'}) 414 ticket.insert() 415 ticket = Ticket(tc.env) 416 ticket.values.update({'reporter': 'claus', 417 'summary': 'This is another summary', 418 'status': 'new'}) 419 ticket.insert()
420
421 -def query2_teardown(tc):
422 tc.env.reset_db()
423 424 425 COMMENT_TEST_CASES = u""" 426 ============================== comment: link resolver (deprecated) 427 comment:ticket:1:1 (deprecated) 428 [comment:ticket:1:1 see above] (deprecated) 429 comment:ticket:1:description (deprecated) 430 [comment:ticket:1:description see descr] (deprecated) 431 comment:ticket:2:1 (deprecated) 432 comment:ticket:2:3 (deprecated) 433 comment:ticket:3:1 (deprecated) 434 comment:tiket:2:1 (deprecated) 435 comment:ticket:two:1 (deprecated) 436 comment:ticket:2:1a (deprecated) 437 comment:ticket:2:one (deprecated) 438 comment:ticket:1: (deprecated) 439 comment:ticket::2 (deprecated) 440 comment:ticket:: (deprecated) 441 ------------------------------ 442 <p> 443 <a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">comment:ticket:1:1</a> (deprecated) 444 <a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">see above</a> (deprecated) 445 <a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">comment:ticket:1:description</a> (deprecated) 446 <a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">see descr</a> (deprecated) 447 <a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:ticket:2:1</a> (deprecated) 448 <a class="missing ticket" title="ticket comment does not exist">comment:ticket:2:3</a> (deprecated) 449 <a class="missing ticket" title="ticket does not exist">comment:ticket:3:1</a> (deprecated) 450 comment:tiket:2:1 (deprecated) 451 comment:ticket:two:1 (deprecated) 452 comment:ticket:2:1a (deprecated) 453 comment:ticket:2:one (deprecated) 454 comment:ticket:1: (deprecated) 455 comment:ticket::2 (deprecated) 456 comment:ticket:: (deprecated) 457 </p> 458 ------------------------------ 459 ============================== comment: link resolver 460 comment:1 461 comment:₁ 462 [comment:1 see above] 463 comment:description 464 [comment:description see descr] 465 comment: 466 comment:one 467 comment:1a 468 ------------------------------ 469 <p> 470 <a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:1</a> 471 <a class="missing ticket" title="ticket comment does not exist">comment:₁</a> 472 <a class="ticket" href="/ticket/2#comment:1" title="Comment 1">see above</a> 473 <a class="ticket" href="/ticket/2#comment:description" title="Description">comment:description</a> 474 <a class="ticket" href="/ticket/2#comment:description" title="Description">see descr</a> 475 comment: 476 comment:one 477 comment:1a 478 </p> 479 ------------------------------ 480 ============================== comment: link resolver with ticket number 481 comment:1:ticket:1 482 comment:₁:ticket:1 483 [comment:1:ticket:1 see above] 484 comment:description:ticket:1 485 [comment:description:ticket:1 see descr] 486 comment:1:ticket:2 487 comment:3:ticket:2 488 comment:1:ticket:3 489 comment:2:tiket:1 490 comment:1:ticket:two 491 comment:one:ticket:1 492 comment:1a:ticket:1 493 comment:ticket:1 494 comment:2:ticket: 495 comment::ticket: 496 ------------------------------ 497 <p> 498 <a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">comment:1:ticket:1</a> 499 <a class="missing ticket" title="ticket comment does not exist">comment:₁:ticket:1</a> 500 <a class="new ticket" href="/ticket/1#comment:1" title="Comment 1 for Ticket #1">see above</a> 501 <a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">comment:description:ticket:1</a> 502 <a class="new ticket" href="/ticket/1#comment:description" title="Description for Ticket #1">see descr</a> 503 <a class="ticket" href="/ticket/2#comment:1" title="Comment 1">comment:1:ticket:2</a> 504 <a class="missing ticket" title="ticket comment does not exist">comment:3:ticket:2</a> 505 <a class="missing ticket" title="ticket does not exist">comment:1:ticket:3</a> 506 comment:2:tiket:1 507 comment:1:ticket:two 508 comment:one:ticket:1 509 comment:1a:ticket:1 510 comment:ticket:1 511 comment:2:ticket: 512 comment::ticket: 513 </p> 514 ------------------------------ 515 """ # " 516 517 # NOTE: the following test cases: 518 # 519 # comment:2 520 # [comment:2 see above] 521 # 522 # would trigger an error in the workaround code ../api.py, line 235 523 # As it's a problem with a temp workaround, I think there's no need 524 # to fix it for now. 525
526 -def comment_setup(tc):
527 ticket1 = Ticket(tc.env) 528 ticket1.values.update({'reporter': 'santa', 529 'summary': 'This is the summary for ticket 1', 530 'status': 'new'}) 531 ticket1.insert() 532 ticket1.save_changes(comment='This is the comment for ticket 1') 533 ticket2 = Ticket(tc.env) 534 ticket2.values.update({'reporter': 'claws', 535 'summary': 'This is the summary for ticket 2', 536 'status': 'closed'}) 537 ticket2.insert() 538 ticket2.save_changes(comment='This is the comment for ticket 2')
539
540 -def comment_teardown(tc):
541 tc.env.reset_db()
542 543
544 -def suite():
545 suite = unittest.TestSuite() 546 suite.addTest(formatter.suite(TICKET_TEST_CASES, ticket_setup, __file__, 547 ticket_teardown)) 548 suite.addTest(formatter.suite(REPORT_TEST_CASES, report_setup, __file__)) 549 suite.addTest(formatter.suite(MILESTONE_TEST_CASES, milestone_setup, 550 __file__, milestone_teardown)) 551 suite.addTest(formatter.suite(QUERY_TEST_CASES, ticket_setup, __file__, 552 ticket_teardown)) 553 suite.addTest(formatter.suite(QUERY2_TEST_CASES, query2_setup, __file__, 554 query2_teardown)) 555 suite.addTest(formatter.suite(COMMENT_TEST_CASES, comment_setup, __file__, 556 comment_teardown, ('ticket', 2))) 557 return suite
558 559 if __name__ == '__main__': 560 unittest.main(defaultTest='suite') 561