1
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
# pylint: disable=invalid-name |
3
|
|
|
|
4
|
|
|
import json |
5
|
|
|
from http import HTTPStatus |
6
|
|
|
from uuslug import slugify |
7
|
|
|
|
8
|
|
|
from django import test |
9
|
|
|
from django.conf import settings |
10
|
|
|
from django.contrib.auth.models import User |
11
|
|
|
from django.urls import reverse |
12
|
|
|
|
13
|
|
|
from tcms.management.models import Product |
14
|
|
|
from tcms.management.models import Version |
15
|
|
|
from tcms.testcases.models import TestCasePlan, TestCaseStatus |
16
|
|
|
from tcms.testplans.models import TestPlan |
17
|
|
|
|
18
|
|
|
from tcms.tests.factories import ClassificationFactory |
19
|
|
|
from tcms.tests.factories import ProductFactory |
20
|
|
|
from tcms.tests.factories import TestCaseFactory, TestCaseTextFactory |
21
|
|
|
from tcms.tests.factories import TestPlanFactory |
22
|
|
|
from tcms.tests.factories import PlanTypeFactory |
23
|
|
|
from tcms.tests.factories import UserFactory |
24
|
|
|
from tcms.tests.factories import VersionFactory |
25
|
|
|
from tcms.tests import BasePlanCase |
26
|
|
|
from tcms.tests import remove_perm_from_user |
27
|
|
|
from tcms.tests import user_should_have_perm |
28
|
|
|
|
29
|
|
|
|
30
|
|
|
class PlanTests(test.TestCase): |
31
|
|
|
|
32
|
|
|
@classmethod |
33
|
|
|
def setUpTestData(cls): |
34
|
|
|
cls.user = UserFactory(username='admin', email='[email protected]') |
35
|
|
|
cls.user.set_password('admin') |
36
|
|
|
cls.user.is_superuser = True |
37
|
|
|
cls.user.is_staff = True |
38
|
|
|
cls.user.save() |
39
|
|
|
|
40
|
|
|
cls.classification = ClassificationFactory(name='Auto') |
41
|
|
|
cls.product = ProductFactory(name='Kiwi', classification=cls.classification) |
42
|
|
|
cls.product_version = VersionFactory(value='0.1', product=cls.product) |
43
|
|
|
cls.plan_type = PlanTypeFactory() |
44
|
|
|
|
45
|
|
|
cls.test_plan = TestPlanFactory(name='another test plan for testing', |
46
|
|
|
product_version=cls.product_version, |
47
|
|
|
author=cls.user, |
48
|
|
|
product=cls.product, |
49
|
|
|
type=cls.plan_type) |
50
|
|
|
# add TestCases to plan with status CONFIRMED |
51
|
|
|
for _i in range(5): |
52
|
|
|
case = TestCaseFactory(plan=[cls.test_plan], |
53
|
|
|
case_status=TestCaseStatus.objects.get(name='CONFIRMED')) |
54
|
|
|
TestCaseTextFactory(case=case) |
55
|
|
|
|
56
|
|
|
# also add a few PROPOSED TestCases |
57
|
|
|
for _i in range(3): |
58
|
|
|
case = TestCaseFactory(plan=[cls.test_plan]) |
59
|
|
|
TestCaseTextFactory(case=case) |
60
|
|
|
|
61
|
|
|
cls.plan_id = cls.test_plan.pk |
62
|
|
|
cls.child_plan = TestPlanFactory(parent=cls.test_plan) |
63
|
|
|
|
64
|
|
|
def setUp(self): |
65
|
|
|
super().setUp() |
66
|
|
|
self.client.login(username=self.user.username, # nosec:B106:hardcoded_password_funcarg |
67
|
|
|
password='admin') |
68
|
|
|
|
69
|
|
|
def test_open_plans_search(self): |
70
|
|
|
location = reverse('plans-search') |
71
|
|
|
response = self.client.get(location) |
72
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
73
|
|
|
|
74
|
|
|
def test_plan_new_get(self): |
75
|
|
|
location = reverse('plans-new') |
76
|
|
|
response = self.client.get(location, follow=True) |
77
|
|
|
|
78
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
79
|
|
|
self.assertContains( |
80
|
|
|
response, |
81
|
|
|
'<input class="bootstrap-switch" name="is_active" type="checkbox" checked>', |
82
|
|
|
html=True) |
83
|
|
|
self.assertContains( |
84
|
|
|
response, |
85
|
|
|
'<input class="bootstrap-switch" name="auto_to_plan_author" type="checkbox" ' |
86
|
|
|
'checked>', html=True) |
87
|
|
|
self.assertContains( |
88
|
|
|
response, |
89
|
|
|
'<input class="bootstrap-switch" name="auto_to_case_owner" type="checkbox" ' |
90
|
|
|
'checked>', html=True) |
91
|
|
|
self.assertContains( |
92
|
|
|
response, |
93
|
|
|
'<input class="bootstrap-switch" name="auto_to_case_default_tester" type="checkbox" ' |
94
|
|
|
'checked>', html=True) |
95
|
|
|
self.assertContains( |
96
|
|
|
response, |
97
|
|
|
'<input class="bootstrap-switch" name="notify_on_plan_update" type="checkbox" ' |
98
|
|
|
'checked>', html=True) |
99
|
|
|
self.assertContains( |
100
|
|
|
response, |
101
|
|
|
'<input class="bootstrap-switch" name="notify_on_case_update" type="checkbox" ' |
102
|
|
|
'checked>', html=True) |
103
|
|
|
|
104
|
|
|
def test_plan_create_new_active(self): |
105
|
|
|
self._test_plan_create_new(is_active=True) |
106
|
|
|
|
107
|
|
|
def test_plan_create_new_inactive(self): |
108
|
|
|
self._test_plan_create_new(is_active=False) |
109
|
|
|
|
110
|
|
|
def _test_plan_create_new(self, is_active): |
111
|
|
|
location = reverse('plans-new') |
112
|
|
|
plan_name = 'plan name' |
113
|
|
|
request = { |
114
|
|
|
'product': self.product.id, |
115
|
|
|
'product_version': self.product_version.id, |
116
|
|
|
'type': self.plan_type.pk, |
117
|
|
|
'name': plan_name, |
118
|
|
|
'is_active': is_active, |
119
|
|
|
'auto_to_plan_author': True, |
120
|
|
|
'auto_to_case_owner': True, |
121
|
|
|
'auto_to_case_default_tester': True, |
122
|
|
|
'notify_on_case_update': False, |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
response = self.client.post(location, request, follow=True) |
126
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
127
|
|
|
|
128
|
|
|
plan = TestPlan.objects.get(name=plan_name) |
129
|
|
|
self.assertEqual(plan.product, self.product) |
130
|
|
|
self.assertEqual(plan.product_version, self.product_version) |
131
|
|
|
self.assertEqual(plan.type, self.plan_type) |
132
|
|
|
self.assertEqual(plan.is_active, is_active) |
133
|
|
|
self.assertTrue(plan.emailing.auto_to_plan_author) |
134
|
|
|
self.assertTrue(plan.emailing.auto_to_case_owner) |
135
|
|
|
self.assertTrue(plan.emailing.auto_to_case_default_tester) |
136
|
|
|
self.assertFalse(plan.emailing.notify_on_plan_update) |
137
|
|
|
self.assertFalse(plan.emailing.notify_on_case_update) |
138
|
|
|
|
139
|
|
|
def test_plan_details(self): |
140
|
|
|
location = reverse('test_plan_url_short', args=[self.plan_id]) |
141
|
|
|
response = self.client.get(location) |
142
|
|
|
self.assertEqual(response.status_code, HTTPStatus.MOVED_PERMANENTLY) |
143
|
|
|
|
144
|
|
|
response = self.client.get(location, follow=True) |
145
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
146
|
|
|
|
147
|
|
|
def test_plan_edit(self): |
148
|
|
|
location = reverse('plan-edit', args=[self.plan_id]) |
149
|
|
|
response = self.client.get(location) |
150
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
151
|
|
|
|
152
|
|
|
def test_plan_printable_without_selected_plan(self): |
153
|
|
|
location = reverse('plans-printable') |
154
|
|
|
response = self.client.post(location, follow=True) |
155
|
|
|
self.assertContains(response, 'At least one test plan is required for print') |
156
|
|
|
|
157
|
|
|
def test_plan_printable(self): |
158
|
|
|
location = reverse('plans-printable') |
159
|
|
|
response = self.client.post(location, {'plan': [self.test_plan.pk]}) |
160
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
161
|
|
|
|
162
|
|
|
self.assertContains(response, self.test_plan.name) |
163
|
|
|
self.assertContains(response, self.test_plan.text) |
164
|
|
|
|
165
|
|
|
confirmed = TestCaseStatus.objects.get(name='CONFIRMED') |
166
|
|
|
for case in self.test_plan.case.filter(case_status=confirmed): |
167
|
|
|
self.assertContains(response, case.summary) |
168
|
|
|
# factory sets all 4 |
169
|
|
|
self.assertContains(response, case.latest_text().setup) |
170
|
|
|
self.assertContains(response, case.latest_text().action) |
171
|
|
|
self.assertContains(response, case.latest_text().effect) |
172
|
|
|
self.assertContains(response, case.latest_text().breakdown) |
173
|
|
|
|
174
|
|
|
def test_plan_attachment(self): |
175
|
|
|
location = reverse('plan-attachment', |
176
|
|
|
args=[self.plan_id]) |
177
|
|
|
response = self.client.get(location) |
178
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
179
|
|
|
|
180
|
|
|
def test_plan_history(self): |
181
|
|
|
# note: the history URL is generated on the fly and not accessible via |
182
|
|
|
# name |
183
|
|
|
location = "/admin/testplans/testplan/%d/history/" % self.plan_id |
184
|
|
|
response = self.client.get(location) |
185
|
|
|
self.assertEqual(response.status_code, HTTPStatus.OK) |
186
|
|
|
|
187
|
|
|
|
188
|
|
|
class TestPlanModel(test.TestCase): |
189
|
|
|
""" Test some model operations directly without a view """ |
190
|
|
|
|
191
|
|
|
@classmethod |
192
|
|
|
def setUpTestData(cls): |
193
|
|
|
cls.plan_1 = TestPlanFactory() |
194
|
|
|
cls.testcase_1 = TestCaseFactory() |
195
|
|
|
cls.testcase_2 = TestCaseFactory() |
196
|
|
|
|
197
|
|
|
cls.plan_1.add_case(cls.testcase_1) |
198
|
|
|
cls.plan_1.add_case(cls.testcase_2) |
199
|
|
|
|
200
|
|
|
def test_plan_delete_case(self): |
201
|
|
|
self.plan_1.delete_case(self.testcase_1) |
202
|
|
|
cases_left = TestCasePlan.objects.filter(plan=self.plan_1.pk) |
203
|
|
|
self.assertEqual(1, cases_left.count()) |
204
|
|
|
self.assertEqual(self.testcase_2.pk, cases_left[0].case.pk) |
205
|
|
|
|
206
|
|
|
|
207
|
|
|
class TestDeleteCasesFromPlan(BasePlanCase): |
208
|
|
|
"""Test case for deleting cases from a plan""" |
209
|
|
|
|
210
|
|
|
@classmethod |
211
|
|
|
def setUpTestData(cls): |
212
|
|
|
super(TestDeleteCasesFromPlan, cls).setUpTestData() |
213
|
|
|
cls.plan_tester = User(username='tester') |
214
|
|
|
cls.plan_tester.set_password('password') |
215
|
|
|
cls.plan_tester.save() |
216
|
|
|
|
217
|
|
|
cls.cases_url = reverse('plan-delete-cases', args=[cls.plan.pk]) |
218
|
|
|
|
219
|
|
|
def test_missing_cases_ids(self): |
220
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
221
|
|
|
username=self.plan_tester.username, |
222
|
|
|
password='password') |
223
|
|
|
|
224
|
|
|
response = self.client.post(self.cases_url) |
225
|
|
|
data = json.loads(str(response.content, encoding=settings.DEFAULT_CHARSET)) |
226
|
|
|
self.assertEqual(1, data['rc']) |
227
|
|
|
self.assertEqual('At least one case is required to delete.', |
228
|
|
|
data['response']) |
229
|
|
|
|
230
|
|
|
def test_delete_cases(self): |
231
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
232
|
|
|
username=self.plan_tester.username, |
233
|
|
|
password='password') |
234
|
|
|
|
235
|
|
|
post_data = {'case': [self.case_1.pk, self.case_3.pk]} |
236
|
|
|
response = self.client.post(self.cases_url, post_data) |
237
|
|
|
data = json.loads(str(response.content, encoding=settings.DEFAULT_CHARSET)) |
238
|
|
|
|
239
|
|
|
self.assertEqual(0, data['rc']) |
240
|
|
|
self.assertEqual('ok', data['response']) |
241
|
|
|
self.assertFalse(self.plan.case.filter( |
242
|
|
|
pk__in=[self.case_1.pk, self.case_3.pk]).exists()) |
243
|
|
|
|
244
|
|
|
|
245
|
|
|
class TestSortCases(BasePlanCase): |
246
|
|
|
"""Test case for sorting cases""" |
247
|
|
|
|
248
|
|
|
@classmethod |
249
|
|
|
def setUpTestData(cls): |
250
|
|
|
super(TestSortCases, cls).setUpTestData() |
251
|
|
|
cls.plan_tester = User(username='tester') |
252
|
|
|
cls.plan_tester.set_password('password') |
253
|
|
|
cls.plan_tester.save() |
254
|
|
|
|
255
|
|
|
cls.cases_url = reverse('plan-reorder-cases', args=[cls.plan.pk]) |
256
|
|
|
|
257
|
|
|
def test_missing_cases_ids(self): |
258
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
259
|
|
|
username=self.plan_tester.username, |
260
|
|
|
password='password') |
261
|
|
|
|
262
|
|
|
response = self.client.post(self.cases_url) |
263
|
|
|
data = json.loads(str(response.content, encoding=settings.DEFAULT_CHARSET)) |
264
|
|
|
self.assertEqual(1, data['rc']) |
265
|
|
|
self.assertEqual('At least one case is required to re-order.', data['response']) |
266
|
|
|
|
267
|
|
|
def test_order_cases(self): |
268
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
269
|
|
|
username=self.plan_tester.username, |
270
|
|
|
password='password') |
271
|
|
|
|
272
|
|
|
post_data = {'case': [self.case_3.pk, self.case_1.pk]} |
273
|
|
|
response = self.client.post(self.cases_url, post_data) |
274
|
|
|
data = json.loads(str(response.content, encoding=settings.DEFAULT_CHARSET)) |
275
|
|
|
|
276
|
|
|
self.assertEqual({'rc': 0, 'response': 'ok'}, data) |
277
|
|
|
|
278
|
|
|
case_plan_rel = TestCasePlan.objects.get(plan=self.plan, case=self.case_3) |
279
|
|
|
self.assertEqual(10, case_plan_rel.sortkey) |
280
|
|
|
|
281
|
|
|
case_plan_rel = TestCasePlan.objects.get(plan=self.plan, case=self.case_1) |
282
|
|
|
self.assertEqual(20, case_plan_rel.sortkey) |
283
|
|
|
|
284
|
|
|
|
285
|
|
|
class TestLinkCases(BasePlanCase): |
286
|
|
|
"""Test case for linking cases from other plans""" |
287
|
|
|
|
288
|
|
|
@classmethod |
289
|
|
|
def setUpTestData(cls): |
290
|
|
|
super(TestLinkCases, cls).setUpTestData() |
291
|
|
|
|
292
|
|
|
cls.another_plan = TestPlanFactory( |
293
|
|
|
author=cls.tester, |
294
|
|
|
product=cls.product, |
295
|
|
|
product_version=cls.version) |
296
|
|
|
|
297
|
|
|
cls.another_case_1 = TestCaseFactory( |
298
|
|
|
author=cls.tester, |
299
|
|
|
default_tester=None, |
300
|
|
|
reviewer=cls.tester, |
301
|
|
|
plan=[cls.another_plan]) |
302
|
|
|
|
303
|
|
|
cls.another_case_2 = TestCaseFactory( |
304
|
|
|
author=cls.tester, |
305
|
|
|
default_tester=None, |
306
|
|
|
reviewer=cls.tester, |
307
|
|
|
plan=[cls.another_plan]) |
308
|
|
|
|
309
|
|
|
cls.plan_tester = User(username='tester') |
310
|
|
|
cls.plan_tester.set_password('password') |
311
|
|
|
cls.plan_tester.save() |
312
|
|
|
|
313
|
|
|
cls.search_cases_for_link_url = reverse('plan-search-cases-for-link', |
314
|
|
|
args=[cls.plan.pk]) |
315
|
|
|
cls.link_cases_url = reverse('plan-link-cases', args=[cls.plan.pk]) |
316
|
|
|
|
317
|
|
|
def tearDown(self): |
318
|
|
|
# Ensure permission is removed whenever it was added during tests |
319
|
|
|
remove_perm_from_user(self.plan_tester, 'testcases.add_testcaseplan') |
320
|
|
|
|
321
|
|
|
def assert_quick_search_is_shown(self, response): |
322
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
323
|
|
|
username=self.plan_tester.username, |
324
|
|
|
password='password') |
325
|
|
|
|
326
|
|
|
self.assertContains( |
327
|
|
|
response, |
328
|
|
|
'<li class="profile_tab_active" id="quick_tab">') |
329
|
|
|
|
330
|
|
|
def assert_normal_search_is_shown(self, response): |
331
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
332
|
|
|
username=self.plan_tester.username, |
333
|
|
|
password='password') |
334
|
|
|
|
335
|
|
|
self.assertContains( |
336
|
|
|
response, |
337
|
|
|
'<li class="profile_tab_active" id="normal_tab">') |
338
|
|
|
|
339
|
|
|
def test_show_quick_search_by_default(self): |
340
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
341
|
|
|
username=self.plan_tester.username, |
342
|
|
|
password='password') |
343
|
|
|
|
344
|
|
|
response = self.client.post(self.search_cases_for_link_url, {}) |
345
|
|
|
self.assert_quick_search_is_shown(response) |
346
|
|
|
|
347
|
|
|
def assert_search_result(self, response): |
348
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
349
|
|
|
username=self.plan_tester.username, |
350
|
|
|
password='password') |
351
|
|
|
|
352
|
|
|
self.assertContains( |
353
|
|
|
response, |
354
|
|
|
'<a href="{}">{}</a>'.format( |
355
|
|
|
reverse('testcases-get', args=[self.another_case_2.pk]), |
356
|
|
|
self.another_case_2.pk)) |
357
|
|
|
|
358
|
|
|
# Assert: Do not list case that already belongs to the plan |
359
|
|
|
self.assertNotContains( |
360
|
|
|
response, |
361
|
|
|
'<a href="{}">{}</a>'.format( |
362
|
|
|
reverse('testcases-get', args=[self.case_2.pk]), |
363
|
|
|
self.case_2.pk)) |
364
|
|
|
|
365
|
|
|
def test_quick_search(self): |
366
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
367
|
|
|
username=self.plan_tester.username, |
368
|
|
|
password='password') |
369
|
|
|
|
370
|
|
|
post_data = { |
371
|
|
|
'search_mode': 'quick', |
372
|
|
|
'case_id_set': ','.join( |
373
|
|
|
map(str, [self.case_1.pk, self.another_case_2.pk])) |
374
|
|
|
} |
375
|
|
|
response = self.client.post(self.search_cases_for_link_url, post_data) |
376
|
|
|
|
377
|
|
|
self.assert_quick_search_is_shown(response) |
378
|
|
|
self.assert_search_result(response) |
379
|
|
|
|
380
|
|
|
def test_normal_search(self): |
381
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
382
|
|
|
username=self.plan_tester.username, |
383
|
|
|
password='password') |
384
|
|
|
|
385
|
|
|
post_data = { |
386
|
|
|
'search_mode': 'normal', |
387
|
|
|
'case_id_set': ','.join( |
388
|
|
|
map(str, [self.case_1.pk, self.another_case_2.pk])) |
389
|
|
|
} |
390
|
|
|
response = self.client.post(self.search_cases_for_link_url, post_data) |
391
|
|
|
|
392
|
|
|
self.assert_normal_search_is_shown(response) |
393
|
|
|
self.assert_search_result(response) |
394
|
|
|
|
395
|
|
|
def test_link_cases(self): |
396
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
397
|
|
|
username=self.plan_tester.username, |
398
|
|
|
password='password') |
399
|
|
|
|
400
|
|
|
user_should_have_perm(self.plan_tester, 'testcases.add_testcaseplan') |
401
|
|
|
|
402
|
|
|
post_data = { |
403
|
|
|
'case': [self.another_case_1.pk, self.another_case_2.pk] |
404
|
|
|
} |
405
|
|
|
response = self.client.post(self.link_cases_url, post_data) |
406
|
|
|
self.assertRedirects( |
407
|
|
|
response, |
408
|
|
|
reverse('test_plan_url', args=[self.plan.pk, slugify(self.plan.name)])) |
409
|
|
|
|
410
|
|
|
self.assertTrue( |
411
|
|
|
TestCasePlan.objects.filter( |
412
|
|
|
plan=self.plan, case=self.another_case_1).exists()) |
413
|
|
|
self.assertTrue( |
414
|
|
|
TestCasePlan.objects.filter( |
415
|
|
|
plan=self.plan, case=self.another_case_2).exists()) |
416
|
|
|
|
417
|
|
|
|
418
|
|
|
class TestCloneView(BasePlanCase): |
419
|
|
|
"""Test case for cloning a plan""" |
420
|
|
|
|
421
|
|
|
@classmethod |
422
|
|
|
def setUpTestData(cls): |
423
|
|
|
super(TestCloneView, cls).setUpTestData() |
424
|
|
|
|
425
|
|
|
cls.another_plan = TestPlanFactory( |
426
|
|
|
name='Another plan for test', |
427
|
|
|
author=cls.tester, |
428
|
|
|
product=cls.product, product_version=cls.version) |
429
|
|
|
cls.another_case_1 = TestCaseFactory( |
430
|
|
|
author=cls.tester, default_tester=None, |
431
|
|
|
reviewer=cls.tester, plan=[cls.another_plan]) |
432
|
|
|
cls.another_case_2 = TestCaseFactory( |
433
|
|
|
author=cls.tester, default_tester=None, |
434
|
|
|
reviewer=cls.tester, plan=[cls.another_plan]) |
435
|
|
|
|
436
|
|
|
cls.third_plan = TestPlanFactory( |
437
|
|
|
name='Third plan for test', |
438
|
|
|
author=cls.tester, |
439
|
|
|
product=cls.product, product_version=cls.version) |
440
|
|
|
cls.third_case_1 = TestCaseFactory( |
441
|
|
|
author=cls.tester, default_tester=None, |
442
|
|
|
reviewer=cls.tester, plan=[cls.third_plan]) |
443
|
|
|
cls.third_case_2 = TestCaseFactory( |
444
|
|
|
author=cls.tester, default_tester=None, |
445
|
|
|
reviewer=cls.tester, plan=[cls.third_plan]) |
446
|
|
|
|
447
|
|
|
cls.totally_new_plan = TestPlanFactory( |
448
|
|
|
name='Test clone plan with copying cases', |
449
|
|
|
author=cls.tester, |
450
|
|
|
product=cls.product, product_version=cls.version) |
451
|
|
|
cls.case_maintain_original_author = TestCaseFactory( |
452
|
|
|
author=cls.tester, default_tester=None, |
453
|
|
|
reviewer=cls.tester, plan=[cls.totally_new_plan]) |
454
|
|
|
cls.case_keep_default_tester = TestCaseFactory( |
455
|
|
|
author=cls.tester, default_tester=None, |
456
|
|
|
reviewer=cls.tester, plan=[cls.totally_new_plan]) |
457
|
|
|
|
458
|
|
|
cls.plan_tester = User.objects.create_user( # nosec:B106:hardcoded_password_funcarg |
459
|
|
|
username='plan_tester', |
460
|
|
|
email='[email protected]', |
461
|
|
|
password='password') |
462
|
|
|
user_should_have_perm(cls.plan_tester, 'testplans.add_testplan') |
463
|
|
|
cls.plan_clone_url = reverse('plans-clone') |
464
|
|
|
|
465
|
|
|
def test_refuse_if_missing_a_plan(self): |
466
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
467
|
|
|
username=self.plan_tester.username, |
468
|
|
|
password='password') |
469
|
|
|
|
470
|
|
|
data_missing_plan = {} # No plan is passed |
471
|
|
|
response = self.client.post(self.plan_clone_url, data_missing_plan, follow=True) |
472
|
|
|
self.assertContains(response, 'TestPlan is required') |
473
|
|
|
|
474
|
|
|
def test_refuse_if_given_nonexisting_plan(self): |
475
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
476
|
|
|
username=self.plan_tester.username, |
477
|
|
|
password='password') |
478
|
|
|
|
479
|
|
|
response = self.client.post(self.plan_clone_url, {'plan': 99999}, follow=True) |
480
|
|
|
self.assert404(response) |
481
|
|
|
|
482
|
|
|
def test_open_clone_page_to_clone_one_plan(self): |
483
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
484
|
|
|
username=self.plan_tester.username, |
485
|
|
|
password='password') |
486
|
|
|
|
487
|
|
|
response = self.client.post(self.plan_clone_url, {'plan': self.plan.pk}) |
488
|
|
|
|
489
|
|
|
self.assertContains( |
490
|
|
|
response, |
491
|
|
|
'<label class="strong" for="id_name">New Plan Name</label>', |
492
|
|
|
html=True) |
493
|
|
|
|
494
|
|
|
self.assertContains( |
495
|
|
|
response, |
496
|
|
|
'<input id="id_name" name="name" type="text" value="Copy of {}">'.format( |
497
|
|
|
self.plan.name), |
498
|
|
|
html=True) |
499
|
|
|
|
500
|
|
|
def verify_cloned_plan(self, original_plan, cloned_plan, |
501
|
|
|
link_cases=True, copy_cases=None, |
502
|
|
|
maintain_case_orignal_author=None, |
503
|
|
|
keep_case_default_tester=None): |
504
|
|
|
self.assertEqual('Copy of {}'.format(original_plan.name), cloned_plan.name) |
505
|
|
|
self.assertEqual(cloned_plan.text, original_plan.text) |
506
|
|
|
self.assertEqual(Product.objects.get(pk=self.product.pk), cloned_plan.product) |
507
|
|
|
self.assertEqual(Version.objects.get(pk=self.version.pk), cloned_plan.product_version) |
508
|
|
|
|
509
|
|
|
self._verify_options(original_plan, cloned_plan, copy_cases, link_cases) |
510
|
|
|
|
511
|
|
|
if link_cases and copy_cases: |
512
|
|
|
# Ensure cases of original plan are not linked to cloned plan |
513
|
|
|
for case in original_plan.case.all(): |
514
|
|
|
original_case_not_linked_to_cloned_plan = TestCasePlan.objects.filter( |
515
|
|
|
plan=cloned_plan, case=case).exists() |
516
|
|
|
self.assertFalse(original_case_not_linked_to_cloned_plan) |
517
|
|
|
|
518
|
|
|
self.assertEqual(cloned_plan.case.count(), original_plan.case.count()) |
519
|
|
|
|
520
|
|
|
# Verify if case' author and default tester are set properly |
521
|
|
|
for original_case, copied_case in zip(original_plan.case.all(), |
522
|
|
|
cloned_plan.case.all()): |
523
|
|
|
if maintain_case_orignal_author: |
524
|
|
|
self.assertEqual(original_case.author, copied_case.author) |
525
|
|
|
else: |
526
|
|
|
me = self.plan_tester |
527
|
|
|
self.assertEqual(me, copied_case.author) |
528
|
|
|
|
529
|
|
|
if keep_case_default_tester: |
530
|
|
|
self.assertEqual(original_case.default_tester, copied_case.default_tester) |
531
|
|
|
else: |
532
|
|
|
me = self.plan_tester |
533
|
|
|
self.assertEqual(me, copied_case.default_tester) |
534
|
|
|
|
535
|
|
|
def _verify_options(self, original_plan, cloned_plan, copy_cases, link_cases): |
536
|
|
|
# Verify option set_parent |
537
|
|
|
self.assertEqual(TestPlan.objects.get(pk=original_plan.pk), cloned_plan.parent) |
538
|
|
|
|
539
|
|
|
# Verify options link_testcases and copy_testcases |
540
|
|
|
if link_cases and not copy_cases: |
541
|
|
|
for case in original_plan.case.all(): |
542
|
|
|
is_case_linked = TestCasePlan.objects.filter(plan=cloned_plan, case=case).exists() |
543
|
|
|
self.assertTrue(is_case_linked) |
544
|
|
|
|
545
|
|
|
def test_clone_a_plan_with_default_options(self): |
546
|
|
|
post_data = { |
547
|
|
|
'name': self.third_plan.make_cloned_name(), |
548
|
|
|
'plan': self.third_plan.pk, |
549
|
|
|
'product': self.product.pk, |
550
|
|
|
'product_version': self.version.pk, |
551
|
|
|
'set_parent': 'on', |
552
|
|
|
'link_testcases': 'on', |
553
|
|
|
'maintain_case_orignal_author': 'on', |
554
|
|
|
'keep_case_default_tester': 'on', |
555
|
|
|
'submit': 'Clone', |
556
|
|
|
} |
557
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
558
|
|
|
username=self.plan_tester.username, |
559
|
|
|
password='password') |
560
|
|
|
response = self.client.post(self.plan_clone_url, post_data) |
561
|
|
|
|
562
|
|
|
cloned_plan = TestPlan.objects.get(name=self.third_plan.make_cloned_name()) |
563
|
|
|
|
564
|
|
|
self.assertRedirects( |
565
|
|
|
response, |
566
|
|
|
reverse('test_plan_url_short', args=[cloned_plan.pk]), |
567
|
|
|
target_status_code=HTTPStatus.MOVED_PERMANENTLY) |
568
|
|
|
|
569
|
|
|
self.verify_cloned_plan(self.third_plan, cloned_plan) |
570
|
|
|
|
571
|
|
|
def test_clone_a_plan_by_copying_cases(self): |
572
|
|
|
post_data = { |
573
|
|
|
'name': self.totally_new_plan.make_cloned_name(), |
574
|
|
|
'plan': self.totally_new_plan.pk, |
575
|
|
|
'product': self.product.pk, |
576
|
|
|
'product_version': self.version.pk, |
577
|
|
|
'set_parent': 'on', |
578
|
|
|
'link_testcases': 'on', |
579
|
|
|
'maintain_case_orignal_author': 'on', |
580
|
|
|
'keep_case_default_tester': 'on', |
581
|
|
|
'submit': 'Clone', |
582
|
|
|
|
583
|
|
|
'copy_testcases': 'on', |
584
|
|
|
} |
585
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
586
|
|
|
username=self.plan_tester.username, |
587
|
|
|
password='password') |
588
|
|
|
self.client.post(self.plan_clone_url, post_data) |
589
|
|
|
cloned_plan = TestPlan.objects.get(name=self.totally_new_plan.make_cloned_name()) |
590
|
|
|
self.verify_cloned_plan(self.totally_new_plan, cloned_plan, |
591
|
|
|
copy_cases=True, |
592
|
|
|
maintain_case_orignal_author=True, |
593
|
|
|
keep_case_default_tester=True) |
594
|
|
|
|
595
|
|
|
def test_clone_a_plan_by_setting_me_to_copied_cases_author_default_tester(self): |
596
|
|
|
post_data = { |
597
|
|
|
'name': self.totally_new_plan.make_cloned_name(), |
598
|
|
|
'plan': self.totally_new_plan.pk, |
599
|
|
|
'product': self.product.pk, |
600
|
|
|
'product_version': self.version.pk, |
601
|
|
|
'set_parent': 'on', |
602
|
|
|
'link_testcases': 'on', |
603
|
|
|
'submit': 'Clone', |
604
|
|
|
|
605
|
|
|
'copy_testcases': 'on', |
606
|
|
|
# Do not pass maintain_case_orignal_author and keep_case_default_tester |
607
|
|
|
} |
608
|
|
|
self.client.login( # nosec:B106:hardcoded_password_funcarg |
609
|
|
|
username=self.plan_tester.username, |
610
|
|
|
password='password') |
611
|
|
|
self.client.post(self.plan_clone_url, post_data) |
612
|
|
|
cloned_plan = TestPlan.objects.get(name=self.totally_new_plan.make_cloned_name()) |
613
|
|
|
self.verify_cloned_plan(self.totally_new_plan, cloned_plan, copy_cases=True) |
614
|
|
|
|