@memberjunction/ng-dashboards 2.117.0 → 2.119.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Testing/components/testing-analytics.component.d.ts +56 -0
- package/dist/Testing/components/testing-analytics.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-analytics.component.js +746 -0
- package/dist/Testing/components/testing-analytics.component.js.map +1 -0
- package/dist/Testing/components/testing-execution.component.d.ts +59 -0
- package/dist/Testing/components/testing-execution.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-execution.component.js +647 -0
- package/dist/Testing/components/testing-execution.component.js.map +1 -0
- package/dist/Testing/components/testing-feedback.component.d.ts +55 -0
- package/dist/Testing/components/testing-feedback.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-feedback.component.js +788 -0
- package/dist/Testing/components/testing-feedback.component.js.map +1 -0
- package/dist/Testing/components/testing-overview.component.d.ts +30 -0
- package/dist/Testing/components/testing-overview.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-overview.component.js +341 -0
- package/dist/Testing/components/testing-overview.component.js.map +1 -0
- package/dist/Testing/components/testing-version-comparison.component.d.ts +58 -0
- package/dist/Testing/components/testing-version-comparison.component.d.ts.map +1 -0
- package/dist/Testing/components/testing-version-comparison.component.js +801 -0
- package/dist/Testing/components/testing-version-comparison.component.js.map +1 -0
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.d.ts +20 -0
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.d.ts.map +1 -0
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js +278 -0
- package/dist/Testing/components/widgets/oracle-breakdown-table.component.js.map +1 -0
- package/dist/Testing/components/widgets/suite-tree.component.d.ts +28 -0
- package/dist/Testing/components/widgets/suite-tree.component.d.ts.map +1 -0
- package/dist/Testing/components/widgets/suite-tree.component.js +275 -0
- package/dist/Testing/components/widgets/suite-tree.component.js.map +1 -0
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts +34 -0
- package/dist/Testing/components/widgets/test-run-detail-panel.component.d.ts.map +1 -0
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js +371 -0
- package/dist/Testing/components/widgets/test-run-detail-panel.component.js.map +1 -0
- package/dist/Testing/services/testing-instrumentation.service.d.ts +152 -0
- package/dist/Testing/services/testing-instrumentation.service.d.ts.map +1 -0
- package/dist/Testing/services/testing-instrumentation.service.js +579 -0
- package/dist/Testing/services/testing-instrumentation.service.js.map +1 -0
- package/dist/Testing/testing-dashboard.component.d.ts +52 -0
- package/dist/Testing/testing-dashboard.component.d.ts.map +1 -0
- package/dist/Testing/testing-dashboard.component.js +271 -0
- package/dist/Testing/testing-dashboard.component.js.map +1 -0
- package/dist/module.d.ts +30 -20
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +50 -9
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +13 -10
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import { Subject, combineLatest } from 'rxjs';
|
|
3
|
+
import { takeUntil, map } from 'rxjs/operators';
|
|
4
|
+
import { CompositeKey } from '@memberjunction/core';
|
|
5
|
+
import { SharedService } from '@memberjunction/ng-shared';
|
|
6
|
+
import { TestRunDialogComponent } from '@memberjunction/ng-testing';
|
|
7
|
+
import * as i0 from "@angular/core";
|
|
8
|
+
import * as i1 from "../services/testing-instrumentation.service";
|
|
9
|
+
import * as i2 from "@progress/kendo-angular-dialog";
|
|
10
|
+
import * as i3 from "@angular/common";
|
|
11
|
+
import * as i4 from "@angular/forms";
|
|
12
|
+
import * as i5 from "@memberjunction/ng-testing";
|
|
13
|
+
const _forTrack0 = ($index, $item) => $item.id;
|
|
14
|
+
const _c0 = () => [];
|
|
15
|
+
function TestingExecutionComponent_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
16
|
+
i0.ɵɵelementStart(0, "div", 51);
|
|
17
|
+
i0.ɵɵelement(1, "span", 52);
|
|
18
|
+
i0.ɵɵelementStart(2, "span", 53);
|
|
19
|
+
i0.ɵɵtext(3, "Live");
|
|
20
|
+
i0.ɵɵelementEnd()();
|
|
21
|
+
} }
|
|
22
|
+
function TestingExecutionComponent_button_48_Template(rf, ctx) { if (rf & 1) {
|
|
23
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
24
|
+
i0.ɵɵelementStart(0, "button", 54);
|
|
25
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_button_48_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.clearSearch()); });
|
|
26
|
+
i0.ɵɵelement(1, "i", 55);
|
|
27
|
+
i0.ɵɵelementEnd();
|
|
28
|
+
} }
|
|
29
|
+
function TestingExecutionComponent_Conditional_103_Template(rf, ctx) { if (rf & 1) {
|
|
30
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
31
|
+
i0.ɵɵelementStart(0, "div", 49);
|
|
32
|
+
i0.ɵɵelement(1, "i", 56);
|
|
33
|
+
i0.ɵɵelementStart(2, "p");
|
|
34
|
+
i0.ɵɵtext(3, "No test executions found");
|
|
35
|
+
i0.ɵɵelementEnd();
|
|
36
|
+
i0.ɵɵelementStart(4, "button", 8);
|
|
37
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Conditional_103_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.startNewTest()); });
|
|
38
|
+
i0.ɵɵelement(5, "i", 9);
|
|
39
|
+
i0.ɵɵtext(6, " Run Your First Test ");
|
|
40
|
+
i0.ɵɵelementEnd()();
|
|
41
|
+
} }
|
|
42
|
+
function TestingExecutionComponent_For_106_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
43
|
+
i0.ɵɵelementStart(0, "div", 64);
|
|
44
|
+
i0.ɵɵelement(1, "div", 76);
|
|
45
|
+
i0.ɵɵelementEnd();
|
|
46
|
+
} if (rf & 2) {
|
|
47
|
+
const execution_r5 = i0.ɵɵnextContext().$implicit;
|
|
48
|
+
i0.ɵɵadvance();
|
|
49
|
+
i0.ɵɵstyleProp("width", execution_r5.progress, "%");
|
|
50
|
+
} }
|
|
51
|
+
function TestingExecutionComponent_For_106_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
52
|
+
const _r6 = i0.ɵɵgetCurrentView();
|
|
53
|
+
i0.ɵɵelementStart(0, "button", 77);
|
|
54
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_106_Conditional_22_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r6); const execution_r5 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.cancelExecution(execution_r5)); });
|
|
55
|
+
i0.ɵɵelement(1, "i", 78);
|
|
56
|
+
i0.ɵɵelementEnd();
|
|
57
|
+
} }
|
|
58
|
+
function TestingExecutionComponent_For_106_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
59
|
+
const _r7 = i0.ɵɵgetCurrentView();
|
|
60
|
+
i0.ɵɵelementStart(0, "button", 79);
|
|
61
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_106_Conditional_23_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r7); const execution_r5 = i0.ɵɵnextContext().$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.rerunTest(execution_r5)); });
|
|
62
|
+
i0.ɵɵelement(1, "i", 80);
|
|
63
|
+
i0.ɵɵelementEnd();
|
|
64
|
+
} }
|
|
65
|
+
function TestingExecutionComponent_For_106_Template(rf, ctx) { if (rf & 1) {
|
|
66
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
67
|
+
i0.ɵɵelementStart(0, "div", 57)(1, "div", 58)(2, "div", 59)(3, "div", 60);
|
|
68
|
+
i0.ɵɵtext(4);
|
|
69
|
+
i0.ɵɵelementEnd();
|
|
70
|
+
i0.ɵɵelementStart(5, "div", 61);
|
|
71
|
+
i0.ɵɵtext(6);
|
|
72
|
+
i0.ɵɵelementEnd()()();
|
|
73
|
+
i0.ɵɵelementStart(7, "div", 62);
|
|
74
|
+
i0.ɵɵelement(8, "app-test-status-badge", 63);
|
|
75
|
+
i0.ɵɵtemplate(9, TestingExecutionComponent_For_106_Conditional_9_Template, 2, 2, "div", 64);
|
|
76
|
+
i0.ɵɵelementEnd();
|
|
77
|
+
i0.ɵɵelementStart(10, "div", 65);
|
|
78
|
+
i0.ɵɵelement(11, "app-score-indicator", 66);
|
|
79
|
+
i0.ɵɵelementEnd();
|
|
80
|
+
i0.ɵɵelementStart(12, "div", 67);
|
|
81
|
+
i0.ɵɵtext(13);
|
|
82
|
+
i0.ɵɵelementEnd();
|
|
83
|
+
i0.ɵɵelementStart(14, "div", 68);
|
|
84
|
+
i0.ɵɵelement(15, "app-cost-display", 69);
|
|
85
|
+
i0.ɵɵelementEnd();
|
|
86
|
+
i0.ɵɵelementStart(16, "div", 70);
|
|
87
|
+
i0.ɵɵtext(17);
|
|
88
|
+
i0.ɵɵpipe(18, "date");
|
|
89
|
+
i0.ɵɵelementEnd();
|
|
90
|
+
i0.ɵɵelementStart(19, "div", 71)(20, "button", 72);
|
|
91
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_106_Template_button_click_20_listener() { const execution_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.viewDetails(execution_r5)); });
|
|
92
|
+
i0.ɵɵelement(21, "i", 73);
|
|
93
|
+
i0.ɵɵelementEnd();
|
|
94
|
+
i0.ɵɵtemplate(22, TestingExecutionComponent_For_106_Conditional_22_Template, 2, 0, "button", 74)(23, TestingExecutionComponent_For_106_Conditional_23_Template, 2, 0, "button", 75);
|
|
95
|
+
i0.ɵɵelementEnd()();
|
|
96
|
+
} if (rf & 2) {
|
|
97
|
+
const execution_r5 = ctx.$implicit;
|
|
98
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
99
|
+
i0.ɵɵclassProp("running", execution_r5.status === "Running");
|
|
100
|
+
i0.ɵɵadvance(4);
|
|
101
|
+
i0.ɵɵtextInterpolate(execution_r5.testName);
|
|
102
|
+
i0.ɵɵadvance(2);
|
|
103
|
+
i0.ɵɵtextInterpolate(execution_r5.suiteName);
|
|
104
|
+
i0.ɵɵadvance(2);
|
|
105
|
+
i0.ɵɵproperty("status", execution_r5.status);
|
|
106
|
+
i0.ɵɵadvance();
|
|
107
|
+
i0.ɵɵconditional(execution_r5.status === "Running" ? 9 : -1);
|
|
108
|
+
i0.ɵɵadvance(2);
|
|
109
|
+
i0.ɵɵproperty("score", execution_r5.score)("showBar", false)("showIcon", false);
|
|
110
|
+
i0.ɵɵadvance(2);
|
|
111
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r1.formatDuration(execution_r5.duration), " ");
|
|
112
|
+
i0.ɵɵadvance(2);
|
|
113
|
+
i0.ɵɵproperty("cost", execution_r5.cost)("showIcon", false);
|
|
114
|
+
i0.ɵɵadvance(2);
|
|
115
|
+
i0.ɵɵtextInterpolate1(" ", i0.ɵɵpipeBind2(18, 14, execution_r5.startedAt, "short"), " ");
|
|
116
|
+
i0.ɵɵadvance(5);
|
|
117
|
+
i0.ɵɵconditional(execution_r5.status === "Running" ? 22 : 23);
|
|
118
|
+
} }
|
|
119
|
+
export class TestingExecutionComponent {
|
|
120
|
+
instrumentationService;
|
|
121
|
+
dialogService;
|
|
122
|
+
cdr;
|
|
123
|
+
initialState;
|
|
124
|
+
stateChange = new EventEmitter();
|
|
125
|
+
destroy$ = new Subject();
|
|
126
|
+
isRefreshing = false;
|
|
127
|
+
filters = {
|
|
128
|
+
status: 'all',
|
|
129
|
+
suite: 'all',
|
|
130
|
+
timeRange: 'today',
|
|
131
|
+
searchText: ''
|
|
132
|
+
};
|
|
133
|
+
executions$;
|
|
134
|
+
filteredExecutions$;
|
|
135
|
+
runningCount$;
|
|
136
|
+
completedTodayCount$;
|
|
137
|
+
failedTodayCount$;
|
|
138
|
+
avgDurationToday$;
|
|
139
|
+
hasRunningTests$;
|
|
140
|
+
constructor(instrumentationService, dialogService, cdr) {
|
|
141
|
+
this.instrumentationService = instrumentationService;
|
|
142
|
+
this.dialogService = dialogService;
|
|
143
|
+
this.cdr = cdr;
|
|
144
|
+
}
|
|
145
|
+
ngOnInit() {
|
|
146
|
+
this.setupObservables();
|
|
147
|
+
if (this.initialState) {
|
|
148
|
+
this.filters = { ...this.filters, ...this.initialState.filters };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
ngOnDestroy() {
|
|
152
|
+
this.destroy$.next();
|
|
153
|
+
this.destroy$.complete();
|
|
154
|
+
}
|
|
155
|
+
setupObservables() {
|
|
156
|
+
this.executions$ = this.instrumentationService.testRuns$.pipe(map(runs => runs.map(run => this.mapToExecutionItem(run))), takeUntil(this.destroy$));
|
|
157
|
+
this.filteredExecutions$ = combineLatest([
|
|
158
|
+
this.executions$
|
|
159
|
+
]).pipe(map(([executions]) => this.applyFilters(executions)), takeUntil(this.destroy$));
|
|
160
|
+
this.runningCount$ = this.executions$.pipe(map(execs => execs.filter(e => e.status === 'Running').length));
|
|
161
|
+
this.completedTodayCount$ = this.executions$.pipe(map(execs => {
|
|
162
|
+
const today = new Date();
|
|
163
|
+
today.setHours(0, 0, 0, 0);
|
|
164
|
+
return execs.filter(e => e.status === 'Passed' &&
|
|
165
|
+
e.startedAt >= today).length;
|
|
166
|
+
}));
|
|
167
|
+
this.failedTodayCount$ = this.executions$.pipe(map(execs => {
|
|
168
|
+
const today = new Date();
|
|
169
|
+
today.setHours(0, 0, 0, 0);
|
|
170
|
+
return execs.filter(e => e.status === 'Failed' &&
|
|
171
|
+
e.startedAt >= today).length;
|
|
172
|
+
}));
|
|
173
|
+
this.avgDurationToday$ = this.executions$.pipe(map(execs => {
|
|
174
|
+
const today = new Date();
|
|
175
|
+
today.setHours(0, 0, 0, 0);
|
|
176
|
+
const todayExecs = execs.filter(e => e.startedAt >= today && e.completedAt);
|
|
177
|
+
if (todayExecs.length === 0)
|
|
178
|
+
return 0;
|
|
179
|
+
const totalDuration = todayExecs.reduce((sum, e) => sum + e.duration, 0);
|
|
180
|
+
return totalDuration / todayExecs.length;
|
|
181
|
+
}));
|
|
182
|
+
this.hasRunningTests$ = this.runningCount$.pipe(map(count => count > 0));
|
|
183
|
+
}
|
|
184
|
+
mapToExecutionItem(run) {
|
|
185
|
+
const startedAt = run.runDateTime;
|
|
186
|
+
const completedAt = null;
|
|
187
|
+
const duration = run.duration;
|
|
188
|
+
const progress = run.status === 'Running' ? Math.random() * 100 : 100;
|
|
189
|
+
return {
|
|
190
|
+
id: run.id,
|
|
191
|
+
testName: run.testName,
|
|
192
|
+
suiteName: run.suiteName,
|
|
193
|
+
status: run.status,
|
|
194
|
+
score: run.score,
|
|
195
|
+
duration,
|
|
196
|
+
cost: run.cost,
|
|
197
|
+
startedAt,
|
|
198
|
+
completedAt,
|
|
199
|
+
progress
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
applyFilters(executions) {
|
|
203
|
+
let filtered = [...executions];
|
|
204
|
+
if (this.filters.status !== 'all') {
|
|
205
|
+
if (this.filters.status === 'running') {
|
|
206
|
+
filtered = filtered.filter(e => e.status === 'Running');
|
|
207
|
+
}
|
|
208
|
+
else if (this.filters.status === 'completed') {
|
|
209
|
+
filtered = filtered.filter(e => e.status === 'Passed' || e.status === 'Skipped');
|
|
210
|
+
}
|
|
211
|
+
else if (this.filters.status === 'failed') {
|
|
212
|
+
filtered = filtered.filter(e => e.status === 'Failed' || e.status === 'Error');
|
|
213
|
+
}
|
|
214
|
+
else if (this.filters.status === 'passed') {
|
|
215
|
+
filtered = filtered.filter(e => e.status === 'Passed');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (this.filters.timeRange !== 'all') {
|
|
219
|
+
const now = new Date();
|
|
220
|
+
let startDate = new Date();
|
|
221
|
+
if (this.filters.timeRange === 'today') {
|
|
222
|
+
startDate.setHours(0, 0, 0, 0);
|
|
223
|
+
}
|
|
224
|
+
else if (this.filters.timeRange === 'week') {
|
|
225
|
+
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
226
|
+
}
|
|
227
|
+
else if (this.filters.timeRange === 'month') {
|
|
228
|
+
startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
229
|
+
}
|
|
230
|
+
filtered = filtered.filter(e => e.startedAt >= startDate);
|
|
231
|
+
}
|
|
232
|
+
if (this.filters.searchText) {
|
|
233
|
+
const searchLower = this.filters.searchText.toLowerCase();
|
|
234
|
+
filtered = filtered.filter(e => e.testName.toLowerCase().includes(searchLower) ||
|
|
235
|
+
e.suiteName.toLowerCase().includes(searchLower));
|
|
236
|
+
}
|
|
237
|
+
filtered.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
238
|
+
return filtered;
|
|
239
|
+
}
|
|
240
|
+
onFilterChange() {
|
|
241
|
+
this.emitStateChange();
|
|
242
|
+
this.cdr.markForCheck();
|
|
243
|
+
}
|
|
244
|
+
clearSearch() {
|
|
245
|
+
this.filters.searchText = '';
|
|
246
|
+
this.onFilterChange();
|
|
247
|
+
}
|
|
248
|
+
refresh() {
|
|
249
|
+
this.isRefreshing = true;
|
|
250
|
+
this.instrumentationService.refresh();
|
|
251
|
+
setTimeout(() => {
|
|
252
|
+
this.isRefreshing = false;
|
|
253
|
+
this.cdr.markForCheck();
|
|
254
|
+
}, 1000);
|
|
255
|
+
}
|
|
256
|
+
startNewTest() {
|
|
257
|
+
const dialogRef = this.dialogService.open({
|
|
258
|
+
content: TestRunDialogComponent,
|
|
259
|
+
width: 1000,
|
|
260
|
+
height: 750,
|
|
261
|
+
title: 'Run Test',
|
|
262
|
+
actions: []
|
|
263
|
+
});
|
|
264
|
+
dialogRef.result.subscribe((result) => {
|
|
265
|
+
if (result && typeof result === 'object' && 'testExecuted' in result && result.testExecuted) {
|
|
266
|
+
this.refresh();
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
viewDetails(execution) {
|
|
271
|
+
SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(execution.id));
|
|
272
|
+
}
|
|
273
|
+
cancelExecution(execution) {
|
|
274
|
+
console.log('Cancel execution:', execution);
|
|
275
|
+
}
|
|
276
|
+
rerunTest(execution) {
|
|
277
|
+
console.log('Re-run test:', execution);
|
|
278
|
+
}
|
|
279
|
+
formatDuration(milliseconds) {
|
|
280
|
+
if (milliseconds < 1000)
|
|
281
|
+
return `${milliseconds}ms`;
|
|
282
|
+
const seconds = Math.floor(milliseconds / 1000);
|
|
283
|
+
const minutes = Math.floor(seconds / 60);
|
|
284
|
+
if (minutes > 0)
|
|
285
|
+
return `${minutes}m ${seconds % 60}s`;
|
|
286
|
+
return `${seconds}s`;
|
|
287
|
+
}
|
|
288
|
+
emitStateChange() {
|
|
289
|
+
this.stateChange.emit({
|
|
290
|
+
filters: this.filters
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
static ɵfac = function TestingExecutionComponent_Factory(t) { return new (t || TestingExecutionComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i2.DialogService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
294
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingExecutionComponent, selectors: [["app-testing-execution"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, decls: 108, vars: 28, consts: [[1, "testing-execution"], [1, "execution-header"], [1, "header-left"], [1, "fa-solid", "fa-play-circle"], ["class", "live-indicator", 4, "ngIf"], [1, "header-actions"], [1, "action-btn", "refresh", 3, "click", "disabled"], [1, "fa-solid", "fa-refresh"], [1, "action-btn", "primary", 3, "click"], [1, "fa-solid", "fa-play"], [1, "execution-filters"], [1, "filter-group"], [3, "ngModelChange", "change", "ngModel"], ["value", "all"], ["value", "running"], ["value", "completed"], ["value", "failed"], ["value", "passed"], ["value", "today"], ["value", "week"], ["value", "month"], [1, "filter-group", "search"], [1, "search-input-wrapper"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search tests...", 3, "ngModelChange", "input", "ngModel"], ["class", "clear-btn", 3, "click", 4, "ngIf"], [1, "execution-summary"], [1, "summary-card"], [1, "summary-icon", "running"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "summary-content"], [1, "summary-value"], [1, "summary-label"], [1, "summary-icon", "completed"], [1, "fa-solid", "fa-check-circle"], [1, "summary-icon", "failed"], [1, "fa-solid", "fa-exclamation-circle"], [1, "summary-icon", "duration"], [1, "fa-solid", "fa-clock"], [1, "execution-content"], [1, "execution-list"], [1, "list-header"], [1, "header-cell", "test-name"], [1, "header-cell", "status"], [1, "header-cell", "score"], [1, "header-cell", "duration"], [1, "header-cell", "cost"], [1, "header-cell", "timestamp"], [1, "header-cell", "actions"], [1, "no-data"], [1, "execution-row", 3, "running"], [1, "live-indicator"], [1, "pulse"], [1, "text"], [1, "clear-btn", 3, "click"], [1, "fa-solid", "fa-times"], [1, "fa-solid", "fa-inbox"], [1, "execution-row"], [1, "cell", "test-name"], [1, "test-info"], [1, "name"], [1, "suite"], [1, "cell", "status"], [3, "status"], [1, "progress-bar"], [1, "cell", "score"], [3, "score", "showBar", "showIcon"], [1, "cell", "duration"], [1, "cell", "cost"], [3, "cost", "showIcon"], [1, "cell", "timestamp"], [1, "cell", "actions"], ["title", "View Details", 1, "icon-btn", 3, "click"], [1, "fa-solid", "fa-eye"], ["title", "Cancel", 1, "icon-btn", "danger"], ["title", "Re-run", 1, "icon-btn"], [1, "progress-fill"], ["title", "Cancel", 1, "icon-btn", "danger", 3, "click"], [1, "fa-solid", "fa-stop"], ["title", "Re-run", 1, "icon-btn", 3, "click"], [1, "fa-solid", "fa-redo"]], template: function TestingExecutionComponent_Template(rf, ctx) { if (rf & 1) {
|
|
295
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "h2");
|
|
296
|
+
i0.ɵɵelement(4, "i", 3);
|
|
297
|
+
i0.ɵɵtext(5, " Test Execution Monitor ");
|
|
298
|
+
i0.ɵɵelementEnd();
|
|
299
|
+
i0.ɵɵtemplate(6, TestingExecutionComponent_div_6_Template, 4, 0, "div", 4);
|
|
300
|
+
i0.ɵɵpipe(7, "async");
|
|
301
|
+
i0.ɵɵelementEnd();
|
|
302
|
+
i0.ɵɵelementStart(8, "div", 5)(9, "button", 6);
|
|
303
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_9_listener() { return ctx.refresh(); });
|
|
304
|
+
i0.ɵɵelement(10, "i", 7);
|
|
305
|
+
i0.ɵɵtext(11, " Refresh ");
|
|
306
|
+
i0.ɵɵelementEnd();
|
|
307
|
+
i0.ɵɵelementStart(12, "button", 8);
|
|
308
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_12_listener() { return ctx.startNewTest(); });
|
|
309
|
+
i0.ɵɵelement(13, "i", 9);
|
|
310
|
+
i0.ɵɵtext(14, " Run Test ");
|
|
311
|
+
i0.ɵɵelementEnd()()();
|
|
312
|
+
i0.ɵɵelementStart(15, "div", 10)(16, "div", 11)(17, "label");
|
|
313
|
+
i0.ɵɵtext(18, "Status");
|
|
314
|
+
i0.ɵɵelementEnd();
|
|
315
|
+
i0.ɵɵelementStart(19, "select", 12);
|
|
316
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_select_ngModelChange_19_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.status, $event) || (ctx.filters.status = $event); return $event; });
|
|
317
|
+
i0.ɵɵlistener("change", function TestingExecutionComponent_Template_select_change_19_listener() { return ctx.onFilterChange(); });
|
|
318
|
+
i0.ɵɵelementStart(20, "option", 13);
|
|
319
|
+
i0.ɵɵtext(21, "All Statuses");
|
|
320
|
+
i0.ɵɵelementEnd();
|
|
321
|
+
i0.ɵɵelementStart(22, "option", 14);
|
|
322
|
+
i0.ɵɵtext(23, "Running");
|
|
323
|
+
i0.ɵɵelementEnd();
|
|
324
|
+
i0.ɵɵelementStart(24, "option", 15);
|
|
325
|
+
i0.ɵɵtext(25, "Completed");
|
|
326
|
+
i0.ɵɵelementEnd();
|
|
327
|
+
i0.ɵɵelementStart(26, "option", 16);
|
|
328
|
+
i0.ɵɵtext(27, "Failed");
|
|
329
|
+
i0.ɵɵelementEnd();
|
|
330
|
+
i0.ɵɵelementStart(28, "option", 17);
|
|
331
|
+
i0.ɵɵtext(29, "Passed");
|
|
332
|
+
i0.ɵɵelementEnd()()();
|
|
333
|
+
i0.ɵɵelementStart(30, "div", 11)(31, "label");
|
|
334
|
+
i0.ɵɵtext(32, "Time Range");
|
|
335
|
+
i0.ɵɵelementEnd();
|
|
336
|
+
i0.ɵɵelementStart(33, "select", 12);
|
|
337
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_select_ngModelChange_33_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.timeRange, $event) || (ctx.filters.timeRange = $event); return $event; });
|
|
338
|
+
i0.ɵɵlistener("change", function TestingExecutionComponent_Template_select_change_33_listener() { return ctx.onFilterChange(); });
|
|
339
|
+
i0.ɵɵelementStart(34, "option", 18);
|
|
340
|
+
i0.ɵɵtext(35, "Today");
|
|
341
|
+
i0.ɵɵelementEnd();
|
|
342
|
+
i0.ɵɵelementStart(36, "option", 19);
|
|
343
|
+
i0.ɵɵtext(37, "This Week");
|
|
344
|
+
i0.ɵɵelementEnd();
|
|
345
|
+
i0.ɵɵelementStart(38, "option", 20);
|
|
346
|
+
i0.ɵɵtext(39, "This Month");
|
|
347
|
+
i0.ɵɵelementEnd();
|
|
348
|
+
i0.ɵɵelementStart(40, "option", 13);
|
|
349
|
+
i0.ɵɵtext(41, "All Time");
|
|
350
|
+
i0.ɵɵelementEnd()()();
|
|
351
|
+
i0.ɵɵelementStart(42, "div", 21)(43, "label");
|
|
352
|
+
i0.ɵɵtext(44, "Search");
|
|
353
|
+
i0.ɵɵelementEnd();
|
|
354
|
+
i0.ɵɵelementStart(45, "div", 22);
|
|
355
|
+
i0.ɵɵelement(46, "i", 23);
|
|
356
|
+
i0.ɵɵelementStart(47, "input", 24);
|
|
357
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_input_ngModelChange_47_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.searchText, $event) || (ctx.filters.searchText = $event); return $event; });
|
|
358
|
+
i0.ɵɵlistener("input", function TestingExecutionComponent_Template_input_input_47_listener() { return ctx.onFilterChange(); });
|
|
359
|
+
i0.ɵɵelementEnd();
|
|
360
|
+
i0.ɵɵtemplate(48, TestingExecutionComponent_button_48_Template, 2, 0, "button", 25);
|
|
361
|
+
i0.ɵɵelementEnd()()();
|
|
362
|
+
i0.ɵɵelementStart(49, "div", 26)(50, "div", 27)(51, "div", 28);
|
|
363
|
+
i0.ɵɵelement(52, "i", 29);
|
|
364
|
+
i0.ɵɵelementEnd();
|
|
365
|
+
i0.ɵɵelementStart(53, "div", 30)(54, "div", 31);
|
|
366
|
+
i0.ɵɵtext(55);
|
|
367
|
+
i0.ɵɵpipe(56, "async");
|
|
368
|
+
i0.ɵɵelementEnd();
|
|
369
|
+
i0.ɵɵelementStart(57, "div", 32);
|
|
370
|
+
i0.ɵɵtext(58, "Running Now");
|
|
371
|
+
i0.ɵɵelementEnd()()();
|
|
372
|
+
i0.ɵɵelementStart(59, "div", 27)(60, "div", 33);
|
|
373
|
+
i0.ɵɵelement(61, "i", 34);
|
|
374
|
+
i0.ɵɵelementEnd();
|
|
375
|
+
i0.ɵɵelementStart(62, "div", 30)(63, "div", 31);
|
|
376
|
+
i0.ɵɵtext(64);
|
|
377
|
+
i0.ɵɵpipe(65, "async");
|
|
378
|
+
i0.ɵɵelementEnd();
|
|
379
|
+
i0.ɵɵelementStart(66, "div", 32);
|
|
380
|
+
i0.ɵɵtext(67, "Completed Today");
|
|
381
|
+
i0.ɵɵelementEnd()()();
|
|
382
|
+
i0.ɵɵelementStart(68, "div", 27)(69, "div", 35);
|
|
383
|
+
i0.ɵɵelement(70, "i", 36);
|
|
384
|
+
i0.ɵɵelementEnd();
|
|
385
|
+
i0.ɵɵelementStart(71, "div", 30)(72, "div", 31);
|
|
386
|
+
i0.ɵɵtext(73);
|
|
387
|
+
i0.ɵɵpipe(74, "async");
|
|
388
|
+
i0.ɵɵelementEnd();
|
|
389
|
+
i0.ɵɵelementStart(75, "div", 32);
|
|
390
|
+
i0.ɵɵtext(76, "Failed Today");
|
|
391
|
+
i0.ɵɵelementEnd()()();
|
|
392
|
+
i0.ɵɵelementStart(77, "div", 27)(78, "div", 37);
|
|
393
|
+
i0.ɵɵelement(79, "i", 38);
|
|
394
|
+
i0.ɵɵelementEnd();
|
|
395
|
+
i0.ɵɵelementStart(80, "div", 30)(81, "div", 31);
|
|
396
|
+
i0.ɵɵtext(82);
|
|
397
|
+
i0.ɵɵpipe(83, "async");
|
|
398
|
+
i0.ɵɵelementEnd();
|
|
399
|
+
i0.ɵɵelementStart(84, "div", 32);
|
|
400
|
+
i0.ɵɵtext(85, "Avg Duration Today");
|
|
401
|
+
i0.ɵɵelementEnd()()()();
|
|
402
|
+
i0.ɵɵelementStart(86, "div", 39)(87, "div", 40)(88, "div", 41)(89, "div", 42);
|
|
403
|
+
i0.ɵɵtext(90, "Test Name");
|
|
404
|
+
i0.ɵɵelementEnd();
|
|
405
|
+
i0.ɵɵelementStart(91, "div", 43);
|
|
406
|
+
i0.ɵɵtext(92, "Status");
|
|
407
|
+
i0.ɵɵelementEnd();
|
|
408
|
+
i0.ɵɵelementStart(93, "div", 44);
|
|
409
|
+
i0.ɵɵtext(94, "Score");
|
|
410
|
+
i0.ɵɵelementEnd();
|
|
411
|
+
i0.ɵɵelementStart(95, "div", 45);
|
|
412
|
+
i0.ɵɵtext(96, "Duration");
|
|
413
|
+
i0.ɵɵelementEnd();
|
|
414
|
+
i0.ɵɵelementStart(97, "div", 46);
|
|
415
|
+
i0.ɵɵtext(98, "Cost");
|
|
416
|
+
i0.ɵɵelementEnd();
|
|
417
|
+
i0.ɵɵelementStart(99, "div", 47);
|
|
418
|
+
i0.ɵɵtext(100, "Started At");
|
|
419
|
+
i0.ɵɵelementEnd();
|
|
420
|
+
i0.ɵɵelementStart(101, "div", 48);
|
|
421
|
+
i0.ɵɵtext(102, "Actions");
|
|
422
|
+
i0.ɵɵelementEnd()();
|
|
423
|
+
i0.ɵɵtemplate(103, TestingExecutionComponent_Conditional_103_Template, 7, 0, "div", 49);
|
|
424
|
+
i0.ɵɵpipe(104, "async");
|
|
425
|
+
i0.ɵɵrepeaterCreate(105, TestingExecutionComponent_For_106_Template, 24, 17, "div", 50, _forTrack0);
|
|
426
|
+
i0.ɵɵpipe(107, "async");
|
|
427
|
+
i0.ɵɵelementEnd()()();
|
|
428
|
+
} if (rf & 2) {
|
|
429
|
+
let tmp_11_0;
|
|
430
|
+
let tmp_12_0;
|
|
431
|
+
i0.ɵɵadvance(6);
|
|
432
|
+
i0.ɵɵproperty("ngIf", i0.ɵɵpipeBind1(7, 13, ctx.hasRunningTests$));
|
|
433
|
+
i0.ɵɵadvance(3);
|
|
434
|
+
i0.ɵɵproperty("disabled", ctx.isRefreshing);
|
|
435
|
+
i0.ɵɵadvance();
|
|
436
|
+
i0.ɵɵclassProp("spinning", ctx.isRefreshing);
|
|
437
|
+
i0.ɵɵadvance(9);
|
|
438
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.filters.status);
|
|
439
|
+
i0.ɵɵadvance(14);
|
|
440
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.filters.timeRange);
|
|
441
|
+
i0.ɵɵadvance(14);
|
|
442
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx.filters.searchText);
|
|
443
|
+
i0.ɵɵadvance();
|
|
444
|
+
i0.ɵɵproperty("ngIf", ctx.filters.searchText);
|
|
445
|
+
i0.ɵɵadvance(7);
|
|
446
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(56, 15, ctx.runningCount$) || 0);
|
|
447
|
+
i0.ɵɵadvance(9);
|
|
448
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(65, 17, ctx.completedTodayCount$) || 0);
|
|
449
|
+
i0.ɵɵadvance(9);
|
|
450
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(74, 19, ctx.failedTodayCount$) || 0);
|
|
451
|
+
i0.ɵɵadvance(9);
|
|
452
|
+
i0.ɵɵtextInterpolate(ctx.formatDuration(i0.ɵɵpipeBind1(83, 21, ctx.avgDurationToday$) || 0));
|
|
453
|
+
i0.ɵɵadvance(21);
|
|
454
|
+
i0.ɵɵconditional(((tmp_11_0 = i0.ɵɵpipeBind1(104, 23, ctx.filteredExecutions$)) == null ? null : tmp_11_0.length) === 0 ? 103 : -1);
|
|
455
|
+
i0.ɵɵadvance(2);
|
|
456
|
+
i0.ɵɵrepeater((tmp_12_0 = i0.ɵɵpipeBind1(107, 25, ctx.filteredExecutions$)) !== null && tmp_12_0 !== undefined ? tmp_12_0 : i0.ɵɵpureFunction0(27, _c0));
|
|
457
|
+
} }, dependencies: [i3.NgIf, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.SelectControlValueAccessor, i4.NgControlStatus, i4.NgModel, i5.TestStatusBadgeComponent, i5.ScoreIndicatorComponent, i5.CostDisplayComponent, i3.AsyncPipe, i3.DatePipe], styles: [".testing-execution[_ngcontent-%COMP%] {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n .execution-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-left[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #2196f3;\n }\n\n .live-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: #e3f2fd;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n color: #2196f3;\n }\n\n .pulse[_ngcontent-%COMP%] {\n width: 8px;\n height: 8px;\n background: #2196f3;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_pulse 2s infinite;\n }\n\n @keyframes _ngcontent-%COMP%_pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(1.2); }\n }\n\n .header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n }\n\n .action-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .action-btn.refresh[_ngcontent-%COMP%] {\n background: white;\n border: 1px solid #ddd;\n color: #666;\n }\n\n .action-btn.refresh[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #f5f5f5;\n }\n\n .action-btn.primary[_ngcontent-%COMP%] {\n background: #2196f3;\n color: white;\n }\n\n .action-btn.primary[_ngcontent-%COMP%]:hover {\n background: #1976d2;\n }\n\n .action-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .action-btn[_ngcontent-%COMP%] i.spinning[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_spin 1s linear infinite;\n }\n\n @keyframes _ngcontent-%COMP%_spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n .execution-filters[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n background: white;\n padding: 16px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .filter-group[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 150px;\n }\n\n .filter-group.search[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .filter-group[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: #666;\n text-transform: uppercase;\n }\n\n .filter-group[_ngcontent-%COMP%] select[_ngcontent-%COMP%] {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n color: #333;\n background: white;\n }\n\n .search-input-wrapper[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-input-wrapper[_ngcontent-%COMP%] i.fa-search[_ngcontent-%COMP%] {\n position: absolute;\n left: 12px;\n color: #999;\n font-size: 12px;\n }\n\n .search-input-wrapper[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n flex: 1;\n padding: 8px 40px 8px 36px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n }\n\n .clear-btn[_ngcontent-%COMP%] {\n position: absolute;\n right: 8px;\n background: none;\n border: none;\n color: #999;\n cursor: pointer;\n padding: 4px;\n }\n\n .clear-btn[_ngcontent-%COMP%]:hover {\n color: #333;\n }\n\n .execution-summary[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .summary-card[_ngcontent-%COMP%] {\n background: white;\n padding: 16px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .summary-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .summary-icon.running[_ngcontent-%COMP%] {\n background: #e3f2fd;\n color: #2196f3;\n }\n\n .summary-icon.completed[_ngcontent-%COMP%] {\n background: #e8f5e9;\n color: #4caf50;\n }\n\n .summary-icon.failed[_ngcontent-%COMP%] {\n background: #ffebee;\n color: #f44336;\n }\n\n .summary-icon.duration[_ngcontent-%COMP%] {\n background: #fff3e0;\n color: #ff9800;\n }\n\n .summary-content[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .summary-value[_ngcontent-%COMP%] {\n font-size: 24px;\n font-weight: 700;\n color: #333;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .summary-label[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n font-weight: 600;\n }\n\n .execution-content[_ngcontent-%COMP%] {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .execution-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n }\n\n .list-header[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 140px 100px 100px 100px 150px 100px;\n gap: 16px;\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 2px solid #e0e0e0;\n font-size: 11px;\n font-weight: 600;\n color: #666;\n text-transform: uppercase;\n }\n\n .execution-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 140px 100px 100px 100px 150px 100px;\n gap: 16px;\n padding: 16px;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.2s ease;\n }\n\n .execution-row[_ngcontent-%COMP%]:hover {\n background: #f8f9fa;\n }\n\n .execution-row.running[_ngcontent-%COMP%] {\n background: #e3f2fd;\n }\n\n .execution-row.running[_ngcontent-%COMP%]:hover {\n background: #bbdefb;\n }\n\n .cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: #333;\n }\n\n .cell.test-name[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n\n .test-info[_ngcontent-%COMP%] .name[_ngcontent-%COMP%] {\n font-weight: 500;\n color: #333;\n }\n\n .test-info[_ngcontent-%COMP%] .suite[_ngcontent-%COMP%] {\n font-size: 11px;\n color: #666;\n }\n\n .cell.status[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n }\n\n .progress-bar[_ngcontent-%COMP%] {\n width: 100%;\n height: 4px;\n background: #e0e0e0;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: #2196f3;\n transition: width 0.3s ease;\n }\n\n .cell.actions[_ngcontent-%COMP%] {\n gap: 8px;\n }\n\n .icon-btn[_ngcontent-%COMP%] {\n background: none;\n border: none;\n color: #666;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s ease;\n font-size: 14px;\n }\n\n .icon-btn[_ngcontent-%COMP%]:hover {\n background: #f0f0f0;\n color: #2196f3;\n }\n\n .icon-btn.danger[_ngcontent-%COMP%]:hover {\n background: #ffebee;\n color: #f44336;\n }\n\n .no-data[_ngcontent-%COMP%] {\n padding: 60px 20px;\n text-align: center;\n color: #999;\n }\n\n .no-data[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .no-data[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 14px;\n margin-bottom: 20px;\n }\n\n @media (max-width: 1200px) {\n .execution-filters[_ngcontent-%COMP%] {\n flex-wrap: wrap;\n }\n\n .filter-group[_ngcontent-%COMP%] {\n min-width: 120px;\n }\n }"], changeDetection: 0 });
|
|
458
|
+
}
|
|
459
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingExecutionComponent, [{
|
|
460
|
+
type: Component,
|
|
461
|
+
args: [{ selector: 'app-testing-execution', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
462
|
+
<div class="testing-execution">
|
|
463
|
+
<div class="execution-header">
|
|
464
|
+
<div class="header-left">
|
|
465
|
+
<h2>
|
|
466
|
+
<i class="fa-solid fa-play-circle"></i>
|
|
467
|
+
Test Execution Monitor
|
|
468
|
+
</h2>
|
|
469
|
+
<div class="live-indicator" *ngIf="hasRunningTests$ | async">
|
|
470
|
+
<span class="pulse"></span>
|
|
471
|
+
<span class="text">Live</span>
|
|
472
|
+
</div>
|
|
473
|
+
</div>
|
|
474
|
+
<div class="header-actions">
|
|
475
|
+
<button class="action-btn refresh" (click)="refresh()" [disabled]="isRefreshing">
|
|
476
|
+
<i class="fa-solid fa-refresh" [class.spinning]="isRefreshing"></i>
|
|
477
|
+
Refresh
|
|
478
|
+
</button>
|
|
479
|
+
<button class="action-btn primary" (click)="startNewTest()">
|
|
480
|
+
<i class="fa-solid fa-play"></i>
|
|
481
|
+
Run Test
|
|
482
|
+
</button>
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
485
|
+
|
|
486
|
+
<div class="execution-filters">
|
|
487
|
+
<div class="filter-group">
|
|
488
|
+
<label>Status</label>
|
|
489
|
+
<select [(ngModel)]="filters.status" (change)="onFilterChange()">
|
|
490
|
+
<option value="all">All Statuses</option>
|
|
491
|
+
<option value="running">Running</option>
|
|
492
|
+
<option value="completed">Completed</option>
|
|
493
|
+
<option value="failed">Failed</option>
|
|
494
|
+
<option value="passed">Passed</option>
|
|
495
|
+
</select>
|
|
496
|
+
</div>
|
|
497
|
+
<div class="filter-group">
|
|
498
|
+
<label>Time Range</label>
|
|
499
|
+
<select [(ngModel)]="filters.timeRange" (change)="onFilterChange()">
|
|
500
|
+
<option value="today">Today</option>
|
|
501
|
+
<option value="week">This Week</option>
|
|
502
|
+
<option value="month">This Month</option>
|
|
503
|
+
<option value="all">All Time</option>
|
|
504
|
+
</select>
|
|
505
|
+
</div>
|
|
506
|
+
<div class="filter-group search">
|
|
507
|
+
<label>Search</label>
|
|
508
|
+
<div class="search-input-wrapper">
|
|
509
|
+
<i class="fa-solid fa-search"></i>
|
|
510
|
+
<input
|
|
511
|
+
type="text"
|
|
512
|
+
[(ngModel)]="filters.searchText"
|
|
513
|
+
(input)="onFilterChange()"
|
|
514
|
+
placeholder="Search tests..."
|
|
515
|
+
/>
|
|
516
|
+
<button
|
|
517
|
+
class="clear-btn"
|
|
518
|
+
*ngIf="filters.searchText"
|
|
519
|
+
(click)="clearSearch()"
|
|
520
|
+
>
|
|
521
|
+
<i class="fa-solid fa-times"></i>
|
|
522
|
+
</button>
|
|
523
|
+
</div>
|
|
524
|
+
</div>
|
|
525
|
+
</div>
|
|
526
|
+
|
|
527
|
+
<div class="execution-summary">
|
|
528
|
+
<div class="summary-card">
|
|
529
|
+
<div class="summary-icon running">
|
|
530
|
+
<i class="fa-solid fa-spinner fa-spin"></i>
|
|
531
|
+
</div>
|
|
532
|
+
<div class="summary-content">
|
|
533
|
+
<div class="summary-value">{{ (runningCount$ | async) || 0 }}</div>
|
|
534
|
+
<div class="summary-label">Running Now</div>
|
|
535
|
+
</div>
|
|
536
|
+
</div>
|
|
537
|
+
<div class="summary-card">
|
|
538
|
+
<div class="summary-icon completed">
|
|
539
|
+
<i class="fa-solid fa-check-circle"></i>
|
|
540
|
+
</div>
|
|
541
|
+
<div class="summary-content">
|
|
542
|
+
<div class="summary-value">{{ (completedTodayCount$ | async) || 0 }}</div>
|
|
543
|
+
<div class="summary-label">Completed Today</div>
|
|
544
|
+
</div>
|
|
545
|
+
</div>
|
|
546
|
+
<div class="summary-card">
|
|
547
|
+
<div class="summary-icon failed">
|
|
548
|
+
<i class="fa-solid fa-exclamation-circle"></i>
|
|
549
|
+
</div>
|
|
550
|
+
<div class="summary-content">
|
|
551
|
+
<div class="summary-value">{{ (failedTodayCount$ | async) || 0 }}</div>
|
|
552
|
+
<div class="summary-label">Failed Today</div>
|
|
553
|
+
</div>
|
|
554
|
+
</div>
|
|
555
|
+
<div class="summary-card">
|
|
556
|
+
<div class="summary-icon duration">
|
|
557
|
+
<i class="fa-solid fa-clock"></i>
|
|
558
|
+
</div>
|
|
559
|
+
<div class="summary-content">
|
|
560
|
+
<div class="summary-value">{{ formatDuration((avgDurationToday$ | async) || 0) }}</div>
|
|
561
|
+
<div class="summary-label">Avg Duration Today</div>
|
|
562
|
+
</div>
|
|
563
|
+
</div>
|
|
564
|
+
</div>
|
|
565
|
+
|
|
566
|
+
<div class="execution-content">
|
|
567
|
+
<div class="execution-list">
|
|
568
|
+
<div class="list-header">
|
|
569
|
+
<div class="header-cell test-name">Test Name</div>
|
|
570
|
+
<div class="header-cell status">Status</div>
|
|
571
|
+
<div class="header-cell score">Score</div>
|
|
572
|
+
<div class="header-cell duration">Duration</div>
|
|
573
|
+
<div class="header-cell cost">Cost</div>
|
|
574
|
+
<div class="header-cell timestamp">Started At</div>
|
|
575
|
+
<div class="header-cell actions">Actions</div>
|
|
576
|
+
</div>
|
|
577
|
+
|
|
578
|
+
@if ((filteredExecutions$ | async)?.length === 0) {
|
|
579
|
+
<div class="no-data">
|
|
580
|
+
<i class="fa-solid fa-inbox"></i>
|
|
581
|
+
<p>No test executions found</p>
|
|
582
|
+
<button class="action-btn primary" (click)="startNewTest()">
|
|
583
|
+
<i class="fa-solid fa-play"></i>
|
|
584
|
+
Run Your First Test
|
|
585
|
+
</button>
|
|
586
|
+
</div>
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
@for (execution of (filteredExecutions$ | async) ?? []; track execution.id) {
|
|
590
|
+
<div class="execution-row" [class.running]="execution.status === 'Running'">
|
|
591
|
+
<div class="cell test-name">
|
|
592
|
+
<div class="test-info">
|
|
593
|
+
<div class="name">{{ execution.testName }}</div>
|
|
594
|
+
<div class="suite">{{ execution.suiteName }}</div>
|
|
595
|
+
</div>
|
|
596
|
+
</div>
|
|
597
|
+
<div class="cell status">
|
|
598
|
+
<app-test-status-badge [status]="execution.status"></app-test-status-badge>
|
|
599
|
+
@if (execution.status === 'Running') {
|
|
600
|
+
<div class="progress-bar">
|
|
601
|
+
<div class="progress-fill" [style.width.%]="execution.progress"></div>
|
|
602
|
+
</div>
|
|
603
|
+
}
|
|
604
|
+
</div>
|
|
605
|
+
<div class="cell score">
|
|
606
|
+
<app-score-indicator
|
|
607
|
+
[score]="execution.score"
|
|
608
|
+
[showBar]="false"
|
|
609
|
+
[showIcon]="false"
|
|
610
|
+
></app-score-indicator>
|
|
611
|
+
</div>
|
|
612
|
+
<div class="cell duration">
|
|
613
|
+
{{ formatDuration(execution.duration) }}
|
|
614
|
+
</div>
|
|
615
|
+
<div class="cell cost">
|
|
616
|
+
<app-cost-display [cost]="execution.cost" [showIcon]="false"></app-cost-display>
|
|
617
|
+
</div>
|
|
618
|
+
<div class="cell timestamp">
|
|
619
|
+
{{ execution.startedAt | date:'short' }}
|
|
620
|
+
</div>
|
|
621
|
+
<div class="cell actions">
|
|
622
|
+
<button class="icon-btn" (click)="viewDetails(execution)" title="View Details">
|
|
623
|
+
<i class="fa-solid fa-eye"></i>
|
|
624
|
+
</button>
|
|
625
|
+
@if (execution.status === 'Running') {
|
|
626
|
+
<button class="icon-btn danger" (click)="cancelExecution(execution)" title="Cancel">
|
|
627
|
+
<i class="fa-solid fa-stop"></i>
|
|
628
|
+
</button>
|
|
629
|
+
} @else {
|
|
630
|
+
<button class="icon-btn" (click)="rerunTest(execution)" title="Re-run">
|
|
631
|
+
<i class="fa-solid fa-redo"></i>
|
|
632
|
+
</button>
|
|
633
|
+
}
|
|
634
|
+
</div>
|
|
635
|
+
</div>
|
|
636
|
+
}
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
</div>
|
|
640
|
+
`, styles: ["\n .testing-execution {\n padding: 20px;\n height: 100%;\n overflow-y: auto;\n background: #f8f9fa;\n }\n\n .execution-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n background: white;\n padding: 20px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-left h2 {\n margin: 0;\n font-size: 20px;\n font-weight: 600;\n color: #333;\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .header-left h2 i {\n color: #2196f3;\n }\n\n .live-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: #e3f2fd;\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n color: #2196f3;\n }\n\n .pulse {\n width: 8px;\n height: 8px;\n background: #2196f3;\n border-radius: 50%;\n animation: pulse 2s infinite;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(1.2); }\n }\n\n .header-actions {\n display: flex;\n gap: 12px;\n }\n\n .action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .action-btn.refresh {\n background: white;\n border: 1px solid #ddd;\n color: #666;\n }\n\n .action-btn.refresh:hover:not(:disabled) {\n background: #f5f5f5;\n }\n\n .action-btn.primary {\n background: #2196f3;\n color: white;\n }\n\n .action-btn.primary:hover {\n background: #1976d2;\n }\n\n .action-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n }\n\n .action-btn i.spinning {\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n from { transform: rotate(0deg); }\n to { transform: rotate(360deg); }\n }\n\n .execution-filters {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n background: white;\n padding: 16px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n }\n\n .filter-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 150px;\n }\n\n .filter-group.search {\n flex: 1;\n }\n\n .filter-group label {\n font-size: 11px;\n font-weight: 600;\n color: #666;\n text-transform: uppercase;\n }\n\n .filter-group select {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n color: #333;\n background: white;\n }\n\n .search-input-wrapper {\n position: relative;\n display: flex;\n align-items: center;\n }\n\n .search-input-wrapper i.fa-search {\n position: absolute;\n left: 12px;\n color: #999;\n font-size: 12px;\n }\n\n .search-input-wrapper input {\n flex: 1;\n padding: 8px 40px 8px 36px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 13px;\n }\n\n .clear-btn {\n position: absolute;\n right: 8px;\n background: none;\n border: none;\n color: #999;\n cursor: pointer;\n padding: 4px;\n }\n\n .clear-btn:hover {\n color: #333;\n }\n\n .execution-summary {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .summary-card {\n background: white;\n padding: 16px;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .summary-icon {\n width: 48px;\n height: 48px;\n border-radius: 8px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .summary-icon.running {\n background: #e3f2fd;\n color: #2196f3;\n }\n\n .summary-icon.completed {\n background: #e8f5e9;\n color: #4caf50;\n }\n\n .summary-icon.failed {\n background: #ffebee;\n color: #f44336;\n }\n\n .summary-icon.duration {\n background: #fff3e0;\n color: #ff9800;\n }\n\n .summary-content {\n flex: 1;\n }\n\n .summary-value {\n font-size: 24px;\n font-weight: 700;\n color: #333;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .summary-label {\n font-size: 11px;\n color: #666;\n text-transform: uppercase;\n font-weight: 600;\n }\n\n .execution-content {\n background: white;\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n overflow: hidden;\n }\n\n .execution-list {\n display: flex;\n flex-direction: column;\n }\n\n .list-header {\n display: grid;\n grid-template-columns: 2fr 140px 100px 100px 100px 150px 100px;\n gap: 16px;\n padding: 16px;\n background: #f8f9fa;\n border-bottom: 2px solid #e0e0e0;\n font-size: 11px;\n font-weight: 600;\n color: #666;\n text-transform: uppercase;\n }\n\n .execution-row {\n display: grid;\n grid-template-columns: 2fr 140px 100px 100px 100px 150px 100px;\n gap: 16px;\n padding: 16px;\n border-bottom: 1px solid #f0f0f0;\n transition: background 0.2s ease;\n }\n\n .execution-row:hover {\n background: #f8f9fa;\n }\n\n .execution-row.running {\n background: #e3f2fd;\n }\n\n .execution-row.running:hover {\n background: #bbdefb;\n }\n\n .cell {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: #333;\n }\n\n .cell.test-name {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n\n .test-info .name {\n font-weight: 500;\n color: #333;\n }\n\n .test-info .suite {\n font-size: 11px;\n color: #666;\n }\n\n .cell.status {\n flex-direction: column;\n gap: 6px;\n align-items: flex-start;\n }\n\n .progress-bar {\n width: 100%;\n height: 4px;\n background: #e0e0e0;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill {\n height: 100%;\n background: #2196f3;\n transition: width 0.3s ease;\n }\n\n .cell.actions {\n gap: 8px;\n }\n\n .icon-btn {\n background: none;\n border: none;\n color: #666;\n cursor: pointer;\n padding: 6px;\n border-radius: 4px;\n transition: all 0.2s ease;\n font-size: 14px;\n }\n\n .icon-btn:hover {\n background: #f0f0f0;\n color: #2196f3;\n }\n\n .icon-btn.danger:hover {\n background: #ffebee;\n color: #f44336;\n }\n\n .no-data {\n padding: 60px 20px;\n text-align: center;\n color: #999;\n }\n\n .no-data i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.5;\n }\n\n .no-data p {\n font-size: 14px;\n margin-bottom: 20px;\n }\n\n @media (max-width: 1200px) {\n .execution-filters {\n flex-wrap: wrap;\n }\n\n .filter-group {\n min-width: 120px;\n }\n }\n "] }]
|
|
641
|
+
}], () => [{ type: i1.TestingInstrumentationService }, { type: i2.DialogService }, { type: i0.ChangeDetectorRef }], { initialState: [{
|
|
642
|
+
type: Input
|
|
643
|
+
}], stateChange: [{
|
|
644
|
+
type: Output
|
|
645
|
+
}] }); })();
|
|
646
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingExecutionComponent, { className: "TestingExecutionComponent", filePath: "src/Testing/components/testing-execution.component.ts", lineNumber: 608 }); })();
|
|
647
|
+
//# sourceMappingURL=testing-execution.component.js.map
|