Passed
Push — master ( 84fcb9...fbaf82 )
by Alexander
04:44 queued 02:05
created

TestGetSelectedTestcases.test_get_selected_testcases_works_with_both_string_and_int_pks()   A

Complexity

Conditions 3

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 16
dl 0
loc 31
rs 9.6
c 0
b 0
f 0
cc 3
nop 1
1
# -*- coding: utf-8 -*-
2
# pylint: disable=invalid-name, too-many-ancestors
3
4
import unittest
5
from http import HTTPStatus
6
from urllib.parse import urlencode
7
8
from django.contrib.auth.models import User
9
from django.urls import reverse
10
from django.forms import ValidationError
11
from django.test import RequestFactory
12
13
from tcms.testcases.fields import MultipleEmailField
14
from tcms.management.models import Priority, Tag
15
from tcms.testcases.models import TestCase
16
from tcms.testcases.models import BugSystem
17
from tcms.testcases.models import TestCasePlan
18
from tcms.testcases.views import get_selected_testcases
19
from tcms.testruns.models import TestCaseRunStatus
20
from tcms.tests.factories import BugFactory
21
from tcms.tests.factories import TestCaseFactory
22
from tcms.tests.factories import TestPlanFactory
23
from tcms.tests import BasePlanCase, BaseCaseRun
24
from tcms.tests import remove_perm_from_user
25
from tcms.tests import user_should_have_perm
26
from tcms.utils.permissions import initiate_user_with_default_setups
27
28
29
class TestGetCaseRunDetailsAsDefaultUser(BaseCaseRun):
30
    """Assert what a default user (non-admin) will see"""
31
32
    @classmethod
33
    def setUpTestData(cls):
34
        super(TestGetCaseRunDetailsAsDefaultUser, cls).setUpTestData()
35
36
    def test_user_in_default_group_sees_comments(self):
37
        # test for https://github.com/kiwitcms/Kiwi/issues/74
38
        initiate_user_with_default_setups(self.tester)
39
40
        url = reverse('caserun-detail-pane', args=[self.case_run_1.case_id])
41
        response = self.client.get(
42
            url,
43
            {
44
                'case_run_id': self.case_run_1.pk,
45
                'case_text_version': self.case_run_1.case.latest_text_version()
46
            }
47
        )
48
49
        self.assertEqual(HTTPStatus.OK, response.status_code)
50
51
        self.assertContains(
52
            response,
53
            '<textarea name="comment" cols="40" id="id_comment" maxlength="10000" '
54
            'rows="10">\n</textarea>',
55
            html=True)
56
57
        for status in TestCaseRunStatus.objects.all():
58
            self.assertContains(
59
                response,
60
                "<input type=\"submit\" class=\"btn btn_%s btn_status js-status-button\" "
61
                "title=\"%s\"" % (status.name.lower(), status.name),
62
                html=False
63
            )
64
65
    def test_user_sees_bugs(self):
66
        bug_1 = BugFactory()
67
        bug_2 = BugFactory()
68
69
        self.case_run_1.add_bug(bug_1.bug_id, bug_1.bug_system.pk)
70
        self.case_run_1.add_bug(bug_2.bug_id, bug_2.bug_system.pk)
71
72
        url = reverse('caserun-detail-pane', args=[self.case_run_1.case.pk])
73
        response = self.client.get(
74
            url,
75
            {
76
                'case_run_id': self.case_run_1.pk,
77
                'case_text_version': self.case_run_1.case.latest_text_version()
78
            }
79
        )
80
81
        self.assertEqual(HTTPStatus.OK, response.status_code)
82
        self.assertContains(response, bug_1.get_full_url())
83
        self.assertContains(response, bug_2.get_full_url())
84
85
86
class TestMultipleEmailField(unittest.TestCase):
87
88
    @classmethod
89
    def setUpClass(cls):
90
        super().setUpClass()
91
        cls.field = MultipleEmailField()
92
93
    def test_to_python(self):
94
        value = u'zhangsan@localhost'
95
        pyobj = self.field.to_python(value)
96
        self.assertEqual(pyobj, [value])
97
98
        value = u'zhangsan@localhost,,[email protected],'
99
        pyobj = self.field.to_python(value)
100
        self.assertEqual(pyobj, [u'zhangsan@localhost', u'[email protected]'])
101
102
        for value in ('', None, []):
103
            pyobj = self.field.to_python(value)
104
            self.assertEqual(pyobj, [])
105
106
    def test_clean(self):
107
        value = u'zhangsan@localhost'
108
        data = self.field.clean(value)
109
        self.assertEqual(data, [value])
110
111
        value = u'zhangsan@localhost,[email protected]'
112
        data = self.field.clean(value)
113
        self.assertEqual(data, [u'zhangsan@localhost', u'[email protected]'])
114
115
        value = u',zhangsan@localhost, ,[email protected], \n'
116
        data = self.field.clean(value)
117
        self.assertEqual(data, [u'zhangsan@localhost', '[email protected]'])
118
119
        value = ',zhangsan,zhangsan@localhost, \n,[email protected], '
120
        self.assertRaises(ValidationError, self.field.clean, value)
121
122
        value = ''
123
        self.field.required = True
124
        self.assertRaises(ValidationError, self.field.clean, value)
125
126
        value = ''
127
        self.field.required = False
128
        data = self.field.clean(value)
129
        self.assertEqual(data, [])
130
131
132
class TestAddIssueToCase(BasePlanCase):
133
    """Tests for adding issue to case"""
134
135
    @classmethod
136
    def setUpTestData(cls):
137
        super(TestAddIssueToCase, cls).setUpTestData()
138
139
        cls.plan_tester = User.objects.create_user(  # nosec:B106:hardcoded_password_funcarg
140
            username='plantester',
141
            email='[email protected]',
142
            password='password')
143
        user_should_have_perm(cls.plan_tester, 'testcases.change_bug')
144
145
        cls.case_bug_url = reverse('testcases-bug', args=[cls.case_1.pk])
146
        cls.issue_tracker = BugSystem.objects.get(name='Bugzilla')
147
148
    def test_add_and_remove_a_bug(self):
149
        user_should_have_perm(self.plan_tester, 'testcases.add_bug')
150
        user_should_have_perm(self.plan_tester, 'testcases.delete_bug')
151
152
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
153
            username=self.plan_tester.username,
154
            password='password')
155
        request_data = {
156
            'handle': 'add',
157
            'case': self.case_1.pk,
158
            'bug_id': '123456',
159
            'bug_system': self.issue_tracker.pk,
160
        }
161
        self.client.get(self.case_bug_url, request_data)
162
        self.assertTrue(self.case_1.case_bug.filter(bug_id='123456').exists())
163
164
        request_data = {
165
            'handle': 'remove',
166
            'case': self.case_1.pk,
167
            'bug_id': '123456',
168
        }
169
        self.client.get(self.case_bug_url, request_data)
170
171
        not_have_bug = self.case_1.case_bug.filter(bug_id='123456').exists()
172
        self.assertTrue(not_have_bug)
173
174
175
class TestOperateCasePlans(BasePlanCase):
176
    """Test operation in case' plans tab"""
177
178
    @classmethod
179
    def setUpTestData(cls):
180
        super(TestOperateCasePlans, cls).setUpTestData()
181
182
        # Besides the plan and its cases created in parent class, this test case
183
        # also needs other cases in order to list multiple plans of a case and
184
        # remove a plan from a case.
185
186
        cls.plan_test_case_plans = TestPlanFactory(author=cls.tester,
187
                                                   product=cls.product,
188
                                                   product_version=cls.version)
189
        cls.plan_test_add = TestPlanFactory(author=cls.tester,
190
                                            product=cls.product,
191
                                            product_version=cls.version)
192
        cls.plan_test_remove = TestPlanFactory(author=cls.tester,
193
                                               product=cls.product,
194
                                               product_version=cls.version)
195
196
        cls.case_1.add_to_plan(cls.plan_test_case_plans)
197
        cls.case_1.add_to_plan(cls.plan_test_remove)
198
199
        cls.plan_tester = User.objects.create_user(  # nosec:B106:hardcoded_password_funcarg
200
            username='plantester',
201
            email='[email protected]',
202
            password='password')
203
204
        cls.case_plans_url = reverse('testcases-plan', args=[cls.case_1.pk])
205
206
    def tearDown(self):
207
        remove_perm_from_user(self.plan_tester, 'testcases.add_testcaseplan')
208
        remove_perm_from_user(self.plan_tester, 'testcases.change_testcaseplan')
209
210
    def assert_list_case_plans(self, response, case):
211
        for case_plan_rel in TestCasePlan.objects.filter(case=case):
212
            plan = case_plan_rel.plan
213
            self.assertContains(
214
                response,
215
                '<a href="{0}">TP-{1}: {2}</a>'.format(
216
                    reverse('test_plan_url_short', args=[plan.pk]),
217
                    plan.pk,
218
                    plan.name),
219
                html=True)
220
221
    def test_list_plans(self):
222
        response = self.client.get(self.case_plans_url)
223
        self.assert_list_case_plans(response, self.case_1)
224
225
    def test_missing_permission_to_add(self):
226
        response = self.client.get(self.case_plans_url,
227
                                   {'a': 'add', 'plan_id': self.plan_test_add.pk})
228
        self.assertContains(response, 'Permission denied')
229
230
    def test_missing_permission_to_remove(self):
231
        response = self.client.get(self.case_plans_url,
232
                                   {'a': 'remove', 'plan_id': self.plan_test_remove.pk})
233
        self.assertContains(response, 'Permission denied')
234
235
    def test_add_a_plan(self):
236
        user_should_have_perm(self.plan_tester, 'testcases.add_testcaseplan')
237
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
238
            username=self.plan_tester.username,
239
            password='password')
240
        response = self.client.get(self.case_plans_url,
241
                                   {'a': 'add', 'plan_id': self.plan_test_add.pk})
242
243
        self.assert_list_case_plans(response, self.case_1)
244
245
        self.assertTrue(TestCasePlan.objects.filter(
246
            plan=self.plan_test_add, case=self.case_1).exists())
247
248
    def test_remove_a_plan(self):
249
        user_should_have_perm(self.plan_tester, 'testcases.change_testcaseplan')
250
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
251
            username=self.plan_tester.username,
252
            password='password')
253
        response = self.client.get(self.case_plans_url,
254
                                   {'a': 'remove', 'plan_id': self.plan_test_remove.pk})
255
256
        self.assert_list_case_plans(response, self.case_1)
257
258
        not_linked_to_plan = not TestCasePlan.objects.filter(
259
            case=self.case_1, plan=self.plan_test_remove).exists()
260
        self.assertTrue(not_linked_to_plan)
261
262
    def test_add_a_few_plans(self):
263
        user_should_have_perm(self.plan_tester, 'testcases.add_testcaseplan')
264
        self.client.login(  # nosec:B106:hardcoded_password_funcarg
265
            username=self.plan_tester.username,
266
            password='password')
267
        # This time, add a few plans to another case
268
        url = reverse('testcases-plan', args=[self.case_2.pk])
269
270
        response = self.client.get(url,
271
                                   {'a': 'add', 'plan_id': [self.plan_test_add.pk,
272
                                                            self.plan_test_remove.pk]})
273
274
        self.assert_list_case_plans(response, self.case_2)
275
276
        self.assertTrue(TestCasePlan.objects.filter(
277
            case=self.case_2, plan=self.plan_test_add).exists())
278
        self.assertTrue(TestCasePlan.objects.filter(
279
            case=self.case_2, plan=self.plan_test_remove).exists())
280
281
282
class TestEditCase(BasePlanCase):
283
    """Test edit view method"""
284
285
    @classmethod
286
    def setUpTestData(cls):
287
        super(TestEditCase, cls).setUpTestData()
288
289
        cls.proposed_case = TestCaseFactory(
290
            author=cls.tester,
291
            default_tester=None,
292
            reviewer=cls.tester,
293
            case_status=cls.case_status_proposed,
294
            plan=[cls.plan])
295
296
        # test data for https://github.com/kiwitcms/Kiwi/issues/334
297
        # pylint: disable=objects-update-used
298
        Priority.objects.filter(value='P4').update(is_active=False)
299
300
        user_should_have_perm(cls.tester, 'testcases.change_testcase')
301
        cls.case_edit_url = reverse('testcases-edit', args=[cls.case_1.pk])
302
303
        # Copy, then modify or add new data for specific tests below
304
        cls.edit_data = {
305
            'from_plan': cls.plan.pk,
306
            'summary': cls.case_1.summary,
307
            'product': cls.case_1.category.product.pk,
308
            'category': cls.case_1.category.pk,
309
            'default_tester': '',
310
            'case_status': cls.case_status_confirmed.pk,
311
            'arguments': '',
312
            'extra_link': '',
313
            'notes': '',
314
            'is_automated': '0',
315
            'requirement': '',
316
            'script': '',
317
            'alias': '',
318
            'priority': cls.case_1.priority.pk,
319
            'tag': 'RHEL',
320
            'setup': '',
321
            'action': '',
322
            'breakdown': '',
323
            'effect': '',
324
            'cc_list': '',
325
        }
326
327
    def test_404_if_case_id_not_exist(self):
328
        url = reverse('testcases-edit', args=[99999])
329
        response = self.client.get(url)
330
        self.assert404(response)
331
332
    def test_404_if_from_plan_not_exist(self):
333
        response = self.client.get(self.case_edit_url, {'from_plan': 9999})
334
        self.assert404(response)
335
336
    def test_show_edit_page(self):
337
        response = self.client.get(self.case_edit_url)
338
        self.assertEqual(200, response.status_code)
339
        self.assertNotContains(response, ">P4</option")
340
341
    def test_edit_a_case(self):
342
        edit_data = self.edit_data.copy()
343
        new_summary = 'Edited: {0}'.format(self.case_1.summary)
344
        edit_data['summary'] = new_summary
345
346
        response = self.client.post(self.case_edit_url, edit_data)
347
348
        redirect_url = '{0}?from_plan={1}'.format(
349
            reverse('testcases-get', args=[self.case_1.pk]),
350
            self.plan.pk,
351
        )
352
        self.assertRedirects(response, redirect_url)
353
354
        edited_case = TestCase.objects.get(pk=self.case_1.pk)
355
        self.assertEqual(new_summary, edited_case.summary)
356
357
    def test_continue_edit_this_case_after_save(self):
358
        edit_data = self.edit_data.copy()
359
        edit_data['_continue'] = 'continue edit'
360
361
        response = self.client.post(self.case_edit_url, edit_data)
362
363
        redirect_url = '{0}?from_plan={1}'.format(
364
            reverse('testcases-edit', args=[self.case_1.pk]),
365
            self.plan.pk,
366
        )
367
        self.assertRedirects(response, redirect_url)
368
369
    def test_continue_edit_next_confirmed_case_after_save(self):
370
        edit_data = self.edit_data.copy()
371
        edit_data['_continuenext'] = 'continue edit next case'
372
373
        response = self.client.post(self.case_edit_url, edit_data)
374
375
        redirect_url = '{0}?from_plan={1}'.format(
376
            reverse('testcases-edit', args=[self.case_2.pk]),
377
            self.plan.pk,
378
        )
379
        self.assertRedirects(response, redirect_url)
380
381
    def test_continue_edit_next_non_confirmed_case_after_save(self):
382
        edit_data = self.edit_data.copy()
383
        edit_data['case_status'] = self.case_status_proposed.pk
384
        edit_data['_continuenext'] = 'continue edit next case'
385
386
        response = self.client.post(self.case_edit_url, edit_data)
387
388
        redirect_url = '{0}?from_plan={1}'.format(
389
            reverse('testcases-edit', args=[self.proposed_case.pk]),
390
            self.plan.pk,
391
        )
392
        self.assertRedirects(response, redirect_url)
393
394
    def test_return_to_plan_confirmed_cases_tab(self):
395
        edit_data = self.edit_data.copy()
396
        edit_data['_returntoplan'] = 'return to plan'
397
398
        response = self.client.post(self.case_edit_url, edit_data)
399
400
        redirect_url = '{0}#testcases'.format(
401
            reverse('test_plan_url_short', args=[self.plan.pk])
402
        )
403
        self.assertRedirects(response, redirect_url, target_status_code=301)
404
405
    def test_return_to_plan_review_cases_tab(self):
406
        edit_data = self.edit_data.copy()
407
        edit_data['case_status'] = self.case_status_proposed.pk
408
        edit_data['_returntoplan'] = 'return to plan'
409
410
        response = self.client.post(self.case_edit_url, edit_data)
411
412
        redirect_url = '{0}#reviewcases'.format(
413
            reverse('test_plan_url_short', args=[self.plan.pk])
414
        )
415
        self.assertRedirects(response, redirect_url, target_status_code=301)
416
417
418
class TestPrintablePage(BasePlanCase):
419
    """Test printable page view method"""
420
421
    @classmethod
422
    def setUpTestData(cls):
423
        super(TestPrintablePage, cls).setUpTestData()
424
        cls.printable_url = reverse('testcases-printable')
425
426
        cls.case_1.add_text(action='action',
427
                            effect='effect',
428
                            setup='setup',
429
                            breakdown='breakdown')
430
        cls.case_2.add_text(action='action',
431
                            effect='effect',
432
                            setup='setup',
433
                            breakdown='breakdown')
434
435
    def test_printable_page(self):
436
        # printing only 1 of the cases
437
        response = self.client.post(self.printable_url,
438
                                    {'case': [self.case_1.pk]})
439
440
        # not printing the Test Plan header section
441
        self.assertNotContains(response, 'Test Plan Document')
442
443
        # response contains the first TestCase
444
        self.assertContains(
445
            response,
446
            '<h3>TC-{0}: {1}</h3>'.format(self.case_1.pk, self.case_1.summary),
447
            html=True
448
        )
449
450
        # but not the second TestCase b/c it was not selected
451
        self.assertNotContains(
452
            response,
453
            '<h3>TC-{0}: {1}'.format(self.case_2.pk, self.case_2.summary),
454
            html=True
455
        )
456
457
458
class TestCloneCase(BasePlanCase):
459
    """Test clone view method"""
460
461
    @classmethod
462
    def setUpTestData(cls):
463
        super(TestCloneCase, cls).setUpTestData()
464
465
        user_should_have_perm(cls.tester, 'testcases.add_testcase')
466
        cls.clone_url = reverse('testcases-clone')
467
468
    def test_refuse_if_missing_argument(self):
469
        # Refuse to clone cases if missing selectAll and case arguments
470
        response = self.client.get(self.clone_url, {}, follow=True)
471
472
        self.assertContains(response, 'At least one TestCase is required')
473
474
    def test_show_clone_page_with_from_plan(self):
475
        response = self.client.get(self.clone_url,
476
                                   {'from_plan': self.plan.pk,
477
                                    'case': [self.case_1.pk, self.case_2.pk]})
478
479
        self.assertContains(
480
            response,
481
            """<div>
482
    <input type="radio" id="id_use_sameplan" name="selectplan" value="{0}">
483
    <label for="id_use_sameplan" class="strong">Use the same Plan -- {0} : {1}</label>
484
</div>""".format(self.plan.pk, self.plan.name),
485
            html=True)
486
487
        for loop_counter, case in enumerate([self.case_1, self.case_2]):
488
            self.assertContains(
489
                response,
490
                '<label for="id_case_{0}">'
491
                '<input checked="checked" id="id_case_{0}" name="case" '
492
                'type="checkbox" value="{1}"> {2}</label>'.format(
493
                    loop_counter, case.pk, case.summary),
494
                html=True)
495
496
    def test_show_clone_page_without_from_plan(self):
497
        response = self.client.get(self.clone_url, {'case': self.case_1.pk})
498
499
        self.assertNotContains(
500
            response,
501
            'Use the same Plan -- {0} : {1}'.format(self.plan.pk,
502
                                                    self.plan.name),
503
        )
504
505
        self.assertContains(
506
            response,
507
            '<label for="id_case_0">'
508
            '<input checked="checked" id="id_case_0" name="case" '
509
            'type="checkbox" value="{0}"> {1}</label>'.format(
510
                self.case_1.pk, self.case_1.summary),
511
            html=True)
512
513
514
class TestSearchCases(BasePlanCase):
515
    """Test search view method"""
516
517
    @classmethod
518
    def setUpTestData(cls):
519
        super().setUpTestData()
520
521
        cls.search_url = reverse('testcases-search')
522
523
    def test_page_renders(self):
524
        response = self.client.get(self.search_url, {})
525
        self.assertContains(response, '<option value="">----------</option>', html=True)
526
527
528
class TestGetCasesFromPlan(BasePlanCase):
529
    @classmethod
530
    def setUpTestData(cls):
531
        super().setUpTestData()
532
        initiate_user_with_default_setups(cls.tester)
533
534
    def test_casetags_are_shown_in_template(self):
535
        # pylint: disable=tag-objects-get_or_create
536
        tag, _ = Tag.objects.get_or_create(name='Linux')
537
        self.case.add_tag(tag)
538
539
        url = reverse('testcases-all')
540
        response_data = urlencode({
541
            'from_plan': self.plan.pk,
542
            'template_type': 'case',
543
            'a': 'initial'})
544
        # note: this is how the UI sends the request
545
        response = self.client.post(url, data=response_data,
546
                                    content_type='application/x-www-form-urlencoded; charset=UTF-8',
547
                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
548
        self.assertEqual(HTTPStatus.OK, response.status_code)
549
        self.assertContains(response, 'Tags:')
550
        self.assertContains(response, '<a href="#testcases">Linux</a>')
551
552
    def test_disabled_priority_now_shown(self):
553
        # test data for https://github.com/kiwitcms/Kiwi/issues/334
554
        # pylint: disable=objects-update-used
555
        Priority.objects.filter(value='P4').update(is_active=False)
556
557
        url = reverse('testcases-all')
558
        response_data = urlencode({
559
            'from_plan': self.plan.pk,
560
            'template_type': 'case',
561
            'a': 'initial'})
562
        # note: this is how the UI sends the request
563
        response = self.client.post(url, data=response_data,
564
                                    content_type='application/x-www-form-urlencoded; charset=UTF-8',
565
                                    HTTP_X_REQUESTED_WITH='XMLHttpRequest')
566
        self.assertEqual(HTTPStatus.OK, response.status_code)
567
        self.assertContains(response, 'Set P3')
568
        self.assertNotContains(response, 'Set P4')
569
570
571
class TestGetSelectedTestcases(BasePlanCase):
572
    def test_get_selected_testcases_works_with_both_string_and_int_pks(self):
573
        """
574
        Assures that tcms.testcases.views.get_selected_testcases
575
        returns the same results, regardless of whether the
576
        passed request contains the case pks as strings or
577
        integers, as long as they are the same in both occasions.
578
        """
579
580
        case_int_pks = [self.case.pk, self.case_1.pk, self.case_2.pk, self.case_3.pk]
581
        case_str_pks = []
582
583
        for _pk in case_int_pks:
584
            case_str_pks.append(str(_pk))
585
586
        int_pk_query = get_selected_testcases(
587
            RequestFactory().post(
588
                reverse('testcases-clone'),
589
                {'case': case_int_pks}
590
            )
591
        )
592
593
        str_pk_query = get_selected_testcases(
594
            RequestFactory().post(
595
                reverse('testcases-clone'),
596
                {'case': case_str_pks}
597
            )
598
        )
599
600
        for case in TestCase.objects.filter(pk__in=case_int_pks):
601
            self.assertTrue(case in int_pk_query)
602
            self.assertTrue(case in str_pk_query)
603