Source code for tcms.testcases.views

# -*- 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_form(self, form_class=None): form = super().get_form(form_class) # clear fields which are set dynamically via JavaScript form.populate(self.request.POST.get("product", -1)) return form
[docs] def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["initial"].update( # pylint: disable=objects-update-used { "author": self.request.user, } ) test_plan = plan_from_request_or_none(self.request) if test_plan: kwargs["initial"]["product"] = test_plan.product_id return kwargs
[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] def form_valid(self, form): test_plan = plan_from_request_or_none(self.request) notify_formset = CaseNotifyFormSet(self.request.POST) if notify_formset.is_valid(): test_case = form.save() if test_plan: test_plan.add_case(test_case) notify_formset.instance = test_case notify_formset.save() return HttpResponseRedirect(reverse("testcases-get", args=[test_case.pk])) # taken from FormMixin.form_invalid() return self.render_to_response( self.get_context_data(notify_formset=notify_formset) )
[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 form_valid(self, form): notify_formset = CaseNotifyFormSet(self.request.POST, instance=self.object) if notify_formset.is_valid(): notify_formset.save() return super().form_valid(form) # taken from FormMixin.form_invalid() return self.render_to_response( self.get_context_data(notify_formset=notify_formset) )
[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_form(self, form_class=None): form = super().get_form(form_class) if self.request.POST.get("product"): form.populate(product_id=self.request.POST["product"]) else: form.populate(product_id=self.object.category.product_id) return form
[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