Skip to content

Commit 349e020

Browse files
committed
Add Testing.individual_test_case_health API
This will be used to render individual test case health widget Add UI
1 parent 2d7275e commit 349e020

File tree

3 files changed

+108
-36
lines changed

3 files changed

+108
-36
lines changed

tcms/telemetry/api.py

+16
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,22 @@ def test_case_health(query=None):
226226
return data
227227

228228

229+
@http_basic_auth_login_required
230+
@rpc_method(name="Testing.individual_test_case_health")
231+
def individual_test_case_health_simple(query=None):
232+
233+
if query is None:
234+
query = {}
235+
236+
res = (
237+
TestExecution.objects.filter(**query)
238+
.values("run__plan", "case_id", "status__name", "status__weight")
239+
.order_by("case", "run__plan", "status__weight")
240+
)
241+
242+
return list(res)
243+
244+
229245
def _remove_all_excellent_executions(data):
230246
for key in dict.fromkeys(data):
231247
if data[key]["count"]["fail"] == 0:

tcms/templates/include/tc_executions.html

+15-1
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,24 @@ <h2 class="card-pf-title">
2323
</div>
2424

2525
<div class="list-view-pf-body">
26-
<div class="list-view-pf-description">
26+
<div class="list-view-pf-description" style="display: flex; justify-content: space-between;">
2727
<div class="list-group-item-text">
2828
<a href="{% url 'test_plan_url_short' execution.run.plan.pk %}">TP-{{ execution.run.plan.pk }}: {{ execution.run.plan.name }}</a>
2929
</div>
30+
<div class="list-group-item-text">
31+
<div class="completion-rate-container">{% trans 'Completion rate' %}: <span class="completion-rate"></span>
32+
<div class="progress">
33+
<div class="progress-bar progress-bar-danger" role="progressbar"></div>
34+
<div class="progress-bar progress-bar-success" role="progressbar"></div>
35+
</div>
36+
</div>
37+
<div class="failure-rate-container">{% trans 'Failure rate' %}:<span class="failure-rate"></span>
38+
<div class="progress">
39+
<div class="progress-bar progress-bar-danger" role="progressbar"></div>
40+
<div class="progress-bar progress-bar-success" role="progressbar"></div>
41+
</div>
42+
</div>
43+
</div>
3044
</div>
3145
</div>
3246
</div> <!-- /main info -->

tcms/testcases/static/testcases/js/get.js

+77-35
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ function addComponent(object_id, _input, to_table) {
55
var _name = _input.value;
66

77
if (_name.length > 0) {
8-
jsonRPC('TestCase.add_component', [object_id, _name], function(data) {
8+
jsonRPC('TestCase.add_component', [object_id, _name], function (data) {
99
if (data !== undefined) {
10-
to_table.row.add({name: data.name, id: data.id}).draw();
10+
to_table.row.add({ name: data.name, id: data.id }).draw();
1111
$(_input).val('');
1212
} else {
1313
$(_input).parents('div.input-group').addClass('has-error');
@@ -21,7 +21,7 @@ function addTestPlanToTestCase(case_id, plans_table) {
2121
const plan_name = $('#input-add-plan')[0].value;
2222
const plan = plan_cache[plan_name];
2323

24-
jsonRPC('TestPlan.add_case', [plan.id, case_id], function(data) {
24+
jsonRPC('TestPlan.add_case', [plan.id, case_id], function (data) {
2525
plans_table.row.add({
2626
id: plan.id,
2727
name: plan.name,
@@ -36,12 +36,12 @@ function addTestPlanToTestCase(case_id, plans_table) {
3636

3737
function initAddPlan(case_id, plans_table) {
3838
// + button
39-
$('#btn-add-plan').click(function() {
39+
$('#btn-add-plan').click(function () {
4040
addTestPlanToTestCase(case_id, plans_table);
4141
});
4242

4343
// Enter key
44-
$('#input-add-plan').keyup(function(event) {
44+
$('#input-add-plan').keyup(function (event) {
4545
if (event.keyCode === 13) {
4646
addTestPlanToTestCase(case_id, plans_table);
4747
};
@@ -51,43 +51,43 @@ function initAddPlan(case_id, plans_table) {
5151
$('#input-add-plan.typeahead').typeahead({
5252
minLength: 1,
5353
highlight: true
54-
}, {
54+
}, {
5555
name: 'plans-autocomplete',
5656
// will display up to X results even if more were returned
5757
limit: 100,
5858
async: true,
59-
display: function(element) {
59+
display: function (element) {
6060
const display_name = 'TP-' + element.id + ': ' + element.name;
6161
plan_cache[display_name] = element;
6262
return display_name;
6363
},
64-
source: function(query, processSync, processAsync) {
64+
source: function (query, processSync, processAsync) {
6565
// accepts "TP-1234" or "tp-1234" or "1234"
6666
query = query.toLowerCase().replace('tp-', '');
6767
if (query === '') {
6868
return;
6969
}
7070

71-
var rpc_query = {pk: query};
71+
var rpc_query = { pk: query };
7272

7373
// or arbitrary string
7474
if (isNaN(query)) {
75-
if (query.length >=3) {
76-
rpc_query = {name__icontains: query};
75+
if (query.length >= 3) {
76+
rpc_query = { name__icontains: query };
7777
} else {
7878
return;
7979
}
8080
}
8181

82-
jsonRPC('TestPlan.filter', rpc_query, function(data) {
82+
jsonRPC('TestPlan.filter', rpc_query, function (data) {
8383
return processAsync(data);
8484
});
8585
}
8686
});
8787
}
8888

8989

90-
$(document).ready(function() {
90+
$(document).ready(function () {
9191
var case_id = $('#test_case_pk').data('pk');
9292
var product_id = $('#product_pk').data('pk');
9393
var perm_remove_tag = $('#test_case_pk').data('perm-remove-tag') === 'True';
@@ -96,12 +96,12 @@ $(document).ready(function() {
9696
var perm_remove_bug = $('#test_case_pk').data('perm-remove-bug') === 'True';
9797

9898
// bind everything in tags table
99-
tagsCard('TestCase', case_id, {case: case_id}, perm_remove_tag);
99+
tagsCard('TestCase', case_id, { case: case_id }, perm_remove_tag);
100100

101101
// components table
102102
var components_table = $('#components').DataTable({
103-
ajax: function(data, callback, settings) {
104-
dataTableJsonRPC('Component.filter', [{'cases': case_id}], callback);
103+
ajax: function (data, callback, settings) {
104+
dataTableJsonRPC('Component.filter', [{ 'cases': case_id }], callback);
105105
},
106106
columns: [
107107
{ data: "name" },
@@ -110,7 +110,7 @@ $(document).ready(function() {
110110
sortable: false,
111111
render: function (data, type, full, meta) {
112112
if (perm_remove_component) {
113-
return '<a href="#components" class="remove-component" data-pk="' + data + '"><span class="pficon-error-circle-o"></span></a>';
113+
return '<a href="#components" class="remove-component" data-pk="' + data + '"><span class="pficon-error-circle-o"></span></a>';
114114
}
115115
return '';
116116
}
@@ -122,26 +122,26 @@ $(document).ready(function() {
122122
processing: '<div class="spinner spinner-lg"></div>',
123123
zeroRecords: "No records found"
124124
},
125-
order: [[ 0, 'asc' ]],
125+
order: [[0, 'asc']],
126126
});
127127

128128
// remove component button
129-
components_table.on('draw', function() {
130-
$('.remove-component').click(function() {
129+
components_table.on('draw', function () {
130+
$('.remove-component').click(function () {
131131
var tr = $(this).parents('tr');
132132

133-
jsonRPC('TestCase.remove_component', [case_id, $(this).data('pk')], function(data) {
133+
jsonRPC('TestCase.remove_component', [case_id, $(this).data('pk')], function (data) {
134134
components_table.row($(tr)).remove().draw();
135135
});
136136
});
137137
});
138138

139139
// add component button and Enter key
140-
$('#add-component').click(function() {
140+
$('#add-component').click(function () {
141141
addComponent(case_id, $('#id_components')[0], components_table);
142142
});
143143

144-
$('#id_components').keyup(function(event) {
144+
$('#id_components').keyup(function (event) {
145145
if (event.keyCode === 13) {
146146
addComponent(case_id, $('#id_components')[0], components_table);
147147
};
@@ -151,16 +151,16 @@ $(document).ready(function() {
151151
$('#id_components.typeahead').typeahead({
152152
minLength: 3,
153153
highlight: true
154-
}, {
154+
}, {
155155
name: 'components-autocomplete',
156156
// will display up to X results even if more were returned
157157
limit: 100,
158158
async: true,
159-
display: function(element) {
159+
display: function (element) {
160160
return element.name;
161161
},
162-
source: function(query, processSync, processAsync) {
163-
jsonRPC('Component.filter', {name__icontains: query, product: product_id}, function(data) {
162+
source: function (query, processSync, processAsync) {
163+
jsonRPC('Component.filter', { name__icontains: query, product: product_id }, function (data) {
164164
data = arrayToDict(data)
165165
return processAsync(Object.values(data));
166166
});
@@ -169,15 +169,15 @@ $(document).ready(function() {
169169

170170
// testplans table
171171
var plans_table = $('#plans').DataTable({
172-
ajax: function(data, callback, settings) {
173-
dataTableJsonRPC('TestPlan.filter', {cases: case_id}, callback);
172+
ajax: function (data, callback, settings) {
173+
dataTableJsonRPC('TestPlan.filter', { cases: case_id }, callback);
174174
},
175175
columns: [
176176
{ data: "id" },
177177
{
178178
data: null,
179179
render: function (data, type, full, meta) {
180-
return '<a href="/plan/'+ data.id + '/">' + escapeHTML(data.name) + '</a>';
180+
return '<a href="/plan/' + data.id + '/">' + escapeHTML(data.name) + '</a>';
181181
}
182182
},
183183
{ data: "author__username" },
@@ -188,7 +188,7 @@ $(document).ready(function() {
188188
sortable: false,
189189
render: function (data, type, full, meta) {
190190
if (perm_remove_plan) {
191-
return '<a href="#plans" class="remove-plan" data-pk="' + data.id + '"><span class="pficon-error-circle-o"></span></a>';
191+
return '<a href="#plans" class="remove-plan" data-pk="' + data.id + '"><span class="pficon-error-circle-o"></span></a>';
192192
}
193193
return '';
194194
}
@@ -200,20 +200,62 @@ $(document).ready(function() {
200200
processing: '<div class="spinner spinner-lg"></div>',
201201
zeroRecords: "No records found"
202202
},
203-
order: [[ 0, 'asc' ]],
203+
order: [[0, 'asc']],
204204
});
205205

206206
// remove plan button
207-
plans_table.on('draw', function() {
208-
$('.remove-plan').click(function() {
207+
plans_table.on('draw', function () {
208+
$('.remove-plan').click(function () {
209209
var tr = $(this).parents('tr');
210210

211-
jsonRPC('TestPlan.remove_case', [$(this).data('pk'), case_id], function(data) {
211+
jsonRPC('TestPlan.remove_case', [$(this).data('pk'), case_id], function (data) {
212212
plans_table.row($(tr)).remove().draw();
213213
});
214214
});
215215
});
216216

217+
jsonRPC('Testing.individual_test_case_health', { case_id: case_id }, ress => {
218+
console.log(ress)
219+
220+
let res = {};
221+
let planId = 0;
222+
ress.forEach(r => {
223+
let positive = 0;
224+
let negative = 0;
225+
let allCount = 0;
226+
if (r.status__weight > 0) {
227+
positive++
228+
} else if (r.status__weight < 0) {
229+
negative++
230+
}
231+
allCount++
232+
233+
if (r.run__plan !== planId) {
234+
planId = r.run__plan;
235+
res[planId] = {
236+
"completion_rate": allCount > 0 ? ((positive + negative) / allCount) : 0,
237+
"failure_rate": allCount > 0 ? (negative / allCount) : 0,
238+
}
239+
positive = 0;
240+
negative = 0;
241+
allCount = 0;
242+
}
243+
})
244+
245+
console.log(res)
246+
247+
Object.entries(res).forEach(([runId, data]) => {
248+
const executionRow = $(`#execution-for-plan-${runId}`)
249+
executionRow.find('.completion-rate').html(data.completion_rate)
250+
executionRow.find('.completion-rate-container .progress-bar-danger').css('width', `${(1 - data.completion_rate) * 100}%`)
251+
executionRow.find('.completion-rate-container .progress-bar-success').css('width', `${(data.completion_rate) * 100}%`)
252+
253+
executionRow.find('.failure-rate').html(data.failure_rate)
254+
executionRow.find('.failure-rate-container .progress-bar-danger').css('width', `${(data.failure_rate) * 100}%`)
255+
executionRow.find('.failure-rate-container .progress-bar-success').css('width', `${(1 - data.failure_rate) * 100}%`)
256+
})
257+
})
258+
217259
// bind add TP to TC widget
218260
initAddPlan(case_id, plans_table);
219261

0 commit comments

Comments
 (0)