Skip to content

Commit 9935f11

Browse files
refactor(ci, observability): separate slowest e2e charts from messenger
Keep the Loop report focused on feature duration status while moving slowest-specs into local and CI artifact renderers for deeper triage. Signed-off-by: Nikita Korolev <nikita.korolev@flant.com>
1 parent 0ac6e2a commit 9935f11

20 files changed

Lines changed: 1122 additions & 671 deletions

.github/scripts/js/e2e/report/cluster-report.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ describe("cluster-report", () => {
477477
},
478478
{
479479
name: "skipped",
480-
group: "skipped",
480+
group: "Top-level Its",
481481
state: "skipped",
482482
runtimeMs: 60000,
483483
labels: [],

.github/scripts/js/e2e/report/messenger-report.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ describe("messenger-report", () => {
182182
test("attaches duration chart files to thread reply without a text caption", async () =>
183183
inTempDir(async (tempDir) => {
184184
const chartFile = {
185-
name: "replicated-slowest-specs.png",
185+
name: "replicated-feature-duration-status.png",
186186
buffer: Buffer.from("png"),
187187
mimeType: "image/png",
188188
};
@@ -585,7 +585,7 @@ describe("messenger-report", () => {
585585
test("posts main report and per-cluster failed tests thread via Loop API", async () =>
586586
inTempDir(async (tempDir) => {
587587
const chartFile = {
588-
name: "replicated-slowest-specs.png",
588+
name: "replicated-feature-duration-status.png",
589589
buffer: Buffer.from("png"),
590590
mimeType: "image/png",
591591
};

.github/scripts/js/e2e/report/messenger/charts/__snapshots__/chart-config.test.js.snap

Lines changed: 6 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -69,105 +69,6 @@ Array [
6969
"x": Object {
7070
"beginAtZero": true,
7171
"stacked": true,
72-
"title": Object {
73-
"display": true,
74-
"text": "Duration, seconds",
75-
},
76-
},
77-
"y": Object {
78-
"stacked": true,
79-
},
80-
},
81-
},
82-
"plugins": Array [
83-
Object {
84-
"afterDatasetsDraw": [Function],
85-
"id": "valueLabels",
86-
},
87-
],
88-
"type": "bar",
89-
},
90-
"name": "feature-duration-status",
91-
},
92-
Object {
93-
"config": Object {
94-
"data": Object {
95-
"datasets": Array [
96-
Object {
97-
"backgroundColor": Array [
98-
"#a371f7",
99-
"#a371f7",
100-
"#58a6ff",
101-
"#76e3ea",
102-
"#76e3ea",
103-
],
104-
"borderColor": Array [
105-
"#d29922",
106-
"#f85149",
107-
"transparent",
108-
"transparent",
109-
"transparent",
110-
],
111-
"borderWidth": Array [
112-
3,
113-
3,
114-
0,
115-
0,
116-
0,
117-
],
118-
"data": Array [
119-
601,
120-
301,
121-
60,
122-
45,
123-
10,
124-
],
125-
"label": "Duration, seconds",
126-
"states": Array [
127-
"errors",
128-
"failed",
129-
"skipped",
130-
"passed",
131-
"passed",
132-
],
133-
},
134-
],
135-
"labels": Array [
136-
"Network / error",
137-
"VM / slow fail",
138-
"Disk / medium skip",
139-
"VM / passing peer",
140-
"VM / fast pass",
141-
],
142-
},
143-
"options": Object {
144-
"animation": false,
145-
"indexAxis": "y",
146-
"layout": Object {
147-
"padding": Object {
148-
"bottom": 8,
149-
"top": 16,
150-
},
151-
},
152-
"plugins": Object {
153-
"legend": Object {
154-
"display": true,
155-
"labels": Object {
156-
"generateLabels": [Function],
157-
},
158-
},
159-
"title": Object {
160-
"display": true,
161-
"text": "Top slowest successful specs and failed specs (It/Entry)",
162-
},
163-
"valueLabels": Object {
164-
"formatter": [Function],
165-
},
166-
},
167-
"responsive": false,
168-
"scales": Object {
169-
"x": Object {
170-
"beginAtZero": true,
17172
"ticks": Object {
17273
"stepSize": 60,
17374
},
@@ -176,106 +77,6 @@ Array [
17677
"text": "Duration, seconds",
17778
},
17879
},
179-
},
180-
},
181-
"plugins": Array [
182-
Object {
183-
"afterDatasetsDraw": [Function],
184-
"id": "valueLabels",
185-
},
186-
],
187-
"type": "bar",
188-
},
189-
"name": "slowest-specs",
190-
"size": Object {
191-
"height": 720,
192-
"width": 2048,
193-
},
194-
},
195-
Object {
196-
"config": Object {
197-
"data": Object {
198-
"datasets": Array [
199-
Object {
200-
"backgroundColor": "#3fb950",
201-
"barPercentage": 0.5,
202-
"categoryPercentage": 0.6,
203-
"data": Array [
204-
0,
205-
0,
206-
2,
207-
],
208-
"label": "passed",
209-
},
210-
Object {
211-
"backgroundColor": "#f85149",
212-
"barPercentage": 0.5,
213-
"categoryPercentage": 0.6,
214-
"data": Array [
215-
1,
216-
0,
217-
0,
218-
],
219-
"label": "failed",
220-
},
221-
Object {
222-
"backgroundColor": "#d29922",
223-
"barPercentage": 0.5,
224-
"categoryPercentage": 0.6,
225-
"data": Array [
226-
1,
227-
0,
228-
0,
229-
],
230-
"label": "errors",
231-
},
232-
Object {
233-
"backgroundColor": "#8b949e",
234-
"barPercentage": 0.5,
235-
"categoryPercentage": 0.6,
236-
"data": Array [
237-
0,
238-
1,
239-
0,
240-
],
241-
"label": "skipped",
242-
},
243-
],
244-
"labels": Array [
245-
"Slow >300s",
246-
"Medium 60-300s",
247-
"Fast <60s",
248-
],
249-
},
250-
"options": Object {
251-
"animation": false,
252-
"indexAxis": "y",
253-
"plugins": Object {
254-
"legend": Object {
255-
"display": true,
256-
},
257-
"title": Object {
258-
"display": true,
259-
"text": "It/Entry duration buckets by status",
260-
},
261-
"valueLabels": Object {
262-
"calloutSmallStackedLabels": true,
263-
"formatter": [Function],
264-
},
265-
},
266-
"responsive": false,
267-
"scales": Object {
268-
"x": Object {
269-
"beginAtZero": true,
270-
"stacked": true,
271-
"ticks": Object {
272-
"precision": 0,
273-
},
274-
"title": Object {
275-
"display": true,
276-
"text": "Specs",
277-
},
278-
},
27980
"y": Object {
28081
"stacked": true,
28182
},
@@ -289,7 +90,12 @@ Array [
28990
],
29091
"type": "bar",
29192
},
292-
"name": "duration-buckets",
93+
"name": "feature-duration-status",
94+
"size": Object {
95+
"height": 640,
96+
"pixelRatio": 2,
97+
"width": 1280,
98+
},
29399
},
294100
]
295101
`;
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// Copyright 2026 Flant JSC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
// Unless required by applicable law or agreed to in writing, software
8+
// distributed under the License is distributed on an "AS IS" BASIS,
9+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
// See the License for the specific language governing permissions and
11+
// limitations under the License.
12+
13+
const {
14+
STATUSES,
15+
STATUS_COLORS,
16+
toSeconds,
17+
formatSeconds,
18+
baseOptions,
19+
} = require("../data");
20+
const { valueLabelsPlugin } = require("../plugins");
21+
22+
function sortedGroups(byGroup, compareFn) {
23+
return [...byGroup.entries()].sort(compareFn);
24+
}
25+
26+
function problemCount(group) {
27+
return group.statusCount.failed + group.statusCount.errors;
28+
}
29+
30+
function featureDurationStatus({ byGroup }) {
31+
// Most-broken features go to the top: failures desc, then total runtime desc,
32+
// then alphabetical for a stable order.
33+
const entries = sortedGroups(byGroup, (left, right) => {
34+
return (
35+
problemCount(right[1]) - problemCount(left[1]) ||
36+
right[1].total - left[1].total ||
37+
left[0].localeCompare(right[0])
38+
);
39+
});
40+
41+
const labels = entries.map(([name]) => name);
42+
const datasets = STATUSES.map((status) => ({
43+
label: status,
44+
data: entries.map(([, group]) => toSeconds(group.statusDurations[status])),
45+
backgroundColor: STATUS_COLORS[status],
46+
}));
47+
const height = Math.max(640, 120 + labels.length * 36);
48+
49+
return {
50+
name: "feature-duration-status",
51+
size: { width: 1280, height, pixelRatio: 2 },
52+
config: {
53+
type: "bar",
54+
data: { labels, datasets },
55+
options: baseOptions("Overall durations for Describes", {
56+
indexAxis: "y",
57+
plugins: {
58+
legend: { display: true },
59+
valueLabels: { formatter: formatSeconds },
60+
},
61+
scales: {
62+
x: {
63+
stacked: true,
64+
beginAtZero: true,
65+
ticks: { stepSize: 60 },
66+
title: { display: true, text: "Duration, seconds" },
67+
},
68+
y: { stacked: true },
69+
},
70+
}),
71+
plugins: [valueLabelsPlugin],
72+
},
73+
};
74+
}
75+
76+
module.exports = featureDurationStatus;

0 commit comments

Comments
 (0)