@memberjunction/ng-dashboards 2.129.0 → 2.130.1
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-execution.component.d.ts +16 -5
- package/dist/Testing/components/testing-execution.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-execution.component.js +452 -273
- package/dist/Testing/components/testing-execution.component.js.map +1 -1
- package/dist/Testing/components/testing-feedback.component.d.ts +70 -14
- package/dist/Testing/components/testing-feedback.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-feedback.component.js +1177 -479
- package/dist/Testing/components/testing-feedback.component.js.map +1 -1
- package/dist/Testing/components/testing-overview.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-overview.component.js +182 -162
- package/dist/Testing/components/testing-overview.component.js.map +1 -1
- package/dist/Testing/components/testing-version-comparison.component.d.ts +4 -0
- package/dist/Testing/components/testing-version-comparison.component.d.ts.map +1 -1
- package/dist/Testing/components/testing-version-comparison.component.js +19 -5
- package/dist/Testing/components/testing-version-comparison.component.js.map +1 -1
- package/dist/Testing/services/testing-instrumentation.service.d.ts +47 -1
- package/dist/Testing/services/testing-instrumentation.service.d.ts.map +1 -1
- package/dist/Testing/services/testing-instrumentation.service.js +243 -60
- package/dist/Testing/services/testing-instrumentation.service.js.map +1 -1
- package/dist/Testing/testing-dashboard.component.js +36 -34
- package/dist/Testing/testing-dashboard.component.js.map +1 -1
- package/package.json +27 -27
|
@@ -1,97 +1,104 @@
|
|
|
1
1
|
import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
-
import { Subject, combineLatest } from 'rxjs';
|
|
2
|
+
import { Subject, combineLatest, BehaviorSubject } from 'rxjs';
|
|
3
3
|
import { takeUntil, map } from 'rxjs/operators';
|
|
4
|
-
import { CompositeKey } from '@memberjunction/core';
|
|
4
|
+
import { CompositeKey, Metadata } from '@memberjunction/core';
|
|
5
5
|
import { SharedService } from '@memberjunction/ng-shared';
|
|
6
|
+
import { GraphQLTestingClient } from '@memberjunction/graphql-dataprovider';
|
|
6
7
|
import { TestRunDialogComponent } from '@memberjunction/ng-testing';
|
|
7
8
|
import * as i0 from "@angular/core";
|
|
8
9
|
import * as i1 from "../services/testing-instrumentation.service";
|
|
9
10
|
import * as i2 from "@progress/kendo-angular-dialog";
|
|
10
|
-
import * as i3 from "@angular/
|
|
11
|
-
import * as i4 from "@
|
|
12
|
-
import * as i5 from "@memberjunction/ng-
|
|
11
|
+
import * as i3 from "@angular/forms";
|
|
12
|
+
import * as i4 from "@memberjunction/ng-testing";
|
|
13
|
+
import * as i5 from "@memberjunction/ng-shared-generic";
|
|
14
|
+
import * as i6 from "@angular/common";
|
|
13
15
|
const _forTrack0 = ($index, $item) => $item.id;
|
|
14
16
|
const _c0 = () => [];
|
|
15
|
-
function
|
|
16
|
-
i0.ɵɵelementStart(0, "div",
|
|
17
|
-
i0.ɵɵelement(1, "span",
|
|
18
|
-
i0.ɵɵelementStart(2, "span",
|
|
17
|
+
function TestingExecutionComponent_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
18
|
+
i0.ɵɵelementStart(0, "div", 9);
|
|
19
|
+
i0.ɵɵelement(1, "span", 61);
|
|
20
|
+
i0.ɵɵelementStart(2, "span", 62);
|
|
19
21
|
i0.ɵɵtext(3, "Live");
|
|
20
22
|
i0.ɵɵelementEnd()();
|
|
21
23
|
} }
|
|
22
|
-
function
|
|
24
|
+
function TestingExecutionComponent_Conditional_51_Template(rf, ctx) { if (rf & 1) {
|
|
23
25
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
24
|
-
i0.ɵɵelementStart(0, "button",
|
|
25
|
-
i0.ɵɵlistener("click", function
|
|
26
|
-
i0.ɵɵelement(1, "i",
|
|
26
|
+
i0.ɵɵelementStart(0, "button", 63);
|
|
27
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Conditional_51_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.clearSearch()); });
|
|
28
|
+
i0.ɵɵelement(1, "i", 23);
|
|
27
29
|
i0.ɵɵelementEnd();
|
|
28
30
|
} }
|
|
29
|
-
function
|
|
31
|
+
function TestingExecutionComponent_Conditional_112_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelementStart(0, "div", 58);
|
|
33
|
+
i0.ɵɵelement(1, "mj-loading", 64);
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
} }
|
|
36
|
+
function TestingExecutionComponent_Conditional_114_Template(rf, ctx) { if (rf & 1) {
|
|
30
37
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
31
|
-
i0.ɵɵelementStart(0, "div",
|
|
32
|
-
i0.ɵɵelement(1, "i",
|
|
38
|
+
i0.ɵɵelementStart(0, "div", 59);
|
|
39
|
+
i0.ɵɵelement(1, "i", 65);
|
|
33
40
|
i0.ɵɵelementStart(2, "p");
|
|
34
41
|
i0.ɵɵtext(3, "No test executions found");
|
|
35
42
|
i0.ɵɵelementEnd();
|
|
36
|
-
i0.ɵɵelementStart(4, "button",
|
|
37
|
-
i0.ɵɵlistener("click", function
|
|
38
|
-
i0.ɵɵelement(5, "i",
|
|
43
|
+
i0.ɵɵelementStart(4, "button", 66);
|
|
44
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Conditional_114_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.startNewTest()); });
|
|
45
|
+
i0.ɵɵelement(5, "i", 14);
|
|
39
46
|
i0.ɵɵtext(6, " Run Your First Test ");
|
|
40
47
|
i0.ɵɵelementEnd()();
|
|
41
48
|
} }
|
|
42
|
-
function
|
|
43
|
-
i0.ɵɵelementStart(0, "div",
|
|
44
|
-
i0.ɵɵelement(1, "div",
|
|
49
|
+
function TestingExecutionComponent_For_116_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
50
|
+
i0.ɵɵelementStart(0, "div", 74);
|
|
51
|
+
i0.ɵɵelement(1, "div", 86);
|
|
45
52
|
i0.ɵɵelementEnd();
|
|
46
53
|
} if (rf & 2) {
|
|
47
54
|
const execution_r5 = i0.ɵɵnextContext().$implicit;
|
|
48
55
|
i0.ɵɵadvance();
|
|
49
56
|
i0.ɵɵstyleProp("width", execution_r5.progress, "%");
|
|
50
57
|
} }
|
|
51
|
-
function
|
|
58
|
+
function TestingExecutionComponent_For_116_Conditional_22_Template(rf, ctx) { if (rf & 1) {
|
|
52
59
|
const _r6 = i0.ɵɵgetCurrentView();
|
|
53
|
-
i0.ɵɵelementStart(0, "button",
|
|
54
|
-
i0.ɵɵlistener("click", function
|
|
55
|
-
i0.ɵɵelement(1, "i",
|
|
60
|
+
i0.ɵɵelementStart(0, "button", 87);
|
|
61
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_116_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)); });
|
|
62
|
+
i0.ɵɵelement(1, "i", 88);
|
|
56
63
|
i0.ɵɵelementEnd();
|
|
57
64
|
} }
|
|
58
|
-
function
|
|
65
|
+
function TestingExecutionComponent_For_116_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
59
66
|
const _r7 = i0.ɵɵgetCurrentView();
|
|
60
|
-
i0.ɵɵelementStart(0, "button",
|
|
61
|
-
i0.ɵɵlistener("click", function
|
|
62
|
-
i0.ɵɵelement(1, "i",
|
|
67
|
+
i0.ɵɵelementStart(0, "button", 89);
|
|
68
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_116_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)); });
|
|
69
|
+
i0.ɵɵelement(1, "i", 90);
|
|
63
70
|
i0.ɵɵelementEnd();
|
|
64
71
|
} }
|
|
65
|
-
function
|
|
72
|
+
function TestingExecutionComponent_For_116_Template(rf, ctx) { if (rf & 1) {
|
|
66
73
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
67
|
-
i0.ɵɵelementStart(0, "div",
|
|
74
|
+
i0.ɵɵelementStart(0, "div", 67)(1, "div", 68)(2, "div", 69)(3, "div", 70);
|
|
68
75
|
i0.ɵɵtext(4);
|
|
69
76
|
i0.ɵɵelementEnd();
|
|
70
|
-
i0.ɵɵelementStart(5, "div",
|
|
77
|
+
i0.ɵɵelementStart(5, "div", 71);
|
|
71
78
|
i0.ɵɵtext(6);
|
|
72
79
|
i0.ɵɵelementEnd()()();
|
|
73
|
-
i0.ɵɵelementStart(7, "div",
|
|
74
|
-
i0.ɵɵelement(8, "app-test-status-badge",
|
|
75
|
-
i0.ɵɵtemplate(9,
|
|
80
|
+
i0.ɵɵelementStart(7, "div", 72);
|
|
81
|
+
i0.ɵɵelement(8, "app-test-status-badge", 73);
|
|
82
|
+
i0.ɵɵtemplate(9, TestingExecutionComponent_For_116_Conditional_9_Template, 2, 2, "div", 74);
|
|
76
83
|
i0.ɵɵelementEnd();
|
|
77
|
-
i0.ɵɵelementStart(10, "div",
|
|
78
|
-
i0.ɵɵelement(11, "app-score-indicator",
|
|
84
|
+
i0.ɵɵelementStart(10, "div", 75);
|
|
85
|
+
i0.ɵɵelement(11, "app-score-indicator", 76);
|
|
79
86
|
i0.ɵɵelementEnd();
|
|
80
|
-
i0.ɵɵelementStart(12, "div",
|
|
87
|
+
i0.ɵɵelementStart(12, "div", 77);
|
|
81
88
|
i0.ɵɵtext(13);
|
|
82
89
|
i0.ɵɵelementEnd();
|
|
83
|
-
i0.ɵɵelementStart(14, "div",
|
|
84
|
-
i0.ɵɵelement(15, "app-cost-display",
|
|
90
|
+
i0.ɵɵelementStart(14, "div", 78);
|
|
91
|
+
i0.ɵɵelement(15, "app-cost-display", 79);
|
|
85
92
|
i0.ɵɵelementEnd();
|
|
86
|
-
i0.ɵɵelementStart(16, "div",
|
|
93
|
+
i0.ɵɵelementStart(16, "div", 80);
|
|
87
94
|
i0.ɵɵtext(17);
|
|
88
95
|
i0.ɵɵpipe(18, "date");
|
|
89
96
|
i0.ɵɵelementEnd();
|
|
90
|
-
i0.ɵɵelementStart(19, "div",
|
|
91
|
-
i0.ɵɵlistener("click", function
|
|
92
|
-
i0.ɵɵelement(21, "i",
|
|
97
|
+
i0.ɵɵelementStart(19, "div", 81)(20, "button", 82);
|
|
98
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_For_116_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)); });
|
|
99
|
+
i0.ɵɵelement(21, "i", 83);
|
|
93
100
|
i0.ɵɵelementEnd();
|
|
94
|
-
i0.ɵɵtemplate(22,
|
|
101
|
+
i0.ɵɵtemplate(22, TestingExecutionComponent_For_116_Conditional_22_Template, 2, 0, "button", 84)(23, TestingExecutionComponent_For_116_Conditional_23_Template, 2, 0, "button", 85);
|
|
95
102
|
i0.ɵɵelementEnd()();
|
|
96
103
|
} if (rf & 2) {
|
|
97
104
|
const execution_r5 = ctx.$implicit;
|
|
@@ -120,17 +127,24 @@ export class TestingExecutionComponent {
|
|
|
120
127
|
instrumentationService;
|
|
121
128
|
dialogService;
|
|
122
129
|
cdr;
|
|
130
|
+
viewContainerRef;
|
|
123
131
|
initialState;
|
|
124
132
|
stateChange = new EventEmitter();
|
|
125
133
|
destroy$ = new Subject();
|
|
126
134
|
activeDialogRef = null;
|
|
127
135
|
isRefreshing = false;
|
|
136
|
+
isLoading = false;
|
|
137
|
+
lastUpdated = new Date();
|
|
128
138
|
filters = {
|
|
129
139
|
status: 'all',
|
|
130
140
|
suite: 'all',
|
|
131
|
-
timeRange: '
|
|
141
|
+
timeRange: 'month', // Default to "This Month" to show more data
|
|
132
142
|
searchText: ''
|
|
133
143
|
};
|
|
144
|
+
// Track previous time range to detect changes requiring server re-query
|
|
145
|
+
previousTimeRange = 'month';
|
|
146
|
+
// BehaviorSubject to trigger client-side filter updates
|
|
147
|
+
filterTrigger$ = new BehaviorSubject(undefined);
|
|
134
148
|
executions$;
|
|
135
149
|
filteredExecutions$;
|
|
136
150
|
runningCount$;
|
|
@@ -138,16 +152,29 @@ export class TestingExecutionComponent {
|
|
|
138
152
|
failedTodayCount$;
|
|
139
153
|
avgDurationToday$;
|
|
140
154
|
hasRunningTests$;
|
|
141
|
-
|
|
155
|
+
testingClient;
|
|
156
|
+
constructor(instrumentationService, dialogService, cdr, viewContainerRef) {
|
|
142
157
|
this.instrumentationService = instrumentationService;
|
|
143
158
|
this.dialogService = dialogService;
|
|
144
159
|
this.cdr = cdr;
|
|
160
|
+
this.viewContainerRef = viewContainerRef;
|
|
161
|
+
// Initialize GraphQL testing client for cancel/rerun operations
|
|
162
|
+
const dataProvider = Metadata.Provider;
|
|
163
|
+
this.testingClient = new GraphQLTestingClient(dataProvider);
|
|
164
|
+
// Subscribe to loading state
|
|
165
|
+
this.instrumentationService.isLoading$.pipe(takeUntil(this.destroy$)).subscribe(loading => {
|
|
166
|
+
this.isLoading = loading;
|
|
167
|
+
this.cdr.markForCheck();
|
|
168
|
+
});
|
|
145
169
|
}
|
|
146
170
|
ngOnInit() {
|
|
147
|
-
|
|
171
|
+
// Apply initial state if provided
|
|
148
172
|
if (this.initialState) {
|
|
149
173
|
this.filters = { ...this.filters, ...this.initialState.filters };
|
|
150
174
|
}
|
|
175
|
+
// Set the service date range based on the selected time range filter
|
|
176
|
+
this.updateServiceDateRange();
|
|
177
|
+
this.setupObservables();
|
|
151
178
|
}
|
|
152
179
|
ngOnDestroy() {
|
|
153
180
|
// Close any open dialog when component is destroyed
|
|
@@ -165,30 +192,22 @@ export class TestingExecutionComponent {
|
|
|
165
192
|
}
|
|
166
193
|
setupObservables() {
|
|
167
194
|
this.executions$ = this.instrumentationService.testRuns$.pipe(map(runs => runs.map(run => this.mapToExecutionItem(run))), takeUntil(this.destroy$));
|
|
195
|
+
// Combine executions with filter trigger to react to client-side filter changes
|
|
168
196
|
this.filteredExecutions$ = combineLatest([
|
|
169
|
-
this.executions
|
|
197
|
+
this.executions$,
|
|
198
|
+
this.filterTrigger$
|
|
170
199
|
]).pipe(map(([executions]) => this.applyFilters(executions)), takeUntil(this.destroy$));
|
|
200
|
+
// KPI counts are now based on the full executions$ (which respects the service date range)
|
|
201
|
+
// This means the counts will match the selected time range filter
|
|
171
202
|
this.runningCount$ = this.executions$.pipe(map(execs => execs.filter(e => e.status === 'Running').length));
|
|
172
|
-
this.completedTodayCount$ = this.executions$.pipe(map(execs =>
|
|
173
|
-
|
|
174
|
-
today.setHours(0, 0, 0, 0);
|
|
175
|
-
return execs.filter(e => e.status === 'Passed' &&
|
|
176
|
-
e.startedAt >= today).length;
|
|
177
|
-
}));
|
|
178
|
-
this.failedTodayCount$ = this.executions$.pipe(map(execs => {
|
|
179
|
-
const today = new Date();
|
|
180
|
-
today.setHours(0, 0, 0, 0);
|
|
181
|
-
return execs.filter(e => e.status === 'Failed' &&
|
|
182
|
-
e.startedAt >= today).length;
|
|
183
|
-
}));
|
|
203
|
+
this.completedTodayCount$ = this.executions$.pipe(map(execs => execs.filter(e => e.status === 'Passed').length));
|
|
204
|
+
this.failedTodayCount$ = this.executions$.pipe(map(execs => execs.filter(e => e.status === 'Failed' || e.status === 'Error').length));
|
|
184
205
|
this.avgDurationToday$ = this.executions$.pipe(map(execs => {
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
const todayExecs = execs.filter(e => e.startedAt >= today && e.completedAt);
|
|
188
|
-
if (todayExecs.length === 0)
|
|
206
|
+
const completedExecs = execs.filter(e => e.completedAt || e.status !== 'Running');
|
|
207
|
+
if (completedExecs.length === 0)
|
|
189
208
|
return 0;
|
|
190
|
-
const totalDuration =
|
|
191
|
-
return totalDuration /
|
|
209
|
+
const totalDuration = completedExecs.reduce((sum, e) => sum + e.duration, 0);
|
|
210
|
+
return totalDuration / completedExecs.length;
|
|
192
211
|
}));
|
|
193
212
|
this.hasRunningTests$ = this.runningCount$.pipe(map(count => count > 0));
|
|
194
213
|
}
|
|
@@ -199,6 +218,7 @@ export class TestingExecutionComponent {
|
|
|
199
218
|
const progress = run.status === 'Running' ? Math.random() * 100 : 100;
|
|
200
219
|
return {
|
|
201
220
|
id: run.id,
|
|
221
|
+
testId: run.testId,
|
|
202
222
|
testName: run.testName,
|
|
203
223
|
suiteName: run.suiteName,
|
|
204
224
|
status: run.status,
|
|
@@ -212,6 +232,7 @@ export class TestingExecutionComponent {
|
|
|
212
232
|
}
|
|
213
233
|
applyFilters(executions) {
|
|
214
234
|
let filtered = [...executions];
|
|
235
|
+
// Apply status filter
|
|
215
236
|
if (this.filters.status !== 'all') {
|
|
216
237
|
if (this.filters.status === 'running') {
|
|
217
238
|
filtered = filtered.filter(e => e.status === 'Running');
|
|
@@ -226,32 +247,51 @@ export class TestingExecutionComponent {
|
|
|
226
247
|
filtered = filtered.filter(e => e.status === 'Passed');
|
|
227
248
|
}
|
|
228
249
|
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (this.filters.timeRange === 'today') {
|
|
233
|
-
startDate.setHours(0, 0, 0, 0);
|
|
234
|
-
}
|
|
235
|
-
else if (this.filters.timeRange === 'week') {
|
|
236
|
-
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
237
|
-
}
|
|
238
|
-
else if (this.filters.timeRange === 'month') {
|
|
239
|
-
startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
240
|
-
}
|
|
241
|
-
filtered = filtered.filter(e => e.startedAt >= startDate);
|
|
242
|
-
}
|
|
250
|
+
// Note: Time range filtering is now handled by the service via updateServiceDateRange()
|
|
251
|
+
// This ensures the KPIs and list use the same data set
|
|
252
|
+
// Apply search text filter
|
|
243
253
|
if (this.filters.searchText) {
|
|
244
254
|
const searchLower = this.filters.searchText.toLowerCase();
|
|
245
255
|
filtered = filtered.filter(e => e.testName.toLowerCase().includes(searchLower) ||
|
|
246
256
|
e.suiteName.toLowerCase().includes(searchLower));
|
|
247
257
|
}
|
|
258
|
+
// Sort by most recent first
|
|
248
259
|
filtered.sort((a, b) => b.startedAt.getTime() - a.startedAt.getTime());
|
|
249
260
|
return filtered;
|
|
250
261
|
}
|
|
251
262
|
onFilterChange() {
|
|
263
|
+
// Only re-query server when time range changes - status/search filtering is client-side only
|
|
264
|
+
if (this.filters.timeRange !== this.previousTimeRange) {
|
|
265
|
+
this.previousTimeRange = this.filters.timeRange;
|
|
266
|
+
this.updateServiceDateRange();
|
|
267
|
+
}
|
|
268
|
+
// Trigger client-side filter update via observable
|
|
269
|
+
this.filterTrigger$.next();
|
|
252
270
|
this.emitStateChange();
|
|
253
271
|
this.cdr.markForCheck();
|
|
254
272
|
}
|
|
273
|
+
updateServiceDateRange() {
|
|
274
|
+
const now = new Date();
|
|
275
|
+
let startDate;
|
|
276
|
+
switch (this.filters.timeRange) {
|
|
277
|
+
case 'today':
|
|
278
|
+
startDate = new Date(now);
|
|
279
|
+
startDate.setHours(0, 0, 0, 0);
|
|
280
|
+
break;
|
|
281
|
+
case 'week':
|
|
282
|
+
startDate = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
283
|
+
break;
|
|
284
|
+
case 'month':
|
|
285
|
+
startDate = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
286
|
+
break;
|
|
287
|
+
case 'all':
|
|
288
|
+
default:
|
|
289
|
+
// For "all time", use a very old date (e.g., 1 year ago)
|
|
290
|
+
startDate = new Date(now.getTime() - 365 * 24 * 60 * 60 * 1000);
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
this.instrumentationService.setDateRange(startDate, now);
|
|
294
|
+
}
|
|
255
295
|
clearSearch() {
|
|
256
296
|
this.filters.searchText = '';
|
|
257
297
|
this.onFilterChange();
|
|
@@ -261,9 +301,14 @@ export class TestingExecutionComponent {
|
|
|
261
301
|
this.instrumentationService.refresh();
|
|
262
302
|
setTimeout(() => {
|
|
263
303
|
this.isRefreshing = false;
|
|
304
|
+
this.lastUpdated = new Date();
|
|
264
305
|
this.cdr.markForCheck();
|
|
265
306
|
}, 1000);
|
|
266
307
|
}
|
|
308
|
+
filterByStatus(status) {
|
|
309
|
+
this.filters.status = status;
|
|
310
|
+
this.onFilterChange();
|
|
311
|
+
}
|
|
267
312
|
startNewTest() {
|
|
268
313
|
this.activeDialogRef = this.dialogService.open({
|
|
269
314
|
content: TestRunDialogComponent,
|
|
@@ -289,11 +334,44 @@ export class TestingExecutionComponent {
|
|
|
289
334
|
viewDetails(execution) {
|
|
290
335
|
SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(execution.id));
|
|
291
336
|
}
|
|
292
|
-
cancelExecution(execution) {
|
|
293
|
-
|
|
337
|
+
async cancelExecution(execution) {
|
|
338
|
+
// For now, show a notification - full cancel support requires server-side CancelTest mutation
|
|
339
|
+
// which we documented in the plan but haven't implemented yet
|
|
340
|
+
SharedService.Instance.CreateSimpleNotification(`Cancellation requested for "${execution.testName}". Full cancellation support coming soon.`, 'warning', 3000);
|
|
341
|
+
// Refresh after a delay to pick up any status changes
|
|
342
|
+
setTimeout(() => this.refresh(), 1500);
|
|
294
343
|
}
|
|
295
|
-
rerunTest(execution) {
|
|
296
|
-
|
|
344
|
+
async rerunTest(execution) {
|
|
345
|
+
if (!execution.testId) {
|
|
346
|
+
SharedService.Instance.CreateSimpleNotification('Cannot re-run: Test ID not available', 'error', 3000);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// Open the test run dialog with the test pre-selected
|
|
350
|
+
this.activeDialogRef = this.dialogService.open({
|
|
351
|
+
content: TestRunDialogComponent,
|
|
352
|
+
width: 1000,
|
|
353
|
+
height: 750,
|
|
354
|
+
title: 'Re-run Test',
|
|
355
|
+
actions: []
|
|
356
|
+
});
|
|
357
|
+
// Pre-configure the dialog with the test
|
|
358
|
+
const dialogComponent = this.activeDialogRef.content.instance;
|
|
359
|
+
dialogComponent.runMode = 'test';
|
|
360
|
+
dialogComponent.selectedTestId = execution.testId;
|
|
361
|
+
this.activeDialogRef.result.pipe(takeUntil(this.destroy$)).subscribe({
|
|
362
|
+
next: (result) => {
|
|
363
|
+
if (result && typeof result === 'object' && 'testExecuted' in result && result.testExecuted) {
|
|
364
|
+
SharedService.Instance.CreateSimpleNotification(`Test "${execution.testName}" completed`, 'success', 3000);
|
|
365
|
+
this.refresh();
|
|
366
|
+
}
|
|
367
|
+
},
|
|
368
|
+
error: () => {
|
|
369
|
+
this.activeDialogRef = null;
|
|
370
|
+
},
|
|
371
|
+
complete: () => {
|
|
372
|
+
this.activeDialogRef = null;
|
|
373
|
+
}
|
|
374
|
+
});
|
|
297
375
|
}
|
|
298
376
|
formatDuration(milliseconds) {
|
|
299
377
|
if (milliseconds < 1000)
|
|
@@ -304,227 +382,304 @@ export class TestingExecutionComponent {
|
|
|
304
382
|
return `${minutes}m ${seconds % 60}s`;
|
|
305
383
|
return `${seconds}s`;
|
|
306
384
|
}
|
|
385
|
+
getTimeRangeLabel() {
|
|
386
|
+
switch (this.filters.timeRange) {
|
|
387
|
+
case 'today': return 'Today';
|
|
388
|
+
case 'week': return 'This Week';
|
|
389
|
+
case 'month': return 'This Month';
|
|
390
|
+
case 'all': return 'All Time';
|
|
391
|
+
default: return '';
|
|
392
|
+
}
|
|
393
|
+
}
|
|
307
394
|
emitStateChange() {
|
|
308
395
|
this.stateChange.emit({
|
|
309
396
|
filters: this.filters
|
|
310
397
|
});
|
|
311
398
|
}
|
|
312
|
-
static ɵfac = function TestingExecutionComponent_Factory(t) { return new (t || TestingExecutionComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i2.DialogService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef)); };
|
|
313
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingExecutionComponent, selectors: [["app-testing-execution"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, decls:
|
|
314
|
-
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "
|
|
315
|
-
i0.ɵɵelement(4, "i",
|
|
316
|
-
i0.ɵɵtext(5, " Test Execution Monitor ");
|
|
399
|
+
static ɵfac = function TestingExecutionComponent_Factory(t) { return new (t || TestingExecutionComponent)(i0.ɵɵdirectiveInject(i1.TestingInstrumentationService), i0.ɵɵdirectiveInject(i2.DialogService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ViewContainerRef)); };
|
|
400
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestingExecutionComponent, selectors: [["app-testing-execution"]], inputs: { initialState: "initialState" }, outputs: { stateChange: "stateChange" }, decls: 118, vars: 41, consts: [["kendoDialogContainer", "", 1, "testing-execution"], [1, "execution-header"], [1, "header-left"], [1, "header-icon"], [1, "fa-solid", "fa-play-circle"], [1, "header-text"], [1, "header-meta"], [1, "last-updated"], [1, "fa-solid", "fa-clock"], [1, "live-indicator"], [1, "header-actions"], [1, "action-btn", "refresh-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-sync-alt"], [1, "action-btn", "primary-btn", 3, "click"], [1, "fa-solid", "fa-play"], [1, "filter-bar"], [1, "filter-chips"], [1, "filter-chip", 3, "click"], [1, "filter-chip", "running", 3, "click"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "filter-chip", "passed", 3, "click"], [1, "fa-solid", "fa-check"], [1, "filter-chip", "failed", 3, "click"], [1, "fa-solid", "fa-times"], [1, "filter-controls"], [1, "time-select"], [3, "ngModelChange", "change", "ngModel"], ["value", "today"], ["value", "week"], ["value", "month"], ["value", "all"], [1, "search-input"], [1, "fa-solid", "fa-search"], ["type", "text", "placeholder", "Search tests...", 3, "ngModelChange", "input", "ngModel"], [1, "clear-btn"], [1, "kpi-grid"], [1, "kpi-card", "running", "clickable", 3, "click"], [1, "kpi-icon"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-arrow"], [1, "fa-solid", "fa-chevron-right"], [1, "kpi-card", "passed", "clickable", 3, "click"], [1, "fa-solid", "fa-check-circle"], [1, "kpi-card", "failed", "clickable", 3, "click"], [1, "fa-solid", "fa-exclamation-circle"], [1, "kpi-card", "duration"], [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, "loading-placeholder"], [1, "no-data"], [1, "execution-row", 3, "running"], [1, "pulse"], [1, "text"], [1, "clear-btn", 3, "click"], ["text", "Loading test executions..."], [1, "fa-solid", "fa-inbox"], [1, "action-btn", "primary", 3, "click"], [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) {
|
|
401
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3);
|
|
402
|
+
i0.ɵɵelement(4, "i", 4);
|
|
317
403
|
i0.ɵɵelementEnd();
|
|
318
|
-
i0.ɵɵ
|
|
319
|
-
i0.ɵɵ
|
|
404
|
+
i0.ɵɵelementStart(5, "div", 5)(6, "h2");
|
|
405
|
+
i0.ɵɵtext(7, "Test Execution Monitor");
|
|
320
406
|
i0.ɵɵelementEnd();
|
|
321
|
-
i0.ɵɵelementStart(8, "div",
|
|
322
|
-
i0.ɵɵ
|
|
323
|
-
i0.ɵɵ
|
|
324
|
-
i0.ɵɵ
|
|
407
|
+
i0.ɵɵelementStart(8, "div", 6)(9, "span", 7);
|
|
408
|
+
i0.ɵɵelement(10, "i", 8);
|
|
409
|
+
i0.ɵɵtext(11);
|
|
410
|
+
i0.ɵɵpipe(12, "date");
|
|
325
411
|
i0.ɵɵelementEnd();
|
|
326
|
-
i0.ɵɵ
|
|
327
|
-
i0.ɵɵ
|
|
328
|
-
i0.ɵɵelement(13, "i", 9);
|
|
329
|
-
i0.ɵɵtext(14, " Run Test ");
|
|
412
|
+
i0.ɵɵtemplate(13, TestingExecutionComponent_Conditional_13_Template, 4, 0, "div", 9);
|
|
413
|
+
i0.ɵɵpipe(14, "async");
|
|
330
414
|
i0.ɵɵelementEnd()()();
|
|
331
|
-
i0.ɵɵelementStart(15, "div", 10)(16, "
|
|
332
|
-
i0.ɵɵ
|
|
333
|
-
i0.ɵɵ
|
|
334
|
-
i0.ɵɵelementStart(
|
|
335
|
-
i0.ɵɵ
|
|
336
|
-
i0.ɵɵ
|
|
337
|
-
i0.ɵɵelementStart(20, "
|
|
338
|
-
i0.ɵɵ
|
|
339
|
-
i0.ɵɵ
|
|
340
|
-
i0.ɵɵelementStart(22, "
|
|
341
|
-
i0.ɵɵtext(23, "
|
|
342
|
-
i0.ɵɵelementEnd();
|
|
343
|
-
i0.ɵɵelementStart(24, "
|
|
344
|
-
i0.ɵɵ
|
|
415
|
+
i0.ɵɵelementStart(15, "div", 10)(16, "button", 11);
|
|
416
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_16_listener() { return ctx.refresh(); });
|
|
417
|
+
i0.ɵɵelement(17, "i", 12);
|
|
418
|
+
i0.ɵɵelementStart(18, "span");
|
|
419
|
+
i0.ɵɵtext(19, "Refresh");
|
|
420
|
+
i0.ɵɵelementEnd()();
|
|
421
|
+
i0.ɵɵelementStart(20, "button", 13);
|
|
422
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_20_listener() { return ctx.startNewTest(); });
|
|
423
|
+
i0.ɵɵelement(21, "i", 14);
|
|
424
|
+
i0.ɵɵelementStart(22, "span");
|
|
425
|
+
i0.ɵɵtext(23, "Run Test");
|
|
426
|
+
i0.ɵɵelementEnd()()()();
|
|
427
|
+
i0.ɵɵelementStart(24, "div", 15)(25, "div", 16)(26, "button", 17);
|
|
428
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_26_listener() { ctx.filters.status = "all"; return ctx.onFilterChange(); });
|
|
429
|
+
i0.ɵɵtext(27, " All ");
|
|
345
430
|
i0.ɵɵelementEnd();
|
|
346
|
-
i0.ɵɵelementStart(
|
|
347
|
-
i0.ɵɵ
|
|
431
|
+
i0.ɵɵelementStart(28, "button", 18);
|
|
432
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_28_listener() { ctx.filters.status = "running"; return ctx.onFilterChange(); });
|
|
433
|
+
i0.ɵɵelement(29, "i", 19);
|
|
434
|
+
i0.ɵɵtext(30, " Running ");
|
|
348
435
|
i0.ɵɵelementEnd();
|
|
349
|
-
i0.ɵɵelementStart(
|
|
350
|
-
i0.ɵɵ
|
|
351
|
-
i0.ɵɵ
|
|
352
|
-
i0.ɵɵ
|
|
353
|
-
i0.ɵɵtext(32, "Time Range");
|
|
436
|
+
i0.ɵɵelementStart(31, "button", 20);
|
|
437
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_31_listener() { ctx.filters.status = "passed"; return ctx.onFilterChange(); });
|
|
438
|
+
i0.ɵɵelement(32, "i", 21);
|
|
439
|
+
i0.ɵɵtext(33, " Passed ");
|
|
354
440
|
i0.ɵɵelementEnd();
|
|
355
|
-
i0.ɵɵelementStart(
|
|
356
|
-
i0.ɵɵ
|
|
357
|
-
i0.ɵɵ
|
|
358
|
-
i0.ɵɵ
|
|
359
|
-
i0.ɵɵ
|
|
441
|
+
i0.ɵɵelementStart(34, "button", 22);
|
|
442
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_button_click_34_listener() { ctx.filters.status = "failed"; return ctx.onFilterChange(); });
|
|
443
|
+
i0.ɵɵelement(35, "i", 23);
|
|
444
|
+
i0.ɵɵtext(36, " Failed ");
|
|
445
|
+
i0.ɵɵelementEnd()();
|
|
446
|
+
i0.ɵɵelementStart(37, "div", 24)(38, "div", 25)(39, "select", 26);
|
|
447
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_select_ngModelChange_39_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.timeRange, $event) || (ctx.filters.timeRange = $event); return $event; });
|
|
448
|
+
i0.ɵɵlistener("change", function TestingExecutionComponent_Template_select_change_39_listener() { return ctx.onFilterChange(); });
|
|
449
|
+
i0.ɵɵelementStart(40, "option", 27);
|
|
450
|
+
i0.ɵɵtext(41, "Today");
|
|
360
451
|
i0.ɵɵelementEnd();
|
|
361
|
-
i0.ɵɵelementStart(
|
|
362
|
-
i0.ɵɵtext(
|
|
452
|
+
i0.ɵɵelementStart(42, "option", 28);
|
|
453
|
+
i0.ɵɵtext(43, "This Week");
|
|
363
454
|
i0.ɵɵelementEnd();
|
|
364
|
-
i0.ɵɵelementStart(
|
|
365
|
-
i0.ɵɵtext(
|
|
455
|
+
i0.ɵɵelementStart(44, "option", 29);
|
|
456
|
+
i0.ɵɵtext(45, "This Month");
|
|
366
457
|
i0.ɵɵelementEnd();
|
|
367
|
-
i0.ɵɵelementStart(
|
|
368
|
-
i0.ɵɵtext(
|
|
458
|
+
i0.ɵɵelementStart(46, "option", 30);
|
|
459
|
+
i0.ɵɵtext(47, "All Time");
|
|
369
460
|
i0.ɵɵelementEnd()()();
|
|
370
|
-
i0.ɵɵelementStart(
|
|
371
|
-
i0.ɵɵ
|
|
372
|
-
i0.ɵɵ
|
|
373
|
-
i0.ɵɵ
|
|
374
|
-
i0.ɵɵ
|
|
375
|
-
i0.ɵɵelementStart(47, "input", 24);
|
|
376
|
-
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_input_ngModelChange_47_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.searchText, $event) || (ctx.filters.searchText = $event); return $event; });
|
|
377
|
-
i0.ɵɵlistener("input", function TestingExecutionComponent_Template_input_input_47_listener() { return ctx.onFilterChange(); });
|
|
461
|
+
i0.ɵɵelementStart(48, "div", 31);
|
|
462
|
+
i0.ɵɵelement(49, "i", 32);
|
|
463
|
+
i0.ɵɵelementStart(50, "input", 33);
|
|
464
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestingExecutionComponent_Template_input_ngModelChange_50_listener($event) { i0.ɵɵtwoWayBindingSet(ctx.filters.searchText, $event) || (ctx.filters.searchText = $event); return $event; });
|
|
465
|
+
i0.ɵɵlistener("input", function TestingExecutionComponent_Template_input_input_50_listener() { return ctx.onFilterChange(); });
|
|
378
466
|
i0.ɵɵelementEnd();
|
|
379
|
-
i0.ɵɵtemplate(
|
|
467
|
+
i0.ɵɵtemplate(51, TestingExecutionComponent_Conditional_51_Template, 2, 0, "button", 34);
|
|
380
468
|
i0.ɵɵelementEnd()()();
|
|
381
|
-
i0.ɵɵelementStart(
|
|
382
|
-
i0.ɵɵ
|
|
469
|
+
i0.ɵɵelementStart(52, "div", 35)(53, "div", 36);
|
|
470
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_div_click_53_listener() { return ctx.filterByStatus("running"); });
|
|
471
|
+
i0.ɵɵelementStart(54, "div", 37);
|
|
472
|
+
i0.ɵɵelement(55, "i", 19);
|
|
383
473
|
i0.ɵɵelementEnd();
|
|
384
|
-
i0.ɵɵelementStart(
|
|
385
|
-
i0.ɵɵtext(
|
|
386
|
-
i0.ɵɵpipe(
|
|
474
|
+
i0.ɵɵelementStart(56, "div", 38)(57, "div", 39);
|
|
475
|
+
i0.ɵɵtext(58);
|
|
476
|
+
i0.ɵɵpipe(59, "async");
|
|
387
477
|
i0.ɵɵelementEnd();
|
|
388
|
-
i0.ɵɵelementStart(
|
|
389
|
-
i0.ɵɵtext(
|
|
390
|
-
i0.ɵɵelementEnd()()
|
|
391
|
-
i0.ɵɵelementStart(
|
|
392
|
-
i0.ɵɵelement(
|
|
478
|
+
i0.ɵɵelementStart(60, "div", 40);
|
|
479
|
+
i0.ɵɵtext(61, "Running Now");
|
|
480
|
+
i0.ɵɵelementEnd()();
|
|
481
|
+
i0.ɵɵelementStart(62, "div", 41);
|
|
482
|
+
i0.ɵɵelement(63, "i", 42);
|
|
483
|
+
i0.ɵɵelementEnd()();
|
|
484
|
+
i0.ɵɵelementStart(64, "div", 43);
|
|
485
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_div_click_64_listener() { return ctx.filterByStatus("passed"); });
|
|
486
|
+
i0.ɵɵelementStart(65, "div", 37);
|
|
487
|
+
i0.ɵɵelement(66, "i", 44);
|
|
393
488
|
i0.ɵɵelementEnd();
|
|
394
|
-
i0.ɵɵelementStart(
|
|
395
|
-
i0.ɵɵtext(
|
|
396
|
-
i0.ɵɵpipe(
|
|
489
|
+
i0.ɵɵelementStart(67, "div", 38)(68, "div", 39);
|
|
490
|
+
i0.ɵɵtext(69);
|
|
491
|
+
i0.ɵɵpipe(70, "async");
|
|
397
492
|
i0.ɵɵelementEnd();
|
|
398
|
-
i0.ɵɵelementStart(
|
|
399
|
-
i0.ɵɵtext(
|
|
400
|
-
i0.ɵɵelementEnd()()
|
|
401
|
-
i0.ɵɵelementStart(
|
|
402
|
-
i0.ɵɵelement(
|
|
493
|
+
i0.ɵɵelementStart(71, "div", 40);
|
|
494
|
+
i0.ɵɵtext(72);
|
|
495
|
+
i0.ɵɵelementEnd()();
|
|
496
|
+
i0.ɵɵelementStart(73, "div", 41);
|
|
497
|
+
i0.ɵɵelement(74, "i", 42);
|
|
498
|
+
i0.ɵɵelementEnd()();
|
|
499
|
+
i0.ɵɵelementStart(75, "div", 45);
|
|
500
|
+
i0.ɵɵlistener("click", function TestingExecutionComponent_Template_div_click_75_listener() { return ctx.filterByStatus("failed"); });
|
|
501
|
+
i0.ɵɵelementStart(76, "div", 37);
|
|
502
|
+
i0.ɵɵelement(77, "i", 46);
|
|
403
503
|
i0.ɵɵelementEnd();
|
|
404
|
-
i0.ɵɵelementStart(
|
|
405
|
-
i0.ɵɵtext(
|
|
406
|
-
i0.ɵɵpipe(
|
|
504
|
+
i0.ɵɵelementStart(78, "div", 38)(79, "div", 39);
|
|
505
|
+
i0.ɵɵtext(80);
|
|
506
|
+
i0.ɵɵpipe(81, "async");
|
|
407
507
|
i0.ɵɵelementEnd();
|
|
408
|
-
i0.ɵɵelementStart(
|
|
409
|
-
i0.ɵɵtext(
|
|
410
|
-
i0.ɵɵelementEnd()()
|
|
411
|
-
i0.ɵɵelementStart(
|
|
412
|
-
i0.ɵɵelement(
|
|
508
|
+
i0.ɵɵelementStart(82, "div", 40);
|
|
509
|
+
i0.ɵɵtext(83);
|
|
510
|
+
i0.ɵɵelementEnd()();
|
|
511
|
+
i0.ɵɵelementStart(84, "div", 41);
|
|
512
|
+
i0.ɵɵelement(85, "i", 42);
|
|
513
|
+
i0.ɵɵelementEnd()();
|
|
514
|
+
i0.ɵɵelementStart(86, "div", 47)(87, "div", 37);
|
|
515
|
+
i0.ɵɵelement(88, "i", 8);
|
|
413
516
|
i0.ɵɵelementEnd();
|
|
414
|
-
i0.ɵɵelementStart(
|
|
415
|
-
i0.ɵɵtext(
|
|
416
|
-
i0.ɵɵpipe(
|
|
517
|
+
i0.ɵɵelementStart(89, "div", 38)(90, "div", 39);
|
|
518
|
+
i0.ɵɵtext(91);
|
|
519
|
+
i0.ɵɵpipe(92, "async");
|
|
417
520
|
i0.ɵɵelementEnd();
|
|
418
|
-
i0.ɵɵelementStart(
|
|
419
|
-
i0.ɵɵtext(
|
|
521
|
+
i0.ɵɵelementStart(93, "div", 40);
|
|
522
|
+
i0.ɵɵtext(94, "Avg Duration");
|
|
420
523
|
i0.ɵɵelementEnd()()()();
|
|
421
|
-
i0.ɵɵelementStart(
|
|
422
|
-
i0.ɵɵtext(
|
|
524
|
+
i0.ɵɵelementStart(95, "div", 48)(96, "div", 49)(97, "div", 50)(98, "div", 51);
|
|
525
|
+
i0.ɵɵtext(99, "Test Name");
|
|
423
526
|
i0.ɵɵelementEnd();
|
|
424
|
-
i0.ɵɵelementStart(
|
|
425
|
-
i0.ɵɵtext(
|
|
527
|
+
i0.ɵɵelementStart(100, "div", 52);
|
|
528
|
+
i0.ɵɵtext(101, "Status");
|
|
426
529
|
i0.ɵɵelementEnd();
|
|
427
|
-
i0.ɵɵelementStart(
|
|
428
|
-
i0.ɵɵtext(
|
|
530
|
+
i0.ɵɵelementStart(102, "div", 53);
|
|
531
|
+
i0.ɵɵtext(103, "Score");
|
|
429
532
|
i0.ɵɵelementEnd();
|
|
430
|
-
i0.ɵɵelementStart(
|
|
431
|
-
i0.ɵɵtext(
|
|
533
|
+
i0.ɵɵelementStart(104, "div", 54);
|
|
534
|
+
i0.ɵɵtext(105, "Duration");
|
|
432
535
|
i0.ɵɵelementEnd();
|
|
433
|
-
i0.ɵɵelementStart(
|
|
434
|
-
i0.ɵɵtext(
|
|
536
|
+
i0.ɵɵelementStart(106, "div", 55);
|
|
537
|
+
i0.ɵɵtext(107, "Cost");
|
|
435
538
|
i0.ɵɵelementEnd();
|
|
436
|
-
i0.ɵɵelementStart(
|
|
437
|
-
i0.ɵɵtext(
|
|
539
|
+
i0.ɵɵelementStart(108, "div", 56);
|
|
540
|
+
i0.ɵɵtext(109, "Started At");
|
|
438
541
|
i0.ɵɵelementEnd();
|
|
439
|
-
i0.ɵɵelementStart(
|
|
440
|
-
i0.ɵɵtext(
|
|
542
|
+
i0.ɵɵelementStart(110, "div", 57);
|
|
543
|
+
i0.ɵɵtext(111, "Actions");
|
|
441
544
|
i0.ɵɵelementEnd()();
|
|
442
|
-
i0.ɵɵtemplate(
|
|
443
|
-
i0.ɵɵpipe(
|
|
444
|
-
i0.ɵɵ
|
|
445
|
-
i0.ɵɵ
|
|
545
|
+
i0.ɵɵtemplate(112, TestingExecutionComponent_Conditional_112_Template, 2, 0, "div", 58);
|
|
546
|
+
i0.ɵɵpipe(113, "async");
|
|
547
|
+
i0.ɵɵtemplate(114, TestingExecutionComponent_Conditional_114_Template, 7, 0, "div", 59);
|
|
548
|
+
i0.ɵɵrepeaterCreate(115, TestingExecutionComponent_For_116_Template, 24, 17, "div", 60, _forTrack0);
|
|
549
|
+
i0.ɵɵpipe(117, "async");
|
|
446
550
|
i0.ɵɵelementEnd()()();
|
|
447
551
|
} if (rf & 2) {
|
|
448
552
|
let tmp_11_0;
|
|
449
553
|
let tmp_12_0;
|
|
450
|
-
|
|
451
|
-
|
|
554
|
+
let tmp_14_0;
|
|
555
|
+
let tmp_16_0;
|
|
556
|
+
let tmp_17_0;
|
|
557
|
+
let tmp_18_0;
|
|
558
|
+
i0.ɵɵadvance(11);
|
|
559
|
+
i0.ɵɵtextInterpolate1(" Updated ", i0.ɵɵpipeBind2(12, 23, ctx.lastUpdated, "shortTime"), " ");
|
|
560
|
+
i0.ɵɵadvance(2);
|
|
561
|
+
i0.ɵɵconditional(i0.ɵɵpipeBind1(14, 26, ctx.hasRunningTests$) ? 13 : -1);
|
|
452
562
|
i0.ɵɵadvance(3);
|
|
453
563
|
i0.ɵɵproperty("disabled", ctx.isRefreshing);
|
|
454
564
|
i0.ɵɵadvance();
|
|
455
565
|
i0.ɵɵclassProp("spinning", ctx.isRefreshing);
|
|
456
566
|
i0.ɵɵadvance(9);
|
|
457
|
-
i0.ɵɵ
|
|
458
|
-
i0.ɵɵadvance(
|
|
567
|
+
i0.ɵɵclassProp("active", ctx.filters.status === "all");
|
|
568
|
+
i0.ɵɵadvance(2);
|
|
569
|
+
i0.ɵɵclassProp("active", ctx.filters.status === "running");
|
|
570
|
+
i0.ɵɵadvance(3);
|
|
571
|
+
i0.ɵɵclassProp("active", ctx.filters.status === "passed");
|
|
572
|
+
i0.ɵɵadvance(3);
|
|
573
|
+
i0.ɵɵclassProp("active", ctx.filters.status === "failed");
|
|
574
|
+
i0.ɵɵadvance(5);
|
|
459
575
|
i0.ɵɵtwoWayProperty("ngModel", ctx.filters.timeRange);
|
|
460
|
-
i0.ɵɵadvance(
|
|
576
|
+
i0.ɵɵadvance(11);
|
|
461
577
|
i0.ɵɵtwoWayProperty("ngModel", ctx.filters.searchText);
|
|
462
578
|
i0.ɵɵadvance();
|
|
463
|
-
i0.ɵɵ
|
|
579
|
+
i0.ɵɵconditional(ctx.filters.searchText ? 51 : -1);
|
|
464
580
|
i0.ɵɵadvance(7);
|
|
465
|
-
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(
|
|
466
|
-
i0.ɵɵadvance(
|
|
467
|
-
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(
|
|
468
|
-
i0.ɵɵadvance(
|
|
469
|
-
i0.ɵɵ
|
|
470
|
-
i0.ɵɵadvance(
|
|
471
|
-
i0.ɵɵtextInterpolate(
|
|
581
|
+
i0.ɵɵtextInterpolate((tmp_11_0 = i0.ɵɵpipeBind1(59, 28, ctx.runningCount$)) !== null && tmp_11_0 !== undefined ? tmp_11_0 : 0);
|
|
582
|
+
i0.ɵɵadvance(11);
|
|
583
|
+
i0.ɵɵtextInterpolate((tmp_12_0 = i0.ɵɵpipeBind1(70, 30, ctx.completedTodayCount$)) !== null && tmp_12_0 !== undefined ? tmp_12_0 : 0);
|
|
584
|
+
i0.ɵɵadvance(3);
|
|
585
|
+
i0.ɵɵtextInterpolate1("Passed ", ctx.getTimeRangeLabel(), "");
|
|
586
|
+
i0.ɵɵadvance(8);
|
|
587
|
+
i0.ɵɵtextInterpolate((tmp_14_0 = i0.ɵɵpipeBind1(81, 32, ctx.failedTodayCount$)) !== null && tmp_14_0 !== undefined ? tmp_14_0 : 0);
|
|
588
|
+
i0.ɵɵadvance(3);
|
|
589
|
+
i0.ɵɵtextInterpolate1("Failed ", ctx.getTimeRangeLabel(), "");
|
|
590
|
+
i0.ɵɵadvance(8);
|
|
591
|
+
i0.ɵɵtextInterpolate(ctx.formatDuration((tmp_16_0 = i0.ɵɵpipeBind1(92, 34, ctx.avgDurationToday$)) !== null && tmp_16_0 !== undefined ? tmp_16_0 : 0));
|
|
472
592
|
i0.ɵɵadvance(21);
|
|
473
|
-
i0.ɵɵconditional(((
|
|
474
|
-
i0.ɵɵadvance(
|
|
475
|
-
i0.ɵɵrepeater((
|
|
476
|
-
} }, dependencies: [i3.NgIf, i4.NgSelectOption, i4.ɵNgSelectMultipleOption, i4.DefaultValueAccessor, i4.SelectControlValueAccessor, i4.NgControlStatus, i4.NgModel, i2.DialogContainerDirective, 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 });
|
|
593
|
+
i0.ɵɵconditional(ctx.isLoading ? 112 : ((tmp_17_0 = i0.ɵɵpipeBind1(113, 36, ctx.filteredExecutions$)) == null ? null : tmp_17_0.length) === 0 ? 114 : -1);
|
|
594
|
+
i0.ɵɵadvance(3);
|
|
595
|
+
i0.ɵɵrepeater((tmp_18_0 = i0.ɵɵpipeBind1(117, 38, ctx.filteredExecutions$)) !== null && tmp_18_0 !== undefined ? tmp_18_0 : i0.ɵɵpureFunction0(40, _c0));
|
|
596
|
+
} }, dependencies: [i3.NgSelectOption, i3.ɵNgSelectMultipleOption, i3.DefaultValueAccessor, i3.SelectControlValueAccessor, i3.NgControlStatus, i3.NgModel, i2.DialogContainerDirective, i4.TestStatusBadgeComponent, i4.ScoreIndicatorComponent, i4.CostDisplayComponent, i5.LoadingComponent, i6.AsyncPipe, i6.DatePipe], styles: ["\n\n\n\n\n .testing-execution[_ngcontent-%COMP%] {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: linear-gradient(135deg, #f8fafc 0%, #eef2f7 100%);\n }\n\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: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n padding: 24px 28px;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(99, 102, 241, 0.25);\n }\n\n .header-left[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 22px;\n color: white;\n }\n\n .header-text[_ngcontent-%COMP%] h2[_ngcontent-%COMP%] {\n margin: 0 0 4px 0;\n font-size: 20px;\n font-weight: 600;\n color: white;\n }\n\n .header-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .last-updated[_ngcontent-%COMP%] {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .last-updated[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n .live-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n color: white;\n }\n\n .pulse[_ngcontent-%COMP%] {\n width: 8px;\n height: 8px;\n background: #22c55e;\n border-radius: 50%;\n animation: _ngcontent-%COMP%_pulse 2s infinite;\n box-shadow: 0 0 8px #22c55e;\n }\n\n @keyframes _ngcontent-%COMP%_pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(1.3); }\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: 8px;\n padding: 10px 18px;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .refresh-btn[_ngcontent-%COMP%] {\n background: rgba(255, 255, 255, 0.15);\n color: white;\n border: 1px solid rgba(255, 255, 255, 0.25);\n }\n\n .refresh-btn[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.25);\n transform: translateY(-1px);\n }\n\n .primary-btn[_ngcontent-%COMP%] {\n background: white;\n color: #6366f1;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .primary-btn[_ngcontent-%COMP%]:hover {\n background: #f8f9ff;\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);\n }\n\n .action-btn[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none !important;\n }\n\n .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 \n\n .filter-bar[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n margin-bottom: 20px;\n padding: 16px 20px;\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n }\n\n .filter-chips[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n }\n\n .filter-chip[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #f1f5f9;\n border: 2px solid transparent;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n cursor: pointer;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .filter-chip[_ngcontent-%COMP%]:hover {\n background: #e2e8f0;\n color: #475569;\n }\n\n .filter-chip.active[_ngcontent-%COMP%] {\n background: #6366f1;\n color: white;\n border-color: #6366f1;\n }\n\n .filter-chip.running.active[_ngcontent-%COMP%] {\n background: #3b82f6;\n border-color: #3b82f6;\n }\n\n .filter-chip.passed.active[_ngcontent-%COMP%] {\n background: #22c55e;\n border-color: #22c55e;\n }\n\n .filter-chip.failed.active[_ngcontent-%COMP%] {\n background: #ef4444;\n border-color: #ef4444;\n }\n\n .filter-chip[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 11px;\n }\n\n .filter-controls[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .time-select[_ngcontent-%COMP%] select[_ngcontent-%COMP%] {\n padding: 10px 14px;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 500;\n color: #475569;\n background: white;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .time-select[_ngcontent-%COMP%] select[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .search-input[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px;\n background: #f8fafc;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n min-width: 250px;\n transition: all 0.2s ease;\n }\n\n .search-input[_ngcontent-%COMP%]:focus-within {\n border-color: #6366f1;\n background: white;\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .search-input[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #94a3b8;\n font-size: 14px;\n }\n\n .search-input[_ngcontent-%COMP%] input[_ngcontent-%COMP%] {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: #334155;\n }\n\n .search-input[_ngcontent-%COMP%] input[_ngcontent-%COMP%]::placeholder {\n color: #94a3b8;\n }\n\n .clear-btn[_ngcontent-%COMP%] {\n padding: 4px 8px;\n border: none;\n background: transparent;\n color: #94a3b8;\n cursor: pointer;\n border-radius: 4px;\n transition: all 0.2s ease;\n }\n\n .clear-btn[_ngcontent-%COMP%]:hover {\n background: #e2e8f0;\n color: #64748b;\n }\n\n \n\n .kpi-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .kpi-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n }\n\n .kpi-card[_ngcontent-%COMP%]::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 4px;\n height: 100%;\n border-radius: 4px 0 0 4px;\n }\n\n .kpi-card.clickable[_ngcontent-%COMP%] {\n cursor: pointer;\n }\n\n .kpi-card.clickable[_ngcontent-%COMP%]:hover {\n transform: translateY(-3px);\n box-shadow: 0 8px 24px rgba(99, 102, 241, 0.15);\n }\n\n .kpi-card.running[_ngcontent-%COMP%]::before { background: linear-gradient(180deg, #3b82f6, #60a5fa); }\n .kpi-card.passed[_ngcontent-%COMP%]::before { background: linear-gradient(180deg, #22c55e, #4ade80); }\n .kpi-card.failed[_ngcontent-%COMP%]::before { background: linear-gradient(180deg, #ef4444, #f87171); }\n .kpi-card.duration[_ngcontent-%COMP%]::before { background: linear-gradient(180deg, #8b5cf6, #a78bfa); }\n\n .kpi-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .kpi-card.running[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: rgba(59, 130, 246, 0.1);\n color: #3b82f6;\n }\n\n .kpi-card.passed[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: rgba(34, 197, 94, 0.1);\n color: #22c55e;\n }\n\n .kpi-card.failed[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: rgba(239, 68, 68, 0.1);\n color: #ef4444;\n }\n\n .kpi-card.duration[_ngcontent-%COMP%] .kpi-icon[_ngcontent-%COMP%] {\n background: rgba(139, 92, 246, 0.1);\n color: #8b5cf6;\n }\n\n .kpi-content[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .kpi-value[_ngcontent-%COMP%] {\n font-size: 26px;\n font-weight: 700;\n color: #1e293b;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .kpi-label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 500;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .kpi-arrow[_ngcontent-%COMP%] {\n color: #cbd5e1;\n font-size: 14px;\n opacity: 0;\n transition: all 0.3s ease;\n }\n\n .kpi-card.clickable[_ngcontent-%COMP%]:hover .kpi-arrow[_ngcontent-%COMP%] {\n opacity: 1;\n color: #6366f1;\n transform: translateX(4px);\n }\n\n \n\n .execution-content[_ngcontent-%COMP%] {\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n overflow: hidden;\n }\n\n .execution-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n }\n\n \n\n .list-header[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 120px 100px 100px 140px 100px;\n gap: 20px;\n padding: 16px 24px;\n background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);\n border-bottom: 1px solid #e2e8f0;\n font-size: 11px;\n font-weight: 700;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .header-cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n }\n\n \n\n .execution-row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 2fr 120px 100px 100px 140px 100px;\n gap: 20px;\n padding: 18px 24px;\n border-bottom: 1px solid #f1f5f9;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .execution-row[_ngcontent-%COMP%]:last-child {\n border-bottom: none;\n }\n\n .execution-row[_ngcontent-%COMP%]:hover {\n background: linear-gradient(90deg, rgba(99, 102, 241, 0.03) 0%, rgba(139, 92, 246, 0.03) 100%);\n }\n\n .execution-row.running[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.04) 100%);\n border-left: 3px solid #3b82f6;\n }\n\n .execution-row.running[_ngcontent-%COMP%]:hover {\n background: linear-gradient(90deg, rgba(59, 130, 246, 0.12) 0%, rgba(59, 130, 246, 0.06) 100%);\n }\n\n \n\n .cell[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: #334155;\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: 600;\n color: #1e293b;\n font-size: 14px;\n }\n\n .test-info[_ngcontent-%COMP%] .suite[_ngcontent-%COMP%] {\n font-size: 12px;\n color: #64748b;\n }\n\n .cell.status[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 8px;\n align-items: flex-start;\n }\n\n .progress-bar[_ngcontent-%COMP%] {\n width: 100%;\n height: 4px;\n background: #e2e8f0;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, #3b82f6, #60a5fa);\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .cell.actions[_ngcontent-%COMP%] {\n gap: 8px;\n justify-content: flex-end;\n }\n\n \n\n .icon-btn[_ngcontent-%COMP%] {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n color: #64748b;\n cursor: pointer;\n border-radius: 10px;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 14px;\n }\n\n .icon-btn[_ngcontent-%COMP%]:hover {\n background: #6366f1;\n border-color: #6366f1;\n color: white;\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);\n }\n\n .icon-btn.danger[_ngcontent-%COMP%]:hover {\n background: #ef4444;\n border-color: #ef4444;\n box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\n }\n\n \n\n .loading-placeholder[_ngcontent-%COMP%] {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 40px;\n background: linear-gradient(180deg, #fafbff 0%, #f8fafc 100%);\n }\n\n \n\n .no-data[_ngcontent-%COMP%] {\n padding: 80px 40px;\n text-align: center;\n background: linear-gradient(180deg, #fafbff 0%, #f8fafc 100%);\n }\n\n .no-data[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 64px;\n color: #cbd5e1;\n margin-bottom: 20px;\n }\n\n .no-data[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n font-size: 16px;\n color: #64748b;\n margin-bottom: 24px;\n }\n\n .no-data[_ngcontent-%COMP%] .action-btn[_ngcontent-%COMP%] {\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n color: white;\n padding: 12px 24px;\n border-radius: 12px;\n font-weight: 600;\n box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);\n }\n\n .no-data[_ngcontent-%COMP%] .action-btn[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 24px rgba(99, 102, 241, 0.4);\n }\n\n \n\n @media (max-width: 1400px) {\n .kpi-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 1200px) {\n .filter-bar[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: stretch;\n }\n\n .filter-chips[_ngcontent-%COMP%] {\n justify-content: center;\n }\n\n .filter-controls[_ngcontent-%COMP%] {\n justify-content: center;\n }\n\n .list-header[_ngcontent-%COMP%], \n .execution-row[_ngcontent-%COMP%] {\n grid-template-columns: 1fr 100px 80px 100px;\n }\n\n .header-cell.cost[_ngcontent-%COMP%], \n .header-cell.timestamp[_ngcontent-%COMP%], \n .cell.cost[_ngcontent-%COMP%], \n .cell.timestamp[_ngcontent-%COMP%] {\n display: none;\n }\n }\n\n @media (max-width: 768px) {\n .kpi-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .search-input[_ngcontent-%COMP%] {\n min-width: 200px;\n }\n }"], changeDetection: 0 });
|
|
477
597
|
}
|
|
478
598
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestingExecutionComponent, [{
|
|
479
599
|
type: Component,
|
|
480
600
|
args: [{ selector: 'app-testing-execution', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
481
601
|
<div class="testing-execution" kendoDialogContainer>
|
|
602
|
+
<!-- Premium Header with Gradient -->
|
|
482
603
|
<div class="execution-header">
|
|
483
604
|
<div class="header-left">
|
|
484
|
-
<
|
|
605
|
+
<div class="header-icon">
|
|
485
606
|
<i class="fa-solid fa-play-circle"></i>
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
<
|
|
490
|
-
|
|
607
|
+
</div>
|
|
608
|
+
<div class="header-text">
|
|
609
|
+
<h2>Test Execution Monitor</h2>
|
|
610
|
+
<div class="header-meta">
|
|
611
|
+
<span class="last-updated">
|
|
612
|
+
<i class="fa-solid fa-clock"></i>
|
|
613
|
+
Updated {{ lastUpdated | date:'shortTime' }}
|
|
614
|
+
</span>
|
|
615
|
+
@if (hasRunningTests$ | async) {
|
|
616
|
+
<div class="live-indicator">
|
|
617
|
+
<span class="pulse"></span>
|
|
618
|
+
<span class="text">Live</span>
|
|
619
|
+
</div>
|
|
620
|
+
}
|
|
621
|
+
</div>
|
|
491
622
|
</div>
|
|
492
623
|
</div>
|
|
493
624
|
<div class="header-actions">
|
|
494
|
-
<button class="action-btn refresh" (click)="refresh()" [disabled]="isRefreshing">
|
|
495
|
-
<i class="fa-solid fa-
|
|
496
|
-
Refresh
|
|
625
|
+
<button class="action-btn refresh-btn" (click)="refresh()" [disabled]="isRefreshing">
|
|
626
|
+
<i class="fa-solid fa-sync-alt" [class.spinning]="isRefreshing"></i>
|
|
627
|
+
<span>Refresh</span>
|
|
497
628
|
</button>
|
|
498
|
-
<button class="action-btn primary" (click)="startNewTest()">
|
|
629
|
+
<button class="action-btn primary-btn" (click)="startNewTest()">
|
|
499
630
|
<i class="fa-solid fa-play"></i>
|
|
500
|
-
Run Test
|
|
631
|
+
<span>Run Test</span>
|
|
501
632
|
</button>
|
|
502
633
|
</div>
|
|
503
634
|
</div>
|
|
504
635
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
<
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
</
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
<
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
636
|
+
<!-- Smart Filter Bar -->
|
|
637
|
+
<div class="filter-bar">
|
|
638
|
+
<div class="filter-chips">
|
|
639
|
+
<button
|
|
640
|
+
class="filter-chip"
|
|
641
|
+
[class.active]="filters.status === 'all'"
|
|
642
|
+
(click)="filters.status = 'all'; onFilterChange()"
|
|
643
|
+
>
|
|
644
|
+
All
|
|
645
|
+
</button>
|
|
646
|
+
<button
|
|
647
|
+
class="filter-chip running"
|
|
648
|
+
[class.active]="filters.status === 'running'"
|
|
649
|
+
(click)="filters.status = 'running'; onFilterChange()"
|
|
650
|
+
>
|
|
651
|
+
<i class="fa-solid fa-spinner fa-spin"></i>
|
|
652
|
+
Running
|
|
653
|
+
</button>
|
|
654
|
+
<button
|
|
655
|
+
class="filter-chip passed"
|
|
656
|
+
[class.active]="filters.status === 'passed'"
|
|
657
|
+
(click)="filters.status = 'passed'; onFilterChange()"
|
|
658
|
+
>
|
|
659
|
+
<i class="fa-solid fa-check"></i>
|
|
660
|
+
Passed
|
|
661
|
+
</button>
|
|
662
|
+
<button
|
|
663
|
+
class="filter-chip failed"
|
|
664
|
+
[class.active]="filters.status === 'failed'"
|
|
665
|
+
(click)="filters.status = 'failed'; onFilterChange()"
|
|
666
|
+
>
|
|
667
|
+
<i class="fa-solid fa-times"></i>
|
|
668
|
+
Failed
|
|
669
|
+
</button>
|
|
524
670
|
</div>
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
<div class="
|
|
671
|
+
|
|
672
|
+
<div class="filter-controls">
|
|
673
|
+
<div class="time-select">
|
|
674
|
+
<select [(ngModel)]="filters.timeRange" (change)="onFilterChange()">
|
|
675
|
+
<option value="today">Today</option>
|
|
676
|
+
<option value="week">This Week</option>
|
|
677
|
+
<option value="month">This Month</option>
|
|
678
|
+
<option value="all">All Time</option>
|
|
679
|
+
</select>
|
|
680
|
+
</div>
|
|
681
|
+
|
|
682
|
+
<div class="search-input">
|
|
528
683
|
<i class="fa-solid fa-search"></i>
|
|
529
684
|
<input
|
|
530
685
|
type="text"
|
|
@@ -532,52 +687,72 @@ export class TestingExecutionComponent {
|
|
|
532
687
|
(input)="onFilterChange()"
|
|
533
688
|
placeholder="Search tests..."
|
|
534
689
|
/>
|
|
535
|
-
|
|
536
|
-
class="clear-btn"
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
<i class="fa-solid fa-times"></i>
|
|
541
|
-
</button>
|
|
690
|
+
@if (filters.searchText) {
|
|
691
|
+
<button class="clear-btn" (click)="clearSearch()">
|
|
692
|
+
<i class="fa-solid fa-times"></i>
|
|
693
|
+
</button>
|
|
694
|
+
}
|
|
542
695
|
</div>
|
|
543
696
|
</div>
|
|
544
697
|
</div>
|
|
545
698
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
699
|
+
<!-- KPI Cards (Actionable) -->
|
|
700
|
+
<div class="kpi-grid">
|
|
701
|
+
<div
|
|
702
|
+
class="kpi-card running clickable"
|
|
703
|
+
(click)="filterByStatus('running')"
|
|
704
|
+
>
|
|
705
|
+
<div class="kpi-icon">
|
|
549
706
|
<i class="fa-solid fa-spinner fa-spin"></i>
|
|
550
707
|
</div>
|
|
551
|
-
<div class="
|
|
552
|
-
<div class="
|
|
553
|
-
<div class="
|
|
708
|
+
<div class="kpi-content">
|
|
709
|
+
<div class="kpi-value">{{ (runningCount$ | async) ?? 0 }}</div>
|
|
710
|
+
<div class="kpi-label">Running Now</div>
|
|
711
|
+
</div>
|
|
712
|
+
<div class="kpi-arrow">
|
|
713
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
554
714
|
</div>
|
|
555
715
|
</div>
|
|
556
|
-
|
|
557
|
-
|
|
716
|
+
|
|
717
|
+
<div
|
|
718
|
+
class="kpi-card passed clickable"
|
|
719
|
+
(click)="filterByStatus('passed')"
|
|
720
|
+
>
|
|
721
|
+
<div class="kpi-icon">
|
|
558
722
|
<i class="fa-solid fa-check-circle"></i>
|
|
559
723
|
</div>
|
|
560
|
-
<div class="
|
|
561
|
-
<div class="
|
|
562
|
-
<div class="
|
|
724
|
+
<div class="kpi-content">
|
|
725
|
+
<div class="kpi-value">{{ (completedTodayCount$ | async) ?? 0 }}</div>
|
|
726
|
+
<div class="kpi-label">Passed {{ getTimeRangeLabel() }}</div>
|
|
727
|
+
</div>
|
|
728
|
+
<div class="kpi-arrow">
|
|
729
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
563
730
|
</div>
|
|
564
731
|
</div>
|
|
565
|
-
|
|
566
|
-
|
|
732
|
+
|
|
733
|
+
<div
|
|
734
|
+
class="kpi-card failed clickable"
|
|
735
|
+
(click)="filterByStatus('failed')"
|
|
736
|
+
>
|
|
737
|
+
<div class="kpi-icon">
|
|
567
738
|
<i class="fa-solid fa-exclamation-circle"></i>
|
|
568
739
|
</div>
|
|
569
|
-
<div class="
|
|
570
|
-
<div class="
|
|
571
|
-
<div class="
|
|
740
|
+
<div class="kpi-content">
|
|
741
|
+
<div class="kpi-value">{{ (failedTodayCount$ | async) ?? 0 }}</div>
|
|
742
|
+
<div class="kpi-label">Failed {{ getTimeRangeLabel() }}</div>
|
|
743
|
+
</div>
|
|
744
|
+
<div class="kpi-arrow">
|
|
745
|
+
<i class="fa-solid fa-chevron-right"></i>
|
|
572
746
|
</div>
|
|
573
747
|
</div>
|
|
574
|
-
|
|
575
|
-
|
|
748
|
+
|
|
749
|
+
<div class="kpi-card duration">
|
|
750
|
+
<div class="kpi-icon">
|
|
576
751
|
<i class="fa-solid fa-clock"></i>
|
|
577
752
|
</div>
|
|
578
|
-
<div class="
|
|
579
|
-
<div class="
|
|
580
|
-
<div class="
|
|
753
|
+
<div class="kpi-content">
|
|
754
|
+
<div class="kpi-value">{{ formatDuration((avgDurationToday$ | async) ?? 0) }}</div>
|
|
755
|
+
<div class="kpi-label">Avg Duration</div>
|
|
581
756
|
</div>
|
|
582
757
|
</div>
|
|
583
758
|
</div>
|
|
@@ -594,7 +769,11 @@ export class TestingExecutionComponent {
|
|
|
594
769
|
<div class="header-cell actions">Actions</div>
|
|
595
770
|
</div>
|
|
596
771
|
|
|
597
|
-
@if (
|
|
772
|
+
@if (isLoading) {
|
|
773
|
+
<div class="loading-placeholder">
|
|
774
|
+
<mj-loading text="Loading test executions..."></mj-loading>
|
|
775
|
+
</div>
|
|
776
|
+
} @else if ((filteredExecutions$ | async)?.length === 0) {
|
|
598
777
|
<div class="no-data">
|
|
599
778
|
<i class="fa-solid fa-inbox"></i>
|
|
600
779
|
<p>No test executions found</p>
|
|
@@ -656,11 +835,11 @@ export class TestingExecutionComponent {
|
|
|
656
835
|
</div>
|
|
657
836
|
</div>
|
|
658
837
|
</div>
|
|
659
|
-
`, 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 "] }]
|
|
660
|
-
}], () => [{ type: i1.TestingInstrumentationService }, { type: i2.DialogService }, { type: i0.ChangeDetectorRef }], { initialState: [{
|
|
838
|
+
`, styles: ["\n /* ============================================\n Testing Execution - Premium Design System\n ============================================ */\n\n .testing-execution {\n padding: 24px;\n height: 100%;\n overflow-y: auto;\n background: linear-gradient(135deg, #f8fafc 0%, #eef2f7 100%);\n }\n\n /* Premium Header */\n .execution-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-bottom: 20px;\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n padding: 24px 28px;\n border-radius: 16px;\n box-shadow: 0 8px 32px rgba(99, 102, 241, 0.25);\n }\n\n .header-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .header-icon {\n width: 48px;\n height: 48px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 22px;\n color: white;\n }\n\n .header-text h2 {\n margin: 0 0 4px 0;\n font-size: 20px;\n font-weight: 600;\n color: white;\n }\n\n .header-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n }\n\n .last-updated {\n font-size: 12px;\n color: rgba(255, 255, 255, 0.8);\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .last-updated i {\n font-size: 11px;\n }\n\n .live-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n background: rgba(255, 255, 255, 0.2);\n border-radius: 12px;\n font-size: 11px;\n font-weight: 600;\n color: white;\n }\n\n .pulse {\n width: 8px;\n height: 8px;\n background: #22c55e;\n border-radius: 50%;\n animation: pulse 2s infinite;\n box-shadow: 0 0 8px #22c55e;\n }\n\n @keyframes pulse {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(1.3); }\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: 8px;\n padding: 10px 18px;\n border: none;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .refresh-btn {\n background: rgba(255, 255, 255, 0.15);\n color: white;\n border: 1px solid rgba(255, 255, 255, 0.25);\n }\n\n .refresh-btn:hover:not(:disabled) {\n background: rgba(255, 255, 255, 0.25);\n transform: translateY(-1px);\n }\n\n .primary-btn {\n background: white;\n color: #6366f1;\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);\n }\n\n .primary-btn:hover {\n background: #f8f9ff;\n transform: translateY(-2px);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.2);\n }\n\n .action-btn:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n transform: none !important;\n }\n\n .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 /* Smart Filter Bar */\n .filter-bar {\n display: flex;\n justify-content: space-between;\n align-items: center;\n gap: 16px;\n margin-bottom: 20px;\n padding: 16px 20px;\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n }\n\n .filter-chips {\n display: flex;\n gap: 8px;\n }\n\n .filter-chip {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #f1f5f9;\n border: 2px solid transparent;\n border-radius: 20px;\n font-size: 12px;\n font-weight: 600;\n color: #64748b;\n cursor: pointer;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .filter-chip:hover {\n background: #e2e8f0;\n color: #475569;\n }\n\n .filter-chip.active {\n background: #6366f1;\n color: white;\n border-color: #6366f1;\n }\n\n .filter-chip.running.active {\n background: #3b82f6;\n border-color: #3b82f6;\n }\n\n .filter-chip.passed.active {\n background: #22c55e;\n border-color: #22c55e;\n }\n\n .filter-chip.failed.active {\n background: #ef4444;\n border-color: #ef4444;\n }\n\n .filter-chip i {\n font-size: 11px;\n }\n\n .filter-controls {\n display: flex;\n gap: 12px;\n align-items: center;\n }\n\n .time-select select {\n padding: 10px 14px;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n font-size: 13px;\n font-weight: 500;\n color: #475569;\n background: white;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .time-select select:focus {\n outline: none;\n border-color: #6366f1;\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .search-input {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 16px;\n background: #f8fafc;\n border: 2px solid #e2e8f0;\n border-radius: 10px;\n min-width: 250px;\n transition: all 0.2s ease;\n }\n\n .search-input:focus-within {\n border-color: #6366f1;\n background: white;\n box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);\n }\n\n .search-input i {\n color: #94a3b8;\n font-size: 14px;\n }\n\n .search-input input {\n flex: 1;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: #334155;\n }\n\n .search-input input::placeholder {\n color: #94a3b8;\n }\n\n .clear-btn {\n padding: 4px 8px;\n border: none;\n background: transparent;\n color: #94a3b8;\n cursor: pointer;\n border-radius: 4px;\n transition: all 0.2s ease;\n }\n\n .clear-btn:hover {\n background: #e2e8f0;\n color: #64748b;\n }\n\n /* KPI Cards Grid */\n .kpi-grid {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n margin-bottom: 20px;\n }\n\n .kpi-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 20px;\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n }\n\n .kpi-card::before {\n content: '';\n position: absolute;\n top: 0;\n left: 0;\n width: 4px;\n height: 100%;\n border-radius: 4px 0 0 4px;\n }\n\n .kpi-card.clickable {\n cursor: pointer;\n }\n\n .kpi-card.clickable:hover {\n transform: translateY(-3px);\n box-shadow: 0 8px 24px rgba(99, 102, 241, 0.15);\n }\n\n .kpi-card.running::before { background: linear-gradient(180deg, #3b82f6, #60a5fa); }\n .kpi-card.passed::before { background: linear-gradient(180deg, #22c55e, #4ade80); }\n .kpi-card.failed::before { background: linear-gradient(180deg, #ef4444, #f87171); }\n .kpi-card.duration::before { background: linear-gradient(180deg, #8b5cf6, #a78bfa); }\n\n .kpi-icon {\n width: 48px;\n height: 48px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 20px;\n }\n\n .kpi-card.running .kpi-icon {\n background: rgba(59, 130, 246, 0.1);\n color: #3b82f6;\n }\n\n .kpi-card.passed .kpi-icon {\n background: rgba(34, 197, 94, 0.1);\n color: #22c55e;\n }\n\n .kpi-card.failed .kpi-icon {\n background: rgba(239, 68, 68, 0.1);\n color: #ef4444;\n }\n\n .kpi-card.duration .kpi-icon {\n background: rgba(139, 92, 246, 0.1);\n color: #8b5cf6;\n }\n\n .kpi-content {\n flex: 1;\n }\n\n .kpi-value {\n font-size: 26px;\n font-weight: 700;\n color: #1e293b;\n line-height: 1;\n margin-bottom: 4px;\n }\n\n .kpi-label {\n font-size: 12px;\n font-weight: 500;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .kpi-arrow {\n color: #cbd5e1;\n font-size: 14px;\n opacity: 0;\n transition: all 0.3s ease;\n }\n\n .kpi-card.clickable:hover .kpi-arrow {\n opacity: 1;\n color: #6366f1;\n transform: translateX(4px);\n }\n\n /* Execution List Container */\n .execution-content {\n background: white;\n border-radius: 14px;\n box-shadow: 0 2px 12px rgba(99, 102, 241, 0.06);\n overflow: hidden;\n }\n\n .execution-list {\n display: flex;\n flex-direction: column;\n }\n\n /* List Header */\n .list-header {\n display: grid;\n grid-template-columns: 2fr 120px 100px 100px 140px 100px;\n gap: 20px;\n padding: 16px 24px;\n background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);\n border-bottom: 1px solid #e2e8f0;\n font-size: 11px;\n font-weight: 700;\n color: #64748b;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .header-cell {\n display: flex;\n align-items: center;\n }\n\n /* Execution Row */\n .execution-row {\n display: grid;\n grid-template-columns: 2fr 120px 100px 100px 140px 100px;\n gap: 20px;\n padding: 18px 24px;\n border-bottom: 1px solid #f1f5f9;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .execution-row:last-child {\n border-bottom: none;\n }\n\n .execution-row:hover {\n background: linear-gradient(90deg, rgba(99, 102, 241, 0.03) 0%, rgba(139, 92, 246, 0.03) 100%);\n }\n\n .execution-row.running {\n background: linear-gradient(90deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.04) 100%);\n border-left: 3px solid #3b82f6;\n }\n\n .execution-row.running:hover {\n background: linear-gradient(90deg, rgba(59, 130, 246, 0.12) 0%, rgba(59, 130, 246, 0.06) 100%);\n }\n\n /* Cells */\n .cell {\n display: flex;\n align-items: center;\n font-size: 13px;\n color: #334155;\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: 600;\n color: #1e293b;\n font-size: 14px;\n }\n\n .test-info .suite {\n font-size: 12px;\n color: #64748b;\n }\n\n .cell.status {\n flex-direction: column;\n gap: 8px;\n align-items: flex-start;\n }\n\n .progress-bar {\n width: 100%;\n height: 4px;\n background: #e2e8f0;\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill {\n height: 100%;\n background: linear-gradient(90deg, #3b82f6, #60a5fa);\n border-radius: 2px;\n transition: width 0.3s ease;\n }\n\n .cell.actions {\n gap: 8px;\n justify-content: flex-end;\n }\n\n /* Action Buttons */\n .icon-btn {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: #f8fafc;\n border: 1px solid #e2e8f0;\n color: #64748b;\n cursor: pointer;\n border-radius: 10px;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n font-size: 14px;\n }\n\n .icon-btn:hover {\n background: #6366f1;\n border-color: #6366f1;\n color: white;\n transform: translateY(-2px);\n box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);\n }\n\n .icon-btn.danger:hover {\n background: #ef4444;\n border-color: #ef4444;\n box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);\n }\n\n /* Loading State */\n .loading-placeholder {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 80px 40px;\n background: linear-gradient(180deg, #fafbff 0%, #f8fafc 100%);\n }\n\n /* Empty State */\n .no-data {\n padding: 80px 40px;\n text-align: center;\n background: linear-gradient(180deg, #fafbff 0%, #f8fafc 100%);\n }\n\n .no-data i {\n font-size: 64px;\n color: #cbd5e1;\n margin-bottom: 20px;\n }\n\n .no-data p {\n font-size: 16px;\n color: #64748b;\n margin-bottom: 24px;\n }\n\n .no-data .action-btn {\n background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);\n color: white;\n padding: 12px 24px;\n border-radius: 12px;\n font-weight: 600;\n box-shadow: 0 4px 16px rgba(99, 102, 241, 0.3);\n }\n\n .no-data .action-btn:hover {\n transform: translateY(-2px);\n box-shadow: 0 6px 24px rgba(99, 102, 241, 0.4);\n }\n\n /* Responsive */\n @media (max-width: 1400px) {\n .kpi-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n }\n\n @media (max-width: 1200px) {\n .filter-bar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .filter-chips {\n justify-content: center;\n }\n\n .filter-controls {\n justify-content: center;\n }\n\n .list-header,\n .execution-row {\n grid-template-columns: 1fr 100px 80px 100px;\n }\n\n .header-cell.cost,\n .header-cell.timestamp,\n .cell.cost,\n .cell.timestamp {\n display: none;\n }\n }\n\n @media (max-width: 768px) {\n .kpi-grid {\n grid-template-columns: 1fr;\n }\n\n .search-input {\n min-width: 200px;\n }\n }\n "] }]
|
|
839
|
+
}], () => [{ type: i1.TestingInstrumentationService }, { type: i2.DialogService }, { type: i0.ChangeDetectorRef }, { type: i0.ViewContainerRef }], { initialState: [{
|
|
661
840
|
type: Input
|
|
662
841
|
}], stateChange: [{
|
|
663
842
|
type: Output
|
|
664
843
|
}] }); })();
|
|
665
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingExecutionComponent, { className: "TestingExecutionComponent", filePath: "src/Testing/components/testing-execution.component.ts", lineNumber:
|
|
844
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestingExecutionComponent, { className: "TestingExecutionComponent", filePath: "src/Testing/components/testing-execution.component.ts", lineNumber: 897 }); })();
|
|
666
845
|
//# sourceMappingURL=testing-execution.component.js.map
|