diff --git a/tcms/rpc/api/testcase.py b/tcms/rpc/api/testcase.py index 1334a3285f..487ef5b255 100644 --- a/tcms/rpc/api/testcase.py +++ b/tcms/rpc/api/testcase.py @@ -5,6 +5,7 @@ from django.db.models.functions import Coalesce from django.forms import EmailField, ValidationError from django.forms.models import model_to_dict +from django.utils.dateparse import parse_duration from modernrpc.core import REQUEST_KEY, rpc_method from tcms.core import helpers @@ -279,9 +280,19 @@ def filter(query=None): # pylint: disable=redefined-builtin :return: Serialized list of :class:`tcms.testcases.models.TestCase` objects. :rtype: list(dict) """ + if query is None: query = {} + if "setup_duration" in query: + query["setup_duration"] = parse_duration(query["setup_duration"]) + + if "testing_duration" in query: + query["testing_duration"] = parse_duration(query["testing_duration"]) + + if "expected_duration" in query: + query["expected_duration"] = parse_duration(query["expected_duration"]) + qs = ( TestCase.objects.annotate( expected_duration=Coalesce("setup_duration", timedelta(0)) diff --git a/tcms/rpc/tests/test_testcase.py b/tcms/rpc/tests/test_testcase.py index ca2e00a37d..3a18f17430 100644 --- a/tcms/rpc/tests/test_testcase.py +++ b/tcms/rpc/tests/test_testcase.py @@ -223,6 +223,35 @@ def test_duration_properties_in_result( self.assertEqual(result[0]["testing_duration"], testing_duration) self.assertEqual(result[0]["expected_duration"], expected_duration) + def test_filter_by_setup_duration(self): + case = TestCaseFactory(setup_duration=timedelta(seconds=45)) + + result = self.rpc_client.TestCase.filter({"setup_duration": "0:00:45"}) + + self.assertIsNotNone(result) + self.assertEqual(len(result), 1) + self.assertEqual(result[0]["id"], case.pk) + + def test_filter_by_testing_duration(self): + case = TestCaseFactory(testing_duration=timedelta(minutes=2)) + + result = self.rpc_client.TestCase.filter({"testing_duration": "0:02:00"}) + + self.assertIsNotNone(result) + self.assertEqual(len(result), 1) + self.assertEqual(result[0]["id"], case.pk) + + def test_filter_by_expected_duration(self): + case = TestCaseFactory( + setup_duration=timedelta(seconds=45), testing_duration=timedelta(minutes=2) + ) + + result = self.rpc_client.TestCase.filter({"expected_duration": "0:02:45"}) + + self.assertIsNotNone(result) + self.assertEqual(len(result), 1) + self.assertEqual(result[0]["id"], case.pk) + class TestUpdate(APITestCase): non_existing_username = "FakeUsername" diff --git a/tcms/testcases/forms.py b/tcms/testcases/forms.py index d3857f963e..5af2b68ae9 100644 --- a/tcms/testcases/forms.py +++ b/tcms/testcases/forms.py @@ -124,6 +124,18 @@ class Meta: widget=forms.CheckboxSelectMultiple(), required=False, ) + setup_duration = forms.DurationField( + widget=DurationWidget(), + required=False, + ) + testing_duration = forms.DurationField( + widget=DurationWidget(), + required=False, + ) + expected_duration = forms.DurationField( + widget=DurationWidget(), + required=False, + ) def populate(self, product_id=None): if product_id: diff --git a/tcms/testcases/static/testcases/js/search.js b/tcms/testcases/static/testcases/js/search.js index 758f0da3b5..3fa2e4b03e 100644 --- a/tcms/testcases/static/testcases/js/search.js +++ b/tcms/testcases/static/testcases/js/search.js @@ -52,6 +52,21 @@ function pre_process_data (data, callback) { }) } +function formatDuration(seconds) { + let numSecondsInDay = 24 * 60 * 60; + let days = Math.floor(seconds / numSecondsInDay); + let rest = seconds % numSecondsInDay; + + let date = new Date(0); + date.setSeconds(rest); + let timeString = date.toISOString().substr(11, 8); + + if (days) { + return `${days} ${timeString}`; + } + return `${timeString}`; +} + $(document).ready(function () { const table = $('#resultsTable').DataTable({ pageLength: $('#navbar').data('defaultpagesize'), @@ -106,6 +121,18 @@ $(document).ready(function () { params.is_automated = false }; + if (!['', '0'].includes($('#id_setup_duration').val())) { + params.setup_duration = $('#id_setup_duration').val() + }; + + if (!['', '0'].includes($('#id_testing_duration').val())) { + params.testing_duration = $('#id_testing_duration').val() + }; + + if (!['', '0'].includes($('#id_expected_duration').val())) { + params.expected_duration = $('#id_expected_duration').val() + }; + const text = $('#id_text').val() if (text) { params.text__icontains = text @@ -145,7 +172,25 @@ $(document).ready(function () { { data: 'case_status__name' }, { data: 'is_automated' }, { data: 'author__username' }, - { data: 'tag_names' } + { data: 'tag_names' }, + { + data: 'setup_duration', + render: function(data, type, full, meta) { + return formatDuration(data); + } + }, + { + data: 'testing_duration', + render: function(data, type, full, meta) { + return formatDuration(data); + } + }, + { + data: 'expected_duration', + render: function(data, type, full, meta) { + return formatDuration(data); + } + } ], dom: 't', language: { diff --git a/tcms/testcases/templates/testcases/search.html b/tcms/testcases/templates/testcases/search.html index 05d7311fc4..444b82cb97 100644 --- a/tcms/testcases/templates/testcases/search.html +++ b/tcms/testcases/templates/testcases/search.html @@ -2,7 +2,10 @@ {% load i18n %} {% load static %} +{% block head %} +{{ form.media }} {% block title %}{% trans "Search test cases" %}{% endblock %} +{% endblock %} {% block contents %}