From 16a124876ca77cb96d3e4048d41dfc8ea2fe5210 Mon Sep 17 00:00:00 2001 From: Alexander Todorov Date: Thu, 14 Nov 2024 23:01:18 +0200 Subject: [PATCH] More speed-up for TestCase filtering on the TestPlan view page building upon the previous change we now either show or hide rows on screen depending on what the actual result set is. When the rows which match the filter are less than the ones which don't we display only them. Else we display everything and hide the rows which don't match the filter. This always results in the minimum number of display updates. For the extreme scenario where 990/1000 rows match is_automated=false this provides 30x improvement! WARNING: for scenarios which are not on the extreme case, yet still result in 100s of rows that need to be displayed this commit does not offer any optimization compared to the previous one! --- tcms/testplans/static/testplans/js/get.js | 80 ++++++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/tcms/testplans/static/testplans/js/get.js b/tcms/testplans/static/testplans/js/get.js index 3064065d3e..994d18fadf 100644 --- a/tcms/testplans/static/testplans/js/get.js +++ b/tcms/testplans/static/testplans/js/get.js @@ -688,6 +688,73 @@ function getSelectedTestCases () { return tcIds } +// returns 2 arrays with selectors that need to be either shown or hidden +// return values will be used as multiple-selector for jQuery +// see https://api.jquery.com/multiple-selector/ +function findSelectorsToShowAndHide (inputData, filterBy, filterValue) { + const hideMe = [] + const showMe = [] + + inputData.forEach(function (element) { + if (element[filterBy] !== undefined && element[filterBy].toString().toLowerCase().indexOf(filterValue) > -1) { + showMe.push(`[data-testcase-pk=${element.id}]`) + } else { + hideMe.push(`[data-testcase-pk=${element.id}]`) + } + }) + + return { + hide: hideMe, + show: showMe + } +} + +// similar to the function above, however it operates on API data returned by +// calls to .filter() API methods. Needs all data as input to be able to calculate +// which rows should not be visible +function findSelectorsToShowAndHideFromAPIData (allData, filteredData) { + const hideMe = [] + const showMe = [] + + // these need to be hidden + const filteredPKs = filteredData.map(element => element.id) + allData.forEach(element => { + if (filteredPKs.indexOf(element.id) === -1) { + hideMe.push(`[data-testcase-pk=${element.id}]`) + } + }) + + // these will remain visible + filteredData.forEach(element => { + showMe.push(`[data-testcase-pk=${element.id}]`) + }) + + return { + hide: hideMe, + show: showMe + } +} + +// update the screen in one swoop trying to perform +// as little display updates as possible +function showOrHideMultipleRows (rootSelector, rows) { + // initial state is that everything is hidden + + if (rows.show.length <= rows.hide.length) { + $(rows.show.join(',')).show() + } else { + /* eslint-disable indent */ + $('body') + .find(rootSelector) + .show() + .end() + .find(rows.hide.join(',')) + .hide() + .end() + /* eslint-enable */ + } +} + function filterTestCasesByProperty (planId, testCases, filterBy, filterValue) { // no input => show all rows if (filterValue.trim().length === 0) { @@ -697,8 +764,6 @@ function filterTestCasesByProperty (planId, testCases, filterBy, filterValue) { $('.js-testcase-row').hide() - // see https://api.jquery.com/multiple-selector/ - const showOnly = [] if (filterBy === 'component' || filterBy === 'tag') { const query = { plan: planId } query[`${filterBy}__name__icontains`] = filterValue @@ -706,15 +771,12 @@ function filterTestCasesByProperty (planId, testCases, filterBy, filterValue) { jsonRPC('TestCase.filter', query, function (filtered) { // hide again if a previous async request showed something else $('.js-testcase-row').hide() - filtered.forEach(tc => showOnly.push(`[data-testcase-pk=${tc.id}]`)) - $(showOnly.join(',')).show() + const rows = findSelectorsToShowAndHideFromAPIData(testCases, filtered) + showOrHideMultipleRows('.js-testcase-row', rows) }) } else { - testCases.filter(function (tc) { - return (tc[filterBy] !== undefined && tc[filterBy].toString().toLowerCase().indexOf(filterValue) > -1) - }).forEach(tc => showOnly.push(`[data-testcase-pk=${tc.id}]`)) - - $(showOnly.join(',')).show() + const rows = findSelectorsToShowAndHide(testCases, filterBy, filterValue) + showOrHideMultipleRows('.js-testcase-row', rows) } }