# -*- coding: utf-8 -*-
from django.contrib import messages
from django.contrib.auth.decorators import permission_required
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.translation import gettext_lazy as _
from django.views.generic import DetailView
from django.views.generic.base import TemplateView, View
from django.views.generic.edit import CreateView, UpdateView
from guardian.decorators import permission_required as object_permission_required
from tcms.testcases.forms import (
CaseNotifyFormSet,
CloneCaseForm,
SearchCaseForm,
TestCaseForm,
)
from tcms.testcases.models import Template, TestCase
from tcms.testplans.models import TestPlan
[docs]def plan_from_request_or_none(request): # pylint: disable=missing-permission-required
"""Get TestPlan from REQUEST
This method relies on the existence of from_plan within REQUEST.
"""
test_plan_id = request.POST.get("from_plan") or request.GET.get("from_plan")
if not test_plan_id:
return None
return get_object_or_404(TestPlan, pk=test_plan_id)
[docs]@method_decorator(permission_required("testcases.add_testcase"), name="dispatch")
class NewCaseView(CreateView):
model = TestCase
form_class = TestCaseForm
template_name = "testcases/mutable.html"
[docs] def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["test_plan"] = plan_from_request_or_none(self.request)
context["notify_formset"] = kwargs.get("notify_formset") or CaseNotifyFormSet()
context["templates"] = Template.objects.all()
return context
[docs]@method_decorator(permission_required("testcases.view_testcase"), name="dispatch")
class TestCaseSearchView(TemplateView):
"""
Shows the search form which uses JSON RPC to fetch the results
"""
template_name = "testcases/search.html"
[docs] def get_context_data(self, **kwargs):
form = SearchCaseForm(self.request.GET)
if self.request.GET.get("product"):
form.populate(product_id=self.request.GET["product"])
else:
form.populate()
return {
"form": form,
}
[docs]@method_decorator(
object_permission_required(
"testcases.view_testcase", (TestCase, "pk", "pk"), accept_global_perms=True
),
name="dispatch",
)
class TestCaseGetView(DetailView):
model = TestCase
template_name = "testcases/get.html"
http_method_names = ["get"]
[docs] def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["executions"] = self.object.executions.select_related(
"run", "tested_by", "assignee", "case", "status"
).order_by("run__plan", "run")
context["OBJECT_MENU_ITEMS"] = [
(
"...",
[
(
_("Edit"),
reverse("testcases-edit", args=[self.object.pk]),
),
(
_("Clone"),
reverse("testcases-clone") + f"?c={self.object.pk}",
),
(
_("History"),
f"/admin/testcases/testcase/{self.object.pk}/history/",
),
("-", "-"),
(
_("Object permissions"),
reverse(
"admin:testcases_testcase_permissions",
args=[self.object.pk],
),
),
("-", "-"),
(
_("Delete"),
reverse(
"admin:testcases_testcase_delete",
args=[self.object.pk],
),
),
],
)
]
return context
[docs]@method_decorator(
object_permission_required(
"testcases.change_testcase", (TestCase, "pk", "pk"), accept_global_perms=True
),
name="dispatch",
)
class EditTestCaseView(UpdateView):
model = TestCase
template_name = "testcases/mutable.html"
form_class = TestCaseForm
[docs] def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["notify_formset"] = kwargs.get("notify_formset") or CaseNotifyFormSet(
instance=self.object
)
return context
[docs] def get_initial(self):
default_tester = None
if self.object.default_tester_id:
default_tester = self.object.default_tester.email
return {
"product": self.object.category.product_id,
"default_tester": default_tester,
}
[docs]@method_decorator(permission_required("testcases.add_testcase"), name="dispatch")
class CloneTestCaseView(View):
"""Clone one case or multiple case into other plan or plans"""
template_name = "testcases/clone.html"
http_method_names = ["get", "post"]
[docs] def post(self, request):
if not self._is_request_data_valid(request):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
# Do the clone action
clone_form = CloneCaseForm(request.POST)
clone_form.populate(case_ids=request.POST.getlist("case"))
if clone_form.is_valid():
for tc_src in clone_form.cleaned_data["case"]:
tc_dest = tc_src.clone(request.user, clone_form.cleaned_data["plan"])
# Detect the number of items and redirect to correct one
if len(clone_form.cleaned_data["case"]) == 1:
return HttpResponseRedirect(
reverse(
"testcases-get",
args=[
tc_dest.pk,
],
)
)
if len(clone_form.cleaned_data["plan"]) == 1:
test_plan = clone_form.cleaned_data["plan"][0]
return HttpResponseRedirect(
reverse("test_plan_url_short", args=[test_plan.pk])
)
# Otherwise tell the user the clone action is successful
messages.add_message(
request, messages.SUCCESS, _("TestCase cloning was successful")
)
return HttpResponseRedirect(reverse("plans-search"))
# invalid form
messages.add_message(request, messages.ERROR, clone_form.errors)
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
[docs] def get(self, request):
if not self._is_request_data_valid(request, "c"):
return HttpResponseRedirect(request.META.get("HTTP_REFERER", "/"))
# account for short param names in URI
get_params = request.GET.copy()
get_params.setlist("case", request.GET.getlist("c"))
del get_params["c"]
clone_form = CloneCaseForm(get_params)
clone_form.populate(case_ids=get_params.getlist("case"))
context = {
"form": clone_form,
}
return render(request, self.template_name, context)
@staticmethod
def _is_request_data_valid(request, field_name="case"):
request_data = getattr(request, request.method)
if field_name not in request_data:
messages.add_message(
request, messages.ERROR, _("At least one TestCase is required")
)
return False
return True