@memberjunction/ng-core-entity-forms 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/lib/custom/Tests/entity-link-pill.component.d.ts +44 -0
- package/dist/lib/custom/Tests/entity-link-pill.component.d.ts.map +1 -0
- package/dist/lib/custom/Tests/entity-link-pill.component.js +124 -0
- package/dist/lib/custom/Tests/entity-link-pill.component.js.map +1 -0
- package/dist/lib/custom/Tests/test-form.component.d.ts +96 -9
- package/dist/lib/custom/Tests/test-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-form.component.js +1529 -277
- package/dist/lib/custom/Tests/test-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-run-form.component.d.ts +50 -9
- package/dist/lib/custom/Tests/test-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-run-form.component.js +1079 -426
- package/dist/lib/custom/Tests/test-run-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-suite-form.component.d.ts +228 -5
- package/dist/lib/custom/Tests/test-suite-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-suite-form.component.js +3309 -201
- package/dist/lib/custom/Tests/test-suite-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts +88 -3
- package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-suite-run-form.component.js +1976 -262
- package/dist/lib/custom/Tests/test-suite-run-form.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +9 -2
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +275 -244
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
- package/dist/lib/custom/custom-forms.module.d.ts +27 -26
- package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
- package/dist/lib/custom/custom-forms.module.js +9 -3
- package/dist/lib/custom/custom-forms.module.js.map +1 -1
- package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js +154 -122
- package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.js +75 -0
- package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.js +121 -0
- package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js +77 -41
- package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIModality/aimodality.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIModality/aimodality.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIModality/aimodality.form.component.js +167 -0
- package/dist/lib/generated/Entities/AIModality/aimodality.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIModel/aimodel.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js +160 -102
- package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.js +73 -0
- package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.js +89 -0
- package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js +27 -13
- package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.js +39 -21
- package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.d.ts +11 -0
- package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.js +95 -0
- package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/Entity/entity.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/Entity/entity.form.component.js +61 -43
- package/dist/lib/generated/Entities/Entity/entity.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/File/file.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/File/file.form.component.js +22 -4
- package/dist/lib/generated/Entities/File/file.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.js +40 -4
- package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/Test/test.form.component.js +17 -15
- package/dist/lib/generated/Entities/Test/test.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/TestRun/testrun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/TestRun/testrun.form.component.js +55 -43
- package/dist/lib/generated/Entities/TestRun/testrun.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.js +9 -15
- package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.js +39 -19
- package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.js +40 -16
- package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.js.map +1 -1
- package/dist/lib/generated/generated-forms.module.d.ts +145 -134
- package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
- package/dist/lib/generated/generated-forms.module.js +168 -87
- package/dist/lib/generated/generated-forms.module.js.map +1 -1
- package/package.json +28 -27
|
@@ -4,13 +4,17 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
-
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
7
|
+
import { Component, ChangeDetectionStrategy, HostListener } from '@angular/core';
|
|
8
8
|
import { Subject } from 'rxjs';
|
|
9
|
-
import {
|
|
9
|
+
import { takeUntil } from 'rxjs/operators';
|
|
10
|
+
import { CompositeKey, Metadata, RunView } from '@memberjunction/core';
|
|
11
|
+
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
10
12
|
import { BaseFormComponent } from '@memberjunction/ng-base-forms';
|
|
11
13
|
import { RegisterClass } from '@memberjunction/global';
|
|
12
14
|
import { SharedService } from '@memberjunction/ng-shared';
|
|
13
15
|
import { TestFormComponent } from '../../generated/Entities/Test/test.form.component';
|
|
16
|
+
import { TagsHelper } from '@memberjunction/ng-testing';
|
|
17
|
+
import { createCopyOnlyToolbar } from '@memberjunction/ng-code-editor';
|
|
14
18
|
import * as i0 from "@angular/core";
|
|
15
19
|
import * as i1 from "@memberjunction/ng-shared";
|
|
16
20
|
import * as i2 from "@angular/router";
|
|
@@ -19,8 +23,23 @@ import * as i4 from "@angular/common";
|
|
|
19
23
|
import * as i5 from "@angular/forms";
|
|
20
24
|
import * as i6 from "@progress/kendo-angular-dialog";
|
|
21
25
|
import * as i7 from "@progress/kendo-angular-buttons";
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
import * as i8 from "@memberjunction/ng-code-editor";
|
|
27
|
+
import * as i9 from "@memberjunction/ng-shared-generic";
|
|
28
|
+
import * as i10 from "./entity-link-pill.component";
|
|
29
|
+
const _c0 = () => [1, 2, 3, 4, 5];
|
|
30
|
+
const _c1 = () => [1, 2, 3];
|
|
31
|
+
function TestFormComponentExtended_span_13_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵelementStart(0, "span", 35);
|
|
33
|
+
i0.ɵɵelement(1, "i", 36);
|
|
34
|
+
i0.ɵɵtext(2);
|
|
35
|
+
i0.ɵɵelementEnd();
|
|
36
|
+
} if (rf & 2) {
|
|
37
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
38
|
+
i0.ɵɵadvance(2);
|
|
39
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.record.Type, " ");
|
|
40
|
+
} }
|
|
41
|
+
function TestFormComponentExtended_div_22_Template(rf, ctx) { if (rf & 1) {
|
|
42
|
+
i0.ɵɵelementStart(0, "div", 37)(1, "p");
|
|
24
43
|
i0.ɵɵtext(2);
|
|
25
44
|
i0.ɵɵelementEnd()();
|
|
26
45
|
} if (rf & 2) {
|
|
@@ -28,30 +47,33 @@ function TestFormComponentExtended_div_20_Template(rf, ctx) { if (rf & 1) {
|
|
|
28
47
|
i0.ɵɵadvance(2);
|
|
29
48
|
i0.ɵɵtextInterpolate(ctx_r0.record.Description);
|
|
30
49
|
} }
|
|
31
|
-
function
|
|
32
|
-
i0.ɵɵelementStart(0, "div",
|
|
50
|
+
function TestFormComponentExtended_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
51
|
+
i0.ɵɵelementStart(0, "div", 38)(1, "div", 39)(2, "div", 40);
|
|
33
52
|
i0.ɵɵtext(3, "Total Runs");
|
|
34
53
|
i0.ɵɵelementEnd();
|
|
35
|
-
i0.ɵɵelementStart(4, "div",
|
|
54
|
+
i0.ɵɵelementStart(4, "div", 41);
|
|
36
55
|
i0.ɵɵtext(5);
|
|
37
56
|
i0.ɵɵelementEnd()();
|
|
38
|
-
i0.ɵɵelementStart(6, "div",
|
|
57
|
+
i0.ɵɵelementStart(6, "div", 39)(7, "div", 40);
|
|
39
58
|
i0.ɵɵtext(8, "Pass Rate");
|
|
40
59
|
i0.ɵɵelementEnd();
|
|
41
|
-
i0.ɵɵelementStart(9, "div",
|
|
60
|
+
i0.ɵɵelementStart(9, "div", 41);
|
|
42
61
|
i0.ɵɵtext(10);
|
|
62
|
+
i0.ɵɵelementEnd();
|
|
63
|
+
i0.ɵɵelementStart(11, "div", 42);
|
|
64
|
+
i0.ɵɵelement(12, "div", 43);
|
|
43
65
|
i0.ɵɵelementEnd()();
|
|
44
|
-
i0.ɵɵelementStart(
|
|
45
|
-
i0.ɵɵtext(
|
|
66
|
+
i0.ɵɵelementStart(13, "div", 39)(14, "div", 40);
|
|
67
|
+
i0.ɵɵtext(15, "Avg Cost");
|
|
46
68
|
i0.ɵɵelementEnd();
|
|
47
|
-
i0.ɵɵelementStart(
|
|
48
|
-
i0.ɵɵtext(
|
|
69
|
+
i0.ɵɵelementStart(16, "div", 41);
|
|
70
|
+
i0.ɵɵtext(17);
|
|
49
71
|
i0.ɵɵelementEnd()();
|
|
50
|
-
i0.ɵɵelementStart(
|
|
51
|
-
i0.ɵɵtext(
|
|
72
|
+
i0.ɵɵelementStart(18, "div", 39)(19, "div", 40);
|
|
73
|
+
i0.ɵɵtext(20, "Avg Duration");
|
|
52
74
|
i0.ɵɵelementEnd();
|
|
53
|
-
i0.ɵɵelementStart(
|
|
54
|
-
i0.ɵɵtext(
|
|
75
|
+
i0.ɵɵelementStart(21, "div", 41);
|
|
76
|
+
i0.ɵɵtext(22);
|
|
55
77
|
i0.ɵɵelementEnd()()();
|
|
56
78
|
} if (rf & 2) {
|
|
57
79
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -59,13 +81,15 @@ function TestFormComponentExtended_div_21_Template(rf, ctx) { if (rf & 1) {
|
|
|
59
81
|
i0.ɵɵtextInterpolate(ctx_r0.testRuns.length);
|
|
60
82
|
i0.ɵɵadvance(5);
|
|
61
83
|
i0.ɵɵtextInterpolate1("", ctx_r0.getPassRate().toFixed(1), "%");
|
|
84
|
+
i0.ɵɵadvance(2);
|
|
85
|
+
i0.ɵɵstyleProp("width", ctx_r0.getPassRate(), "%");
|
|
62
86
|
i0.ɵɵadvance(5);
|
|
63
87
|
i0.ɵɵtextInterpolate(ctx_r0.formatCost(ctx_r0.getAverageCost()));
|
|
64
88
|
i0.ɵɵadvance(5);
|
|
65
89
|
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(ctx_r0.getAverageDuration()));
|
|
66
90
|
} }
|
|
67
|
-
function
|
|
68
|
-
i0.ɵɵelementStart(0, "span",
|
|
91
|
+
function TestFormComponentExtended_span_38_Template(rf, ctx) { if (rf & 1) {
|
|
92
|
+
i0.ɵɵelementStart(0, "span", 44);
|
|
69
93
|
i0.ɵɵtext(1);
|
|
70
94
|
i0.ɵɵelementEnd();
|
|
71
95
|
} if (rf & 2) {
|
|
@@ -73,8 +97,8 @@ function TestFormComponentExtended_span_36_Template(rf, ctx) { if (rf & 1) {
|
|
|
73
97
|
i0.ɵɵadvance();
|
|
74
98
|
i0.ɵɵtextInterpolate(ctx_r0.testRuns.length);
|
|
75
99
|
} }
|
|
76
|
-
function
|
|
77
|
-
i0.ɵɵelementStart(0, "span",
|
|
100
|
+
function TestFormComponentExtended_span_43_Template(rf, ctx) { if (rf & 1) {
|
|
101
|
+
i0.ɵɵelementStart(0, "span", 44);
|
|
78
102
|
i0.ɵɵtext(1);
|
|
79
103
|
i0.ɵɵelementEnd();
|
|
80
104
|
} if (rf & 2) {
|
|
@@ -82,90 +106,110 @@ function TestFormComponentExtended_span_41_Template(rf, ctx) { if (rf & 1) {
|
|
|
82
106
|
i0.ɵɵadvance();
|
|
83
107
|
i0.ɵɵtextInterpolate(ctx_r0.suiteTests.length);
|
|
84
108
|
} }
|
|
85
|
-
function
|
|
109
|
+
function TestFormComponentExtended_div_49_Template(rf, ctx) { if (rf & 1) {
|
|
86
110
|
const _r2 = i0.ɵɵgetCurrentView();
|
|
87
|
-
i0.ɵɵelementStart(0, "div",
|
|
88
|
-
i0.ɵɵ
|
|
111
|
+
i0.ɵɵelementStart(0, "div", 45)(1, "div", 46)(2, "h3");
|
|
112
|
+
i0.ɵɵelement(3, "i", 47);
|
|
113
|
+
i0.ɵɵtext(4, " Test Information");
|
|
89
114
|
i0.ɵɵelementEnd();
|
|
90
|
-
i0.ɵɵelementStart(
|
|
91
|
-
i0.ɵɵtext(
|
|
115
|
+
i0.ɵɵelementStart(5, "div", 48)(6, "div", 49)(7, "div", 50);
|
|
116
|
+
i0.ɵɵtext(8, "Name");
|
|
92
117
|
i0.ɵɵelementEnd();
|
|
93
|
-
i0.ɵɵelementStart(
|
|
94
|
-
i0.ɵɵtext(
|
|
118
|
+
i0.ɵɵelementStart(9, "div", 51);
|
|
119
|
+
i0.ɵɵtext(10);
|
|
95
120
|
i0.ɵɵelementEnd()();
|
|
96
|
-
i0.ɵɵelementStart(
|
|
97
|
-
i0.ɵɵtext(
|
|
121
|
+
i0.ɵɵelementStart(11, "div", 49)(12, "div", 50);
|
|
122
|
+
i0.ɵɵtext(13, "Type");
|
|
98
123
|
i0.ɵɵelementEnd();
|
|
99
|
-
i0.ɵɵelementStart(
|
|
100
|
-
i0.ɵɵtext(
|
|
124
|
+
i0.ɵɵelementStart(14, "div", 51);
|
|
125
|
+
i0.ɵɵtext(15);
|
|
101
126
|
i0.ɵɵelementEnd()();
|
|
102
|
-
i0.ɵɵelementStart(
|
|
103
|
-
i0.ɵɵtext(
|
|
127
|
+
i0.ɵɵelementStart(16, "div", 49)(17, "div", 50);
|
|
128
|
+
i0.ɵɵtext(18, "Status");
|
|
104
129
|
i0.ɵɵelementEnd();
|
|
105
|
-
i0.ɵɵelementStart(
|
|
106
|
-
i0.ɵɵtext(
|
|
130
|
+
i0.ɵɵelementStart(19, "div", 51)(20, "span", 52);
|
|
131
|
+
i0.ɵɵtext(21);
|
|
132
|
+
i0.ɵɵelementEnd()()();
|
|
133
|
+
i0.ɵɵelementStart(22, "div", 49)(23, "div", 50);
|
|
134
|
+
i0.ɵɵtext(24, "Priority");
|
|
135
|
+
i0.ɵɵelementEnd();
|
|
136
|
+
i0.ɵɵelementStart(25, "div", 51);
|
|
137
|
+
i0.ɵɵtext(26);
|
|
107
138
|
i0.ɵɵelementEnd()();
|
|
108
|
-
i0.ɵɵelementStart(
|
|
109
|
-
i0.ɵɵtext(
|
|
139
|
+
i0.ɵɵelementStart(27, "div", 49)(28, "div", 50);
|
|
140
|
+
i0.ɵɵtext(29, "Estimated Duration");
|
|
110
141
|
i0.ɵɵelementEnd();
|
|
111
|
-
i0.ɵɵelementStart(
|
|
112
|
-
i0.ɵɵtext(
|
|
142
|
+
i0.ɵɵelementStart(30, "div", 51);
|
|
143
|
+
i0.ɵɵtext(31);
|
|
144
|
+
i0.ɵɵelementEnd()();
|
|
145
|
+
i0.ɵɵelementStart(32, "div", 49)(33, "div", 50);
|
|
146
|
+
i0.ɵɵtext(34, "Estimated Cost");
|
|
147
|
+
i0.ɵɵelementEnd();
|
|
148
|
+
i0.ɵɵelementStart(35, "div", 51);
|
|
149
|
+
i0.ɵɵtext(36);
|
|
113
150
|
i0.ɵɵelementEnd()();
|
|
114
|
-
i0.ɵɵelementStart(
|
|
115
|
-
i0.ɵɵtext(
|
|
151
|
+
i0.ɵɵelementStart(37, "div", 49)(38, "div", 50);
|
|
152
|
+
i0.ɵɵtext(39, "Repeat Count");
|
|
116
153
|
i0.ɵɵelementEnd();
|
|
117
|
-
i0.ɵɵelementStart(
|
|
118
|
-
i0.ɵɵtext(
|
|
154
|
+
i0.ɵɵelementStart(40, "div", 51);
|
|
155
|
+
i0.ɵɵtext(41);
|
|
119
156
|
i0.ɵɵelementEnd()();
|
|
120
|
-
i0.ɵɵelementStart(
|
|
121
|
-
i0.ɵɵtext(
|
|
157
|
+
i0.ɵɵelementStart(42, "div", 49)(43, "div", 50);
|
|
158
|
+
i0.ɵɵtext(44, "Max Execution Time");
|
|
122
159
|
i0.ɵɵelementEnd();
|
|
123
|
-
i0.ɵɵelementStart(
|
|
124
|
-
i0.ɵɵtext(
|
|
160
|
+
i0.ɵɵelementStart(45, "div", 51);
|
|
161
|
+
i0.ɵɵtext(46);
|
|
125
162
|
i0.ɵɵelementEnd()();
|
|
126
|
-
i0.ɵɵelementStart(
|
|
127
|
-
i0.ɵɵtext(
|
|
163
|
+
i0.ɵɵelementStart(47, "div", 49)(48, "div", 50);
|
|
164
|
+
i0.ɵɵtext(49, "Created");
|
|
128
165
|
i0.ɵɵelementEnd();
|
|
129
|
-
i0.ɵɵelementStart(
|
|
130
|
-
i0.ɵɵtext(
|
|
166
|
+
i0.ɵɵelementStart(50, "div", 51);
|
|
167
|
+
i0.ɵɵtext(51);
|
|
168
|
+
i0.ɵɵpipe(52, "date");
|
|
131
169
|
i0.ɵɵelementEnd()();
|
|
132
|
-
i0.ɵɵelementStart(
|
|
133
|
-
i0.ɵɵtext(
|
|
170
|
+
i0.ɵɵelementStart(53, "div", 49)(54, "div", 50);
|
|
171
|
+
i0.ɵɵtext(55, "Updated");
|
|
134
172
|
i0.ɵɵelementEnd();
|
|
135
|
-
i0.ɵɵelementStart(
|
|
136
|
-
i0.ɵɵtext(
|
|
137
|
-
i0.ɵɵpipe(
|
|
173
|
+
i0.ɵɵelementStart(56, "div", 51);
|
|
174
|
+
i0.ɵɵtext(57);
|
|
175
|
+
i0.ɵɵpipe(58, "date");
|
|
138
176
|
i0.ɵɵelementEnd()()()();
|
|
139
|
-
i0.ɵɵelementStart(
|
|
140
|
-
i0.ɵɵ
|
|
177
|
+
i0.ɵɵelementStart(59, "div", 53)(60, "h3");
|
|
178
|
+
i0.ɵɵelement(61, "i", 54);
|
|
179
|
+
i0.ɵɵtext(62, " Test Definition");
|
|
141
180
|
i0.ɵɵelementEnd();
|
|
142
|
-
i0.ɵɵelementStart(
|
|
143
|
-
i0.ɵɵlistener("click", function
|
|
144
|
-
i0.ɵɵ
|
|
181
|
+
i0.ɵɵelementStart(63, "div", 55)(64, "button", 56);
|
|
182
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_49_Template_button_click_64_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("input")); });
|
|
183
|
+
i0.ɵɵelement(65, "i", 57);
|
|
184
|
+
i0.ɵɵtext(66, " Input Definition ");
|
|
145
185
|
i0.ɵɵelementEnd();
|
|
146
|
-
i0.ɵɵelementStart(
|
|
147
|
-
i0.ɵɵlistener("click", function
|
|
148
|
-
i0.ɵɵ
|
|
186
|
+
i0.ɵɵelementStart(67, "button", 56);
|
|
187
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_49_Template_button_click_67_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("expected")); });
|
|
188
|
+
i0.ɵɵelement(68, "i", 58);
|
|
189
|
+
i0.ɵɵtext(69, " Expected Outcomes ");
|
|
149
190
|
i0.ɵɵelementEnd();
|
|
150
|
-
i0.ɵɵelementStart(
|
|
151
|
-
i0.ɵɵlistener("click", function
|
|
152
|
-
i0.ɵɵ
|
|
191
|
+
i0.ɵɵelementStart(70, "button", 56);
|
|
192
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_49_Template_button_click_70_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("config")); });
|
|
193
|
+
i0.ɵɵelement(71, "i", 59);
|
|
194
|
+
i0.ɵɵtext(72, " Configuration ");
|
|
153
195
|
i0.ɵɵelementEnd();
|
|
154
|
-
i0.ɵɵelementStart(
|
|
155
|
-
i0.ɵɵlistener("click", function
|
|
156
|
-
i0.ɵɵ
|
|
196
|
+
i0.ɵɵelementStart(73, "button", 56);
|
|
197
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_49_Template_button_click_73_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("tags")); });
|
|
198
|
+
i0.ɵɵelement(74, "i", 60);
|
|
199
|
+
i0.ɵɵtext(75, " Tags ");
|
|
157
200
|
i0.ɵɵelementEnd()();
|
|
158
|
-
i0.ɵɵelementStart(
|
|
159
|
-
i0.ɵɵ
|
|
160
|
-
i0.ɵɵ
|
|
161
|
-
i0.ɵɵelementEnd()()()();
|
|
201
|
+
i0.ɵɵelementStart(76, "div", 61);
|
|
202
|
+
i0.ɵɵelement(77, "mj-code-editor", 62);
|
|
203
|
+
i0.ɵɵelementEnd()()();
|
|
162
204
|
} if (rf & 2) {
|
|
163
205
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
164
|
-
i0.ɵɵadvance(
|
|
206
|
+
i0.ɵɵadvance(10);
|
|
165
207
|
i0.ɵɵtextInterpolate(ctx_r0.record.Name);
|
|
166
208
|
i0.ɵɵadvance(5);
|
|
167
|
-
i0.ɵɵtextInterpolate(ctx_r0.record.Type);
|
|
209
|
+
i0.ɵɵtextInterpolate(ctx_r0.record.Type || "N/A");
|
|
168
210
|
i0.ɵɵadvance(5);
|
|
211
|
+
i0.ɵɵproperty("ngClass", ctx_r0.getStatusClass());
|
|
212
|
+
i0.ɵɵadvance();
|
|
169
213
|
i0.ɵɵtextInterpolate(ctx_r0.record.Status);
|
|
170
214
|
i0.ɵɵadvance(5);
|
|
171
215
|
i0.ɵɵtextInterpolate(ctx_r0.record.Priority || "N/A");
|
|
@@ -176,74 +220,92 @@ function TestFormComponentExtended_div_43_Template(rf, ctx) { if (rf & 1) {
|
|
|
176
220
|
i0.ɵɵadvance(5);
|
|
177
221
|
i0.ɵɵtextInterpolate(ctx_r0.record.RepeatCount || 1);
|
|
178
222
|
i0.ɵɵadvance(5);
|
|
179
|
-
i0.ɵɵtextInterpolate(
|
|
223
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatTimeout(ctx_r0.record.MaxExecutionTimeMS));
|
|
224
|
+
i0.ɵɵadvance(5);
|
|
225
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(52, 23, ctx_r0.record.__mj_CreatedAt, "medium"));
|
|
180
226
|
i0.ɵɵadvance(6);
|
|
227
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(58, 26, ctx_r0.record.__mj_UpdatedAt, "medium"));
|
|
228
|
+
i0.ɵɵadvance(7);
|
|
181
229
|
i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "input");
|
|
182
|
-
i0.ɵɵadvance(
|
|
230
|
+
i0.ɵɵadvance(3);
|
|
183
231
|
i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "expected");
|
|
184
|
-
i0.ɵɵadvance(
|
|
232
|
+
i0.ɵɵadvance(3);
|
|
185
233
|
i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "config");
|
|
186
|
-
i0.ɵɵadvance(
|
|
234
|
+
i0.ɵɵadvance(3);
|
|
187
235
|
i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "tags");
|
|
188
236
|
i0.ɵɵadvance(4);
|
|
189
|
-
i0.ɵɵ
|
|
237
|
+
i0.ɵɵproperty("value", ctx_r0.getJsonData())("readonly", true)("toolbar", ctx_r0.jsonToolbar)("lineWrapping", true);
|
|
190
238
|
} }
|
|
191
|
-
function
|
|
239
|
+
function TestFormComponentExtended_div_50_Template(rf, ctx) { if (rf & 1) {
|
|
192
240
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
193
|
-
i0.ɵɵelementStart(0, "div",
|
|
194
|
-
i0.ɵɵ
|
|
241
|
+
i0.ɵɵelementStart(0, "div", 63)(1, "div", 64)(2, "h3");
|
|
242
|
+
i0.ɵɵelement(3, "i", 65);
|
|
243
|
+
i0.ɵɵtext(4, " Execution Settings");
|
|
195
244
|
i0.ɵɵelementEnd();
|
|
196
|
-
i0.ɵɵelementStart(
|
|
197
|
-
i0.ɵɵtext(
|
|
245
|
+
i0.ɵɵelementStart(5, "div", 66)(6, "div", 67)(7, "label");
|
|
246
|
+
i0.ɵɵtext(8, "Priority");
|
|
198
247
|
i0.ɵɵelementEnd();
|
|
199
|
-
i0.ɵɵelementStart(
|
|
200
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
248
|
+
i0.ɵɵelementStart(9, "input", 68);
|
|
249
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_50_Template_input_ngModelChange_9_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.Priority, $event) || (ctx_r0.record.Priority = $event); return i0.ɵɵresetView($event); });
|
|
201
250
|
i0.ɵɵelementEnd()();
|
|
202
|
-
i0.ɵɵelementStart(
|
|
203
|
-
i0.ɵɵtext(
|
|
251
|
+
i0.ɵɵelementStart(10, "div", 67)(11, "label");
|
|
252
|
+
i0.ɵɵtext(12, "Repeat Count");
|
|
204
253
|
i0.ɵɵelementEnd();
|
|
205
|
-
i0.ɵɵelementStart(
|
|
206
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
254
|
+
i0.ɵɵelementStart(13, "input", 69);
|
|
255
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_50_Template_input_ngModelChange_13_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.RepeatCount, $event) || (ctx_r0.record.RepeatCount = $event); return i0.ɵɵresetView($event); });
|
|
207
256
|
i0.ɵɵelementEnd()();
|
|
208
|
-
i0.ɵɵelementStart(
|
|
209
|
-
i0.ɵɵtext(
|
|
257
|
+
i0.ɵɵelementStart(14, "div", 67)(15, "label");
|
|
258
|
+
i0.ɵɵtext(16, "Estimated Duration (seconds)");
|
|
210
259
|
i0.ɵɵelementEnd();
|
|
211
|
-
i0.ɵɵelementStart(
|
|
212
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
260
|
+
i0.ɵɵelementStart(17, "input", 70);
|
|
261
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_50_Template_input_ngModelChange_17_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.EstimatedDurationSeconds, $event) || (ctx_r0.record.EstimatedDurationSeconds = $event); return i0.ɵɵresetView($event); });
|
|
213
262
|
i0.ɵɵelementEnd()();
|
|
214
|
-
i0.ɵɵelementStart(
|
|
215
|
-
i0.ɵɵtext(
|
|
216
|
-
i0.ɵɵelementEnd();
|
|
217
|
-
i0.ɵɵelementStart(20, "input", 50);
|
|
218
|
-
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_input_ngModelChange_20_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.EstimatedCostUSD, $event) || (ctx_r0.record.EstimatedCostUSD = $event); return i0.ɵɵresetView($event); });
|
|
219
|
-
i0.ɵɵelementEnd()()()();
|
|
220
|
-
i0.ɵɵelementStart(21, "div", 46)(22, "h3");
|
|
221
|
-
i0.ɵɵtext(23, "Input Definition (JSON)");
|
|
263
|
+
i0.ɵɵelementStart(18, "div", 67)(19, "label");
|
|
264
|
+
i0.ɵɵtext(20, "Estimated Cost (USD)");
|
|
222
265
|
i0.ɵɵelementEnd();
|
|
223
|
-
i0.ɵɵelementStart(
|
|
224
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
266
|
+
i0.ɵɵelementStart(21, "input", 71);
|
|
267
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_50_Template_input_ngModelChange_21_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.EstimatedCostUSD, $event) || (ctx_r0.record.EstimatedCostUSD = $event); return i0.ɵɵresetView($event); });
|
|
225
268
|
i0.ɵɵelementEnd()();
|
|
226
|
-
i0.ɵɵelementStart(
|
|
227
|
-
i0.ɵɵtext(
|
|
269
|
+
i0.ɵɵelementStart(22, "div", 72)(23, "label");
|
|
270
|
+
i0.ɵɵtext(24, "Max Execution Time (ms)");
|
|
228
271
|
i0.ɵɵelementEnd();
|
|
229
|
-
i0.ɵɵelementStart(
|
|
230
|
-
i0.ɵɵtwoWayListener("ngModelChange", function
|
|
231
|
-
i0.ɵɵelementEnd()();
|
|
232
|
-
i0.ɵɵelementStart(29, "div", 46)(30, "h3");
|
|
233
|
-
i0.ɵɵtext(31, "Configuration (JSON)");
|
|
272
|
+
i0.ɵɵelementStart(25, "input", 73);
|
|
273
|
+
i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_50_Template_input_ngModelChange_25_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.MaxExecutionTimeMS, $event) || (ctx_r0.record.MaxExecutionTimeMS = $event); return i0.ɵɵresetView($event); });
|
|
234
274
|
i0.ɵɵelementEnd();
|
|
235
|
-
i0.ɵɵelementStart(
|
|
236
|
-
i0.ɵɵ
|
|
237
|
-
i0.ɵɵelementEnd()();
|
|
238
|
-
i0.ɵɵelementStart(
|
|
239
|
-
i0.ɵɵ
|
|
275
|
+
i0.ɵɵelementStart(26, "span", 74);
|
|
276
|
+
i0.ɵɵtext(27, "Leave empty for default 5 minute timeout");
|
|
277
|
+
i0.ɵɵelementEnd()()()();
|
|
278
|
+
i0.ɵɵelementStart(28, "div", 64)(29, "h3");
|
|
279
|
+
i0.ɵɵelement(30, "i", 57);
|
|
280
|
+
i0.ɵɵtext(31, " Input Definition (JSON)");
|
|
281
|
+
i0.ɵɵelementEnd();
|
|
282
|
+
i0.ɵɵelementStart(32, "div", 75)(33, "mj-code-editor", 76);
|
|
283
|
+
i0.ɵɵlistener("change", function TestFormComponentExtended_div_50_Template_mj_code_editor_change_33_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.InputDefinition = $event); });
|
|
284
|
+
i0.ɵɵelementEnd()()();
|
|
285
|
+
i0.ɵɵelementStart(34, "div", 64)(35, "h3");
|
|
286
|
+
i0.ɵɵelement(36, "i", 58);
|
|
287
|
+
i0.ɵɵtext(37, " Expected Outcomes (JSON)");
|
|
240
288
|
i0.ɵɵelementEnd();
|
|
241
|
-
i0.ɵɵelementStart(
|
|
242
|
-
i0.ɵɵ
|
|
289
|
+
i0.ɵɵelementStart(38, "div", 75)(39, "mj-code-editor", 76);
|
|
290
|
+
i0.ɵɵlistener("change", function TestFormComponentExtended_div_50_Template_mj_code_editor_change_39_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.ExpectedOutcomes = $event); });
|
|
243
291
|
i0.ɵɵelementEnd()()();
|
|
292
|
+
i0.ɵɵelementStart(40, "div", 64)(41, "h3");
|
|
293
|
+
i0.ɵɵelement(42, "i", 59);
|
|
294
|
+
i0.ɵɵtext(43, " Configuration (JSON)");
|
|
295
|
+
i0.ɵɵelementEnd();
|
|
296
|
+
i0.ɵɵelementStart(44, "div", 75)(45, "mj-code-editor", 76);
|
|
297
|
+
i0.ɵɵlistener("change", function TestFormComponentExtended_div_50_Template_mj_code_editor_change_45_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Configuration = $event); });
|
|
298
|
+
i0.ɵɵelementEnd()()();
|
|
299
|
+
i0.ɵɵelementStart(46, "div", 64)(47, "h3");
|
|
300
|
+
i0.ɵɵelement(48, "i", 60);
|
|
301
|
+
i0.ɵɵtext(49, " Tags (JSON Array)");
|
|
302
|
+
i0.ɵɵelementEnd();
|
|
303
|
+
i0.ɵɵelementStart(50, "div", 77)(51, "mj-code-editor", 76);
|
|
304
|
+
i0.ɵɵlistener("change", function TestFormComponentExtended_div_50_Template_mj_code_editor_change_51_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Tags = $event); });
|
|
305
|
+
i0.ɵɵelementEnd()()()();
|
|
244
306
|
} if (rf & 2) {
|
|
245
307
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
246
|
-
i0.ɵɵadvance(
|
|
308
|
+
i0.ɵɵadvance(9);
|
|
247
309
|
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.Priority);
|
|
248
310
|
i0.ɵɵadvance(4);
|
|
249
311
|
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.RepeatCount);
|
|
@@ -252,186 +314,864 @@ function TestFormComponentExtended_div_44_Template(rf, ctx) { if (rf & 1) {
|
|
|
252
314
|
i0.ɵɵadvance(4);
|
|
253
315
|
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.EstimatedCostUSD);
|
|
254
316
|
i0.ɵɵadvance(4);
|
|
255
|
-
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.
|
|
256
|
-
i0.ɵɵadvance(
|
|
257
|
-
i0.ɵɵ
|
|
258
|
-
i0.ɵɵadvance(
|
|
259
|
-
i0.ɵɵ
|
|
260
|
-
i0.ɵɵadvance(
|
|
261
|
-
i0.ɵɵ
|
|
317
|
+
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.MaxExecutionTimeMS);
|
|
318
|
+
i0.ɵɵadvance(8);
|
|
319
|
+
i0.ɵɵproperty("value", ctx_r0.record.InputDefinition || "")("readonly", false)("lineWrapping", true);
|
|
320
|
+
i0.ɵɵadvance(6);
|
|
321
|
+
i0.ɵɵproperty("value", ctx_r0.record.ExpectedOutcomes || "")("readonly", false)("lineWrapping", true);
|
|
322
|
+
i0.ɵɵadvance(6);
|
|
323
|
+
i0.ɵɵproperty("value", ctx_r0.record.Configuration || "")("readonly", false)("lineWrapping", true);
|
|
324
|
+
i0.ɵɵadvance(6);
|
|
325
|
+
i0.ɵɵproperty("value", ctx_r0.record.Tags || "[]")("readonly", false)("lineWrapping", true);
|
|
326
|
+
} }
|
|
327
|
+
function TestFormComponentExtended_div_51_div_1_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
328
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
329
|
+
i0.ɵɵelement(1, "div", 86);
|
|
330
|
+
i0.ɵɵelementStart(2, "div", 87);
|
|
331
|
+
i0.ɵɵelement(3, "div", 88)(4, "div", 89);
|
|
332
|
+
i0.ɵɵelementEnd()();
|
|
262
333
|
} }
|
|
263
|
-
function
|
|
334
|
+
function TestFormComponentExtended_div_51_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
335
|
+
i0.ɵɵelementStart(0, "div", 82)(1, "div", 83);
|
|
336
|
+
i0.ɵɵtemplate(2, TestFormComponentExtended_div_51_div_1_div_2_Template, 5, 0, "div", 84);
|
|
337
|
+
i0.ɵɵelementEnd()();
|
|
338
|
+
} if (rf & 2) {
|
|
339
|
+
i0.ɵɵadvance(2);
|
|
340
|
+
i0.ɵɵproperty("ngForOf", i0.ɵɵpureFunction0(1, _c0));
|
|
341
|
+
} }
|
|
342
|
+
function TestFormComponentExtended_div_51_div_2_div_1_span_13_Template(rf, ctx) { if (rf & 1) {
|
|
264
343
|
i0.ɵɵelementStart(0, "span");
|
|
265
|
-
i0.ɵɵ
|
|
344
|
+
i0.ɵɵelement(1, "i", 107);
|
|
345
|
+
i0.ɵɵtext(2);
|
|
266
346
|
i0.ɵɵelementEnd();
|
|
267
347
|
} if (rf & 2) {
|
|
268
348
|
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
269
349
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
270
|
-
i0.ɵɵadvance();
|
|
271
|
-
i0.ɵɵ
|
|
350
|
+
i0.ɵɵadvance(2);
|
|
351
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(run_r5.DurationSeconds), "");
|
|
272
352
|
} }
|
|
273
|
-
function
|
|
353
|
+
function TestFormComponentExtended_div_51_div_2_div_1_span_14_Template(rf, ctx) { if (rf & 1) {
|
|
274
354
|
i0.ɵɵelementStart(0, "span");
|
|
275
|
-
i0.ɵɵ
|
|
355
|
+
i0.ɵɵelement(1, "i", 108);
|
|
356
|
+
i0.ɵɵtext(2);
|
|
276
357
|
i0.ɵɵelementEnd();
|
|
277
358
|
} if (rf & 2) {
|
|
278
359
|
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
279
360
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
361
|
+
i0.ɵɵadvance(2);
|
|
362
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatCost(run_r5.CostUSD), "");
|
|
363
|
+
} }
|
|
364
|
+
function TestFormComponentExtended_div_51_div_2_div_1_mj_entity_link_pill_15_Template(rf, ctx) { if (rf & 1) {
|
|
365
|
+
i0.ɵɵelement(0, "mj-entity-link-pill", 109);
|
|
366
|
+
} if (rf & 2) {
|
|
367
|
+
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
368
|
+
i0.ɵɵproperty("entityName", run_r5.TargetLogEntity)("recordId", run_r5.TargetLogID);
|
|
369
|
+
} }
|
|
370
|
+
function TestFormComponentExtended_div_51_div_2_div_1_span_17_Template(rf, ctx) { if (rf & 1) {
|
|
371
|
+
i0.ɵɵelementStart(0, "span", 110);
|
|
372
|
+
i0.ɵɵelement(1, "i", 94);
|
|
373
|
+
i0.ɵɵelementStart(2, "span");
|
|
374
|
+
i0.ɵɵtext(3);
|
|
375
|
+
i0.ɵɵelementEnd()();
|
|
376
|
+
} if (rf & 2) {
|
|
377
|
+
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
378
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
379
|
+
i0.ɵɵproperty("ngClass", "status-" + run_r5.Status.toLowerCase())("title", ctx_r0.getStatusTooltip(run_r5.Status));
|
|
280
380
|
i0.ɵɵadvance();
|
|
281
|
-
i0.ɵɵ
|
|
381
|
+
i0.ɵɵclassProp("fa-check", run_r5.Status === "Passed")("fa-times", run_r5.Status === "Failed")("fa-exclamation", run_r5.Status === "Error")("fa-hourglass-end", run_r5.Status === "Timeout")("fa-forward", run_r5.Status === "Skipped")("fa-spinner", run_r5.Status === "Running")("fa-clock", run_r5.Status === "Pending");
|
|
382
|
+
i0.ɵɵadvance(2);
|
|
383
|
+
i0.ɵɵtextInterpolate(run_r5.Status);
|
|
282
384
|
} }
|
|
283
|
-
function
|
|
284
|
-
i0.ɵɵelementStart(0, "span");
|
|
385
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_span_1_Template(rf, ctx) { if (rf & 1) {
|
|
386
|
+
i0.ɵɵelementStart(0, "span", 113);
|
|
387
|
+
i0.ɵɵelement(1, "i", 114);
|
|
388
|
+
i0.ɵɵelementEnd();
|
|
389
|
+
} }
|
|
390
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_span_2_Template(rf, ctx) { if (rf & 1) {
|
|
391
|
+
i0.ɵɵelementStart(0, "span", 115);
|
|
392
|
+
i0.ɵɵelement(1, "i", 116);
|
|
393
|
+
i0.ɵɵelementStart(2, "span");
|
|
394
|
+
i0.ɵɵtext(3);
|
|
395
|
+
i0.ɵɵelementEnd()();
|
|
396
|
+
} if (rf & 2) {
|
|
397
|
+
let tmp_11_0;
|
|
398
|
+
const rating_r6 = ctx.ngIf;
|
|
399
|
+
const run_r5 = i0.ɵɵnextContext(2).$implicit;
|
|
400
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
401
|
+
i0.ɵɵclassProp("rating-low", rating_r6 <= 4)("rating-medium", rating_r6 >= 5 && rating_r6 <= 6)("rating-good", rating_r6 >= 7 && rating_r6 <= 8)("rating-excellent", rating_r6 >= 9);
|
|
402
|
+
i0.ɵɵproperty("title", ctx_r0.getHumanTooltip(rating_r6, ((tmp_11_0 = ctx_r0.getFeedbackForRun(run_r5.ID)) == null ? null : tmp_11_0.CorrectionSummary) || ((tmp_11_0 = ctx_r0.getFeedbackForRun(run_r5.ID)) == null ? null : tmp_11_0.Comments) || null));
|
|
403
|
+
i0.ɵɵadvance(3);
|
|
404
|
+
i0.ɵɵtextInterpolate(rating_r6);
|
|
405
|
+
} }
|
|
406
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_Template(rf, ctx) { if (rf & 1) {
|
|
407
|
+
i0.ɵɵelementContainerStart(0);
|
|
408
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_span_1_Template, 2, 0, "span", 111)(2, TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_span_2_Template, 4, 10, "span", 112);
|
|
409
|
+
i0.ɵɵelementContainerEnd();
|
|
410
|
+
} if (rf & 2) {
|
|
411
|
+
let tmp_5_0;
|
|
412
|
+
let tmp_6_0;
|
|
413
|
+
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
414
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
415
|
+
i0.ɵɵadvance();
|
|
416
|
+
i0.ɵɵproperty("ngIf", !((tmp_5_0 = ctx_r0.getFeedbackForRun(run_r5.ID)) == null ? null : tmp_5_0.Rating));
|
|
417
|
+
i0.ɵɵadvance();
|
|
418
|
+
i0.ɵɵproperty("ngIf", (tmp_6_0 = ctx_r0.getFeedbackForRun(run_r5.ID)) == null ? null : tmp_6_0.Rating);
|
|
419
|
+
} }
|
|
420
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_span_1_Template(rf, ctx) { if (rf & 1) {
|
|
421
|
+
i0.ɵɵelementStart(0, "span", 119);
|
|
422
|
+
i0.ɵɵelement(1, "i", 120);
|
|
423
|
+
i0.ɵɵelementEnd();
|
|
424
|
+
} }
|
|
425
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_span_2_Template(rf, ctx) { if (rf & 1) {
|
|
426
|
+
i0.ɵɵelementStart(0, "span", 121);
|
|
427
|
+
i0.ɵɵelement(1, "i", 120);
|
|
428
|
+
i0.ɵɵelementStart(2, "span");
|
|
429
|
+
i0.ɵɵtext(3);
|
|
430
|
+
i0.ɵɵelementEnd()();
|
|
431
|
+
} if (rf & 2) {
|
|
432
|
+
const run_r5 = i0.ɵɵnextContext(2).$implicit;
|
|
433
|
+
i0.ɵɵclassProp("score-low", run_r5.Score < 0.5)("score-medium", run_r5.Score >= 0.5 && run_r5.Score < 0.7)("score-good", run_r5.Score >= 0.7 && run_r5.Score < 0.85)("score-excellent", run_r5.Score >= 0.85);
|
|
434
|
+
i0.ɵɵproperty("title", "Auto Score: " + (run_r5.Score * 100).toFixed(0) + "% automated evaluation");
|
|
435
|
+
i0.ɵɵadvance(3);
|
|
436
|
+
i0.ɵɵtextInterpolate1("", (run_r5.Score * 100).toFixed(0), "%");
|
|
437
|
+
} }
|
|
438
|
+
function TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_Template(rf, ctx) { if (rf & 1) {
|
|
439
|
+
i0.ɵɵelementContainerStart(0);
|
|
440
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_span_1_Template, 2, 0, "span", 117)(2, TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_span_2_Template, 4, 10, "span", 118);
|
|
441
|
+
i0.ɵɵelementContainerEnd();
|
|
442
|
+
} if (rf & 2) {
|
|
443
|
+
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
444
|
+
i0.ɵɵadvance();
|
|
445
|
+
i0.ɵɵproperty("ngIf", run_r5.Score == null);
|
|
446
|
+
i0.ɵɵadvance();
|
|
447
|
+
i0.ɵɵproperty("ngIf", run_r5.Score != null);
|
|
448
|
+
} }
|
|
449
|
+
function TestFormComponentExtended_div_51_div_2_div_1_div_20_span_1_Template(rf, ctx) { if (rf & 1) {
|
|
450
|
+
i0.ɵɵelementStart(0, "span", 125);
|
|
285
451
|
i0.ɵɵtext(1);
|
|
286
452
|
i0.ɵɵelementEnd();
|
|
453
|
+
} if (rf & 2) {
|
|
454
|
+
const tag_r7 = ctx.$implicit;
|
|
455
|
+
i0.ɵɵadvance();
|
|
456
|
+
i0.ɵɵtextInterpolate(tag_r7);
|
|
457
|
+
} }
|
|
458
|
+
function TestFormComponentExtended_div_51_div_2_div_1_div_20_span_2_Template(rf, ctx) { if (rf & 1) {
|
|
459
|
+
i0.ɵɵelementStart(0, "span", 126);
|
|
460
|
+
i0.ɵɵtext(1);
|
|
461
|
+
i0.ɵɵelementEnd();
|
|
462
|
+
} if (rf & 2) {
|
|
463
|
+
const run_r5 = i0.ɵɵnextContext(2).$implicit;
|
|
464
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
465
|
+
i0.ɵɵadvance();
|
|
466
|
+
i0.ɵɵtextInterpolate1("+", ctx_r0.getRunTags(run_r5).length - 3, "");
|
|
467
|
+
} }
|
|
468
|
+
function TestFormComponentExtended_div_51_div_2_div_1_div_20_Template(rf, ctx) { if (rf & 1) {
|
|
469
|
+
i0.ɵɵelementStart(0, "div", 122);
|
|
470
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_51_div_2_div_1_div_20_span_1_Template, 2, 1, "span", 123)(2, TestFormComponentExtended_div_51_div_2_div_1_div_20_span_2_Template, 2, 1, "span", 124);
|
|
471
|
+
i0.ɵɵelementEnd();
|
|
287
472
|
} if (rf & 2) {
|
|
288
473
|
const run_r5 = i0.ɵɵnextContext().$implicit;
|
|
474
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
475
|
+
i0.ɵɵadvance();
|
|
476
|
+
i0.ɵɵproperty("ngForOf", ctx_r0.getRunTags(run_r5).slice(0, 3));
|
|
289
477
|
i0.ɵɵadvance();
|
|
290
|
-
i0.ɵɵ
|
|
478
|
+
i0.ɵɵproperty("ngIf", ctx_r0.getRunTags(run_r5).length > 3);
|
|
291
479
|
} }
|
|
292
|
-
function
|
|
480
|
+
function TestFormComponentExtended_div_51_div_2_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
293
481
|
const _r4 = i0.ɵɵgetCurrentView();
|
|
294
|
-
i0.ɵɵelementStart(0, "div",
|
|
295
|
-
i0.ɵɵlistener("click", function
|
|
296
|
-
i0.ɵɵelementStart(1, "div",
|
|
297
|
-
i0.ɵɵelement(2, "i",
|
|
482
|
+
i0.ɵɵelementStart(0, "div", 92);
|
|
483
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_51_div_2_div_1_Template_div_click_0_listener() { const run_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestRun(run_r5.ID)); });
|
|
484
|
+
i0.ɵɵelementStart(1, "div", 93);
|
|
485
|
+
i0.ɵɵelement(2, "i", 94);
|
|
298
486
|
i0.ɵɵelementEnd();
|
|
299
|
-
i0.ɵɵelementStart(3, "div",
|
|
487
|
+
i0.ɵɵelementStart(3, "div", 95)(4, "div", 96)(5, "span", 97);
|
|
300
488
|
i0.ɵɵtext(6);
|
|
301
489
|
i0.ɵɵelementEnd();
|
|
302
|
-
i0.ɵɵelementStart(7, "span",
|
|
490
|
+
i0.ɵɵelementStart(7, "span", 98);
|
|
303
491
|
i0.ɵɵtext(8);
|
|
304
492
|
i0.ɵɵelementEnd()();
|
|
305
|
-
i0.ɵɵelementStart(9, "div",
|
|
306
|
-
i0.ɵɵ
|
|
307
|
-
i0.ɵɵ
|
|
493
|
+
i0.ɵɵelementStart(9, "div", 99)(10, "span");
|
|
494
|
+
i0.ɵɵelement(11, "i", 100);
|
|
495
|
+
i0.ɵɵtext(12);
|
|
308
496
|
i0.ɵɵelementEnd();
|
|
309
|
-
i0.ɵɵtemplate(13,
|
|
310
|
-
i0.ɵɵelementEnd()
|
|
311
|
-
i0.ɵɵ
|
|
497
|
+
i0.ɵɵtemplate(13, TestFormComponentExtended_div_51_div_2_div_1_span_13_Template, 3, 1, "span", 101)(14, TestFormComponentExtended_div_51_div_2_div_1_span_14_Template, 3, 1, "span", 101)(15, TestFormComponentExtended_div_51_div_2_div_1_mj_entity_link_pill_15_Template, 1, 2, "mj-entity-link-pill", 102);
|
|
498
|
+
i0.ɵɵelementEnd();
|
|
499
|
+
i0.ɵɵelementStart(16, "div", 103);
|
|
500
|
+
i0.ɵɵtemplate(17, TestFormComponentExtended_div_51_div_2_div_1_span_17_Template, 4, 17, "span", 104)(18, TestFormComponentExtended_div_51_div_2_div_1_ng_container_18_Template, 3, 2, "ng-container", 101)(19, TestFormComponentExtended_div_51_div_2_div_1_ng_container_19_Template, 3, 2, "ng-container", 101);
|
|
501
|
+
i0.ɵɵelementEnd();
|
|
502
|
+
i0.ɵɵtemplate(20, TestFormComponentExtended_div_51_div_2_div_1_div_20_Template, 3, 2, "div", 105);
|
|
503
|
+
i0.ɵɵelementEnd();
|
|
504
|
+
i0.ɵɵelement(21, "i", 106);
|
|
312
505
|
i0.ɵɵelementEnd();
|
|
313
506
|
} if (rf & 2) {
|
|
314
507
|
const run_r5 = ctx.$implicit;
|
|
508
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
315
509
|
i0.ɵɵadvance();
|
|
316
|
-
i0.ɵɵstyleProp("background-color",
|
|
510
|
+
i0.ɵɵstyleProp("background-color", ctx_r0.getRunStatusColor(run_r5.Status));
|
|
317
511
|
i0.ɵɵadvance();
|
|
318
|
-
i0.ɵɵclassProp("fa-check", run_r5.Status === "Passed")("fa-times", run_r5.Status === "Failed")("fa-exclamation", run_r5.Status === "Error");
|
|
512
|
+
i0.ɵɵclassProp("fa-check", run_r5.Status === "Passed")("fa-times", run_r5.Status === "Failed")("fa-exclamation", run_r5.Status === "Error")("fa-stopwatch", run_r5.Status === "Timeout")("fa-spinner", run_r5.Status === "Running")("fa-clock", run_r5.Status === "Pending");
|
|
319
513
|
i0.ɵɵadvance(4);
|
|
320
514
|
i0.ɵɵtextInterpolate1("Run #", run_r5.ID.substring(0, 8), "");
|
|
321
515
|
i0.ɵɵadvance();
|
|
322
|
-
i0.ɵɵstyleProp("color",
|
|
516
|
+
i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(run_r5.Status));
|
|
323
517
|
i0.ɵɵadvance();
|
|
324
518
|
i0.ɵɵtextInterpolate(run_r5.Status);
|
|
325
|
-
i0.ɵɵadvance(
|
|
326
|
-
i0.ɵɵ
|
|
327
|
-
i0.ɵɵadvance(
|
|
519
|
+
i0.ɵɵadvance(4);
|
|
520
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.getRelativeTime(run_r5.StartedAt), "");
|
|
521
|
+
i0.ɵɵadvance();
|
|
328
522
|
i0.ɵɵproperty("ngIf", run_r5.DurationSeconds);
|
|
329
523
|
i0.ɵɵadvance();
|
|
330
524
|
i0.ɵɵproperty("ngIf", run_r5.CostUSD);
|
|
331
525
|
i0.ɵɵadvance();
|
|
332
|
-
i0.ɵɵproperty("ngIf", run_r5.
|
|
526
|
+
i0.ɵɵproperty("ngIf", run_r5.TargetLogEntityID && run_r5.TargetLogID);
|
|
527
|
+
i0.ɵɵadvance(2);
|
|
528
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
529
|
+
i0.ɵɵadvance();
|
|
530
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showHuman);
|
|
531
|
+
i0.ɵɵadvance();
|
|
532
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showAuto);
|
|
533
|
+
i0.ɵɵadvance();
|
|
534
|
+
i0.ɵɵproperty("ngIf", ctx_r0.getRunTags(run_r5).length > 0);
|
|
333
535
|
} }
|
|
334
|
-
function
|
|
335
|
-
i0.ɵɵelementStart(0, "div",
|
|
336
|
-
i0.ɵɵtemplate(1,
|
|
536
|
+
function TestFormComponentExtended_div_51_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
537
|
+
i0.ɵɵelementStart(0, "div", 90);
|
|
538
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_51_div_2_div_1_Template, 22, 26, "div", 91);
|
|
337
539
|
i0.ɵɵelementEnd();
|
|
338
540
|
} if (rf & 2) {
|
|
339
541
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
340
542
|
i0.ɵɵadvance();
|
|
341
543
|
i0.ɵɵproperty("ngForOf", ctx_r0.testRuns);
|
|
342
544
|
} }
|
|
343
|
-
function
|
|
344
|
-
i0.ɵɵ
|
|
345
|
-
i0.ɵɵ
|
|
346
|
-
i0.ɵɵ
|
|
347
|
-
i0.ɵɵ
|
|
545
|
+
function TestFormComponentExtended_div_51_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
546
|
+
const _r8 = i0.ɵɵgetCurrentView();
|
|
547
|
+
i0.ɵɵelementStart(0, "div", 127)(1, "div", 128);
|
|
548
|
+
i0.ɵɵelement(2, "i", 129);
|
|
549
|
+
i0.ɵɵelementEnd();
|
|
550
|
+
i0.ɵɵelementStart(3, "h4");
|
|
551
|
+
i0.ɵɵtext(4, "No Test Runs Yet");
|
|
552
|
+
i0.ɵɵelementEnd();
|
|
553
|
+
i0.ɵɵelementStart(5, "p");
|
|
554
|
+
i0.ɵɵtext(6, "Run this test to see execution history and results here.");
|
|
555
|
+
i0.ɵɵelementEnd();
|
|
556
|
+
i0.ɵɵelementStart(7, "button", 12);
|
|
557
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_51_div_3_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r8); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.runTest()); });
|
|
558
|
+
i0.ɵɵelement(8, "i", 13);
|
|
559
|
+
i0.ɵɵtext(9, " Run Test Now ");
|
|
348
560
|
i0.ɵɵelementEnd()();
|
|
349
561
|
} }
|
|
350
|
-
function
|
|
351
|
-
i0.ɵɵelementStart(0, "div",
|
|
352
|
-
i0.ɵɵtemplate(1,
|
|
562
|
+
function TestFormComponentExtended_div_51_Template(rf, ctx) { if (rf & 1) {
|
|
563
|
+
i0.ɵɵelementStart(0, "div", 78);
|
|
564
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_51_div_1_Template, 3, 2, "div", 79)(2, TestFormComponentExtended_div_51_div_2_Template, 2, 1, "div", 80)(3, TestFormComponentExtended_div_51_div_3_Template, 10, 0, "div", 81);
|
|
353
565
|
i0.ɵɵelementEnd();
|
|
354
566
|
} if (rf & 2) {
|
|
355
567
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
356
568
|
i0.ɵɵadvance();
|
|
357
|
-
i0.ɵɵproperty("ngIf", ctx_r0.
|
|
569
|
+
i0.ɵɵproperty("ngIf", ctx_r0.loadingRuns);
|
|
570
|
+
i0.ɵɵadvance();
|
|
571
|
+
i0.ɵɵproperty("ngIf", !ctx_r0.loadingRuns && ctx_r0.testRuns.length > 0);
|
|
358
572
|
i0.ɵɵadvance();
|
|
359
|
-
i0.ɵɵproperty("ngIf", ctx_r0.testRunsLoaded && ctx_r0.testRuns.length === 0);
|
|
573
|
+
i0.ɵɵproperty("ngIf", ctx_r0.testRunsLoaded && !ctx_r0.loadingRuns && ctx_r0.testRuns.length === 0);
|
|
574
|
+
} }
|
|
575
|
+
function TestFormComponentExtended_div_52_div_1_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
576
|
+
i0.ɵɵelementStart(0, "div", 85);
|
|
577
|
+
i0.ɵɵelement(1, "div", 86);
|
|
578
|
+
i0.ɵɵelementStart(2, "div", 87);
|
|
579
|
+
i0.ɵɵelement(3, "div", 88)(4, "div", 89);
|
|
580
|
+
i0.ɵɵelementEnd()();
|
|
581
|
+
} }
|
|
582
|
+
function TestFormComponentExtended_div_52_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
583
|
+
i0.ɵɵelementStart(0, "div", 82)(1, "div", 83);
|
|
584
|
+
i0.ɵɵtemplate(2, TestFormComponentExtended_div_52_div_1_div_2_Template, 5, 0, "div", 84);
|
|
585
|
+
i0.ɵɵelementEnd()();
|
|
586
|
+
} if (rf & 2) {
|
|
587
|
+
i0.ɵɵadvance(2);
|
|
588
|
+
i0.ɵɵproperty("ngForOf", i0.ɵɵpureFunction0(1, _c1));
|
|
360
589
|
} }
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
-
i0.ɵɵ
|
|
364
|
-
i0.ɵɵ
|
|
365
|
-
i0.ɵɵ
|
|
366
|
-
|
|
590
|
+
function TestFormComponentExtended_div_52_div_2_div_1_span_10_Template(rf, ctx) { if (rf & 1) {
|
|
591
|
+
i0.ɵɵelementStart(0, "span");
|
|
592
|
+
i0.ɵɵelement(1, "i", 47);
|
|
593
|
+
i0.ɵɵtext(2);
|
|
594
|
+
i0.ɵɵelementEnd();
|
|
595
|
+
} if (rf & 2) {
|
|
596
|
+
const suiteTest_r10 = i0.ɵɵnextContext().$implicit;
|
|
597
|
+
i0.ɵɵadvance(2);
|
|
598
|
+
i0.ɵɵtextInterpolate1(" ", suiteTest_r10.Status, "");
|
|
599
|
+
} }
|
|
600
|
+
function TestFormComponentExtended_div_52_div_2_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
601
|
+
const _r9 = i0.ɵɵgetCurrentView();
|
|
602
|
+
i0.ɵɵelementStart(0, "div", 134);
|
|
603
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_52_div_2_div_1_Template_div_click_0_listener() { const suiteTest_r10 = i0.ɵɵrestoreView(_r9).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestSuite(suiteTest_r10.SuiteID)); });
|
|
604
|
+
i0.ɵɵelementStart(1, "div", 135);
|
|
605
|
+
i0.ɵɵelement(2, "i", 24);
|
|
367
606
|
i0.ɵɵelementEnd();
|
|
368
|
-
i0.ɵɵelementStart(3, "div",
|
|
607
|
+
i0.ɵɵelementStart(3, "div", 136)(4, "div", 137);
|
|
369
608
|
i0.ɵɵtext(5);
|
|
370
609
|
i0.ɵɵelementEnd();
|
|
371
|
-
i0.ɵɵelementStart(6, "div",
|
|
372
|
-
i0.ɵɵ
|
|
373
|
-
i0.ɵɵ
|
|
374
|
-
i0.ɵɵ
|
|
610
|
+
i0.ɵɵelementStart(6, "div", 138)(7, "span");
|
|
611
|
+
i0.ɵɵelement(8, "i", 139);
|
|
612
|
+
i0.ɵɵtext(9);
|
|
613
|
+
i0.ɵɵelementEnd();
|
|
614
|
+
i0.ɵɵtemplate(10, TestFormComponentExtended_div_52_div_2_div_1_span_10_Template, 3, 1, "span", 101);
|
|
615
|
+
i0.ɵɵelementEnd()();
|
|
616
|
+
i0.ɵɵelement(11, "i", 106);
|
|
375
617
|
i0.ɵɵelementEnd();
|
|
376
618
|
} if (rf & 2) {
|
|
377
|
-
const
|
|
619
|
+
const suiteTest_r10 = ctx.$implicit;
|
|
378
620
|
i0.ɵɵadvance(5);
|
|
379
|
-
i0.ɵɵtextInterpolate(
|
|
380
|
-
i0.ɵɵadvance(
|
|
381
|
-
i0.ɵɵtextInterpolate1("Sequence: ",
|
|
621
|
+
i0.ɵɵtextInterpolate(suiteTest_r10.Suite);
|
|
622
|
+
i0.ɵɵadvance(4);
|
|
623
|
+
i0.ɵɵtextInterpolate1(" Sequence: ", suiteTest_r10.Sequence, "");
|
|
624
|
+
i0.ɵɵadvance();
|
|
625
|
+
i0.ɵɵproperty("ngIf", suiteTest_r10.Status);
|
|
382
626
|
} }
|
|
383
|
-
function
|
|
384
|
-
i0.ɵɵelementStart(0, "div",
|
|
385
|
-
i0.ɵɵtemplate(1,
|
|
627
|
+
function TestFormComponentExtended_div_52_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
628
|
+
i0.ɵɵelementStart(0, "div", 132);
|
|
629
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_52_div_2_div_1_Template, 12, 3, "div", 133);
|
|
386
630
|
i0.ɵɵelementEnd();
|
|
387
631
|
} if (rf & 2) {
|
|
388
632
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
389
633
|
i0.ɵɵadvance();
|
|
390
634
|
i0.ɵɵproperty("ngForOf", ctx_r0.suiteTests);
|
|
391
635
|
} }
|
|
392
|
-
function
|
|
393
|
-
i0.ɵɵelementStart(0, "div",
|
|
394
|
-
i0.ɵɵelement(
|
|
395
|
-
i0.ɵɵ
|
|
396
|
-
i0.ɵɵ
|
|
636
|
+
function TestFormComponentExtended_div_52_div_3_Template(rf, ctx) { if (rf & 1) {
|
|
637
|
+
i0.ɵɵelementStart(0, "div", 127)(1, "div", 128);
|
|
638
|
+
i0.ɵɵelement(2, "i", 140);
|
|
639
|
+
i0.ɵɵelementEnd();
|
|
640
|
+
i0.ɵɵelementStart(3, "h4");
|
|
641
|
+
i0.ɵɵtext(4, "Not Part of Any Suite");
|
|
642
|
+
i0.ɵɵelementEnd();
|
|
643
|
+
i0.ɵɵelementStart(5, "p");
|
|
644
|
+
i0.ɵɵtext(6, "This test is not included in any test suites. Add it to a suite to run it with other tests.");
|
|
645
|
+
i0.ɵɵelementEnd()();
|
|
646
|
+
} }
|
|
647
|
+
function TestFormComponentExtended_div_52_Template(rf, ctx) { if (rf & 1) {
|
|
648
|
+
i0.ɵɵelementStart(0, "div", 130);
|
|
649
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_52_div_1_Template, 3, 2, "div", 79)(2, TestFormComponentExtended_div_52_div_2_Template, 2, 1, "div", 131)(3, TestFormComponentExtended_div_52_div_3_Template, 7, 0, "div", 81);
|
|
650
|
+
i0.ɵɵelementEnd();
|
|
651
|
+
} if (rf & 2) {
|
|
652
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
653
|
+
i0.ɵɵadvance();
|
|
654
|
+
i0.ɵɵproperty("ngIf", ctx_r0.loadingSuites);
|
|
655
|
+
i0.ɵɵadvance();
|
|
656
|
+
i0.ɵɵproperty("ngIf", !ctx_r0.loadingSuites && ctx_r0.suiteTests.length > 0);
|
|
657
|
+
i0.ɵɵadvance();
|
|
658
|
+
i0.ɵɵproperty("ngIf", ctx_r0.suiteTestsLoaded && !ctx_r0.loadingSuites && ctx_r0.suiteTests.length === 0);
|
|
659
|
+
} }
|
|
660
|
+
function TestFormComponentExtended_div_53_div_1_Template(rf, ctx) { if (rf & 1) {
|
|
661
|
+
i0.ɵɵelementStart(0, "div", 82);
|
|
662
|
+
i0.ɵɵelement(1, "mj-loading", 143);
|
|
663
|
+
i0.ɵɵelementEnd();
|
|
664
|
+
} }
|
|
665
|
+
function TestFormComponentExtended_div_53_div_2_div_17_div_9_Template(rf, ctx) { if (rf & 1) {
|
|
666
|
+
i0.ɵɵelementStart(0, "div", 154)(1, "div", 160);
|
|
667
|
+
i0.ɵɵelement(2, "i", 161);
|
|
668
|
+
i0.ɵɵelementEnd();
|
|
669
|
+
i0.ɵɵelementStart(3, "div", 156)(4, "div", 157);
|
|
670
|
+
i0.ɵɵtext(5);
|
|
671
|
+
i0.ɵɵelement(6, "i", 162);
|
|
672
|
+
i0.ɵɵelementEnd();
|
|
673
|
+
i0.ɵɵelementStart(7, "div", 158);
|
|
674
|
+
i0.ɵɵtext(8, "Pass Rate");
|
|
675
|
+
i0.ɵɵelementEnd()()();
|
|
676
|
+
} if (rf & 2) {
|
|
677
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
678
|
+
i0.ɵɵadvance(5);
|
|
679
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.getOverallPassRate().toFixed(1), "% ");
|
|
680
|
+
i0.ɵɵadvance();
|
|
681
|
+
i0.ɵɵclassProp("fa-arrow-up", ctx_r0.getPassRateTrend() === "up")("fa-arrow-down", ctx_r0.getPassRateTrend() === "down")("fa-minus", ctx_r0.getPassRateTrend() === "stable")("trend-up", ctx_r0.getPassRateTrend() === "up")("trend-down", ctx_r0.getPassRateTrend() === "down");
|
|
682
|
+
} }
|
|
683
|
+
function TestFormComponentExtended_div_53_div_2_div_17_div_10_Template(rf, ctx) { if (rf & 1) {
|
|
684
|
+
i0.ɵɵelementStart(0, "div", 154)(1, "div", 155);
|
|
685
|
+
i0.ɵɵelement(2, "i", 163);
|
|
686
|
+
i0.ɵɵelementEnd();
|
|
687
|
+
i0.ɵɵelementStart(3, "div", 156)(4, "div", 157);
|
|
688
|
+
i0.ɵɵtext(5);
|
|
689
|
+
i0.ɵɵelementEnd();
|
|
690
|
+
i0.ɵɵelementStart(6, "div", 158);
|
|
691
|
+
i0.ɵɵtext(7, "Avg Score");
|
|
692
|
+
i0.ɵɵelementEnd()()();
|
|
693
|
+
} if (rf & 2) {
|
|
694
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
695
|
+
i0.ɵɵadvance(5);
|
|
696
|
+
i0.ɵɵtextInterpolate1("", (ctx_r0.getOverallAvgScore() * 100).toFixed(1), "%");
|
|
697
|
+
} }
|
|
698
|
+
function TestFormComponentExtended_div_53_div_2_div_17_Template(rf, ctx) { if (rf & 1) {
|
|
699
|
+
i0.ɵɵelementStart(0, "div", 153)(1, "div", 154)(2, "div", 155);
|
|
700
|
+
i0.ɵɵelement(3, "i", 129);
|
|
701
|
+
i0.ɵɵelementEnd();
|
|
702
|
+
i0.ɵɵelementStart(4, "div", 156)(5, "div", 157);
|
|
703
|
+
i0.ɵɵtext(6);
|
|
704
|
+
i0.ɵɵelementEnd();
|
|
705
|
+
i0.ɵɵelementStart(7, "div", 158);
|
|
706
|
+
i0.ɵɵtext(8, "Total Runs");
|
|
707
|
+
i0.ɵɵelementEnd()()();
|
|
708
|
+
i0.ɵɵtemplate(9, TestFormComponentExtended_div_53_div_2_div_17_div_9_Template, 9, 11, "div", 159)(10, TestFormComponentExtended_div_53_div_2_div_17_div_10_Template, 8, 1, "div", 159);
|
|
709
|
+
i0.ɵɵelementStart(11, "div", 154)(12, "div", 155);
|
|
710
|
+
i0.ɵɵelement(13, "i", 107);
|
|
711
|
+
i0.ɵɵelementEnd();
|
|
712
|
+
i0.ɵɵelementStart(14, "div", 156)(15, "div", 157);
|
|
713
|
+
i0.ɵɵtext(16);
|
|
714
|
+
i0.ɵɵelementEnd();
|
|
715
|
+
i0.ɵɵelementStart(17, "div", 158);
|
|
716
|
+
i0.ɵɵtext(18, "Avg Duration");
|
|
717
|
+
i0.ɵɵelementEnd()()();
|
|
718
|
+
i0.ɵɵelementStart(19, "div", 154)(20, "div", 155);
|
|
719
|
+
i0.ɵɵelement(21, "i", 108);
|
|
720
|
+
i0.ɵɵelementEnd();
|
|
721
|
+
i0.ɵɵelementStart(22, "div", 156)(23, "div", 157);
|
|
722
|
+
i0.ɵɵtext(24);
|
|
723
|
+
i0.ɵɵelementEnd();
|
|
724
|
+
i0.ɵɵelementStart(25, "div", 158);
|
|
725
|
+
i0.ɵɵtext(26, "Avg Cost");
|
|
726
|
+
i0.ɵɵelementEnd()()()();
|
|
727
|
+
} if (rf & 2) {
|
|
728
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
729
|
+
i0.ɵɵadvance(6);
|
|
730
|
+
i0.ɵɵtextInterpolate(ctx_r0.getTotalRuns());
|
|
731
|
+
i0.ɵɵadvance(3);
|
|
732
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
733
|
+
i0.ɵɵadvance();
|
|
734
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showAuto);
|
|
735
|
+
i0.ɵɵadvance(6);
|
|
736
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(ctx_r0.getOverallAvgDuration()));
|
|
737
|
+
i0.ɵɵadvance(8);
|
|
738
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatCost(ctx_r0.getOverallAvgCost()));
|
|
739
|
+
} }
|
|
740
|
+
function TestFormComponentExtended_div_53_div_2_div_18_th_12_Template(rf, ctx) { if (rf & 1) {
|
|
741
|
+
i0.ɵɵelementStart(0, "th");
|
|
742
|
+
i0.ɵɵtext(1, "Passed");
|
|
743
|
+
i0.ɵɵelementEnd();
|
|
744
|
+
} }
|
|
745
|
+
function TestFormComponentExtended_div_53_div_2_div_18_th_13_Template(rf, ctx) { if (rf & 1) {
|
|
746
|
+
i0.ɵɵelementStart(0, "th");
|
|
747
|
+
i0.ɵɵtext(1, "Failed");
|
|
748
|
+
i0.ɵɵelementEnd();
|
|
749
|
+
} }
|
|
750
|
+
function TestFormComponentExtended_div_53_div_2_div_18_th_14_Template(rf, ctx) { if (rf & 1) {
|
|
751
|
+
i0.ɵɵelementStart(0, "th");
|
|
752
|
+
i0.ɵɵtext(1, "Pass Rate");
|
|
753
|
+
i0.ɵɵelementEnd();
|
|
754
|
+
} }
|
|
755
|
+
function TestFormComponentExtended_div_53_div_2_div_18_th_15_Template(rf, ctx) { if (rf & 1) {
|
|
756
|
+
i0.ɵɵelementStart(0, "th");
|
|
757
|
+
i0.ɵɵtext(1, "Avg Score");
|
|
758
|
+
i0.ɵɵelementEnd();
|
|
759
|
+
} }
|
|
760
|
+
function TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_6_Template(rf, ctx) { if (rf & 1) {
|
|
761
|
+
i0.ɵɵelementStart(0, "td", 177);
|
|
762
|
+
i0.ɵɵtext(1);
|
|
763
|
+
i0.ɵɵelementEnd();
|
|
764
|
+
} if (rf & 2) {
|
|
765
|
+
const day_r12 = i0.ɵɵnextContext().$implicit;
|
|
766
|
+
i0.ɵɵadvance();
|
|
767
|
+
i0.ɵɵtextInterpolate(day_r12.passCount);
|
|
768
|
+
} }
|
|
769
|
+
function TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_7_Template(rf, ctx) { if (rf & 1) {
|
|
770
|
+
i0.ɵɵelementStart(0, "td", 178);
|
|
771
|
+
i0.ɵɵtext(1);
|
|
772
|
+
i0.ɵɵelementEnd();
|
|
773
|
+
} if (rf & 2) {
|
|
774
|
+
const day_r12 = i0.ɵɵnextContext().$implicit;
|
|
775
|
+
i0.ɵɵadvance();
|
|
776
|
+
i0.ɵɵtextInterpolate(day_r12.failCount);
|
|
777
|
+
} }
|
|
778
|
+
function TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_8_Template(rf, ctx) { if (rf & 1) {
|
|
779
|
+
i0.ɵɵelementStart(0, "td", 179)(1, "div", 180);
|
|
780
|
+
i0.ɵɵelement(2, "div", 181);
|
|
781
|
+
i0.ɵɵelementStart(3, "span", 182);
|
|
782
|
+
i0.ɵɵtext(4);
|
|
783
|
+
i0.ɵɵelementEnd()()();
|
|
784
|
+
} if (rf & 2) {
|
|
785
|
+
const day_r12 = i0.ɵɵnextContext().$implicit;
|
|
786
|
+
i0.ɵɵadvance(2);
|
|
787
|
+
i0.ɵɵstyleProp("width", day_r12.passRate, "%");
|
|
788
|
+
i0.ɵɵadvance(2);
|
|
789
|
+
i0.ɵɵtextInterpolate1("", day_r12.passRate.toFixed(1), "%");
|
|
790
|
+
} }
|
|
791
|
+
function TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_9_Template(rf, ctx) { if (rf & 1) {
|
|
792
|
+
i0.ɵɵelementStart(0, "td", 183);
|
|
793
|
+
i0.ɵɵtext(1);
|
|
794
|
+
i0.ɵɵelementEnd();
|
|
795
|
+
} if (rf & 2) {
|
|
796
|
+
const day_r12 = i0.ɵɵnextContext().$implicit;
|
|
797
|
+
i0.ɵɵadvance();
|
|
798
|
+
i0.ɵɵtextInterpolate1("", (day_r12.avgScore * 100).toFixed(1), "%");
|
|
799
|
+
} }
|
|
800
|
+
function TestFormComponentExtended_div_53_div_2_div_18_tr_21_Template(rf, ctx) { if (rf & 1) {
|
|
801
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 169);
|
|
802
|
+
i0.ɵɵtext(2);
|
|
803
|
+
i0.ɵɵpipe(3, "date");
|
|
804
|
+
i0.ɵɵelementEnd();
|
|
805
|
+
i0.ɵɵelementStart(4, "td", 170);
|
|
806
|
+
i0.ɵɵtext(5);
|
|
807
|
+
i0.ɵɵelementEnd();
|
|
808
|
+
i0.ɵɵtemplate(6, TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_6_Template, 2, 1, "td", 171)(7, TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_7_Template, 2, 1, "td", 172)(8, TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_8_Template, 5, 3, "td", 173)(9, TestFormComponentExtended_div_53_div_2_div_18_tr_21_td_9_Template, 2, 1, "td", 174);
|
|
809
|
+
i0.ɵɵelementStart(10, "td", 175);
|
|
810
|
+
i0.ɵɵtext(11);
|
|
811
|
+
i0.ɵɵelementEnd();
|
|
812
|
+
i0.ɵɵelementStart(12, "td", 176);
|
|
813
|
+
i0.ɵɵtext(13);
|
|
814
|
+
i0.ɵɵelementEnd()();
|
|
815
|
+
} if (rf & 2) {
|
|
816
|
+
const day_r12 = ctx.$implicit;
|
|
817
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
818
|
+
i0.ɵɵadvance(2);
|
|
819
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, day_r12.date, "mediumDate"));
|
|
820
|
+
i0.ɵɵadvance(3);
|
|
821
|
+
i0.ɵɵtextInterpolate(day_r12.runCount);
|
|
822
|
+
i0.ɵɵadvance();
|
|
823
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
824
|
+
i0.ɵɵadvance();
|
|
825
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
826
|
+
i0.ɵɵadvance();
|
|
827
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
828
|
+
i0.ɵɵadvance();
|
|
829
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showAuto);
|
|
830
|
+
i0.ɵɵadvance(2);
|
|
831
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(day_r12.avgDuration));
|
|
832
|
+
i0.ɵɵadvance(2);
|
|
833
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatCost(day_r12.avgCost));
|
|
834
|
+
} }
|
|
835
|
+
function TestFormComponentExtended_div_53_div_2_div_18_Template(rf, ctx) { if (rf & 1) {
|
|
836
|
+
i0.ɵɵelementStart(0, "div", 164)(1, "h3");
|
|
837
|
+
i0.ɵɵelement(2, "i", 165);
|
|
838
|
+
i0.ɵɵtext(3, " Daily Performance");
|
|
839
|
+
i0.ɵɵelementEnd();
|
|
840
|
+
i0.ɵɵelementStart(4, "div", 166)(5, "table", 167)(6, "thead")(7, "tr")(8, "th");
|
|
841
|
+
i0.ɵɵtext(9, "Date");
|
|
842
|
+
i0.ɵɵelementEnd();
|
|
843
|
+
i0.ɵɵelementStart(10, "th");
|
|
844
|
+
i0.ɵɵtext(11, "Runs");
|
|
845
|
+
i0.ɵɵelementEnd();
|
|
846
|
+
i0.ɵɵtemplate(12, TestFormComponentExtended_div_53_div_2_div_18_th_12_Template, 2, 0, "th", 101)(13, TestFormComponentExtended_div_53_div_2_div_18_th_13_Template, 2, 0, "th", 101)(14, TestFormComponentExtended_div_53_div_2_div_18_th_14_Template, 2, 0, "th", 101)(15, TestFormComponentExtended_div_53_div_2_div_18_th_15_Template, 2, 0, "th", 101);
|
|
847
|
+
i0.ɵɵelementStart(16, "th");
|
|
848
|
+
i0.ɵɵtext(17, "Avg Duration");
|
|
849
|
+
i0.ɵɵelementEnd();
|
|
850
|
+
i0.ɵɵelementStart(18, "th");
|
|
851
|
+
i0.ɵɵtext(19, "Avg Cost");
|
|
852
|
+
i0.ɵɵelementEnd()()();
|
|
853
|
+
i0.ɵɵelementStart(20, "tbody");
|
|
854
|
+
i0.ɵɵtemplate(21, TestFormComponentExtended_div_53_div_2_div_18_tr_21_Template, 14, 11, "tr", 168);
|
|
855
|
+
i0.ɵɵelementEnd()()()();
|
|
856
|
+
} if (rf & 2) {
|
|
857
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
858
|
+
i0.ɵɵadvance(12);
|
|
859
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
860
|
+
i0.ɵɵadvance();
|
|
861
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
862
|
+
i0.ɵɵadvance();
|
|
863
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
864
|
+
i0.ɵɵadvance();
|
|
865
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showAuto);
|
|
866
|
+
i0.ɵɵadvance(6);
|
|
867
|
+
i0.ɵɵproperty("ngForOf", ctx_r0.historyData);
|
|
868
|
+
} }
|
|
869
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_span_1_Template(rf, ctx) { if (rf & 1) {
|
|
870
|
+
i0.ɵɵelementStart(0, "span", 199);
|
|
871
|
+
i0.ɵɵtext(1);
|
|
872
|
+
i0.ɵɵelementEnd();
|
|
873
|
+
} if (rf & 2) {
|
|
874
|
+
const tag_r15 = ctx.$implicit;
|
|
875
|
+
i0.ɵɵadvance();
|
|
876
|
+
i0.ɵɵtextInterpolate(tag_r15);
|
|
877
|
+
} }
|
|
878
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_span_2_Template(rf, ctx) { if (rf & 1) {
|
|
879
|
+
i0.ɵɵelementStart(0, "span", 200);
|
|
880
|
+
i0.ɵɵtext(1);
|
|
881
|
+
i0.ɵɵelementEnd();
|
|
882
|
+
} if (rf & 2) {
|
|
883
|
+
const suite_r14 = i0.ɵɵnextContext(2).$implicit;
|
|
884
|
+
i0.ɵɵadvance();
|
|
885
|
+
i0.ɵɵtextInterpolate1("+", suite_r14.tags.length - 3, "");
|
|
886
|
+
} }
|
|
887
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_Template(rf, ctx) { if (rf & 1) {
|
|
888
|
+
i0.ɵɵelementStart(0, "div", 196);
|
|
889
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_span_1_Template, 2, 1, "span", 197)(2, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_span_2_Template, 2, 1, "span", 198);
|
|
890
|
+
i0.ɵɵelementEnd();
|
|
891
|
+
} if (rf & 2) {
|
|
892
|
+
const suite_r14 = i0.ɵɵnextContext().$implicit;
|
|
893
|
+
i0.ɵɵadvance();
|
|
894
|
+
i0.ɵɵproperty("ngForOf", suite_r14.tags.slice(0, 3));
|
|
895
|
+
i0.ɵɵadvance();
|
|
896
|
+
i0.ɵɵproperty("ngIf", suite_r14.tags.length > 3);
|
|
897
|
+
} }
|
|
898
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_11_Template(rf, ctx) { if (rf & 1) {
|
|
899
|
+
i0.ɵɵelementStart(0, "div", 191)(1, "span", 201);
|
|
900
|
+
i0.ɵɵtext(2);
|
|
901
|
+
i0.ɵɵelementEnd();
|
|
902
|
+
i0.ɵɵelementStart(3, "span", 193);
|
|
903
|
+
i0.ɵɵtext(4, "Pass Rate");
|
|
904
|
+
i0.ɵɵelementEnd()();
|
|
905
|
+
} if (rf & 2) {
|
|
906
|
+
const suite_r14 = i0.ɵɵnextContext().$implicit;
|
|
907
|
+
i0.ɵɵadvance();
|
|
908
|
+
i0.ɵɵclassProp("high", suite_r14.passRate >= 80)("low", suite_r14.passRate < 50);
|
|
909
|
+
i0.ɵɵadvance();
|
|
910
|
+
i0.ɵɵtextInterpolate1(" ", suite_r14.passRate.toFixed(1), "% ");
|
|
911
|
+
} }
|
|
912
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_12_Template(rf, ctx) { if (rf & 1) {
|
|
913
|
+
i0.ɵɵelementStart(0, "div", 191)(1, "span", 192);
|
|
914
|
+
i0.ɵɵtext(2);
|
|
915
|
+
i0.ɵɵelementEnd();
|
|
916
|
+
i0.ɵɵelementStart(3, "span", 193);
|
|
917
|
+
i0.ɵɵtext(4, "Avg Score");
|
|
918
|
+
i0.ɵɵelementEnd()();
|
|
919
|
+
} if (rf & 2) {
|
|
920
|
+
const suite_r14 = i0.ɵɵnextContext().$implicit;
|
|
921
|
+
i0.ɵɵadvance(2);
|
|
922
|
+
i0.ɵɵtextInterpolate1("", (suite_r14.avgScore * 100).toFixed(1), "%");
|
|
923
|
+
} }
|
|
924
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_div_23_Template(rf, ctx) { if (rf & 1) {
|
|
925
|
+
i0.ɵɵelementStart(0, "div", 191)(1, "span", 192);
|
|
926
|
+
i0.ɵɵtext(2);
|
|
927
|
+
i0.ɵɵelementEnd();
|
|
928
|
+
i0.ɵɵelementStart(3, "span", 193);
|
|
929
|
+
i0.ɵɵtext(4, "Last Run");
|
|
930
|
+
i0.ɵɵelementEnd()();
|
|
931
|
+
} if (rf & 2) {
|
|
932
|
+
const suite_r14 = i0.ɵɵnextContext().$implicit;
|
|
933
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
934
|
+
i0.ɵɵadvance(2);
|
|
935
|
+
i0.ɵɵtextInterpolate(ctx_r0.getRelativeTime(suite_r14.lastRun));
|
|
936
|
+
} }
|
|
937
|
+
function TestFormComponentExtended_div_53_div_2_div_19_div_5_Template(rf, ctx) { if (rf & 1) {
|
|
938
|
+
const _r13 = i0.ɵɵgetCurrentView();
|
|
939
|
+
i0.ɵɵelementStart(0, "div", 186);
|
|
940
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_div_19_div_5_Template_div_click_0_listener() { const suite_r14 = i0.ɵɵrestoreView(_r13).$implicit; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.openSuiteFromHistory(suite_r14.suiteId)); });
|
|
941
|
+
i0.ɵɵelementStart(1, "div", 187)(2, "div", 188);
|
|
942
|
+
i0.ɵɵtext(3);
|
|
943
|
+
i0.ɵɵelementEnd();
|
|
944
|
+
i0.ɵɵtemplate(4, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_4_Template, 3, 2, "div", 189);
|
|
945
|
+
i0.ɵɵelementEnd();
|
|
946
|
+
i0.ɵɵelementStart(5, "div", 190)(6, "div", 191)(7, "span", 192);
|
|
947
|
+
i0.ɵɵtext(8);
|
|
948
|
+
i0.ɵɵelementEnd();
|
|
949
|
+
i0.ɵɵelementStart(9, "span", 193);
|
|
950
|
+
i0.ɵɵtext(10, "Runs");
|
|
951
|
+
i0.ɵɵelementEnd()();
|
|
952
|
+
i0.ɵɵtemplate(11, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_11_Template, 5, 5, "div", 194)(12, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_12_Template, 5, 1, "div", 194);
|
|
953
|
+
i0.ɵɵelementStart(13, "div", 191)(14, "span", 192);
|
|
954
|
+
i0.ɵɵtext(15);
|
|
955
|
+
i0.ɵɵelementEnd();
|
|
956
|
+
i0.ɵɵelementStart(16, "span", 193);
|
|
957
|
+
i0.ɵɵtext(17, "Avg Duration");
|
|
958
|
+
i0.ɵɵelementEnd()();
|
|
959
|
+
i0.ɵɵelementStart(18, "div", 191)(19, "span", 192);
|
|
960
|
+
i0.ɵɵtext(20);
|
|
961
|
+
i0.ɵɵelementEnd();
|
|
962
|
+
i0.ɵɵelementStart(21, "span", 193);
|
|
963
|
+
i0.ɵɵtext(22, "Avg Cost");
|
|
964
|
+
i0.ɵɵelementEnd()();
|
|
965
|
+
i0.ɵɵtemplate(23, TestFormComponentExtended_div_53_div_2_div_19_div_5_div_23_Template, 5, 1, "div", 194);
|
|
966
|
+
i0.ɵɵelementEnd();
|
|
967
|
+
i0.ɵɵelement(24, "i", 195);
|
|
968
|
+
i0.ɵɵelementEnd();
|
|
969
|
+
} if (rf & 2) {
|
|
970
|
+
const suite_r14 = ctx.$implicit;
|
|
971
|
+
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
972
|
+
i0.ɵɵadvance(3);
|
|
973
|
+
i0.ɵɵtextInterpolate(suite_r14.suiteName);
|
|
974
|
+
i0.ɵɵadvance();
|
|
975
|
+
i0.ɵɵproperty("ngIf", suite_r14.tags.length > 0);
|
|
976
|
+
i0.ɵɵadvance(4);
|
|
977
|
+
i0.ɵɵtextInterpolate(suite_r14.totalRuns);
|
|
978
|
+
i0.ɵɵadvance(3);
|
|
979
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showExecution);
|
|
980
|
+
i0.ɵɵadvance();
|
|
981
|
+
i0.ɵɵproperty("ngIf", ctx_r0.evalPreferences.showAuto);
|
|
982
|
+
i0.ɵɵadvance(3);
|
|
983
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(suite_r14.avgDuration));
|
|
984
|
+
i0.ɵɵadvance(5);
|
|
985
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatCost(suite_r14.avgCost));
|
|
986
|
+
i0.ɵɵadvance(3);
|
|
987
|
+
i0.ɵɵproperty("ngIf", suite_r14.lastRun);
|
|
988
|
+
} }
|
|
989
|
+
function TestFormComponentExtended_div_53_div_2_div_19_Template(rf, ctx) { if (rf & 1) {
|
|
990
|
+
i0.ɵɵelementStart(0, "div", 164)(1, "h3");
|
|
991
|
+
i0.ɵɵelement(2, "i", 24);
|
|
992
|
+
i0.ɵɵtext(3, " Performance by Suite");
|
|
993
|
+
i0.ɵɵelementEnd();
|
|
994
|
+
i0.ɵɵelementStart(4, "div", 184);
|
|
995
|
+
i0.ɵɵtemplate(5, TestFormComponentExtended_div_53_div_2_div_19_div_5_Template, 25, 8, "div", 185);
|
|
397
996
|
i0.ɵɵelementEnd()();
|
|
997
|
+
} if (rf & 2) {
|
|
998
|
+
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
999
|
+
i0.ɵɵadvance(5);
|
|
1000
|
+
i0.ɵɵproperty("ngForOf", ctx_r0.suitePerformance);
|
|
398
1001
|
} }
|
|
399
|
-
function
|
|
400
|
-
i0.ɵɵ
|
|
401
|
-
i0.ɵɵ
|
|
1002
|
+
function TestFormComponentExtended_div_53_div_2_div_20_Template(rf, ctx) { if (rf & 1) {
|
|
1003
|
+
const _r16 = i0.ɵɵgetCurrentView();
|
|
1004
|
+
i0.ɵɵelementStart(0, "div", 127)(1, "div", 128);
|
|
1005
|
+
i0.ɵɵelement(2, "i", 25);
|
|
1006
|
+
i0.ɵɵelementEnd();
|
|
1007
|
+
i0.ɵɵelementStart(3, "h4");
|
|
1008
|
+
i0.ɵɵtext(4, "No History Available");
|
|
1009
|
+
i0.ɵɵelementEnd();
|
|
1010
|
+
i0.ɵɵelementStart(5, "p");
|
|
1011
|
+
i0.ɵɵtext(6, "Run this test to start building history and analytics data.");
|
|
1012
|
+
i0.ɵɵelementEnd();
|
|
1013
|
+
i0.ɵɵelementStart(7, "button", 12);
|
|
1014
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_div_20_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r16); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.runTest()); });
|
|
1015
|
+
i0.ɵɵelement(8, "i", 13);
|
|
1016
|
+
i0.ɵɵtext(9, " Run Test Now ");
|
|
1017
|
+
i0.ɵɵelementEnd()();
|
|
1018
|
+
} }
|
|
1019
|
+
function TestFormComponentExtended_div_53_div_2_Template(rf, ctx) { if (rf & 1) {
|
|
1020
|
+
const _r11 = i0.ɵɵgetCurrentView();
|
|
1021
|
+
i0.ɵɵelementStart(0, "div", 144)(1, "div", 145)(2, "div", 146)(3, "span", 147);
|
|
1022
|
+
i0.ɵɵtext(4, "Time Range:");
|
|
1023
|
+
i0.ɵɵelementEnd();
|
|
1024
|
+
i0.ɵɵelementStart(5, "button", 148);
|
|
1025
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("7d")); });
|
|
1026
|
+
i0.ɵɵtext(6, "7 Days");
|
|
1027
|
+
i0.ɵɵelementEnd();
|
|
1028
|
+
i0.ɵɵelementStart(7, "button", 148);
|
|
1029
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("30d")); });
|
|
1030
|
+
i0.ɵɵtext(8, "30 Days");
|
|
1031
|
+
i0.ɵɵelementEnd();
|
|
1032
|
+
i0.ɵɵelementStart(9, "button", 148);
|
|
1033
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("90d")); });
|
|
1034
|
+
i0.ɵɵtext(10, "90 Days");
|
|
1035
|
+
i0.ɵɵelementEnd();
|
|
1036
|
+
i0.ɵɵelementStart(11, "button", 148);
|
|
1037
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("all")); });
|
|
1038
|
+
i0.ɵɵtext(12, "All Time");
|
|
1039
|
+
i0.ɵɵelementEnd()();
|
|
1040
|
+
i0.ɵɵelementStart(13, "div", 149)(14, "button", 14);
|
|
1041
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_53_div_2_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r11); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.exportHistoryToCSV()); });
|
|
1042
|
+
i0.ɵɵelement(15, "i", 150);
|
|
1043
|
+
i0.ɵɵtext(16, " Export CSV ");
|
|
1044
|
+
i0.ɵɵelementEnd()()();
|
|
1045
|
+
i0.ɵɵtemplate(17, TestFormComponentExtended_div_53_div_2_div_17_Template, 27, 5, "div", 151)(18, TestFormComponentExtended_div_53_div_2_div_18_Template, 22, 5, "div", 152)(19, TestFormComponentExtended_div_53_div_2_div_19_Template, 6, 1, "div", 152)(20, TestFormComponentExtended_div_53_div_2_div_20_Template, 10, 0, "div", 81);
|
|
1046
|
+
i0.ɵɵelementEnd();
|
|
1047
|
+
} if (rf & 2) {
|
|
1048
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
1049
|
+
i0.ɵɵadvance(5);
|
|
1050
|
+
i0.ɵɵclassProp("active", ctx_r0.historyTimeRange === "7d");
|
|
1051
|
+
i0.ɵɵadvance(2);
|
|
1052
|
+
i0.ɵɵclassProp("active", ctx_r0.historyTimeRange === "30d");
|
|
1053
|
+
i0.ɵɵadvance(2);
|
|
1054
|
+
i0.ɵɵclassProp("active", ctx_r0.historyTimeRange === "90d");
|
|
1055
|
+
i0.ɵɵadvance(2);
|
|
1056
|
+
i0.ɵɵclassProp("active", ctx_r0.historyTimeRange === "all");
|
|
1057
|
+
i0.ɵɵadvance(3);
|
|
1058
|
+
i0.ɵɵproperty("disabled", ctx_r0.historyData.length === 0);
|
|
1059
|
+
i0.ɵɵadvance(3);
|
|
1060
|
+
i0.ɵɵproperty("ngIf", ctx_r0.historyData.length > 0);
|
|
1061
|
+
i0.ɵɵadvance();
|
|
1062
|
+
i0.ɵɵproperty("ngIf", ctx_r0.historyData.length > 0);
|
|
1063
|
+
i0.ɵɵadvance();
|
|
1064
|
+
i0.ɵɵproperty("ngIf", ctx_r0.suitePerformance.length > 0);
|
|
1065
|
+
i0.ɵɵadvance();
|
|
1066
|
+
i0.ɵɵproperty("ngIf", ctx_r0.historyData.length === 0);
|
|
1067
|
+
} }
|
|
1068
|
+
function TestFormComponentExtended_div_53_Template(rf, ctx) { if (rf & 1) {
|
|
1069
|
+
i0.ɵɵelementStart(0, "div", 141);
|
|
1070
|
+
i0.ɵɵtemplate(1, TestFormComponentExtended_div_53_div_1_Template, 2, 0, "div", 79)(2, TestFormComponentExtended_div_53_div_2_Template, 21, 13, "div", 142);
|
|
402
1071
|
i0.ɵɵelementEnd();
|
|
403
1072
|
} if (rf & 2) {
|
|
404
1073
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
405
1074
|
i0.ɵɵadvance();
|
|
406
|
-
i0.ɵɵproperty("ngIf", ctx_r0.
|
|
1075
|
+
i0.ɵɵproperty("ngIf", ctx_r0.loadingHistory);
|
|
407
1076
|
i0.ɵɵadvance();
|
|
408
|
-
i0.ɵɵproperty("ngIf", ctx_r0.
|
|
1077
|
+
i0.ɵɵproperty("ngIf", !ctx_r0.loadingHistory && ctx_r0.historyLoaded);
|
|
1078
|
+
} }
|
|
1079
|
+
function TestFormComponentExtended_div_56_Template(rf, ctx) { if (rf & 1) {
|
|
1080
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
1081
|
+
i0.ɵɵelementStart(0, "div", 202)(1, "div", 203);
|
|
1082
|
+
i0.ɵɵelement(2, "i", 33);
|
|
1083
|
+
i0.ɵɵtext(3, " Shortcuts ");
|
|
1084
|
+
i0.ɵɵelementStart(4, "button", 204);
|
|
1085
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_div_56_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.toggleShortcuts()); });
|
|
1086
|
+
i0.ɵɵelement(5, "i", 205);
|
|
1087
|
+
i0.ɵɵelementEnd()();
|
|
1088
|
+
i0.ɵɵelementStart(6, "div", 206)(7, "div", 207)(8, "span");
|
|
1089
|
+
i0.ɵɵtext(9, "Refresh");
|
|
1090
|
+
i0.ɵɵelementEnd();
|
|
1091
|
+
i0.ɵɵelementStart(10, "span", 208)(11, "kbd");
|
|
1092
|
+
i0.ɵɵtext(12, "Cmd");
|
|
1093
|
+
i0.ɵɵelementEnd();
|
|
1094
|
+
i0.ɵɵelementStart(13, "kbd");
|
|
1095
|
+
i0.ɵɵtext(14, "R");
|
|
1096
|
+
i0.ɵɵelementEnd()()();
|
|
1097
|
+
i0.ɵɵelementStart(15, "div", 207)(16, "span");
|
|
1098
|
+
i0.ɵɵtext(17, "Run Test");
|
|
1099
|
+
i0.ɵɵelementEnd();
|
|
1100
|
+
i0.ɵɵelementStart(18, "span", 208)(19, "kbd");
|
|
1101
|
+
i0.ɵɵtext(20, "Cmd");
|
|
1102
|
+
i0.ɵɵelementEnd();
|
|
1103
|
+
i0.ɵɵelementStart(21, "kbd");
|
|
1104
|
+
i0.ɵɵtext(22, "Enter");
|
|
1105
|
+
i0.ɵɵelementEnd()()();
|
|
1106
|
+
i0.ɵɵelementStart(23, "div", 207)(24, "span");
|
|
1107
|
+
i0.ɵɵtext(25, "Switch Tabs");
|
|
1108
|
+
i0.ɵɵelementEnd();
|
|
1109
|
+
i0.ɵɵelementStart(26, "span", 208)(27, "kbd");
|
|
1110
|
+
i0.ɵɵtext(28, "1");
|
|
1111
|
+
i0.ɵɵelementEnd();
|
|
1112
|
+
i0.ɵɵtext(29, "-");
|
|
1113
|
+
i0.ɵɵelementStart(30, "kbd");
|
|
1114
|
+
i0.ɵɵtext(31, "5");
|
|
1115
|
+
i0.ɵɵelementEnd()()()()();
|
|
409
1116
|
} }
|
|
1117
|
+
/** Settings key for keyboard shortcuts visibility */
|
|
1118
|
+
const SHORTCUTS_SETTINGS_KEY = '__mj.Testing.ShowKeyboardShortcuts';
|
|
410
1119
|
let TestFormComponentExtended = class TestFormComponentExtended extends TestFormComponent {
|
|
411
|
-
constructor(elementRef, sharedService, router, route, cdr, testingDialogService) {
|
|
1120
|
+
constructor(elementRef, sharedService, router, route, cdr, testingDialogService, evalPrefsService, viewContainerRef) {
|
|
412
1121
|
super(elementRef, sharedService, router, route, cdr);
|
|
413
1122
|
this.router = router;
|
|
414
1123
|
this.cdr = cdr;
|
|
415
1124
|
this.testingDialogService = testingDialogService;
|
|
1125
|
+
this.evalPrefsService = evalPrefsService;
|
|
1126
|
+
this.viewContainerRef = viewContainerRef;
|
|
416
1127
|
this.destroy$ = new Subject();
|
|
417
1128
|
// UI state
|
|
418
1129
|
this.activeTab = 'overview';
|
|
419
1130
|
this.loading = false;
|
|
1131
|
+
this.loadingRuns = false;
|
|
1132
|
+
this.loadingSuites = false;
|
|
420
1133
|
this.error = null;
|
|
421
1134
|
this.testRunsLoaded = false;
|
|
422
1135
|
this.suiteTestsLoaded = false;
|
|
1136
|
+
this.isRefreshing = false;
|
|
423
1137
|
// Related data
|
|
424
1138
|
this.testRuns = [];
|
|
425
1139
|
this.suiteTests = [];
|
|
1140
|
+
// Human feedback map: testRunId -> feedback entity
|
|
1141
|
+
this.feedbackMap = new Map();
|
|
1142
|
+
// History tab data
|
|
1143
|
+
this.historyLoaded = false;
|
|
1144
|
+
this.loadingHistory = false;
|
|
1145
|
+
this.historyTimeRange = '30d';
|
|
1146
|
+
this.historyData = [];
|
|
1147
|
+
this.suitePerformance = [];
|
|
1148
|
+
this.uniqueTags = [];
|
|
1149
|
+
this.selectedTagFilter = null;
|
|
426
1150
|
// Parsed JSON fields
|
|
427
1151
|
this.parsedData = {};
|
|
428
1152
|
// Active JSON view
|
|
429
1153
|
this.activeJsonView = 'input';
|
|
1154
|
+
// Code editor configuration
|
|
1155
|
+
this.jsonToolbar = createCopyOnlyToolbar();
|
|
1156
|
+
// Keyboard shortcuts
|
|
1157
|
+
this.keyboardShortcutsEnabled = true;
|
|
1158
|
+
this.showShortcuts = false; // Hidden by default
|
|
1159
|
+
this.shortcutsSettingEntity = null;
|
|
1160
|
+
this.metadata = new Metadata();
|
|
1161
|
+
// Evaluation preferences
|
|
1162
|
+
this.evalPreferences = { showExecution: true, showHuman: true, showAuto: false };
|
|
430
1163
|
}
|
|
431
1164
|
async ngOnInit() {
|
|
432
1165
|
await super.ngOnInit();
|
|
1166
|
+
this.loadShortcutsSetting();
|
|
1167
|
+
// Subscribe to evaluation preferences
|
|
1168
|
+
this.evalPrefsService.preferences$
|
|
1169
|
+
.pipe(takeUntil(this.destroy$))
|
|
1170
|
+
.subscribe(prefs => {
|
|
1171
|
+
this.evalPreferences = prefs;
|
|
1172
|
+
this.cdr.markForCheck();
|
|
1173
|
+
});
|
|
433
1174
|
if (this.record && this.record.ID) {
|
|
434
|
-
await this.loadRelatedData();
|
|
435
1175
|
this.parseJsonFields();
|
|
436
1176
|
}
|
|
437
1177
|
}
|
|
@@ -439,23 +1179,48 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
439
1179
|
this.destroy$.next();
|
|
440
1180
|
this.destroy$.complete();
|
|
441
1181
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
1182
|
+
// Keyboard shortcuts
|
|
1183
|
+
handleKeyboardShortcut(event) {
|
|
1184
|
+
if (!this.keyboardShortcutsEnabled)
|
|
1185
|
+
return;
|
|
1186
|
+
// Cmd/Ctrl + R: Refresh
|
|
1187
|
+
if ((event.metaKey || event.ctrlKey) && event.key === 'r' && !event.shiftKey) {
|
|
1188
|
+
event.preventDefault();
|
|
1189
|
+
this.refresh();
|
|
1190
|
+
return;
|
|
446
1191
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
1192
|
+
// Cmd/Ctrl + Enter: Run test
|
|
1193
|
+
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
|
1194
|
+
event.preventDefault();
|
|
1195
|
+
this.runTest();
|
|
1196
|
+
return;
|
|
450
1197
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1198
|
+
// Number keys for tabs (1-5)
|
|
1199
|
+
if (!event.metaKey && !event.ctrlKey && !event.altKey) {
|
|
1200
|
+
switch (event.key) {
|
|
1201
|
+
case '1':
|
|
1202
|
+
this.changeTab('overview');
|
|
1203
|
+
break;
|
|
1204
|
+
case '2':
|
|
1205
|
+
this.changeTab('config');
|
|
1206
|
+
break;
|
|
1207
|
+
case '3':
|
|
1208
|
+
this.changeTab('runs');
|
|
1209
|
+
break;
|
|
1210
|
+
case '4':
|
|
1211
|
+
this.changeTab('suites');
|
|
1212
|
+
break;
|
|
1213
|
+
case '5':
|
|
1214
|
+
this.changeTab('analytics');
|
|
1215
|
+
break;
|
|
1216
|
+
}
|
|
454
1217
|
}
|
|
455
1218
|
}
|
|
456
1219
|
async loadTestRuns() {
|
|
457
1220
|
if (this.testRunsLoaded)
|
|
458
1221
|
return;
|
|
1222
|
+
this.loadingRuns = true;
|
|
1223
|
+
this.cdr.markForCheck();
|
|
459
1224
|
try {
|
|
460
1225
|
const rv = new RunView();
|
|
461
1226
|
const result = await rv.RunView({
|
|
@@ -467,17 +1232,87 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
467
1232
|
});
|
|
468
1233
|
if (result.Success) {
|
|
469
1234
|
this.testRuns = result.Results || [];
|
|
1235
|
+
// Load feedbacks for all test runs
|
|
1236
|
+
if (this.testRuns.length > 0) {
|
|
1237
|
+
await this.loadFeedbacksForTestRuns(this.testRuns.map(r => r.ID));
|
|
1238
|
+
}
|
|
470
1239
|
}
|
|
471
1240
|
this.testRunsLoaded = true;
|
|
472
|
-
this.cdr.markForCheck();
|
|
473
1241
|
}
|
|
474
1242
|
catch (error) {
|
|
475
1243
|
console.error('Error loading test runs:', error);
|
|
1244
|
+
SharedService.Instance.CreateSimpleNotification('Failed to load test runs', 'error', 3000);
|
|
476
1245
|
}
|
|
1246
|
+
finally {
|
|
1247
|
+
this.loadingRuns = false;
|
|
1248
|
+
this.cdr.markForCheck();
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Load feedbacks for a batch of test run IDs
|
|
1253
|
+
*/
|
|
1254
|
+
async loadFeedbacksForTestRuns(testRunIds) {
|
|
1255
|
+
if (testRunIds.length === 0)
|
|
1256
|
+
return;
|
|
1257
|
+
try {
|
|
1258
|
+
const rv = new RunView();
|
|
1259
|
+
const chunkSize = 50;
|
|
1260
|
+
for (let i = 0; i < testRunIds.length; i += chunkSize) {
|
|
1261
|
+
const chunk = testRunIds.slice(i, i + chunkSize);
|
|
1262
|
+
const inClause = chunk.map(id => `'${id}'`).join(',');
|
|
1263
|
+
const result = await rv.RunView({
|
|
1264
|
+
EntityName: 'MJ: Test Run Feedbacks',
|
|
1265
|
+
ExtraFilter: `TestRunID IN (${inClause})`,
|
|
1266
|
+
ResultType: 'entity_object'
|
|
1267
|
+
});
|
|
1268
|
+
if (result.Success && result.Results) {
|
|
1269
|
+
for (const feedback of result.Results) {
|
|
1270
|
+
this.feedbackMap.set(feedback.TestRunID, feedback);
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
catch (error) {
|
|
1276
|
+
console.warn('Failed to load feedbacks:', error);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Get feedback for a specific test run
|
|
1281
|
+
*/
|
|
1282
|
+
getFeedbackForRun(testRunId) {
|
|
1283
|
+
return this.feedbackMap.get(testRunId);
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Get tooltip for status indicator
|
|
1287
|
+
*/
|
|
1288
|
+
getStatusTooltip(status) {
|
|
1289
|
+
switch (status) {
|
|
1290
|
+
case 'Passed': return 'Status: Passed - Test completed without error';
|
|
1291
|
+
case 'Failed': return 'Status: Failed - Test assertions did not pass';
|
|
1292
|
+
case 'Error': return 'Status: Error - Test encountered an exception';
|
|
1293
|
+
case 'Timeout': return 'Status: Timeout - Test exceeded time limit';
|
|
1294
|
+
case 'Skipped': return 'Status: Skipped - Test was not executed';
|
|
1295
|
+
case 'Running': return 'Status: Running - Test is currently executing';
|
|
1296
|
+
case 'Pending': return 'Status: Pending - Test waiting to run';
|
|
1297
|
+
default: return `Status: ${status}`;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Get tooltip for human review with rating and optional comments
|
|
1302
|
+
*/
|
|
1303
|
+
getHumanTooltip(rating, comments) {
|
|
1304
|
+
let tooltip = `Human Review: ${rating}/10 rating`;
|
|
1305
|
+
if (comments) {
|
|
1306
|
+
const truncated = comments.length > 200 ? comments.substring(0, 200) + '...' : comments;
|
|
1307
|
+
tooltip += `\n\n"${truncated}"`;
|
|
1308
|
+
}
|
|
1309
|
+
return tooltip;
|
|
477
1310
|
}
|
|
478
1311
|
async loadSuiteTests() {
|
|
479
1312
|
if (this.suiteTestsLoaded)
|
|
480
1313
|
return;
|
|
1314
|
+
this.loadingSuites = true;
|
|
1315
|
+
this.cdr.markForCheck();
|
|
481
1316
|
try {
|
|
482
1317
|
const rv = new RunView();
|
|
483
1318
|
const result = await rv.RunView({
|
|
@@ -490,10 +1325,14 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
490
1325
|
this.suiteTests = result.Results || [];
|
|
491
1326
|
}
|
|
492
1327
|
this.suiteTestsLoaded = true;
|
|
493
|
-
this.cdr.markForCheck();
|
|
494
1328
|
}
|
|
495
1329
|
catch (error) {
|
|
496
1330
|
console.error('Error loading suite tests:', error);
|
|
1331
|
+
SharedService.Instance.CreateSimpleNotification('Failed to load test suites', 'error', 3000);
|
|
1332
|
+
}
|
|
1333
|
+
finally {
|
|
1334
|
+
this.loadingSuites = false;
|
|
1335
|
+
this.cdr.markForCheck();
|
|
497
1336
|
}
|
|
498
1337
|
}
|
|
499
1338
|
parseJsonFields() {
|
|
@@ -524,6 +1363,9 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
524
1363
|
if (tab === 'suites' && !this.suiteTestsLoaded) {
|
|
525
1364
|
this.loadSuiteTests();
|
|
526
1365
|
}
|
|
1366
|
+
if (tab === 'analytics' && !this.historyLoaded) {
|
|
1367
|
+
this.loadHistory();
|
|
1368
|
+
}
|
|
527
1369
|
this.cdr.markForCheck();
|
|
528
1370
|
}
|
|
529
1371
|
setJsonView(view) {
|
|
@@ -532,10 +1374,10 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
532
1374
|
}
|
|
533
1375
|
getStatusColor() {
|
|
534
1376
|
switch (this.record.Status) {
|
|
535
|
-
case 'Active': return '#
|
|
536
|
-
case 'Disabled': return '#
|
|
537
|
-
case 'Pending': return '#
|
|
538
|
-
default: return '#
|
|
1377
|
+
case 'Active': return '#10b981';
|
|
1378
|
+
case 'Disabled': return '#6b7280';
|
|
1379
|
+
case 'Pending': return '#f59e0b';
|
|
1380
|
+
default: return '#9ca3af';
|
|
539
1381
|
}
|
|
540
1382
|
}
|
|
541
1383
|
getStatusIcon() {
|
|
@@ -546,6 +1388,20 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
546
1388
|
default: return 'fa-circle-question';
|
|
547
1389
|
}
|
|
548
1390
|
}
|
|
1391
|
+
getStatusClass() {
|
|
1392
|
+
return `status-${this.record.Status?.toLowerCase() || 'unknown'}`;
|
|
1393
|
+
}
|
|
1394
|
+
getRunStatusColor(status) {
|
|
1395
|
+
switch (status) {
|
|
1396
|
+
case 'Passed': return '#10b981';
|
|
1397
|
+
case 'Failed': return '#ef4444';
|
|
1398
|
+
case 'Error': return '#f59e0b';
|
|
1399
|
+
case 'Timeout': return '#f97316';
|
|
1400
|
+
case 'Running': return '#3b82f6';
|
|
1401
|
+
case 'Pending': return '#8b5cf6';
|
|
1402
|
+
default: return '#6b7280';
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
549
1405
|
getPassRate() {
|
|
550
1406
|
if (this.testRuns.length === 0)
|
|
551
1407
|
return 0;
|
|
@@ -579,40 +1435,399 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
579
1435
|
formatCost(cost) {
|
|
580
1436
|
return `$${cost.toFixed(6)}`;
|
|
581
1437
|
}
|
|
1438
|
+
formatTimeout(ms) {
|
|
1439
|
+
if (ms === null || ms === undefined)
|
|
1440
|
+
return 'Default (5 min)';
|
|
1441
|
+
if (ms < 1000)
|
|
1442
|
+
return `${ms}ms`;
|
|
1443
|
+
if (ms < 60000)
|
|
1444
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
1445
|
+
if (ms < 3600000) {
|
|
1446
|
+
const mins = Math.floor(ms / 60000);
|
|
1447
|
+
const secs = Math.floor((ms % 60000) / 1000);
|
|
1448
|
+
return secs > 0 ? `${mins}m ${secs}s` : `${mins}m`;
|
|
1449
|
+
}
|
|
1450
|
+
const hours = Math.floor(ms / 3600000);
|
|
1451
|
+
const mins = Math.floor((ms % 3600000) / 60000);
|
|
1452
|
+
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
1453
|
+
}
|
|
582
1454
|
openTestRun(runId) {
|
|
583
1455
|
SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(runId));
|
|
584
1456
|
}
|
|
1457
|
+
getRunTags(run) {
|
|
1458
|
+
return TagsHelper.parseTags(run.Tags);
|
|
1459
|
+
}
|
|
585
1460
|
openTestSuite(suiteId) {
|
|
586
1461
|
SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
|
|
587
1462
|
}
|
|
588
1463
|
async runTest() {
|
|
589
1464
|
if (this.record?.ID) {
|
|
590
|
-
this.testingDialogService.OpenTestDialog(this.record.ID);
|
|
1465
|
+
this.testingDialogService.OpenTestDialog(this.record.ID, this.viewContainerRef);
|
|
591
1466
|
}
|
|
592
1467
|
}
|
|
593
1468
|
async refresh() {
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
await this.
|
|
1469
|
+
this.isRefreshing = true;
|
|
1470
|
+
this.cdr.markForCheck();
|
|
1471
|
+
try {
|
|
1472
|
+
await this.record.Load(this.record.ID);
|
|
1473
|
+
this.parseJsonFields();
|
|
1474
|
+
// Reset lazy-loaded data to force reload
|
|
1475
|
+
if (this.testRunsLoaded) {
|
|
1476
|
+
this.testRunsLoaded = false;
|
|
1477
|
+
this.testRuns = [];
|
|
1478
|
+
await this.loadTestRuns();
|
|
1479
|
+
}
|
|
1480
|
+
if (this.suiteTestsLoaded) {
|
|
1481
|
+
this.suiteTestsLoaded = false;
|
|
1482
|
+
this.suiteTests = [];
|
|
1483
|
+
await this.loadSuiteTests();
|
|
1484
|
+
}
|
|
1485
|
+
SharedService.Instance.CreateSimpleNotification('Refreshed successfully', 'success', 2000);
|
|
598
1486
|
}
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
1487
|
+
catch {
|
|
1488
|
+
SharedService.Instance.CreateSimpleNotification('Failed to refresh', 'error', 3000);
|
|
1489
|
+
}
|
|
1490
|
+
finally {
|
|
1491
|
+
this.isRefreshing = false;
|
|
1492
|
+
this.cdr.markForCheck();
|
|
602
1493
|
}
|
|
603
|
-
this.cdr.markForCheck();
|
|
604
1494
|
}
|
|
605
1495
|
getJsonData() {
|
|
1496
|
+
let data;
|
|
606
1497
|
switch (this.activeJsonView) {
|
|
607
|
-
case 'input':
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
case '
|
|
611
|
-
|
|
1498
|
+
case 'input':
|
|
1499
|
+
data = this.parsedData.inputDefinition;
|
|
1500
|
+
break;
|
|
1501
|
+
case 'expected':
|
|
1502
|
+
data = this.parsedData.expectedOutcomes;
|
|
1503
|
+
break;
|
|
1504
|
+
case 'config':
|
|
1505
|
+
data = this.parsedData.configuration;
|
|
1506
|
+
break;
|
|
1507
|
+
case 'tags':
|
|
1508
|
+
data = this.parsedData.tags;
|
|
1509
|
+
break;
|
|
1510
|
+
}
|
|
1511
|
+
return data ? JSON.stringify(data, null, 2) : '// No data available';
|
|
1512
|
+
}
|
|
1513
|
+
getRelativeTime(date) {
|
|
1514
|
+
if (!date)
|
|
1515
|
+
return 'N/A';
|
|
1516
|
+
const d = new Date(date);
|
|
1517
|
+
const now = new Date();
|
|
1518
|
+
const diffMs = now.getTime() - d.getTime();
|
|
1519
|
+
const diffMins = Math.floor(diffMs / 60000);
|
|
1520
|
+
const diffHours = Math.floor(diffMs / 3600000);
|
|
1521
|
+
const diffDays = Math.floor(diffMs / 86400000);
|
|
1522
|
+
if (diffMins < 1)
|
|
1523
|
+
return 'Just now';
|
|
1524
|
+
if (diffMins < 60)
|
|
1525
|
+
return `${diffMins}m ago`;
|
|
1526
|
+
if (diffHours < 24)
|
|
1527
|
+
return `${diffHours}h ago`;
|
|
1528
|
+
if (diffDays < 7)
|
|
1529
|
+
return `${diffDays}d ago`;
|
|
1530
|
+
return d.toLocaleDateString();
|
|
1531
|
+
}
|
|
1532
|
+
// ===========================
|
|
1533
|
+
// History Tab Methods
|
|
1534
|
+
// ===========================
|
|
1535
|
+
async loadHistory() {
|
|
1536
|
+
if (this.historyLoaded)
|
|
1537
|
+
return;
|
|
1538
|
+
this.loadingHistory = true;
|
|
1539
|
+
this.cdr.markForCheck();
|
|
1540
|
+
try {
|
|
1541
|
+
// Load all test runs for this test
|
|
1542
|
+
const rv = new RunView();
|
|
1543
|
+
const runsResult = await rv.RunView({
|
|
1544
|
+
EntityName: 'MJ: Test Runs',
|
|
1545
|
+
ExtraFilter: `TestID='${this.record.ID}'`,
|
|
1546
|
+
OrderBy: 'StartedAt DESC',
|
|
1547
|
+
ResultType: 'entity_object'
|
|
1548
|
+
});
|
|
1549
|
+
if (runsResult.Success && runsResult.Results) {
|
|
1550
|
+
const allRuns = runsResult.Results;
|
|
1551
|
+
// Extract unique tags from all runs
|
|
1552
|
+
this.uniqueTags = TagsHelper.getUniqueTags(allRuns.map(r => r.Tags));
|
|
1553
|
+
// Build history data (aggregated by date)
|
|
1554
|
+
this.historyData = this.buildHistoryData(allRuns);
|
|
1555
|
+
// Build suite performance data
|
|
1556
|
+
await this.buildSuitePerformance(allRuns);
|
|
1557
|
+
}
|
|
1558
|
+
this.historyLoaded = true;
|
|
1559
|
+
}
|
|
1560
|
+
catch (error) {
|
|
1561
|
+
console.error('Error loading history:', error);
|
|
1562
|
+
SharedService.Instance.CreateSimpleNotification('Failed to load history', 'error', 3000);
|
|
1563
|
+
}
|
|
1564
|
+
finally {
|
|
1565
|
+
this.loadingHistory = false;
|
|
1566
|
+
this.cdr.markForCheck();
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
buildHistoryData(runs) {
|
|
1570
|
+
// Filter by time range
|
|
1571
|
+
const filteredRuns = this.filterRunsByTimeRange(runs);
|
|
1572
|
+
// Group by date
|
|
1573
|
+
const dateMap = new Map();
|
|
1574
|
+
for (const run of filteredRuns) {
|
|
1575
|
+
if (run.StartedAt) {
|
|
1576
|
+
const dateKey = new Date(run.StartedAt).toISOString().split('T')[0];
|
|
1577
|
+
if (!dateMap.has(dateKey)) {
|
|
1578
|
+
dateMap.set(dateKey, []);
|
|
1579
|
+
}
|
|
1580
|
+
dateMap.get(dateKey).push(run);
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
// Convert to data points
|
|
1584
|
+
const dataPoints = [];
|
|
1585
|
+
for (const [dateKey, dateRuns] of dateMap) {
|
|
1586
|
+
const passCount = dateRuns.filter(r => r.Status === 'Passed').length;
|
|
1587
|
+
const failCount = dateRuns.filter(r => r.Status === 'Failed' || r.Status === 'Error').length;
|
|
1588
|
+
const scores = dateRuns.filter(r => r.Score != null).map(r => r.Score);
|
|
1589
|
+
const durations = dateRuns.filter(r => r.DurationSeconds != null).map(r => r.DurationSeconds);
|
|
1590
|
+
const costs = dateRuns.filter(r => r.CostUSD != null).map(r => r.CostUSD);
|
|
1591
|
+
dataPoints.push({
|
|
1592
|
+
date: new Date(dateKey),
|
|
1593
|
+
passRate: dateRuns.length > 0 ? (passCount / dateRuns.length) * 100 : 0,
|
|
1594
|
+
avgScore: scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0,
|
|
1595
|
+
avgDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
1596
|
+
avgCost: costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0,
|
|
1597
|
+
runCount: dateRuns.length,
|
|
1598
|
+
passCount,
|
|
1599
|
+
failCount
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
// Sort by date descending
|
|
1603
|
+
return dataPoints.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
1604
|
+
}
|
|
1605
|
+
filterRunsByTimeRange(runs) {
|
|
1606
|
+
if (this.historyTimeRange === 'all')
|
|
1607
|
+
return runs;
|
|
1608
|
+
const now = new Date();
|
|
1609
|
+
let cutoff;
|
|
1610
|
+
switch (this.historyTimeRange) {
|
|
1611
|
+
case '7d':
|
|
1612
|
+
cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
1613
|
+
break;
|
|
1614
|
+
case '30d':
|
|
1615
|
+
cutoff = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
|
|
1616
|
+
break;
|
|
1617
|
+
case '90d':
|
|
1618
|
+
cutoff = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000);
|
|
1619
|
+
break;
|
|
1620
|
+
default:
|
|
1621
|
+
return runs;
|
|
1622
|
+
}
|
|
1623
|
+
return runs.filter(r => r.StartedAt && new Date(r.StartedAt) >= cutoff);
|
|
1624
|
+
}
|
|
1625
|
+
async buildSuitePerformance(runs) {
|
|
1626
|
+
// Group runs by suite
|
|
1627
|
+
const suiteMap = new Map();
|
|
1628
|
+
for (const run of runs) {
|
|
1629
|
+
if (run.TestSuiteRunID) {
|
|
1630
|
+
if (!suiteMap.has(run.TestSuiteRunID)) {
|
|
1631
|
+
suiteMap.set(run.TestSuiteRunID, []);
|
|
1632
|
+
}
|
|
1633
|
+
suiteMap.get(run.TestSuiteRunID).push(run);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
// Load suite run info for each unique suite run
|
|
1637
|
+
if (suiteMap.size > 0) {
|
|
1638
|
+
const suiteRunIds = Array.from(suiteMap.keys()).map(id => `'${id}'`).join(',');
|
|
1639
|
+
const rv = new RunView();
|
|
1640
|
+
const suiteRunsResult = await rv.RunView({
|
|
1641
|
+
EntityName: 'MJ: Test Suite Runs',
|
|
1642
|
+
ExtraFilter: `ID IN (${suiteRunIds})`,
|
|
1643
|
+
ResultType: 'entity_object'
|
|
1644
|
+
});
|
|
1645
|
+
if (suiteRunsResult.Success && suiteRunsResult.Results) {
|
|
1646
|
+
// Group by suite ID
|
|
1647
|
+
const suiteIdMap = new Map();
|
|
1648
|
+
for (const suiteRun of suiteRunsResult.Results) {
|
|
1649
|
+
const suiteId = suiteRun.SuiteID;
|
|
1650
|
+
if (!suiteIdMap.has(suiteId)) {
|
|
1651
|
+
suiteIdMap.set(suiteId, { runs: [], suiteRuns: [] });
|
|
1652
|
+
}
|
|
1653
|
+
suiteIdMap.get(suiteId).suiteRuns.push(suiteRun);
|
|
1654
|
+
const testRuns = suiteMap.get(suiteRun.ID) || [];
|
|
1655
|
+
suiteIdMap.get(suiteId).runs.push(...testRuns);
|
|
1656
|
+
}
|
|
1657
|
+
// Build performance data for each suite
|
|
1658
|
+
this.suitePerformance = [];
|
|
1659
|
+
for (const [suiteId, data] of suiteIdMap) {
|
|
1660
|
+
const suiteName = data.suiteRuns[0]?.Suite || 'Unknown Suite';
|
|
1661
|
+
const totalRuns = data.runs.length;
|
|
1662
|
+
const passedRuns = data.runs.filter(r => r.Status === 'Passed').length;
|
|
1663
|
+
const failedRuns = data.runs.filter(r => r.Status === 'Failed' || r.Status === 'Error').length;
|
|
1664
|
+
const scores = data.runs.filter(r => r.Score != null).map(r => r.Score);
|
|
1665
|
+
const durations = data.runs.filter(r => r.DurationSeconds != null).map(r => r.DurationSeconds);
|
|
1666
|
+
const costs = data.runs.filter(r => r.CostUSD != null).map(r => r.CostUSD);
|
|
1667
|
+
// Collect all tags from suite runs
|
|
1668
|
+
const allTags = TagsHelper.getUniqueTags(data.suiteRuns.map(sr => sr.Tags));
|
|
1669
|
+
// Find most recent run
|
|
1670
|
+
const lastRun = data.runs
|
|
1671
|
+
.filter(r => r.StartedAt)
|
|
1672
|
+
.sort((a, b) => new Date(b.StartedAt).getTime() - new Date(a.StartedAt).getTime())[0];
|
|
1673
|
+
this.suitePerformance.push({
|
|
1674
|
+
suiteId,
|
|
1675
|
+
suiteName,
|
|
1676
|
+
totalRuns,
|
|
1677
|
+
passedRuns,
|
|
1678
|
+
failedRuns,
|
|
1679
|
+
passRate: totalRuns > 0 ? (passedRuns / totalRuns) * 100 : 0,
|
|
1680
|
+
avgScore: scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0,
|
|
1681
|
+
avgDuration: durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0,
|
|
1682
|
+
avgCost: costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0,
|
|
1683
|
+
lastRun: lastRun?.StartedAt ? new Date(lastRun.StartedAt) : null,
|
|
1684
|
+
tags: allTags
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
// Sort by total runs descending
|
|
1688
|
+
this.suitePerformance.sort((a, b) => b.totalRuns - a.totalRuns);
|
|
1689
|
+
}
|
|
612
1690
|
}
|
|
613
1691
|
}
|
|
614
|
-
|
|
615
|
-
|
|
1692
|
+
setHistoryTimeRange(range) {
|
|
1693
|
+
this.historyTimeRange = range;
|
|
1694
|
+
this.historyLoaded = false;
|
|
1695
|
+
this.loadHistory();
|
|
1696
|
+
}
|
|
1697
|
+
setTagFilter(tag) {
|
|
1698
|
+
this.selectedTagFilter = tag;
|
|
1699
|
+
this.cdr.markForCheck();
|
|
1700
|
+
}
|
|
1701
|
+
getFilteredHistoryData() {
|
|
1702
|
+
return this.historyData;
|
|
1703
|
+
}
|
|
1704
|
+
getOverallPassRate() {
|
|
1705
|
+
if (this.historyData.length === 0)
|
|
1706
|
+
return 0;
|
|
1707
|
+
const totalRuns = this.historyData.reduce((sum, d) => sum + d.runCount, 0);
|
|
1708
|
+
const totalPassed = this.historyData.reduce((sum, d) => sum + d.passCount, 0);
|
|
1709
|
+
return totalRuns > 0 ? (totalPassed / totalRuns) * 100 : 0;
|
|
1710
|
+
}
|
|
1711
|
+
getOverallAvgScore() {
|
|
1712
|
+
if (this.historyData.length === 0)
|
|
1713
|
+
return 0;
|
|
1714
|
+
const scores = this.historyData.filter(d => d.avgScore > 0).map(d => d.avgScore);
|
|
1715
|
+
return scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0;
|
|
1716
|
+
}
|
|
1717
|
+
getOverallAvgDuration() {
|
|
1718
|
+
if (this.historyData.length === 0)
|
|
1719
|
+
return 0;
|
|
1720
|
+
const durations = this.historyData.filter(d => d.avgDuration > 0).map(d => d.avgDuration);
|
|
1721
|
+
return durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
|
|
1722
|
+
}
|
|
1723
|
+
getOverallAvgCost() {
|
|
1724
|
+
if (this.historyData.length === 0)
|
|
1725
|
+
return 0;
|
|
1726
|
+
const costs = this.historyData.filter(d => d.avgCost > 0).map(d => d.avgCost);
|
|
1727
|
+
return costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0;
|
|
1728
|
+
}
|
|
1729
|
+
getTotalRuns() {
|
|
1730
|
+
return this.historyData.reduce((sum, d) => sum + d.runCount, 0);
|
|
1731
|
+
}
|
|
1732
|
+
getPassRateTrend() {
|
|
1733
|
+
if (this.historyData.length < 2)
|
|
1734
|
+
return 'stable';
|
|
1735
|
+
// Compare recent half to older half
|
|
1736
|
+
const mid = Math.floor(this.historyData.length / 2);
|
|
1737
|
+
const recentData = this.historyData.slice(0, mid);
|
|
1738
|
+
const olderData = this.historyData.slice(mid);
|
|
1739
|
+
const recentRate = recentData.reduce((sum, d) => sum + d.passRate, 0) / recentData.length;
|
|
1740
|
+
const olderRate = olderData.reduce((sum, d) => sum + d.passRate, 0) / olderData.length;
|
|
1741
|
+
const diff = recentRate - olderRate;
|
|
1742
|
+
if (diff > 5)
|
|
1743
|
+
return 'up';
|
|
1744
|
+
if (diff < -5)
|
|
1745
|
+
return 'down';
|
|
1746
|
+
return 'stable';
|
|
1747
|
+
}
|
|
1748
|
+
exportHistoryToCSV() {
|
|
1749
|
+
const headers = ['Date', 'Run Count', 'Passed', 'Failed', 'Pass Rate (%)', 'Avg Score', 'Avg Duration (s)', 'Avg Cost (USD)'];
|
|
1750
|
+
const rows = this.historyData.map(d => [
|
|
1751
|
+
d.date.toISOString().split('T')[0],
|
|
1752
|
+
d.runCount.toString(),
|
|
1753
|
+
d.passCount.toString(),
|
|
1754
|
+
d.failCount.toString(),
|
|
1755
|
+
d.passRate.toFixed(1),
|
|
1756
|
+
d.avgScore.toFixed(4),
|
|
1757
|
+
d.avgDuration.toFixed(2),
|
|
1758
|
+
d.avgCost.toFixed(6)
|
|
1759
|
+
]);
|
|
1760
|
+
const csvContent = [headers, ...rows]
|
|
1761
|
+
.map(row => row.map(cell => `"${cell}"`).join(','))
|
|
1762
|
+
.join('\n');
|
|
1763
|
+
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
1764
|
+
const link = document.createElement('a');
|
|
1765
|
+
link.href = URL.createObjectURL(blob);
|
|
1766
|
+
link.download = `test-${this.record.ID.substring(0, 8)}-history.csv`;
|
|
1767
|
+
link.click();
|
|
1768
|
+
URL.revokeObjectURL(link.href);
|
|
1769
|
+
SharedService.Instance.CreateSimpleNotification('Export complete', 'success', 2000);
|
|
1770
|
+
}
|
|
1771
|
+
openSuiteFromHistory(suiteId) {
|
|
1772
|
+
SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
|
|
1773
|
+
}
|
|
1774
|
+
// ===========================
|
|
1775
|
+
// Keyboard Shortcuts Settings
|
|
1776
|
+
// ===========================
|
|
1777
|
+
/**
|
|
1778
|
+
* Load keyboard shortcuts visibility setting from user settings
|
|
1779
|
+
*/
|
|
1780
|
+
loadShortcutsSetting() {
|
|
1781
|
+
try {
|
|
1782
|
+
const engine = UserInfoEngine.Instance;
|
|
1783
|
+
const setting = engine.UserSettings.find(s => s.Setting === SHORTCUTS_SETTINGS_KEY);
|
|
1784
|
+
if (setting) {
|
|
1785
|
+
this.shortcutsSettingEntity = setting;
|
|
1786
|
+
this.showShortcuts = setting.Value === 'true';
|
|
1787
|
+
}
|
|
1788
|
+
else {
|
|
1789
|
+
// Default to hidden
|
|
1790
|
+
this.showShortcuts = false;
|
|
1791
|
+
}
|
|
1792
|
+
this.cdr.markForCheck();
|
|
1793
|
+
}
|
|
1794
|
+
catch (error) {
|
|
1795
|
+
console.warn('Failed to load shortcuts setting:', error);
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
/**
|
|
1799
|
+
* Toggle keyboard shortcuts visibility and save preference
|
|
1800
|
+
*/
|
|
1801
|
+
async toggleShortcuts() {
|
|
1802
|
+
this.showShortcuts = !this.showShortcuts;
|
|
1803
|
+
this.cdr.markForCheck();
|
|
1804
|
+
try {
|
|
1805
|
+
const userId = this.metadata.CurrentUser?.ID;
|
|
1806
|
+
if (!userId)
|
|
1807
|
+
return;
|
|
1808
|
+
if (!this.shortcutsSettingEntity) {
|
|
1809
|
+
const engine = UserInfoEngine.Instance;
|
|
1810
|
+
const setting = engine.UserSettings.find(s => s.Setting === SHORTCUTS_SETTINGS_KEY);
|
|
1811
|
+
if (setting) {
|
|
1812
|
+
this.shortcutsSettingEntity = setting;
|
|
1813
|
+
}
|
|
1814
|
+
else {
|
|
1815
|
+
this.shortcutsSettingEntity = await this.metadata.GetEntityObject('MJ: User Settings');
|
|
1816
|
+
this.shortcutsSettingEntity.UserID = userId;
|
|
1817
|
+
this.shortcutsSettingEntity.Setting = SHORTCUTS_SETTINGS_KEY;
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
this.shortcutsSettingEntity.Value = this.showShortcuts ? 'true' : 'false';
|
|
1821
|
+
await this.shortcutsSettingEntity.Save();
|
|
1822
|
+
}
|
|
1823
|
+
catch (error) {
|
|
1824
|
+
console.warn('Failed to save shortcuts setting:', error);
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
static { this.ɵfac = function TestFormComponentExtended_Factory(t) { return new (t || TestFormComponentExtended)(i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.SharedService), i0.ɵɵdirectiveInject(i2.Router), i0.ɵɵdirectiveInject(i2.ActivatedRoute), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i3.TestingDialogService), i0.ɵɵdirectiveInject(i3.EvaluationPreferencesService), i0.ɵɵdirectiveInject(i0.ViewContainerRef)); }; }
|
|
1828
|
+
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestFormComponentExtended, selectors: [["mj-test-form"]], hostBindings: function TestFormComponentExtended_HostBindings(rf, ctx) { if (rf & 1) {
|
|
1829
|
+
i0.ɵɵlistener("keydown", function TestFormComponentExtended_keydown_HostBindingHandler($event) { return ctx.handleKeyboardShortcut($event); }, false, i0.ɵɵresolveDocument);
|
|
1830
|
+
} }, features: [i0.ɵɵInheritDefinitionFeature], decls: 57, vars: 36, consts: [["kendoDialogContainer", "", 1, "test-form"], [1, "test-header"], [1, "header-content"], [1, "header-left"], [1, "test-icon"], [1, "fas", "fa-flask"], [1, "test-info"], [1, "test-meta"], [1, "status-badge", 3, "ngClass"], [1, "fas", 3, "ngClass"], ["class", "test-type", 4, "ngIf"], [1, "header-actions"], ["kendoButton", "", "themeColor", "primary", 3, "click"], [1, "fas", "fa-play"], ["kendoButton", "", 3, "click", "disabled"], ["class", "test-description", 4, "ngIf"], ["class", "metrics-bar", 4, "ngIf"], [1, "tabs-container"], ["role", "tablist", 1, "tabs"], ["role", "tab", 1, "tab", 3, "click"], [1, "fas", "fa-th-large"], [1, "fas", "fa-sliders-h"], [1, "fas", "fa-history"], ["class", "tab-badge", 4, "ngIf"], [1, "fas", "fa-layer-group"], [1, "fas", "fa-chart-line"], [1, "tab-content"], ["class", "overview-tab", 4, "ngIf"], ["class", "config-tab", 4, "ngIf"], ["class", "runs-tab", 4, "ngIf"], ["class", "suites-tab", 4, "ngIf"], ["class", "history-tab", 4, "ngIf"], [1, "shortcuts-toggle", 3, "click", "title"], [1, "fas", "fa-keyboard"], ["class", "keyboard-shortcuts", 4, "ngIf"], [1, "test-type"], [1, "fas", "fa-tag"], [1, "test-description"], [1, "metrics-bar"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "metric-progress"], [1, "metric-progress-fill"], [1, "tab-badge"], [1, "overview-tab"], [1, "info-section"], [1, "fas", "fa-info-circle"], [1, "info-grid"], [1, "info-item"], [1, "info-label"], [1, "info-value"], [1, "status-badge-inline", 3, "ngClass"], [1, "json-section"], [1, "fas", "fa-code"], [1, "json-tabs"], [1, "json-tab", 3, "click"], [1, "fas", "fa-sign-in-alt"], [1, "fas", "fa-check-double"], [1, "fas", "fa-cog"], [1, "fas", "fa-tags"], [1, "code-editor-container"], ["language", "json", 3, "value", "readonly", "toolbar", "lineWrapping"], [1, "config-tab"], [1, "config-section"], [1, "fas", "fa-cogs"], [1, "config-grid"], [1, "config-item"], ["type", "number", "placeholder", "Lower = Higher Priority", 1, "config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1", 1, "config-input", 3, "ngModelChange", "ngModel"], ["type", "number", 1, "config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "step", "0.000001", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-item", "full-width"], ["type", "number", "placeholder", "Default: 300000 (5 min)", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-hint"], [1, "config-editor-container"], ["language", "json", 3, "change", "value", "readonly", "lineWrapping"], [1, "config-editor-container", "small"], [1, "runs-tab"], ["class", "loading-state", 4, "ngIf"], ["class", "runs-list", 4, "ngIf"], ["class", "empty-state", 4, "ngIf"], [1, "loading-state"], [1, "skeleton-list"], ["class", "skeleton-card", 4, "ngFor", "ngForOf"], [1, "skeleton-card"], [1, "skeleton-icon"], [1, "skeleton-content"], [1, "skeleton-line", "wide"], [1, "skeleton-line", "narrow"], [1, "runs-list"], ["class", "run-item", 3, "click", 4, "ngFor", "ngForOf"], [1, "run-item", 3, "click"], [1, "run-icon"], [1, "fas"], [1, "run-content"], [1, "run-header"], [1, "run-id"], [1, "run-status"], [1, "run-meta"], [1, "fas", "fa-calendar"], [4, "ngIf"], [3, "entityName", "recordId", 4, "ngIf"], [1, "run-eval-stack"], ["class", "eval-pill status-pill", 3, "ngClass", "title", 4, "ngIf"], ["class", "run-tags", 4, "ngIf"], [1, "fas", "fa-chevron-right"], [1, "fas", "fa-clock"], [1, "fas", "fa-dollar-sign"], [3, "entityName", "recordId"], [1, "eval-pill", "status-pill", 3, "ngClass", "title"], ["class", "eval-pill human-pill no-feedback", "title", "Human Review: No rating submitted yet", 4, "ngIf"], ["class", "eval-pill human-pill has-feedback", 3, "rating-low", "rating-medium", "rating-good", "rating-excellent", "title", 4, "ngIf"], ["title", "Human Review: No rating submitted yet", 1, "eval-pill", "human-pill", "no-feedback"], [1, "fas", "fa-user-slash"], [1, "eval-pill", "human-pill", "has-feedback", 3, "title"], [1, "fas", "fa-user"], ["class", "eval-pill auto-pill no-score", "title", "Auto Score: No automated score available", 4, "ngIf"], ["class", "eval-pill auto-pill has-score", 3, "score-low", "score-medium", "score-good", "score-excellent", "title", 4, "ngIf"], ["title", "Auto Score: No automated score available", 1, "eval-pill", "auto-pill", "no-score"], [1, "fas", "fa-robot"], [1, "eval-pill", "auto-pill", "has-score", 3, "title"], [1, "run-tags"], ["class", "run-tag", 4, "ngFor", "ngForOf"], ["class", "run-tag-more", 4, "ngIf"], [1, "run-tag"], [1, "run-tag-more"], [1, "empty-state"], [1, "empty-icon"], [1, "fas", "fa-play-circle"], [1, "suites-tab"], ["class", "suites-list", 4, "ngIf"], [1, "suites-list"], ["class", "suite-item", 3, "click", 4, "ngFor", "ngForOf"], [1, "suite-item", 3, "click"], [1, "suite-icon"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"], [1, "fas", "fa-sort-numeric-up"], [1, "fas", "fa-folder-open"], [1, "history-tab"], ["class", "history-content", 4, "ngIf"], ["text", "Loading history..."], [1, "history-content"], [1, "history-filters"], [1, "time-range-filters"], [1, "filter-label"], [1, "filter-btn", 3, "click"], [1, "history-actions"], [1, "fas", "fa-download"], ["class", "history-kpi-cards", 4, "ngIf"], ["class", "history-section", 4, "ngIf"], [1, "history-kpi-cards"], [1, "kpi-card"], [1, "kpi-icon"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], ["class", "kpi-card", 4, "ngIf"], [1, "kpi-icon", "pass-rate"], [1, "fas", "fa-percentage"], [1, "fas", "trend-icon"], [1, "fas", "fa-star"], [1, "history-section"], [1, "fas", "fa-calendar-alt"], [1, "history-table-container"], [1, "history-table"], [4, "ngFor", "ngForOf"], [1, "date-cell"], [1, "runs-cell"], ["class", "passed-cell", 4, "ngIf"], ["class", "failed-cell", 4, "ngIf"], ["class", "pass-rate-cell", 4, "ngIf"], ["class", "score-cell", 4, "ngIf"], [1, "duration-cell"], [1, "cost-cell"], [1, "passed-cell"], [1, "failed-cell"], [1, "pass-rate-cell"], [1, "pass-rate-bar"], [1, "pass-rate-fill"], [1, "pass-rate-text"], [1, "score-cell"], [1, "suite-performance-list"], ["class", "suite-perf-card", 3, "click", 4, "ngFor", "ngForOf"], [1, "suite-perf-card", 3, "click"], [1, "suite-perf-header"], [1, "suite-perf-name"], ["class", "suite-perf-tags", 4, "ngIf"], [1, "suite-perf-stats"], [1, "suite-stat"], [1, "stat-value"], [1, "stat-label"], ["class", "suite-stat", 4, "ngIf"], [1, "fas", "fa-chevron-right", "suite-perf-arrow"], [1, "suite-perf-tags"], ["class", "tag-mini", 4, "ngFor", "ngForOf"], ["class", "tag-more", 4, "ngIf"], [1, "tag-mini"], [1, "tag-more"], [1, "stat-value", "pass-rate"], [1, "keyboard-shortcuts"], [1, "shortcuts-header"], ["title", "Hide shortcuts", 1, "shortcuts-close", 3, "click"], [1, "fas", "fa-times"], [1, "shortcut-list"], [1, "shortcut-item"], [1, "shortcut-keys"]], template: function TestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
|
|
616
1831
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3)(4, "div", 4);
|
|
617
1832
|
i0.ɵɵelement(5, "i", 5);
|
|
618
1833
|
i0.ɵɵelementEnd();
|
|
@@ -623,80 +1838,108 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
623
1838
|
i0.ɵɵelement(11, "i", 9);
|
|
624
1839
|
i0.ɵɵtext(12);
|
|
625
1840
|
i0.ɵɵelementEnd();
|
|
626
|
-
i0.ɵɵ
|
|
627
|
-
i0.ɵɵ
|
|
628
|
-
i0.ɵɵ
|
|
629
|
-
i0.ɵɵ
|
|
1841
|
+
i0.ɵɵtemplate(13, TestFormComponentExtended_span_13_Template, 3, 1, "span", 10);
|
|
1842
|
+
i0.ɵɵelementEnd()()();
|
|
1843
|
+
i0.ɵɵelementStart(14, "div", 11);
|
|
1844
|
+
i0.ɵɵelement(15, "app-evaluation-mode-toggle");
|
|
1845
|
+
i0.ɵɵelementStart(16, "button", 12);
|
|
630
1846
|
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_16_listener() { return ctx.runTest(); });
|
|
631
|
-
i0.ɵɵ
|
|
1847
|
+
i0.ɵɵelement(17, "i", 13);
|
|
1848
|
+
i0.ɵɵtext(18, " Run Test ");
|
|
632
1849
|
i0.ɵɵelementEnd();
|
|
633
|
-
i0.ɵɵelementStart(
|
|
634
|
-
i0.ɵɵlistener("click", function
|
|
635
|
-
i0.ɵɵ
|
|
1850
|
+
i0.ɵɵelementStart(19, "button", 14);
|
|
1851
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_19_listener() { return ctx.refresh(); });
|
|
1852
|
+
i0.ɵɵelement(20, "i", 9);
|
|
1853
|
+
i0.ɵɵtext(21);
|
|
636
1854
|
i0.ɵɵelementEnd()()();
|
|
637
|
-
i0.ɵɵtemplate(
|
|
1855
|
+
i0.ɵɵtemplate(22, TestFormComponentExtended_div_22_Template, 3, 1, "div", 15)(23, TestFormComponentExtended_div_23_Template, 23, 6, "div", 16);
|
|
638
1856
|
i0.ɵɵelementEnd();
|
|
639
|
-
i0.ɵɵelementStart(
|
|
640
|
-
i0.ɵɵlistener("click", function
|
|
641
|
-
i0.ɵɵelement(
|
|
642
|
-
i0.ɵɵelementStart(
|
|
643
|
-
i0.ɵɵtext(
|
|
1857
|
+
i0.ɵɵelementStart(24, "div", 17)(25, "div", 18)(26, "button", 19);
|
|
1858
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_26_listener() { return ctx.changeTab("overview"); });
|
|
1859
|
+
i0.ɵɵelement(27, "i", 20);
|
|
1860
|
+
i0.ɵɵelementStart(28, "span");
|
|
1861
|
+
i0.ɵɵtext(29, "Overview");
|
|
644
1862
|
i0.ɵɵelementEnd()();
|
|
645
|
-
i0.ɵɵelementStart(
|
|
646
|
-
i0.ɵɵlistener("click", function
|
|
647
|
-
i0.ɵɵelement(
|
|
648
|
-
i0.ɵɵelementStart(
|
|
649
|
-
i0.ɵɵtext(
|
|
1863
|
+
i0.ɵɵelementStart(30, "button", 19);
|
|
1864
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_30_listener() { return ctx.changeTab("config"); });
|
|
1865
|
+
i0.ɵɵelement(31, "i", 21);
|
|
1866
|
+
i0.ɵɵelementStart(32, "span");
|
|
1867
|
+
i0.ɵɵtext(33, "Configuration");
|
|
650
1868
|
i0.ɵɵelementEnd()();
|
|
651
|
-
i0.ɵɵelementStart(
|
|
652
|
-
i0.ɵɵlistener("click", function
|
|
653
|
-
i0.ɵɵelement(
|
|
654
|
-
i0.ɵɵelementStart(
|
|
655
|
-
i0.ɵɵtext(
|
|
1869
|
+
i0.ɵɵelementStart(34, "button", 19);
|
|
1870
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_34_listener() { return ctx.changeTab("runs"); });
|
|
1871
|
+
i0.ɵɵelement(35, "i", 22);
|
|
1872
|
+
i0.ɵɵelementStart(36, "span");
|
|
1873
|
+
i0.ɵɵtext(37, "Runs");
|
|
656
1874
|
i0.ɵɵelementEnd();
|
|
657
|
-
i0.ɵɵtemplate(
|
|
1875
|
+
i0.ɵɵtemplate(38, TestFormComponentExtended_span_38_Template, 2, 1, "span", 23);
|
|
658
1876
|
i0.ɵɵelementEnd();
|
|
659
|
-
i0.ɵɵelementStart(
|
|
660
|
-
i0.ɵɵlistener("click", function
|
|
661
|
-
i0.ɵɵelement(
|
|
662
|
-
i0.ɵɵelementStart(
|
|
663
|
-
i0.ɵɵtext(
|
|
1877
|
+
i0.ɵɵelementStart(39, "button", 19);
|
|
1878
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_39_listener() { return ctx.changeTab("suites"); });
|
|
1879
|
+
i0.ɵɵelement(40, "i", 24);
|
|
1880
|
+
i0.ɵɵelementStart(41, "span");
|
|
1881
|
+
i0.ɵɵtext(42, "Test Suites");
|
|
1882
|
+
i0.ɵɵelementEnd();
|
|
1883
|
+
i0.ɵɵtemplate(43, TestFormComponentExtended_span_43_Template, 2, 1, "span", 23);
|
|
1884
|
+
i0.ɵɵelementEnd();
|
|
1885
|
+
i0.ɵɵelementStart(44, "button", 19);
|
|
1886
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_44_listener() { return ctx.changeTab("analytics"); });
|
|
1887
|
+
i0.ɵɵelement(45, "i", 25);
|
|
1888
|
+
i0.ɵɵelementStart(46, "span");
|
|
1889
|
+
i0.ɵɵtext(47, "Analytics");
|
|
1890
|
+
i0.ɵɵelementEnd()()()();
|
|
1891
|
+
i0.ɵɵelementStart(48, "div", 26);
|
|
1892
|
+
i0.ɵɵtemplate(49, TestFormComponentExtended_div_49_Template, 78, 29, "div", 27)(50, TestFormComponentExtended_div_50_Template, 52, 17, "div", 28)(51, TestFormComponentExtended_div_51_Template, 4, 3, "div", 29)(52, TestFormComponentExtended_div_52_Template, 4, 3, "div", 30)(53, TestFormComponentExtended_div_53_Template, 3, 2, "div", 31);
|
|
1893
|
+
i0.ɵɵelementEnd();
|
|
1894
|
+
i0.ɵɵelementStart(54, "button", 32);
|
|
1895
|
+
i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_54_listener() { return ctx.toggleShortcuts(); });
|
|
1896
|
+
i0.ɵɵelement(55, "i", 33);
|
|
1897
|
+
i0.ɵɵelementEnd();
|
|
1898
|
+
i0.ɵɵtemplate(56, TestFormComponentExtended_div_56_Template, 32, 0, "div", 34);
|
|
664
1899
|
i0.ɵɵelementEnd();
|
|
665
|
-
i0.ɵɵtemplate(41, TestFormComponentExtended_span_41_Template, 2, 1, "span", 22);
|
|
666
|
-
i0.ɵɵelementEnd()()();
|
|
667
|
-
i0.ɵɵelementStart(42, "div", 24);
|
|
668
|
-
i0.ɵɵtemplate(43, TestFormComponentExtended_div_43_Template, 62, 22, "div", 25)(44, TestFormComponentExtended_div_44_Template, 37, 8, "div", 26)(45, TestFormComponentExtended_div_45_Template, 3, 2, "div", 27)(46, TestFormComponentExtended_div_46_Template, 3, 2, "div", 28);
|
|
669
|
-
i0.ɵɵelementEnd()();
|
|
670
1900
|
} if (rf & 2) {
|
|
671
1901
|
i0.ɵɵadvance(4);
|
|
672
1902
|
i0.ɵɵstyleProp("background-color", ctx.getStatusColor());
|
|
673
1903
|
i0.ɵɵadvance(4);
|
|
674
1904
|
i0.ɵɵtextInterpolate(ctx.record.Name);
|
|
675
1905
|
i0.ɵɵadvance(2);
|
|
676
|
-
i0.ɵɵ
|
|
1906
|
+
i0.ɵɵproperty("ngClass", ctx.getStatusClass());
|
|
677
1907
|
i0.ɵɵadvance();
|
|
678
1908
|
i0.ɵɵproperty("ngClass", ctx.getStatusIcon());
|
|
679
1909
|
i0.ɵɵadvance();
|
|
680
1910
|
i0.ɵɵtextInterpolate1(" ", ctx.record.Status, " ");
|
|
681
|
-
i0.ɵɵadvance(
|
|
682
|
-
i0.ɵɵ
|
|
1911
|
+
i0.ɵɵadvance();
|
|
1912
|
+
i0.ɵɵproperty("ngIf", ctx.record.Type);
|
|
683
1913
|
i0.ɵɵadvance(6);
|
|
1914
|
+
i0.ɵɵproperty("disabled", ctx.isRefreshing);
|
|
1915
|
+
i0.ɵɵadvance();
|
|
1916
|
+
i0.ɵɵproperty("ngClass", ctx.isRefreshing ? "fa-sync fa-spin" : "fa-sync");
|
|
1917
|
+
i0.ɵɵadvance();
|
|
1918
|
+
i0.ɵɵtextInterpolate1(" ", ctx.isRefreshing ? "Refreshing..." : "Refresh", " ");
|
|
1919
|
+
i0.ɵɵadvance();
|
|
684
1920
|
i0.ɵɵproperty("ngIf", ctx.record.Description);
|
|
685
1921
|
i0.ɵɵadvance();
|
|
686
1922
|
i0.ɵɵproperty("ngIf", ctx.testRunsLoaded && ctx.testRuns.length > 0);
|
|
687
1923
|
i0.ɵɵadvance(3);
|
|
688
1924
|
i0.ɵɵclassProp("active", ctx.activeTab === "overview");
|
|
1925
|
+
i0.ɵɵattribute("aria-selected", ctx.activeTab === "overview");
|
|
689
1926
|
i0.ɵɵadvance(4);
|
|
690
1927
|
i0.ɵɵclassProp("active", ctx.activeTab === "config");
|
|
1928
|
+
i0.ɵɵattribute("aria-selected", ctx.activeTab === "config");
|
|
691
1929
|
i0.ɵɵadvance(4);
|
|
692
1930
|
i0.ɵɵclassProp("active", ctx.activeTab === "runs");
|
|
1931
|
+
i0.ɵɵattribute("aria-selected", ctx.activeTab === "runs");
|
|
693
1932
|
i0.ɵɵadvance(4);
|
|
694
1933
|
i0.ɵɵproperty("ngIf", ctx.testRunsLoaded);
|
|
695
1934
|
i0.ɵɵadvance();
|
|
696
1935
|
i0.ɵɵclassProp("active", ctx.activeTab === "suites");
|
|
1936
|
+
i0.ɵɵattribute("aria-selected", ctx.activeTab === "suites");
|
|
697
1937
|
i0.ɵɵadvance(4);
|
|
698
1938
|
i0.ɵɵproperty("ngIf", ctx.suiteTestsLoaded);
|
|
699
|
-
i0.ɵɵadvance(
|
|
1939
|
+
i0.ɵɵadvance();
|
|
1940
|
+
i0.ɵɵclassProp("active", ctx.activeTab === "analytics");
|
|
1941
|
+
i0.ɵɵattribute("aria-selected", ctx.activeTab === "analytics");
|
|
1942
|
+
i0.ɵɵadvance(5);
|
|
700
1943
|
i0.ɵɵproperty("ngIf", ctx.activeTab === "overview");
|
|
701
1944
|
i0.ɵɵadvance();
|
|
702
1945
|
i0.ɵɵproperty("ngIf", ctx.activeTab === "config");
|
|
@@ -704,7 +1947,13 @@ let TestFormComponentExtended = class TestFormComponentExtended extends TestForm
|
|
|
704
1947
|
i0.ɵɵproperty("ngIf", ctx.activeTab === "runs");
|
|
705
1948
|
i0.ɵɵadvance();
|
|
706
1949
|
i0.ɵɵproperty("ngIf", ctx.activeTab === "suites");
|
|
707
|
-
|
|
1950
|
+
i0.ɵɵadvance();
|
|
1951
|
+
i0.ɵɵproperty("ngIf", ctx.activeTab === "analytics");
|
|
1952
|
+
i0.ɵɵadvance();
|
|
1953
|
+
i0.ɵɵproperty("title", ctx.showShortcuts ? "Hide keyboard shortcuts" : "Show keyboard shortcuts");
|
|
1954
|
+
i0.ɵɵadvance(2);
|
|
1955
|
+
i0.ɵɵproperty("ngIf", ctx.showShortcuts);
|
|
1956
|
+
} }, dependencies: [i4.NgClass, i4.NgForOf, i4.NgIf, i5.DefaultValueAccessor, i5.NumberValueAccessor, i5.NgControlStatus, i5.MinValidator, i5.NgModel, i6.DialogContainerDirective, i7.ButtonComponent, i8.CodeEditorComponent, i3.EvaluationModeToggleComponent, i9.LoadingComponent, i10.EntityLinkPillComponent, i4.DatePipe], styles: ["\n\n\n\n\n\n\n\n[_nghost-%COMP%] {\n --test-primary: #2563eb;\n --test-primary-light: #3b82f6;\n --test-primary-dark: #1d4ed8;\n --test-success: #10b981;\n --test-success-light: #d1fae5;\n --test-error: #ef4444;\n --test-error-light: #fee2e2;\n --test-warning: #f59e0b;\n --test-warning-light: #fef3c7;\n --test-disabled: #6b7280;\n --test-bg: #f8fafc;\n --test-surface: #ffffff;\n --test-border: #e2e8f0;\n --test-text: #1e293b;\n --test-text-secondary: #64748b;\n --test-text-muted: #94a3b8;\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);\n --test-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --test-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n\n\n.test-form[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--test-bg);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n\n\n\n\n.test-header[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n padding: 20px;\n}\n\n.header-content[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n gap: 16px;\n}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n flex: 1;\n min-width: 0;\n}\n\n\n\n.test-icon[_ngcontent-%COMP%] {\n width: 56px;\n height: 56px;\n border-radius: var(--test-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 24px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-md);\n transition: var(--test-transition);\n}\n\n.test-icon[_ngcontent-%COMP%]:hover {\n transform: scale(1.05);\n}\n\n\n\n.test-info[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.test-info[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: clamp(18px, 4vw, 24px);\n font-weight: 700;\n color: var(--test-text);\n line-height: 1.2;\n word-wrap: break-word;\n}\n\n.test-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n\n\n.status-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border-radius: 20px;\n color: white;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.status-badge.status-active[_ngcontent-%COMP%] { background: linear-gradient(135deg, var(--test-success) 0%, #059669 100%); }\n.status-badge.status-disabled[_ngcontent-%COMP%] { background: linear-gradient(135deg, var(--test-disabled) 0%, #4b5563 100%); }\n.status-badge.status-pending[_ngcontent-%COMP%] { background: linear-gradient(135deg, var(--test-warning) 0%, #d97706 100%); }\n\n.status-badge-inline[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: white;\n font-size: 11px;\n font-weight: 600;\n}\n\n.status-badge-inline.status-active[_ngcontent-%COMP%] { background: var(--test-success); }\n.status-badge-inline.status-disabled[_ngcontent-%COMP%] { background: var(--test-disabled); }\n.status-badge-inline.status-pending[_ngcontent-%COMP%] { background: var(--test-warning); }\n\n.test-type[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: var(--test-text-secondary);\n padding: 4px 10px;\n background: var(--test-bg);\n border-radius: var(--test-radius-sm);\n}\n\n.header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.header-actions[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n white-space: nowrap;\n}\n\n\n\n.test-description[_ngcontent-%COMP%] {\n padding: 16px;\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border-radius: var(--test-radius-md);\n margin-bottom: 16px;\n border: 1px solid var(--test-border);\n}\n\n.test-description[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n color: var(--test-text-secondary);\n line-height: 1.6;\n font-size: 14px;\n}\n\n\n\n\n\n.metrics-bar[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.metric-card[_ngcontent-%COMP%] {\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 14px;\n text-align: center;\n transition: var(--test-transition);\n}\n\n.metric-card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.metric-label[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n margin-bottom: 6px;\n}\n\n.metric-value[_ngcontent-%COMP%] {\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n\n\n.metric-progress[_ngcontent-%COMP%] {\n margin-top: 8px;\n height: 4px;\n background: var(--test-border);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.metric-progress-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, var(--test-success) 0%, #34d399 100%);\n border-radius: 2px;\n transition: width 0.5s ease-out;\n}\n\n\n\n\n\n.tabs-container[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tabs[_ngcontent-%COMP%] {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tabs[_ngcontent-%COMP%]::-webkit-scrollbar {\n display: none;\n}\n\n.tab[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 18px;\n border: none;\n background: transparent;\n border-bottom: 3px solid transparent;\n color: var(--test-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n white-space: nowrap;\n}\n\n.tab[_ngcontent-%COMP%]:hover {\n color: var(--test-primary);\n background: rgba(37, 99, 235, 0.05);\n}\n\n.tab.active[_ngcontent-%COMP%] {\n color: var(--test-primary);\n border-bottom-color: var(--test-primary);\n font-weight: 600;\n}\n\n.tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 15px;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\n background: var(--test-border);\n color: var(--test-text-secondary);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.tab.active[_ngcontent-%COMP%] .tab-badge[_ngcontent-%COMP%] {\n background: rgba(37, 99, 235, 0.15);\n color: var(--test-primary);\n}\n\n.tab-shortcut[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n margin-left: 4px;\n}\n\n\n\n\n\n.tab-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n scroll-behavior: smooth;\n}\n\n\n\n\n\n.overview-tab[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: _ngcontent-%COMP%_fadeIn 0.3s ease-out;\n}\n\n@keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n\n\n.info-section[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.info-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.info-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--test-primary);\n}\n\n.info-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n}\n\n.info-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.info-label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.info-value[_ngcontent-%COMP%] {\n font-size: 14px;\n color: var(--test-text);\n word-wrap: break-word;\n font-weight: 500;\n}\n\n\n\n.json-section[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.json-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--test-primary);\n}\n\n.json-tabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n background: var(--test-bg);\n border-radius: var(--test-radius-md);\n padding: 4px;\n flex-wrap: wrap;\n}\n\n.json-tab[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 100px;\n padding: 10px 14px;\n border: none;\n background: transparent;\n color: var(--test-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n border-radius: var(--test-radius-sm);\n transition: var(--test-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n\n.json-tab[_ngcontent-%COMP%]:hover {\n color: var(--test-text);\n background: rgba(0, 0, 0, 0.05);\n}\n\n.json-tab.active[_ngcontent-%COMP%] {\n background: var(--test-surface);\n color: var(--test-primary);\n font-weight: 600;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 12px;\n}\n\n.code-editor-container[_ngcontent-%COMP%] {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n max-height: 400px;\n}\n\n\n\n\n\n.config-tab[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: _ngcontent-%COMP%_fadeIn 0.3s ease-out;\n}\n\n.config-section[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.config-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.config-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--test-primary);\n}\n\n.config-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n gap: 20px;\n}\n\n.config-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.config-item.full-width[_ngcontent-%COMP%] {\n grid-column: 1 / -1;\n}\n\n.config-item[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.config-input[_ngcontent-%COMP%] {\n padding: 10px 14px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n font-size: 14px;\n transition: var(--test-transition);\n background: var(--test-surface);\n}\n\n.config-input[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);\n}\n\n.config-input[_ngcontent-%COMP%]::placeholder {\n color: var(--test-text-muted);\n}\n\n.config-hint[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--test-text-muted);\n margin-top: 4px;\n}\n\n.config-editor-container[_ngcontent-%COMP%] {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n}\n\n.config-editor-container.small[_ngcontent-%COMP%] {\n min-height: 100px;\n max-height: 150px;\n}\n\n\n\n\n\n.runs-tab[_ngcontent-%COMP%], \n.suites-tab[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_fadeIn 0.3s ease-out;\n}\n\n\n\n.loading-state[_ngcontent-%COMP%] {\n padding: 0;\n}\n\n.skeleton-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.skeleton-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n border: 1px solid var(--test-border);\n}\n\n.skeleton-icon[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n border-radius: var(--test-radius-md);\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n animation: _ngcontent-%COMP%_shimmer 1.5s infinite;\n flex-shrink: 0;\n}\n\n.skeleton-content[_ngcontent-%COMP%] {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.skeleton-line[_ngcontent-%COMP%] {\n height: 14px;\n border-radius: 4px;\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n animation: _ngcontent-%COMP%_shimmer 1.5s infinite;\n}\n\n.skeleton-line.wide[_ngcontent-%COMP%] { width: 70%; }\n.skeleton-line.narrow[_ngcontent-%COMP%] { width: 40%; }\n\n@keyframes _ngcontent-%COMP%_shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n\n\n.runs-list[_ngcontent-%COMP%], \n.suites-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.run-item[_ngcontent-%COMP%], \n.suite-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.run-item[_ngcontent-%COMP%]:hover, \n.suite-item[_ngcontent-%COMP%]:hover {\n background: rgba(37, 99, 235, 0.05);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n box-shadow: var(--test-shadow-sm);\n}\n\n.run-icon[_ngcontent-%COMP%], \n.suite-icon[_ngcontent-%COMP%] {\n width: 44px;\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--test-radius-md);\n color: white;\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon[_ngcontent-%COMP%] {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n}\n\n.run-content[_ngcontent-%COMP%], \n.suite-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.run-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 4px;\n}\n\n.run-id[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.run-status[_ngcontent-%COMP%] {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.suite-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 4px;\n}\n\n.run-meta[_ngcontent-%COMP%], \n.suite-meta[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n font-size: 12px;\n color: var(--test-text-secondary);\n flex-wrap: wrap;\n}\n\n.run-meta[_ngcontent-%COMP%] span[_ngcontent-%COMP%], \n.suite-meta[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.run-meta[_ngcontent-%COMP%] span[_ngcontent-%COMP%] i[_ngcontent-%COMP%], \n.suite-meta[_ngcontent-%COMP%] span[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--test-text-muted);\n font-size: 11px;\n}\n\n.run-item[_ngcontent-%COMP%] > i.fa-chevron-right[_ngcontent-%COMP%], \n.suite-item[_ngcontent-%COMP%] > i.fa-chevron-right[_ngcontent-%COMP%] {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.run-item[_ngcontent-%COMP%]:hover > i.fa-chevron-right[_ngcontent-%COMP%], \n.suite-item[_ngcontent-%COMP%]:hover > i.fa-chevron-right[_ngcontent-%COMP%] {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n\n\n.run-eval-stack[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n\n\n.eval-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.eval-pill[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 10px;\n}\n\n\n\n.eval-pill.status-pill.status-passed[_ngcontent-%COMP%] {\n background: #dcfce7;\n color: #16a34a;\n}\n\n.eval-pill.status-pill.status-failed[_ngcontent-%COMP%] {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.status-pill.status-error[_ngcontent-%COMP%] {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.status-pill.status-timeout[_ngcontent-%COMP%] {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.status-pill.status-skipped[_ngcontent-%COMP%], \n.eval-pill.status-pill.status-pending[_ngcontent-%COMP%] {\n background: #f1f5f9;\n color: #64748b;\n}\n\n.eval-pill.status-pill.status-running[_ngcontent-%COMP%] {\n background: #dbeafe;\n color: #2563eb;\n}\n\n\n\n.eval-pill.human-pill.no-feedback[_ngcontent-%COMP%] {\n background: #f1f5f9;\n color: #94a3b8;\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low[_ngcontent-%COMP%] {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium[_ngcontent-%COMP%] {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.human-pill.has-feedback.rating-good[_ngcontent-%COMP%] {\n background: #d1fae5;\n color: #059669;\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent[_ngcontent-%COMP%] {\n background: #dcfce7;\n color: #16a34a;\n}\n\n\n\n.eval-pill.auto-pill.no-score[_ngcontent-%COMP%] {\n background: #f1f5f9;\n color: #94a3b8;\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low[_ngcontent-%COMP%] {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.auto-pill.has-score.score-medium[_ngcontent-%COMP%] {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.auto-pill.has-score.score-good[_ngcontent-%COMP%] {\n background: #d1fae5;\n color: #059669;\n}\n\n.eval-pill.auto-pill.has-score.score-excellent[_ngcontent-%COMP%] {\n background: #dcfce7;\n color: #16a34a;\n}\n\n\n\n.run-tags[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n.run-tag[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: rgba(37, 99, 235, 0.1);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.run-tag-more[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n\n\n\n\n.empty-state[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 24px;\n text-align: center;\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n}\n\n.empty-icon[_ngcontent-%COMP%] {\n width: 80px;\n height: 80px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--test-bg);\n border-radius: 50%;\n margin-bottom: 20px;\n}\n\n.empty-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 36px;\n color: var(--test-text-muted);\n}\n\n.empty-state[_ngcontent-%COMP%] h4[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.empty-state[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--test-text-secondary);\n max-width: 300px;\n}\n\n\n\n.no-data[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--test-text-muted);\n text-align: center;\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n}\n\n.no-data[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.3;\n}\n\n.no-data[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n}\n\n\n\n\n\n\n\n.shortcuts-toggle[_ngcontent-%COMP%] {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n box-shadow: var(--test-shadow-md);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--test-text-secondary);\n font-size: 14px;\n z-index: 99;\n transition: var(--test-transition);\n opacity: 0.7;\n}\n\n.shortcuts-toggle[_ngcontent-%COMP%]:hover {\n opacity: 1;\n transform: scale(1.1);\n color: var(--test-primary);\n border-color: var(--test-primary);\n}\n\n.keyboard-shortcuts[_ngcontent-%COMP%] {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 12px 16px;\n box-shadow: var(--test-shadow-lg);\n font-size: 12px;\n color: var(--test-text-secondary);\n z-index: 100;\n max-width: 260px;\n}\n\n.shortcuts-header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 10px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--test-border);\n font-weight: 600;\n color: var(--test-text);\n}\n\n.shortcuts-close[_ngcontent-%COMP%] {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--test-text-muted);\n font-size: 12px;\n padding: 2px 4px;\n border-radius: 4px;\n transition: var(--test-transition);\n}\n\n.shortcuts-close[_ngcontent-%COMP%]:hover {\n color: var(--test-text);\n background: var(--test-border);\n}\n\n.shortcut-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.shortcut-item[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.shortcut-keys[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n}\n\n.shortcut-keys[_ngcontent-%COMP%] kbd[_ngcontent-%COMP%] {\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: 4px;\n padding: 2px 6px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 11px;\n color: var(--test-text);\n}\n\n\n\n\n\n@media (max-width: 1024px) {\n .metrics-bar[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .config-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .keyboard-shortcuts[_ngcontent-%COMP%], .shortcuts-toggle[_ngcontent-%COMP%] {\n display: none;\n }\n}\n\n\n\n\n\n@media (max-width: 768px) {\n .test-form[_ngcontent-%COMP%] {\n height: auto;\n min-height: 100%;\n }\n\n .test-header[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .header-content[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 16px;\n }\n\n .header-left[_ngcontent-%COMP%] {\n width: 100%;\n }\n\n .test-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n font-size: 20px;\n }\n\n .test-info[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n font-size: 18px;\n }\n\n .test-meta[_ngcontent-%COMP%] {\n gap: 8px;\n }\n\n .status-badge[_ngcontent-%COMP%] {\n padding: 4px 10px;\n font-size: 11px;\n }\n\n .header-actions[_ngcontent-%COMP%] {\n width: 100%;\n justify-content: stretch;\n }\n\n .header-actions[_ngcontent-%COMP%] button[_ngcontent-%COMP%] {\n flex: 1;\n }\n\n .metrics-bar[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n }\n\n .metric-card[_ngcontent-%COMP%] {\n padding: 12px;\n }\n\n .metric-value[_ngcontent-%COMP%] {\n font-size: 16px;\n }\n\n .tabs[_ngcontent-%COMP%] {\n padding: 0 12px;\n }\n\n .tab[_ngcontent-%COMP%] {\n padding: 12px 14px;\n font-size: 13px;\n gap: 6px;\n }\n\n .tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 14px;\n }\n\n .tab-shortcut[_ngcontent-%COMP%] {\n display: none;\n }\n\n .tab-content[_ngcontent-%COMP%] {\n padding: 16px;\n }\n\n .info-section[_ngcontent-%COMP%], \n .json-section[_ngcontent-%COMP%], \n .config-section[_ngcontent-%COMP%] {\n padding: 18px;\n }\n\n .info-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .json-tabs[_ngcontent-%COMP%] {\n flex-direction: column;\n }\n\n .json-tab[_ngcontent-%COMP%] {\n min-width: 0;\n justify-content: flex-start;\n }\n\n .run-item[_ngcontent-%COMP%], \n .suite-item[_ngcontent-%COMP%] {\n padding: 14px;\n }\n\n .run-icon[_ngcontent-%COMP%], \n .suite-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n font-size: 16px;\n }\n\n .run-meta[_ngcontent-%COMP%], \n .suite-meta[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 4px;\n }\n\n .empty-state[_ngcontent-%COMP%] {\n padding: 40px 20px;\n }\n\n .empty-icon[_ngcontent-%COMP%] {\n width: 64px;\n height: 64px;\n }\n\n .empty-icon[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 28px;\n }\n}\n\n\n\n\n\n@media (max-width: 480px) {\n .test-header[_ngcontent-%COMP%] {\n padding: 12px;\n }\n\n .header-left[_ngcontent-%COMP%] {\n gap: 12px;\n }\n\n .test-icon[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n font-size: 18px;\n border-radius: 8px;\n }\n\n .test-info[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n font-size: 16px;\n }\n\n .metrics-bar[_ngcontent-%COMP%] {\n grid-template-columns: 1fr 1fr;\n }\n\n .tabs[_ngcontent-%COMP%] {\n padding: 0 8px;\n }\n\n .tab[_ngcontent-%COMP%] {\n padding: 10px 12px;\n font-size: 12px;\n }\n\n .tab-badge[_ngcontent-%COMP%] {\n display: none;\n }\n\n .tab-content[_ngcontent-%COMP%] {\n padding: 12px;\n }\n\n .run-header[_ngcontent-%COMP%] {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n}\n\n\n\n\n\n@media (hover: none) and (pointer: coarse) {\n .tab[_ngcontent-%COMP%], \n .json-tab[_ngcontent-%COMP%], \n .run-item[_ngcontent-%COMP%], \n .suite-item[_ngcontent-%COMP%] {\n -webkit-tap-highlight-color: transparent;\n }\n\n .run-item[_ngcontent-%COMP%]:active, \n .suite-item[_ngcontent-%COMP%]:active {\n background: rgba(37, 99, 235, 0.1);\n transform: scale(0.98);\n }\n\n .tab[_ngcontent-%COMP%]:active, \n .json-tab[_ngcontent-%COMP%]:active {\n background: rgba(37, 99, 235, 0.1);\n }\n\n \n\n .tab[_ngcontent-%COMP%] {\n min-height: 48px;\n }\n\n .run-item[_ngcontent-%COMP%], \n .suite-item[_ngcontent-%COMP%] {\n min-height: 64px;\n }\n}\n\n\n\n\n\n@media (prefers-reduced-motion: reduce) {\n *[_ngcontent-%COMP%], \n *[_ngcontent-%COMP%]::before, \n *[_ngcontent-%COMP%]::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n\n .skeleton-icon[_ngcontent-%COMP%], \n .skeleton-line[_ngcontent-%COMP%] {\n animation: none;\n background: #e2e8f0;\n }\n}\n\n\n\n\n\n.history-tab[_ngcontent-%COMP%] {\n animation: _ngcontent-%COMP%_fadeIn 0.3s ease-out;\n}\n\n.history-content[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n\n\n.history-filters[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n}\n\n.time-range-filters[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.filter-label[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 600;\n color: var(--test-text-secondary);\n margin-right: 4px;\n}\n\n.filter-btn[_ngcontent-%COMP%] {\n padding: 8px 16px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n color: var(--test-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.filter-btn[_ngcontent-%COMP%]:hover {\n border-color: var(--test-primary-light);\n color: var(--test-primary);\n background: rgba(37, 99, 235, 0.05);\n}\n\n.filter-btn.active[_ngcontent-%COMP%] {\n background: var(--test-primary);\n color: white;\n border-color: var(--test-primary);\n}\n\n.history-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n\n\n.history-kpi-cards[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n}\n\n.kpi-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n transition: var(--test-transition);\n}\n\n.kpi-card[_ngcontent-%COMP%]:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.kpi-icon[_ngcontent-%COMP%] {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, var(--test-primary) 0%, var(--test-primary-dark) 100%);\n border-radius: var(--test-radius-md);\n color: white;\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate[_ngcontent-%COMP%] {\n background: linear-gradient(135deg, var(--test-success) 0%, #059669 100%);\n}\n\n.kpi-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.kpi-value[_ngcontent-%COMP%] {\n font-size: 22px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.kpi-label[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.trend-icon[_ngcontent-%COMP%] {\n font-size: 14px;\n}\n\n.trend-icon.trend-up[_ngcontent-%COMP%] {\n color: var(--test-success);\n}\n\n.trend-icon.trend-down[_ngcontent-%COMP%] {\n color: var(--test-error);\n}\n\n\n\n.history-section[_ngcontent-%COMP%] {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.history-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.history-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--test-primary);\n}\n\n\n\n.history-table-container[_ngcontent-%COMP%] {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.history-table[_ngcontent-%COMP%] {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n}\n\n.history-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%], \n.history-table[_ngcontent-%COMP%] td[_ngcontent-%COMP%] {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--test-border);\n}\n\n.history-table[_ngcontent-%COMP%] th[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n}\n\n.history-table[_ngcontent-%COMP%] tbody[_ngcontent-%COMP%] tr[_ngcontent-%COMP%]:hover {\n background: rgba(37, 99, 235, 0.03);\n}\n\n.date-cell[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--test-text);\n}\n\n.passed-cell[_ngcontent-%COMP%] {\n color: var(--test-success);\n font-weight: 600;\n}\n\n.failed-cell[_ngcontent-%COMP%] {\n color: var(--test-error);\n font-weight: 600;\n}\n\n.pass-rate-cell[_ngcontent-%COMP%] {\n min-width: 120px;\n}\n\n.pass-rate-bar[_ngcontent-%COMP%] {\n position: relative;\n height: 24px;\n background: var(--test-bg);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.pass-rate-fill[_ngcontent-%COMP%] {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: linear-gradient(90deg, var(--test-success) 0%, #34d399 100%);\n border-radius: 12px;\n transition: width 0.5s ease-out;\n}\n\n.pass-rate-text[_ngcontent-%COMP%] {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n font-size: 11px;\n font-weight: 700;\n color: var(--test-text);\n z-index: 1;\n}\n\n\n\n.suite-performance-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.suite-perf-card[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 18px;\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.suite-perf-card[_ngcontent-%COMP%]:hover {\n background: rgba(37, 99, 235, 0.05);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n}\n\n.suite-perf-header[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n}\n\n.suite-perf-name[_ngcontent-%COMP%] {\n font-size: 15px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 6px;\n}\n\n.suite-perf-tags[_ngcontent-%COMP%] {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.tag-mini[_ngcontent-%COMP%] {\n display: inline-flex;\n padding: 2px 8px;\n background: rgba(37, 99, 235, 0.1);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.tag-more[_ngcontent-%COMP%] {\n display: inline-flex;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.suite-perf-stats[_ngcontent-%COMP%] {\n display: flex;\n gap: 24px;\n flex-wrap: wrap;\n}\n\n.suite-stat[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n}\n\n.suite-stat[_ngcontent-%COMP%] .stat-value[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n.suite-stat[_ngcontent-%COMP%] .stat-value.pass-rate.high[_ngcontent-%COMP%] {\n color: var(--test-success);\n}\n\n.suite-stat[_ngcontent-%COMP%] .stat-value.pass-rate.low[_ngcontent-%COMP%] {\n color: var(--test-error);\n}\n\n.suite-stat[_ngcontent-%COMP%] .stat-label[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suite-perf-arrow[_ngcontent-%COMP%] {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.suite-perf-card[_ngcontent-%COMP%]:hover .suite-perf-arrow[_ngcontent-%COMP%] {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n\n\n\n\n@media print {\n .test-form[_ngcontent-%COMP%] {\n background: white;\n height: auto;\n }\n\n .header-actions[_ngcontent-%COMP%], \n .tabs-container[_ngcontent-%COMP%], \n .keyboard-shortcuts[_ngcontent-%COMP%] {\n display: none !important;\n }\n\n .tab-content[_ngcontent-%COMP%] {\n overflow: visible;\n padding: 0;\n }\n\n .info-section[_ngcontent-%COMP%], \n .json-section[_ngcontent-%COMP%], \n .config-section[_ngcontent-%COMP%], \n .empty-state[_ngcontent-%COMP%] {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid #ddd;\n }\n}"], changeDetection: 0 }); }
|
|
708
1957
|
};
|
|
709
1958
|
TestFormComponentExtended = __decorate([
|
|
710
1959
|
RegisterClass(BaseFormComponent, 'MJ: Tests')
|
|
@@ -712,9 +1961,12 @@ TestFormComponentExtended = __decorate([
|
|
|
712
1961
|
export { TestFormComponentExtended };
|
|
713
1962
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(TestFormComponentExtended, [{
|
|
714
1963
|
type: Component,
|
|
715
|
-
args: [{ selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\" kendoDialogContainer>\n <!-- Header Section -->\n <div class=\"test-header\">\n <div class=\"header-content\">\n <div class=\"header-left\">\n <div class=\"test-icon\" [style.background-color]=\"getStatusColor()\">\n <i class=\"fas fa-flask\"></i>\n </div>\n <div class=\"test-info\">\n <h1>{{ record.Name }}</h1>\n <div class=\"test-meta\">\n <span class=\"status-badge\" [style.background-color]=\"getStatusColor()\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon()\"></i>\n {{ record.Status }}\n </span>\n <span class=\"test-type\">{{ record.Type }}</span>\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <button kendoButton (click)=\"runTest()\" icon=\"play\">\n Run Test\n </button>\n <button kendoButton (click)=\"refresh()\" icon=\"sync\">\n Refresh\n </button>\n </div>\n </div>\n\n <!-- Test Description -->\n <div class=\"test-description\" *ngIf=\"record.Description\">\n <p>{{ record.Description }}</p>\n </div>\n\n <!-- Metrics Bar -->\n <div class=\"metrics-bar\" *ngIf=\"testRunsLoaded && testRuns.length > 0\">\n <div class=\"metric-card\">\n <div class=\"metric-label\">Total Runs</div>\n <div class=\"metric-value\">{{ testRuns.length }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Pass Rate</div>\n <div class=\"metric-value\">{{ getPassRate().toFixed(1) }}%</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Cost</div>\n <div class=\"metric-value\">{{ formatCost(getAverageCost()) }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Duration</div>\n <div class=\"metric-value\">{{ formatDuration(getAverageDuration()) }}</div>\n </div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs-container\">\n <div class=\"tabs\">\n <button class=\"tab\" [class.active]=\"activeTab === 'overview'\" (click)=\"changeTab('overview')\">\n <i class=\"fas fa-th-large\"></i>\n <span>Overview</span>\n </button>\n <button class=\"tab\" [class.active]=\"activeTab === 'config'\" (click)=\"changeTab('config')\">\n <i class=\"fas fa-sliders-h\"></i>\n <span>Configuration</span>\n </button>\n <button class=\"tab\" [class.active]=\"activeTab === 'runs'\" (click)=\"changeTab('runs')\">\n <i class=\"fas fa-list\"></i>\n <span>Test Runs</span>\n <span class=\"tab-badge\" *ngIf=\"testRunsLoaded\">{{ testRuns.length }}</span>\n </button>\n <button class=\"tab\" [class.active]=\"activeTab === 'suites'\" (click)=\"changeTab('suites')\">\n <i class=\"fas fa-layer-group\"></i>\n <span>Test Suites</span>\n <span class=\"tab-badge\" *ngIf=\"suiteTestsLoaded\">{{ suiteTests.length }}</span>\n </button>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tab-content\">\n <!-- Overview Tab -->\n <div class=\"overview-tab\" *ngIf=\"activeTab === 'overview'\">\n <!-- Basic Information -->\n <div class=\"info-section\">\n <h3>Test Information</h3>\n <div class=\"info-grid\">\n <div class=\"info-item\">\n <div class=\"info-label\">Name</div>\n <div class=\"info-value\">{{ record.Name }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Type</div>\n <div class=\"info-value\">{{ record.Type }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Status</div>\n <div class=\"info-value\">{{ record.Status }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Priority</div>\n <div class=\"info-value\">{{ record.Priority || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Duration</div>\n <div class=\"info-value\">{{ record.EstimatedDurationSeconds ? formatDuration(record.EstimatedDurationSeconds) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Cost</div>\n <div class=\"info-value\">{{ record.EstimatedCostUSD ? formatCost(record.EstimatedCostUSD) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Repeat Count</div>\n <div class=\"info-value\">{{ record.RepeatCount || 1 }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Created</div>\n <div class=\"info-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n </div>\n </div>\n\n <!-- JSON Data Views -->\n <div class=\"json-section\">\n <h3>Test Definition</h3>\n <div class=\"json-tabs\">\n <button class=\"json-tab\" [class.active]=\"activeJsonView === 'input'\" (click)=\"setJsonView('input')\">\n Input Definition\n </button>\n <button class=\"json-tab\" [class.active]=\"activeJsonView === 'expected'\" (click)=\"setJsonView('expected')\">\n Expected Outcomes\n </button>\n <button class=\"json-tab\" [class.active]=\"activeJsonView === 'config'\" (click)=\"setJsonView('config')\">\n Configuration\n </button>\n <button class=\"json-tab\" [class.active]=\"activeJsonView === 'tags'\" (click)=\"setJsonView('tags')\">\n Tags\n </button>\n </div>\n <div class=\"json-content\">\n <pre>{{ getJsonData() | json }}</pre>\n </div>\n </div>\n </div>\n\n <!-- Configuration Tab -->\n <div class=\"config-tab\" *ngIf=\"activeTab === 'config'\">\n <div class=\"config-section\">\n <h3>Execution Settings</h3>\n <div class=\"config-grid\">\n <div class=\"config-item\">\n <label>Priority</label>\n <input type=\"number\" [(ngModel)]=\"record.Priority\" class=\"config-input\" />\n </div>\n <div class=\"config-item\">\n <label>Repeat Count</label>\n <input type=\"number\" [(ngModel)]=\"record.RepeatCount\" class=\"config-input\" />\n </div>\n <div class=\"config-item\">\n <label>Estimated Duration (seconds)</label>\n <input type=\"number\" [(ngModel)]=\"record.EstimatedDurationSeconds\" class=\"config-input\" />\n </div>\n <div class=\"config-item\">\n <label>Estimated Cost (USD)</label>\n <input type=\"number\" step=\"0.000001\" [(ngModel)]=\"record.EstimatedCostUSD\" class=\"config-input\" />\n </div>\n </div>\n </div>\n\n <div class=\"config-section\">\n <h3>Input Definition (JSON)</h3>\n <textarea class=\"json-editor\" [(ngModel)]=\"record.InputDefinition\" rows=\"10\"></textarea>\n </div>\n\n <div class=\"config-section\">\n <h3>Expected Outcomes (JSON)</h3>\n <textarea class=\"json-editor\" [(ngModel)]=\"record.ExpectedOutcomes\" rows=\"10\"></textarea>\n </div>\n\n <div class=\"config-section\">\n <h3>Configuration (JSON)</h3>\n <textarea class=\"json-editor\" [(ngModel)]=\"record.Configuration\" rows=\"10\"></textarea>\n </div>\n\n <div class=\"config-section\">\n <h3>Tags (JSON Array)</h3>\n <textarea class=\"json-editor\" [(ngModel)]=\"record.Tags\" rows=\"5\"></textarea>\n </div>\n </div>\n\n <!-- Test Runs Tab -->\n <div class=\"runs-tab\" *ngIf=\"activeTab === 'runs'\">\n <div class=\"runs-list\" *ngIf=\"testRuns.length > 0\">\n <div class=\"run-item\" *ngFor=\"let run of testRuns\" (click)=\"openTestRun(run.ID)\">\n <div class=\"run-icon\" [style.background-color]=\"run.Status === 'Passed' ? '#4caf50' : run.Status === 'Failed' ? '#f44336' : '#ff9800'\">\n <i class=\"fas\" [class.fa-check]=\"run.Status === 'Passed'\" [class.fa-times]=\"run.Status === 'Failed'\" [class.fa-exclamation]=\"run.Status === 'Error'\"></i>\n </div>\n <div class=\"run-content\">\n <div class=\"run-header\">\n <span class=\"run-id\">Run #{{ run.ID.substring(0, 8) }}</span>\n <span class=\"run-status\" [style.color]=\"run.Status === 'Passed' ? '#4caf50' : run.Status === 'Failed' ? '#f44336' : '#ff9800'\">{{ run.Status }}</span>\n </div>\n <div class=\"run-meta\">\n <span>{{ run.StartedAt | date:'short' }}</span>\n <span *ngIf=\"run.DurationSeconds\">{{ formatDuration(run.DurationSeconds) }}</span>\n <span *ngIf=\"run.CostUSD\">{{ formatCost(run.CostUSD) }}</span>\n <span *ngIf=\"run.Score != null\">Score: {{ run.Score.toFixed(4) }}</span>\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n </div>\n\n <div class=\"no-data\" *ngIf=\"testRunsLoaded && testRuns.length === 0\">\n <i class=\"fas fa-inbox\"></i>\n <p>No test runs found for this test</p>\n </div>\n </div>\n\n <!-- Test Suites Tab -->\n <div class=\"suites-tab\" *ngIf=\"activeTab === 'suites'\">\n <div class=\"suites-list\" *ngIf=\"suiteTests.length > 0\">\n <div class=\"suite-item\" *ngFor=\"let suiteTest of suiteTests\" (click)=\"openTestSuite(suiteTest.SuiteID)\">\n <div class=\"suite-icon\">\n <i class=\"fas fa-folder\"></i>\n </div>\n <div class=\"suite-content\">\n <div class=\"suite-name\">{{ suiteTest.Suite }}</div>\n <div class=\"suite-meta\">\n <span>Sequence: {{ suiteTest.Sequence }}</span>\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n </div>\n\n <div class=\"no-data\" *ngIf=\"suiteTestsLoaded && suiteTests.length === 0\">\n <i class=\"fas fa-inbox\"></i>\n <p>This test is not part of any test suite</p>\n </div>\n </div>\n </div>\n</div>\n", styles: [".test-form {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #f8f9fa;\n}\n\n/* Header */\n.test-header {\n background: white;\n border-bottom: 1px solid #e0e0e0;\n padding: 20px;\n}\n\n.header-content {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n}\n\n.header-left {\n display: flex;\n gap: 16px;\n}\n\n.test-icon {\n width: 56px;\n height: 56px;\n border-radius: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 24px;\n}\n\n.test-info h1 {\n margin: 0 0 8px 0;\n font-size: 24px;\n font-weight: 600;\n color: #333;\n}\n\n.test-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 12px;\n border-radius: 12px;\n color: white;\n font-size: 12px;\n font-weight: 600;\n}\n\n.test-type {\n font-size: 14px;\n color: #666;\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n}\n\n.test-description {\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 16px;\n}\n\n.test-description p {\n margin: 0;\n color: #666;\n line-height: 1.5;\n}\n\n.metrics-bar {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n}\n\n.metric-card {\n background: #f5f7fa;\n border-radius: 8px;\n padding: 12px;\n text-align: center;\n}\n\n.metric-label {\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #999;\n margin-bottom: 4px;\n}\n\n.metric-value {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n/* Tabs */\n.tabs-container {\n background: white;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.tabs {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 16px 20px;\n border: none;\n background: transparent;\n border-bottom: 3px solid transparent;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.tab:hover {\n color: #2196f3;\n background: rgba(33, 150, 243, 0.05);\n}\n\n.tab.active {\n color: #2196f3;\n border-bottom-color: #2196f3;\n}\n\n.tab i {\n font-size: 16px;\n}\n\n.tab-badge {\n background: #e0e0e0;\n color: #666;\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.tab.active .tab-badge {\n background: #e3f2fd;\n color: #2196f3;\n}\n\n/* Tab Content */\n.tab-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n/* Overview Tab */\n.overview-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.info-section,\n.json-section,\n.config-section {\n background: white;\n border-radius: 12px;\n padding: 20px;\n}\n\n.info-section h3,\n.json-section h3,\n.config-section h3 {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n}\n\n.info-grid,\n.config-grid {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 16px;\n}\n\n.info-item,\n.config-item {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.info-label,\n.config-item label {\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: #999;\n}\n\n.info-value {\n font-size: 14px;\n color: #333;\n word-wrap: break-word;\n}\n\n.config-input {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.config-input:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n.json-tabs {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n border-bottom: 2px solid #e0e0e0;\n}\n\n.json-tab {\n padding: 10px 20px;\n border: none;\n background: transparent;\n color: #666;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n border-bottom: 3px solid transparent;\n margin-bottom: -2px;\n transition: all 0.2s ease;\n}\n\n.json-tab:hover {\n color: #2196f3;\n}\n\n.json-tab.active {\n color: #2196f3;\n border-bottom-color: #2196f3;\n}\n\n.json-content {\n background: #1e1e1e;\n border-radius: 8px;\n padding: 16px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.json-content pre {\n margin: 0;\n font-family: 'Courier New', monospace;\n font-size: 13px;\n line-height: 1.5;\n color: #e0e0e0;\n white-space: pre-wrap;\n word-wrap: break-word;\n}\n\n.json-editor {\n width: 100%;\n padding: 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-family: 'Courier New', monospace;\n font-size: 13px;\n resize: vertical;\n}\n\n.json-editor:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n/* Runs Tab */\n.runs-list,\n.suites-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.run-item,\n.suite-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px;\n background: white;\n border: 2px solid transparent;\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.2s ease;\n}\n\n.run-item:hover,\n.suite-item:hover {\n background: #e3f2fd;\n border-color: #90caf9;\n}\n\n.run-icon,\n.suite-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 8px;\n color: white;\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.suite-icon {\n background: #ff9800;\n}\n\n.run-content,\n.suite-content {\n flex: 1;\n}\n\n.run-header,\n.suite-name {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 4px;\n}\n\n.run-id {\n margin-right: 12px;\n}\n\n.run-status {\n font-weight: 700;\n}\n\n.run-meta,\n.suite-meta {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: #666;\n}\n\n.run-item > i,\n.suite-item > i {\n color: #999;\n font-size: 14px;\n}\n\n/* No Data State */\n.no-data {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: #999;\n text-align: center;\n background: white;\n border-radius: 12px;\n}\n\n.no-data i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.3;\n}\n\n.no-data p {\n margin: 0;\n font-size: 14px;\n}\n\n/* Responsive */\n@media (max-width: 1200px) {\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid,\n .config-grid {\n grid-template-columns: 1fr;\n }\n}\n\n@media (max-width: 768px) {\n .metrics-bar {\n grid-template-columns: 1fr;\n }\n\n .header-content {\n flex-direction: column;\n gap: 16px;\n }\n\n .tabs {\n overflow-x: auto;\n }\n}\n"] }]
|
|
716
|
-
}], () => [{ type: i0.ElementRef }, { type: i1.SharedService }, { type: i2.Router }, { type: i2.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: i3.TestingDialogService }],
|
|
717
|
-
|
|
1964
|
+
args: [{ selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\" kendoDialogContainer>\n <!-- Header Section -->\n <div class=\"test-header\">\n <div class=\"header-content\">\n <div class=\"header-left\">\n <div class=\"test-icon\" [style.background-color]=\"getStatusColor()\">\n <i class=\"fas fa-flask\"></i>\n </div>\n <div class=\"test-info\">\n <h1>{{ record.Name }}</h1>\n <div class=\"test-meta\">\n <span class=\"status-badge\" [ngClass]=\"getStatusClass()\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon()\"></i>\n {{ record.Status }}\n </span>\n <span class=\"test-type\" *ngIf=\"record.Type\">\n <i class=\"fas fa-tag\"></i>\n {{ record.Type }}\n </span>\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n <button kendoButton (click)=\"runTest()\" themeColor=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test\n </button>\n <button kendoButton (click)=\"refresh()\" [disabled]=\"isRefreshing\">\n <i class=\"fas\" [ngClass]=\"isRefreshing ? 'fa-sync fa-spin' : 'fa-sync'\"></i>\n {{ isRefreshing ? 'Refreshing...' : 'Refresh' }}\n </button>\n </div>\n </div>\n\n <!-- Test Description -->\n <div class=\"test-description\" *ngIf=\"record.Description\">\n <p>{{ record.Description }}</p>\n </div>\n\n <!-- Metrics Bar -->\n <div class=\"metrics-bar\" *ngIf=\"testRunsLoaded && testRuns.length > 0\">\n <div class=\"metric-card\">\n <div class=\"metric-label\">Total Runs</div>\n <div class=\"metric-value\">{{ testRuns.length }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Pass Rate</div>\n <div class=\"metric-value\">{{ getPassRate().toFixed(1) }}%</div>\n <div class=\"metric-progress\">\n <div class=\"metric-progress-fill\" [style.width.%]=\"getPassRate()\"></div>\n </div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Cost</div>\n <div class=\"metric-value\">{{ formatCost(getAverageCost()) }}</div>\n </div>\n <div class=\"metric-card\">\n <div class=\"metric-label\">Avg Duration</div>\n <div class=\"metric-value\">{{ formatDuration(getAverageDuration()) }}</div>\n </div>\n </div>\n </div>\n\n <!-- Tabs -->\n <div class=\"tabs-container\">\n <div class=\"tabs\" role=\"tablist\">\n <button class=\"tab\"\n [class.active]=\"activeTab === 'overview'\"\n (click)=\"changeTab('overview')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'overview'\">\n <i class=\"fas fa-th-large\"></i>\n <span>Overview</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'config'\"\n (click)=\"changeTab('config')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'config'\">\n <i class=\"fas fa-sliders-h\"></i>\n <span>Configuration</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'runs'\"\n (click)=\"changeTab('runs')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'runs'\">\n <i class=\"fas fa-history\"></i>\n <span>Runs</span>\n <span class=\"tab-badge\" *ngIf=\"testRunsLoaded\">{{ testRuns.length }}</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'suites'\"\n (click)=\"changeTab('suites')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'suites'\">\n <i class=\"fas fa-layer-group\"></i>\n <span>Test Suites</span>\n <span class=\"tab-badge\" *ngIf=\"suiteTestsLoaded\">{{ suiteTests.length }}</span>\n </button>\n <button class=\"tab\"\n [class.active]=\"activeTab === 'analytics'\"\n (click)=\"changeTab('analytics')\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab === 'analytics'\">\n <i class=\"fas fa-chart-line\"></i>\n <span>Analytics</span>\n </button>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tab-content\">\n <!-- Overview Tab -->\n <div class=\"overview-tab\" *ngIf=\"activeTab === 'overview'\">\n <!-- Basic Information -->\n <div class=\"info-section\">\n <h3><i class=\"fas fa-info-circle\"></i> Test Information</h3>\n <div class=\"info-grid\">\n <div class=\"info-item\">\n <div class=\"info-label\">Name</div>\n <div class=\"info-value\">{{ record.Name }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Type</div>\n <div class=\"info-value\">{{ record.Type || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Status</div>\n <div class=\"info-value\">\n <span class=\"status-badge-inline\" [ngClass]=\"getStatusClass()\">{{ record.Status }}</span>\n </div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Priority</div>\n <div class=\"info-value\">{{ record.Priority || 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Duration</div>\n <div class=\"info-value\">{{ record.EstimatedDurationSeconds ? formatDuration(record.EstimatedDurationSeconds) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Estimated Cost</div>\n <div class=\"info-value\">{{ record.EstimatedCostUSD ? formatCost(record.EstimatedCostUSD) : 'N/A' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Repeat Count</div>\n <div class=\"info-value\">{{ record.RepeatCount || 1 }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Max Execution Time</div>\n <div class=\"info-value\">{{ formatTimeout(record.MaxExecutionTimeMS) }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Created</div>\n <div class=\"info-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"info-item\">\n <div class=\"info-label\">Updated</div>\n <div class=\"info-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n </div>\n </div>\n\n <!-- JSON Data Views -->\n <div class=\"json-section\">\n <h3><i class=\"fas fa-code\"></i> Test Definition</h3>\n <div class=\"json-tabs\">\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'input'\"\n (click)=\"setJsonView('input')\">\n <i class=\"fas fa-sign-in-alt\"></i>\n Input Definition\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'expected'\"\n (click)=\"setJsonView('expected')\">\n <i class=\"fas fa-check-double\"></i>\n Expected Outcomes\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'config'\"\n (click)=\"setJsonView('config')\">\n <i class=\"fas fa-cog\"></i>\n Configuration\n </button>\n <button class=\"json-tab\"\n [class.active]=\"activeJsonView === 'tags'\"\n (click)=\"setJsonView('tags')\">\n <i class=\"fas fa-tags\"></i>\n Tags\n </button>\n </div>\n <div class=\"code-editor-container\">\n <mj-code-editor\n [value]=\"getJsonData()\"\n language=\"json\"\n [readonly]=\"true\"\n [toolbar]=\"jsonToolbar\"\n [lineWrapping]=\"true\">\n </mj-code-editor>\n </div>\n </div>\n </div>\n\n <!-- Configuration Tab -->\n <div class=\"config-tab\" *ngIf=\"activeTab === 'config'\">\n <div class=\"config-section\">\n <h3><i class=\"fas fa-cogs\"></i> Execution Settings</h3>\n <div class=\"config-grid\">\n <div class=\"config-item\">\n <label>Priority</label>\n <input type=\"number\" [(ngModel)]=\"record.Priority\" class=\"config-input\" placeholder=\"Lower = Higher Priority\" />\n </div>\n <div class=\"config-item\">\n <label>Repeat Count</label>\n <input type=\"number\" [(ngModel)]=\"record.RepeatCount\" class=\"config-input\" min=\"1\" />\n </div>\n <div class=\"config-item\">\n <label>Estimated Duration (seconds)</label>\n <input type=\"number\" [(ngModel)]=\"record.EstimatedDurationSeconds\" class=\"config-input\" />\n </div>\n <div class=\"config-item\">\n <label>Estimated Cost (USD)</label>\n <input type=\"number\" step=\"0.000001\" [(ngModel)]=\"record.EstimatedCostUSD\" class=\"config-input\" />\n </div>\n <div class=\"config-item full-width\">\n <label>Max Execution Time (ms)</label>\n <input type=\"number\" [(ngModel)]=\"record.MaxExecutionTimeMS\" class=\"config-input\" placeholder=\"Default: 300000 (5 min)\" />\n <span class=\"config-hint\">Leave empty for default 5 minute timeout</span>\n </div>\n </div>\n </div>\n\n <div class=\"config-section\">\n <h3><i class=\"fas fa-sign-in-alt\"></i> Input Definition (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.InputDefinition || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.InputDefinition = $event\">\n </mj-code-editor>\n </div>\n </div>\n\n <div class=\"config-section\">\n <h3><i class=\"fas fa-check-double\"></i> Expected Outcomes (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.ExpectedOutcomes || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.ExpectedOutcomes = $event\">\n </mj-code-editor>\n </div>\n </div>\n\n <div class=\"config-section\">\n <h3><i class=\"fas fa-cog\"></i> Configuration (JSON)</h3>\n <div class=\"config-editor-container\">\n <mj-code-editor\n [value]=\"record.Configuration || ''\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.Configuration = $event\">\n </mj-code-editor>\n </div>\n </div>\n\n <div class=\"config-section\">\n <h3><i class=\"fas fa-tags\"></i> Tags (JSON Array)</h3>\n <div class=\"config-editor-container small\">\n <mj-code-editor\n [value]=\"record.Tags || '[]'\"\n language=\"json\"\n [readonly]=\"false\"\n [lineWrapping]=\"true\"\n (change)=\"record.Tags = $event\">\n </mj-code-editor>\n </div>\n </div>\n </div>\n\n <!-- Test Runs Tab -->\n <div class=\"runs-tab\" *ngIf=\"activeTab === 'runs'\">\n <!-- Loading State -->\n <div class=\"loading-state\" *ngIf=\"loadingRuns\">\n <div class=\"skeleton-list\">\n <div class=\"skeleton-card\" *ngFor=\"let i of [1,2,3,4,5]\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Runs List -->\n <div class=\"runs-list\" *ngIf=\"!loadingRuns && testRuns.length > 0\">\n <div class=\"run-item\" *ngFor=\"let run of testRuns\" (click)=\"openTestRun(run.ID)\">\n <div class=\"run-icon\" [style.background-color]=\"getRunStatusColor(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-stopwatch]=\"run.Status === 'Timeout'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n </div>\n <div class=\"run-content\">\n <div class=\"run-header\">\n <span class=\"run-id\">Run #{{ run.ID.substring(0, 8) }}</span>\n <span class=\"run-status\" [style.color]=\"getRunStatusColor(run.Status)\">{{ run.Status }}</span>\n </div>\n <div class=\"run-meta\">\n <span><i class=\"fas fa-calendar\"></i> {{ getRelativeTime(run.StartedAt) }}</span>\n <span *ngIf=\"run.DurationSeconds\"><i class=\"fas fa-clock\"></i> {{ formatDuration(run.DurationSeconds) }}</span>\n <span *ngIf=\"run.CostUSD\"><i class=\"fas fa-dollar-sign\"></i> {{ formatCost(run.CostUSD) }}</span>\n <mj-entity-link-pill\n *ngIf=\"run.TargetLogEntityID && run.TargetLogID\"\n [entityName]=\"run.TargetLogEntity\"\n [recordId]=\"run.TargetLogID\">\n </mj-entity-link-pill>\n </div>\n <!-- Evaluation indicators -->\n <div class=\"run-eval-stack\">\n <!-- Status indicator -->\n <span class=\"eval-pill status-pill\" *ngIf=\"evalPreferences.showExecution\"\n [ngClass]=\"'status-' + run.Status.toLowerCase()\"\n [title]=\"getStatusTooltip(run.Status)\">\n <i class=\"fas\"\n [class.fa-check]=\"run.Status === 'Passed'\"\n [class.fa-times]=\"run.Status === 'Failed'\"\n [class.fa-exclamation]=\"run.Status === 'Error'\"\n [class.fa-hourglass-end]=\"run.Status === 'Timeout'\"\n [class.fa-forward]=\"run.Status === 'Skipped'\"\n [class.fa-spinner]=\"run.Status === 'Running'\"\n [class.fa-clock]=\"run.Status === 'Pending'\"></i>\n <span>{{ run.Status }}</span>\n </span>\n <!-- Human feedback indicator -->\n <ng-container *ngIf=\"evalPreferences.showHuman\">\n <span class=\"eval-pill human-pill no-feedback\" *ngIf=\"!getFeedbackForRun(run.ID)?.Rating\"\n title=\"Human Review: No rating submitted yet\">\n <i class=\"fas fa-user-slash\"></i>\n </span>\n <span class=\"eval-pill human-pill has-feedback\" *ngIf=\"getFeedbackForRun(run.ID)?.Rating as rating\"\n [class.rating-low]=\"rating <= 4\"\n [class.rating-medium]=\"rating >= 5 && rating <= 6\"\n [class.rating-good]=\"rating >= 7 && rating <= 8\"\n [class.rating-excellent]=\"rating >= 9\"\n [title]=\"getHumanTooltip(rating, getFeedbackForRun(run.ID)?.CorrectionSummary || getFeedbackForRun(run.ID)?.Comments || null)\">\n <i class=\"fas fa-user\"></i>\n <span>{{ rating }}</span>\n </span>\n </ng-container>\n <!-- Auto score indicator -->\n <ng-container *ngIf=\"evalPreferences.showAuto\">\n <span class=\"eval-pill auto-pill no-score\" *ngIf=\"run.Score == null\"\n title=\"Auto Score: No automated score available\">\n <i class=\"fas fa-robot\"></i>\n </span>\n <span class=\"eval-pill auto-pill has-score\" *ngIf=\"run.Score != null\"\n [class.score-low]=\"run.Score < 0.5\"\n [class.score-medium]=\"run.Score >= 0.5 && run.Score < 0.7\"\n [class.score-good]=\"run.Score >= 0.7 && run.Score < 0.85\"\n [class.score-excellent]=\"run.Score >= 0.85\"\n [title]=\"'Auto Score: ' + (run.Score * 100).toFixed(0) + '% automated evaluation'\">\n <i class=\"fas fa-robot\"></i>\n <span>{{ (run.Score * 100).toFixed(0) }}%</span>\n </span>\n </ng-container>\n </div>\n <div class=\"run-tags\" *ngIf=\"getRunTags(run).length > 0\">\n <span class=\"run-tag\" *ngFor=\"let tag of getRunTags(run).slice(0, 3)\">{{ tag }}</span>\n <span class=\"run-tag-more\" *ngIf=\"getRunTags(run).length > 3\">+{{ getRunTags(run).length - 3 }}</span>\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n </div>\n\n <!-- Empty State -->\n <div class=\"empty-state\" *ngIf=\"testRunsLoaded && !loadingRuns && testRuns.length === 0\">\n <div class=\"empty-icon\">\n <i class=\"fas fa-play-circle\"></i>\n </div>\n <h4>No Test Runs Yet</h4>\n <p>Run this test to see execution history and results here.</p>\n <button kendoButton (click)=\"runTest()\" themeColor=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test Now\n </button>\n </div>\n </div>\n\n <!-- Test Suites Tab -->\n <div class=\"suites-tab\" *ngIf=\"activeTab === 'suites'\">\n <!-- Loading State -->\n <div class=\"loading-state\" *ngIf=\"loadingSuites\">\n <div class=\"skeleton-list\">\n <div class=\"skeleton-card\" *ngFor=\"let i of [1,2,3]\">\n <div class=\"skeleton-icon\"></div>\n <div class=\"skeleton-content\">\n <div class=\"skeleton-line wide\"></div>\n <div class=\"skeleton-line narrow\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Suites List -->\n <div class=\"suites-list\" *ngIf=\"!loadingSuites && suiteTests.length > 0\">\n <div class=\"suite-item\" *ngFor=\"let suiteTest of suiteTests\" (click)=\"openTestSuite(suiteTest.SuiteID)\">\n <div class=\"suite-icon\">\n <i class=\"fas fa-layer-group\"></i>\n </div>\n <div class=\"suite-content\">\n <div class=\"suite-name\">{{ suiteTest.Suite }}</div>\n <div class=\"suite-meta\">\n <span><i class=\"fas fa-sort-numeric-up\"></i> Sequence: {{ suiteTest.Sequence }}</span>\n <span *ngIf=\"suiteTest.Status\"><i class=\"fas fa-info-circle\"></i> {{ suiteTest.Status }}</span>\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n </div>\n\n <!-- Empty State -->\n <div class=\"empty-state\" *ngIf=\"suiteTestsLoaded && !loadingSuites && suiteTests.length === 0\">\n <div class=\"empty-icon\">\n <i class=\"fas fa-folder-open\"></i>\n </div>\n <h4>Not Part of Any Suite</h4>\n <p>This test is not included in any test suites. Add it to a suite to run it with other tests.</p>\n </div>\n </div>\n\n <!-- Analytics Tab -->\n <div class=\"history-tab\" *ngIf=\"activeTab === 'analytics'\">\n <!-- Loading State -->\n <div class=\"loading-state\" *ngIf=\"loadingHistory\">\n <mj-loading text=\"Loading history...\"></mj-loading>\n </div>\n\n <!-- History Content -->\n <div class=\"history-content\" *ngIf=\"!loadingHistory && historyLoaded\">\n <!-- Filters -->\n <div class=\"history-filters\">\n <div class=\"time-range-filters\">\n <span class=\"filter-label\">Time Range:</span>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '7d'\"\n (click)=\"setHistoryTimeRange('7d')\">7 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '30d'\"\n (click)=\"setHistoryTimeRange('30d')\">30 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === '90d'\"\n (click)=\"setHistoryTimeRange('90d')\">90 Days</button>\n <button class=\"filter-btn\"\n [class.active]=\"historyTimeRange === 'all'\"\n (click)=\"setHistoryTimeRange('all')\">All Time</button>\n </div>\n <div class=\"history-actions\">\n <button kendoButton (click)=\"exportHistoryToCSV()\" [disabled]=\"historyData.length === 0\">\n <i class=\"fas fa-download\"></i> Export CSV\n </button>\n </div>\n </div>\n\n <!-- KPI Cards -->\n <div class=\"history-kpi-cards\" *ngIf=\"historyData.length > 0\">\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-play-circle\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ getTotalRuns() }}</div>\n <div class=\"kpi-label\">Total Runs</div>\n </div>\n </div>\n <div class=\"kpi-card\" *ngIf=\"evalPreferences.showExecution\">\n <div class=\"kpi-icon pass-rate\">\n <i class=\"fas fa-percentage\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">\n {{ getOverallPassRate().toFixed(1) }}%\n <i class=\"fas trend-icon\"\n [class.fa-arrow-up]=\"getPassRateTrend() === 'up'\"\n [class.fa-arrow-down]=\"getPassRateTrend() === 'down'\"\n [class.fa-minus]=\"getPassRateTrend() === 'stable'\"\n [class.trend-up]=\"getPassRateTrend() === 'up'\"\n [class.trend-down]=\"getPassRateTrend() === 'down'\"></i>\n </div>\n <div class=\"kpi-label\">Pass Rate</div>\n </div>\n </div>\n <div class=\"kpi-card\" *ngIf=\"evalPreferences.showAuto\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-star\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ (getOverallAvgScore() * 100).toFixed(1) }}%</div>\n <div class=\"kpi-label\">Avg Score</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-clock\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatDuration(getOverallAvgDuration()) }}</div>\n <div class=\"kpi-label\">Avg Duration</div>\n </div>\n </div>\n <div class=\"kpi-card\">\n <div class=\"kpi-icon\">\n <i class=\"fas fa-dollar-sign\"></i>\n </div>\n <div class=\"kpi-content\">\n <div class=\"kpi-value\">{{ formatCost(getOverallAvgCost()) }}</div>\n <div class=\"kpi-label\">Avg Cost</div>\n </div>\n </div>\n </div>\n\n <!-- Daily History Table -->\n <div class=\"history-section\" *ngIf=\"historyData.length > 0\">\n <h3><i class=\"fas fa-calendar-alt\"></i> Daily Performance</h3>\n <div class=\"history-table-container\">\n <table class=\"history-table\">\n <thead>\n <tr>\n <th>Date</th>\n <th>Runs</th>\n <th *ngIf=\"evalPreferences.showExecution\">Passed</th>\n <th *ngIf=\"evalPreferences.showExecution\">Failed</th>\n <th *ngIf=\"evalPreferences.showExecution\">Pass Rate</th>\n <th *ngIf=\"evalPreferences.showAuto\">Avg Score</th>\n <th>Avg Duration</th>\n <th>Avg Cost</th>\n </tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let day of historyData\">\n <td class=\"date-cell\">{{ day.date | date:'mediumDate' }}</td>\n <td class=\"runs-cell\">{{ day.runCount }}</td>\n <td class=\"passed-cell\" *ngIf=\"evalPreferences.showExecution\">{{ day.passCount }}</td>\n <td class=\"failed-cell\" *ngIf=\"evalPreferences.showExecution\">{{ day.failCount }}</td>\n <td class=\"pass-rate-cell\" *ngIf=\"evalPreferences.showExecution\">\n <div class=\"pass-rate-bar\">\n <div class=\"pass-rate-fill\" [style.width.%]=\"day.passRate\"></div>\n <span class=\"pass-rate-text\">{{ day.passRate.toFixed(1) }}%</span>\n </div>\n </td>\n <td class=\"score-cell\" *ngIf=\"evalPreferences.showAuto\">{{ (day.avgScore * 100).toFixed(1) }}%</td>\n <td class=\"duration-cell\">{{ formatDuration(day.avgDuration) }}</td>\n <td class=\"cost-cell\">{{ formatCost(day.avgCost) }}</td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Suite Performance -->\n <div class=\"history-section\" *ngIf=\"suitePerformance.length > 0\">\n <h3><i class=\"fas fa-layer-group\"></i> Performance by Suite</h3>\n <div class=\"suite-performance-list\">\n <div class=\"suite-perf-card\" *ngFor=\"let suite of suitePerformance\" (click)=\"openSuiteFromHistory(suite.suiteId)\">\n <div class=\"suite-perf-header\">\n <div class=\"suite-perf-name\">{{ suite.suiteName }}</div>\n <div class=\"suite-perf-tags\" *ngIf=\"suite.tags.length > 0\">\n <span class=\"tag-mini\" *ngFor=\"let tag of suite.tags.slice(0, 3)\">{{ tag }}</span>\n <span class=\"tag-more\" *ngIf=\"suite.tags.length > 3\">+{{ suite.tags.length - 3 }}</span>\n </div>\n </div>\n <div class=\"suite-perf-stats\">\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ suite.totalRuns }}</span>\n <span class=\"stat-label\">Runs</span>\n </div>\n <div class=\"suite-stat\" *ngIf=\"evalPreferences.showExecution\">\n <span class=\"stat-value pass-rate\" [class.high]=\"suite.passRate >= 80\" [class.low]=\"suite.passRate < 50\">\n {{ suite.passRate.toFixed(1) }}%\n </span>\n <span class=\"stat-label\">Pass Rate</span>\n </div>\n <div class=\"suite-stat\" *ngIf=\"evalPreferences.showAuto\">\n <span class=\"stat-value\">{{ (suite.avgScore * 100).toFixed(1) }}%</span>\n <span class=\"stat-label\">Avg Score</span>\n </div>\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatDuration(suite.avgDuration) }}</span>\n <span class=\"stat-label\">Avg Duration</span>\n </div>\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ formatCost(suite.avgCost) }}</span>\n <span class=\"stat-label\">Avg Cost</span>\n </div>\n <div class=\"suite-stat\" *ngIf=\"suite.lastRun\">\n <span class=\"stat-value\">{{ getRelativeTime(suite.lastRun) }}</span>\n <span class=\"stat-label\">Last Run</span>\n </div>\n </div>\n <i class=\"fas fa-chevron-right suite-perf-arrow\"></i>\n </div>\n </div>\n </div>\n\n <!-- Empty State -->\n <div class=\"empty-state\" *ngIf=\"historyData.length === 0\">\n <div class=\"empty-icon\">\n <i class=\"fas fa-chart-line\"></i>\n </div>\n <h4>No History Available</h4>\n <p>Run this test to start building history and analytics data.</p>\n <button kendoButton (click)=\"runTest()\" themeColor=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test Now\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Keyboard Shortcuts Toggle Button -->\n <button class=\"shortcuts-toggle\" (click)=\"toggleShortcuts()\" [title]=\"showShortcuts ? 'Hide keyboard shortcuts' : 'Show keyboard shortcuts'\">\n <i class=\"fas fa-keyboard\"></i>\n </button>\n\n <!-- Keyboard Shortcuts Hint (Desktop Only) -->\n <div class=\"keyboard-shortcuts\" *ngIf=\"showShortcuts\">\n <div class=\"shortcuts-header\">\n <i class=\"fas fa-keyboard\"></i>\n Shortcuts\n <button class=\"shortcuts-close\" (click)=\"toggleShortcuts()\" title=\"Hide shortcuts\">\n <i class=\"fas fa-times\"></i>\n </button>\n </div>\n <div class=\"shortcut-list\">\n <div class=\"shortcut-item\">\n <span>Refresh</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>R</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Run Test</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>Enter</kbd></span>\n </div>\n <div class=\"shortcut-item\">\n <span>Switch Tabs</span>\n <span class=\"shortcut-keys\"><kbd>1</kbd>-<kbd>5</kbd></span>\n </div>\n </div>\n </div>\n</div>\n", styles: ["/* ===========================\n Test Form - World-Class UX\n Premium Test Definition Styles\n =========================== */\n\n/* CSS Custom Properties for Theming - using :host for Angular encapsulation */\n:host {\n --test-primary: #2563eb;\n --test-primary-light: #3b82f6;\n --test-primary-dark: #1d4ed8;\n --test-success: #10b981;\n --test-success-light: #d1fae5;\n --test-error: #ef4444;\n --test-error-light: #fee2e2;\n --test-warning: #f59e0b;\n --test-warning-light: #fef3c7;\n --test-disabled: #6b7280;\n --test-bg: #f8fafc;\n --test-surface: #ffffff;\n --test-border: #e2e8f0;\n --test-text: #1e293b;\n --test-text-secondary: #64748b;\n --test-text-muted: #94a3b8;\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05);\n --test-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);\n --test-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n/* Base Container */\n.test-form {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--test-bg);\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\n/* ===========================\n Header Section\n =========================== */\n.test-header {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n padding: 20px;\n}\n\n.header-content {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n margin-bottom: 16px;\n gap: 16px;\n}\n\n.header-left {\n display: flex;\n gap: 16px;\n flex: 1;\n min-width: 0;\n}\n\n/* Test Icon */\n.test-icon {\n width: 56px;\n height: 56px;\n border-radius: var(--test-radius-md);\n display: flex;\n align-items: center;\n justify-content: center;\n color: white;\n font-size: 24px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-md);\n transition: var(--test-transition);\n}\n\n.test-icon:hover {\n transform: scale(1.05);\n}\n\n/* Test Info */\n.test-info {\n flex: 1;\n min-width: 0;\n}\n\n.test-info h1 {\n margin: 0 0 8px 0;\n font-size: clamp(18px, 4vw, 24px);\n font-weight: 700;\n color: var(--test-text);\n line-height: 1.2;\n word-wrap: break-word;\n}\n\n.test-meta {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* Status Badge */\n.status-badge {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 14px;\n border-radius: 20px;\n color: white;\n font-size: 12px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.status-badge.status-active { background: linear-gradient(135deg, var(--test-success) 0%, #059669 100%); }\n.status-badge.status-disabled { background: linear-gradient(135deg, var(--test-disabled) 0%, #4b5563 100%); }\n.status-badge.status-pending { background: linear-gradient(135deg, var(--test-warning) 0%, #d97706 100%); }\n\n.status-badge-inline {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: white;\n font-size: 11px;\n font-weight: 600;\n}\n\n.status-badge-inline.status-active { background: var(--test-success); }\n.status-badge-inline.status-disabled { background: var(--test-disabled); }\n.status-badge-inline.status-pending { background: var(--test-warning); }\n\n.test-type {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n font-size: 14px;\n color: var(--test-text-secondary);\n padding: 4px 10px;\n background: var(--test-bg);\n border-radius: var(--test-radius-sm);\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.header-actions button {\n white-space: nowrap;\n}\n\n/* Test Description */\n.test-description {\n padding: 16px;\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border-radius: var(--test-radius-md);\n margin-bottom: 16px;\n border: 1px solid var(--test-border);\n}\n\n.test-description p {\n margin: 0;\n color: var(--test-text-secondary);\n line-height: 1.6;\n font-size: 14px;\n}\n\n/* ===========================\n Metrics Bar\n =========================== */\n.metrics-bar {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: 12px;\n}\n\n.metric-card {\n background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 14px;\n text-align: center;\n transition: var(--test-transition);\n}\n\n.metric-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.metric-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n margin-bottom: 6px;\n}\n\n.metric-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n/* Progress bar in metric card */\n.metric-progress {\n margin-top: 8px;\n height: 4px;\n background: var(--test-border);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.metric-progress-fill {\n height: 100%;\n background: linear-gradient(90deg, var(--test-success) 0%, #34d399 100%);\n border-radius: 2px;\n transition: width 0.5s ease-out;\n}\n\n/* ===========================\n Tabs Navigation\n =========================== */\n.tabs-container {\n background: var(--test-surface);\n border-bottom: 1px solid var(--test-border);\n position: sticky;\n top: 0;\n z-index: 10;\n}\n\n.tabs {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n scrollbar-width: none;\n -ms-overflow-style: none;\n}\n\n.tabs::-webkit-scrollbar {\n display: none;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 18px;\n border: none;\n background: transparent;\n border-bottom: 3px solid transparent;\n color: var(--test-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n white-space: nowrap;\n}\n\n.tab:hover {\n color: var(--test-primary);\n background: rgba(37, 99, 235, 0.05);\n}\n\n.tab.active {\n color: var(--test-primary);\n border-bottom-color: var(--test-primary);\n font-weight: 600;\n}\n\n.tab i {\n font-size: 15px;\n}\n\n.tab-badge {\n background: var(--test-border);\n color: var(--test-text-secondary);\n padding: 2px 8px;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.tab.active .tab-badge {\n background: rgba(37, 99, 235, 0.15);\n color: var(--test-primary);\n}\n\n.tab-shortcut {\n font-size: 10px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n padding: 2px 6px;\n border-radius: 4px;\n font-weight: 600;\n margin-left: 4px;\n}\n\n/* ===========================\n Tab Content Area\n =========================== */\n.tab-content {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n scroll-behavior: smooth;\n}\n\n/* ===========================\n Overview Tab\n =========================== */\n.overview-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Info Section */\n.info-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.info-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.info-section h3 i {\n color: var(--test-primary);\n}\n\n.info-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 16px;\n}\n\n.info-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.info-label {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.info-value {\n font-size: 14px;\n color: var(--test-text);\n word-wrap: break-word;\n font-weight: 500;\n}\n\n/* JSON Section */\n.json-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-section h3 {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.json-section h3 i {\n color: var(--test-primary);\n}\n\n.json-tabs {\n display: flex;\n gap: 4px;\n margin-bottom: 16px;\n background: var(--test-bg);\n border-radius: var(--test-radius-md);\n padding: 4px;\n flex-wrap: wrap;\n}\n\n.json-tab {\n flex: 1;\n min-width: 100px;\n padding: 10px 14px;\n border: none;\n background: transparent;\n color: var(--test-text-secondary);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n border-radius: var(--test-radius-sm);\n transition: var(--test-transition);\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 6px;\n}\n\n.json-tab:hover {\n color: var(--test-text);\n background: rgba(0, 0, 0, 0.05);\n}\n\n.json-tab.active {\n background: var(--test-surface);\n color: var(--test-primary);\n font-weight: 600;\n box-shadow: var(--test-shadow-sm);\n}\n\n.json-tab i {\n font-size: 12px;\n}\n\n.code-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n max-height: 400px;\n}\n\n/* ===========================\n Configuration Tab\n =========================== */\n.config-tab {\n display: flex;\n flex-direction: column;\n gap: 20px;\n animation: fadeIn 0.3s ease-out;\n}\n\n.config-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.config-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.config-section h3 i {\n color: var(--test-primary);\n}\n\n.config-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));\n gap: 20px;\n}\n\n.config-item {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.config-item.full-width {\n grid-column: 1 / -1;\n}\n\n.config-item label {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n}\n\n.config-input {\n padding: 10px 14px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n font-size: 14px;\n transition: var(--test-transition);\n background: var(--test-surface);\n}\n\n.config-input:focus {\n outline: none;\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);\n}\n\n.config-input::placeholder {\n color: var(--test-text-muted);\n}\n\n.config-hint {\n font-size: 11px;\n color: var(--test-text-muted);\n margin-top: 4px;\n}\n\n.config-editor-container {\n border-radius: var(--test-radius-md);\n overflow: hidden;\n border: 1px solid var(--test-border);\n min-height: 200px;\n}\n\n.config-editor-container.small {\n min-height: 100px;\n max-height: 150px;\n}\n\n/* ===========================\n Runs Tab\n =========================== */\n.runs-tab,\n.suites-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n/* Loading States & Skeletons */\n.loading-state {\n padding: 0;\n}\n\n.skeleton-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.skeleton-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n border: 1px solid var(--test-border);\n}\n\n.skeleton-icon {\n width: 44px;\n height: 44px;\n border-radius: var(--test-radius-md);\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n flex-shrink: 0;\n}\n\n.skeleton-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.skeleton-line {\n height: 14px;\n border-radius: 4px;\n background: linear-gradient(90deg, #e2e8f0 25%, #f1f5f9 50%, #e2e8f0 75%);\n background-size: 200% 100%;\n animation: shimmer 1.5s infinite;\n}\n\n.skeleton-line.wide { width: 70%; }\n.skeleton-line.narrow { width: 40%; }\n\n@keyframes shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n}\n\n/* Runs List */\n.runs-list,\n.suites-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.run-item,\n.suite-item {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 16px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.run-item:hover,\n.suite-item:hover {\n background: rgba(37, 99, 235, 0.05);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n box-shadow: var(--test-shadow-sm);\n}\n\n.run-icon,\n.suite-icon {\n width: 44px;\n height: 44px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: var(--test-radius-md);\n color: white;\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon {\n background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);\n}\n\n.run-content,\n.suite-content {\n flex: 1;\n min-width: 0;\n}\n\n.run-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 4px;\n}\n\n.run-id {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.run-status {\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n}\n\n.suite-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 4px;\n}\n\n.run-meta,\n.suite-meta {\n display: flex;\n gap: 16px;\n font-size: 12px;\n color: var(--test-text-secondary);\n flex-wrap: wrap;\n}\n\n.run-meta span,\n.suite-meta span {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.run-meta span i,\n.suite-meta span i {\n color: var(--test-text-muted);\n font-size: 11px;\n}\n\n.run-item > i.fa-chevron-right,\n.suite-item > i.fa-chevron-right {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.run-item:hover > i.fa-chevron-right,\n.suite-item:hover > i.fa-chevron-right {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* Run Evaluation Stack */\n.run-eval-stack {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n/* Evaluation Pills */\n.eval-pill {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n border-radius: 16px;\n font-size: 11px;\n font-weight: 600;\n transition: var(--test-transition);\n}\n\n.eval-pill i {\n font-size: 10px;\n}\n\n/* Status pill colors */\n.eval-pill.status-pill.status-passed {\n background: #dcfce7;\n color: #16a34a;\n}\n\n.eval-pill.status-pill.status-failed {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.status-pill.status-error {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.status-pill.status-timeout {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.status-pill.status-skipped,\n.eval-pill.status-pill.status-pending {\n background: #f1f5f9;\n color: #64748b;\n}\n\n.eval-pill.status-pill.status-running {\n background: #dbeafe;\n color: #2563eb;\n}\n\n/* Human feedback pills */\n.eval-pill.human-pill.no-feedback {\n background: #f1f5f9;\n color: #94a3b8;\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.human-pill.has-feedback.rating-good {\n background: #d1fae5;\n color: #059669;\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent {\n background: #dcfce7;\n color: #16a34a;\n}\n\n/* Auto score pills */\n.eval-pill.auto-pill.no-score {\n background: #f1f5f9;\n color: #94a3b8;\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low {\n background: #fee2e2;\n color: #dc2626;\n}\n\n.eval-pill.auto-pill.has-score.score-medium {\n background: #fef3c7;\n color: #d97706;\n}\n\n.eval-pill.auto-pill.has-score.score-good {\n background: #d1fae5;\n color: #059669;\n}\n\n.eval-pill.auto-pill.has-score.score-excellent {\n background: #dcfce7;\n color: #16a34a;\n}\n\n/* Run Tags */\n.run-tags {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 8px;\n flex-wrap: wrap;\n}\n\n.run-tag {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: rgba(37, 99, 235, 0.1);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n.run-tag-more {\n display: inline-flex;\n align-items: center;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 11px;\n font-weight: 500;\n}\n\n/* ===========================\n Empty States\n =========================== */\n.empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 24px;\n text-align: center;\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n}\n\n.empty-icon {\n width: 80px;\n height: 80px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--test-bg);\n border-radius: 50%;\n margin-bottom: 20px;\n}\n\n.empty-icon i {\n font-size: 36px;\n color: var(--test-text-muted);\n}\n\n.empty-state h4 {\n margin: 0 0 8px 0;\n font-size: 18px;\n font-weight: 600;\n color: var(--test-text);\n}\n\n.empty-state p {\n margin: 0 0 20px 0;\n font-size: 14px;\n color: var(--test-text-secondary);\n max-width: 300px;\n}\n\n/* Legacy no-data for backwards compatibility */\n.no-data {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n color: var(--test-text-muted);\n text-align: center;\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n}\n\n.no-data i {\n font-size: 48px;\n margin-bottom: 16px;\n opacity: 0.3;\n}\n\n.no-data p {\n margin: 0;\n font-size: 14px;\n}\n\n/* ===========================\n Keyboard Shortcuts Popup\n =========================== */\n/* Keyboard shortcuts toggle button */\n.shortcuts-toggle {\n position: fixed;\n bottom: 20px;\n right: 20px;\n width: 36px;\n height: 36px;\n border-radius: 50%;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n box-shadow: var(--test-shadow-md);\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n color: var(--test-text-secondary);\n font-size: 14px;\n z-index: 99;\n transition: var(--test-transition);\n opacity: 0.7;\n}\n\n.shortcuts-toggle:hover {\n opacity: 1;\n transform: scale(1.1);\n color: var(--test-primary);\n border-color: var(--test-primary);\n}\n\n.keyboard-shortcuts {\n position: fixed;\n bottom: 20px;\n right: 20px;\n background: var(--test-surface);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n padding: 12px 16px;\n box-shadow: var(--test-shadow-lg);\n font-size: 12px;\n color: var(--test-text-secondary);\n z-index: 100;\n max-width: 260px;\n}\n\n.shortcuts-header {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-bottom: 10px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--test-border);\n font-weight: 600;\n color: var(--test-text);\n}\n\n.shortcuts-close {\n margin-left: auto;\n background: none;\n border: none;\n cursor: pointer;\n color: var(--test-text-muted);\n font-size: 12px;\n padding: 2px 4px;\n border-radius: 4px;\n transition: var(--test-transition);\n}\n\n.shortcuts-close:hover {\n color: var(--test-text);\n background: var(--test-border);\n}\n\n.shortcut-list {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.shortcut-item {\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.shortcut-keys {\n display: flex;\n gap: 4px;\n}\n\n.shortcut-keys kbd {\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: 4px;\n padding: 2px 6px;\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n font-size: 11px;\n color: var(--test-text);\n}\n\n/* ===========================\n Responsive Design - Tablet\n =========================== */\n@media (max-width: 1024px) {\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .config-grid {\n grid-template-columns: 1fr;\n }\n\n .keyboard-shortcuts, .shortcuts-toggle {\n display: none;\n }\n}\n\n/* ===========================\n Responsive Design - Mobile\n =========================== */\n@media (max-width: 768px) {\n .test-form {\n height: auto;\n min-height: 100%;\n }\n\n .test-header {\n padding: 16px;\n }\n\n .header-content {\n flex-direction: column;\n gap: 16px;\n }\n\n .header-left {\n width: 100%;\n }\n\n .test-icon {\n width: 48px;\n height: 48px;\n font-size: 20px;\n }\n\n .test-info h1 {\n font-size: 18px;\n }\n\n .test-meta {\n gap: 8px;\n }\n\n .status-badge {\n padding: 4px 10px;\n font-size: 11px;\n }\n\n .header-actions {\n width: 100%;\n justify-content: stretch;\n }\n\n .header-actions button {\n flex: 1;\n }\n\n .metrics-bar {\n grid-template-columns: repeat(2, 1fr);\n gap: 10px;\n }\n\n .metric-card {\n padding: 12px;\n }\n\n .metric-value {\n font-size: 16px;\n }\n\n .tabs {\n padding: 0 12px;\n }\n\n .tab {\n padding: 12px 14px;\n font-size: 13px;\n gap: 6px;\n }\n\n .tab i {\n font-size: 14px;\n }\n\n .tab-shortcut {\n display: none;\n }\n\n .tab-content {\n padding: 16px;\n }\n\n .info-section,\n .json-section,\n .config-section {\n padding: 18px;\n }\n\n .info-grid {\n grid-template-columns: 1fr;\n }\n\n .json-tabs {\n flex-direction: column;\n }\n\n .json-tab {\n min-width: 0;\n justify-content: flex-start;\n }\n\n .run-item,\n .suite-item {\n padding: 14px;\n }\n\n .run-icon,\n .suite-icon {\n width: 40px;\n height: 40px;\n font-size: 16px;\n }\n\n .run-meta,\n .suite-meta {\n flex-direction: column;\n gap: 4px;\n }\n\n .empty-state {\n padding: 40px 20px;\n }\n\n .empty-icon {\n width: 64px;\n height: 64px;\n }\n\n .empty-icon i {\n font-size: 28px;\n }\n}\n\n/* ===========================\n Responsive Design - Small Mobile\n =========================== */\n@media (max-width: 480px) {\n .test-header {\n padding: 12px;\n }\n\n .header-left {\n gap: 12px;\n }\n\n .test-icon {\n width: 40px;\n height: 40px;\n font-size: 18px;\n border-radius: 8px;\n }\n\n .test-info h1 {\n font-size: 16px;\n }\n\n .metrics-bar {\n grid-template-columns: 1fr 1fr;\n }\n\n .tabs {\n padding: 0 8px;\n }\n\n .tab {\n padding: 10px 12px;\n font-size: 12px;\n }\n\n .tab-badge {\n display: none;\n }\n\n .tab-content {\n padding: 12px;\n }\n\n .run-header {\n flex-direction: column;\n align-items: flex-start;\n gap: 4px;\n }\n}\n\n/* ===========================\n Touch Device Optimizations\n =========================== */\n@media (hover: none) and (pointer: coarse) {\n .tab,\n .json-tab,\n .run-item,\n .suite-item {\n -webkit-tap-highlight-color: transparent;\n }\n\n .run-item:active,\n .suite-item:active {\n background: rgba(37, 99, 235, 0.1);\n transform: scale(0.98);\n }\n\n .tab:active,\n .json-tab:active {\n background: rgba(37, 99, 235, 0.1);\n }\n\n /* Larger touch targets */\n .tab {\n min-height: 48px;\n }\n\n .run-item,\n .suite-item {\n min-height: 64px;\n }\n}\n\n/* ===========================\n Reduced Motion\n =========================== */\n@media (prefers-reduced-motion: reduce) {\n *,\n *::before,\n *::after {\n animation-duration: 0.01ms !important;\n animation-iteration-count: 1 !important;\n transition-duration: 0.01ms !important;\n }\n\n .skeleton-icon,\n .skeleton-line {\n animation: none;\n background: #e2e8f0;\n }\n}\n\n/* ===========================\n History Tab\n =========================== */\n.history-tab {\n animation: fadeIn 0.3s ease-out;\n}\n\n.history-content {\n display: flex;\n flex-direction: column;\n gap: 24px;\n}\n\n/* History Filters */\n.history-filters {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-wrap: wrap;\n gap: 16px;\n padding: 16px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n}\n\n.time-range-filters {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.filter-label {\n font-size: 13px;\n font-weight: 600;\n color: var(--test-text-secondary);\n margin-right: 4px;\n}\n\n.filter-btn {\n padding: 8px 16px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n color: var(--test-text-secondary);\n font-size: 13px;\n font-weight: 500;\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.filter-btn:hover {\n border-color: var(--test-primary-light);\n color: var(--test-primary);\n background: rgba(37, 99, 235, 0.05);\n}\n\n.filter-btn.active {\n background: var(--test-primary);\n color: white;\n border-color: var(--test-primary);\n}\n\n.history-actions {\n display: flex;\n gap: 8px;\n}\n\n/* KPI Cards */\n.history-kpi-cards {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));\n gap: 16px;\n}\n\n.kpi-card {\n display: flex;\n align-items: center;\n gap: 14px;\n padding: 18px;\n background: var(--test-surface);\n border-radius: var(--test-radius-md);\n box-shadow: var(--test-shadow-sm);\n transition: var(--test-transition);\n}\n\n.kpi-card:hover {\n transform: translateY(-2px);\n box-shadow: var(--test-shadow-md);\n}\n\n.kpi-icon {\n width: 48px;\n height: 48px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: linear-gradient(135deg, var(--test-primary) 0%, var(--test-primary-dark) 100%);\n border-radius: var(--test-radius-md);\n color: white;\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate {\n background: linear-gradient(135deg, var(--test-success) 0%, #059669 100%);\n}\n\n.kpi-content {\n flex: 1;\n min-width: 0;\n}\n\n.kpi-value {\n font-size: 22px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.kpi-label {\n font-size: 12px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n margin-top: 2px;\n}\n\n.trend-icon {\n font-size: 14px;\n}\n\n.trend-icon.trend-up {\n color: var(--test-success);\n}\n\n.trend-icon.trend-down {\n color: var(--test-error);\n}\n\n/* History Section */\n.history-section {\n background: var(--test-surface);\n border-radius: var(--test-radius-lg);\n padding: 24px;\n box-shadow: var(--test-shadow-sm);\n}\n\n.history-section h3 {\n margin: 0 0 20px 0;\n font-size: 18px;\n font-weight: 700;\n color: var(--test-text);\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n.history-section h3 i {\n color: var(--test-primary);\n}\n\n/* History Table */\n.history-table-container {\n overflow-x: auto;\n -webkit-overflow-scrolling: touch;\n}\n\n.history-table {\n width: 100%;\n border-collapse: collapse;\n font-size: 14px;\n}\n\n.history-table th,\n.history-table td {\n padding: 12px 16px;\n text-align: left;\n border-bottom: 1px solid var(--test-border);\n}\n\n.history-table th {\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--test-text-muted);\n background: var(--test-bg);\n}\n\n.history-table tbody tr:hover {\n background: rgba(37, 99, 235, 0.03);\n}\n\n.date-cell {\n font-weight: 600;\n color: var(--test-text);\n}\n\n.passed-cell {\n color: var(--test-success);\n font-weight: 600;\n}\n\n.failed-cell {\n color: var(--test-error);\n font-weight: 600;\n}\n\n.pass-rate-cell {\n min-width: 120px;\n}\n\n.pass-rate-bar {\n position: relative;\n height: 24px;\n background: var(--test-bg);\n border-radius: 12px;\n overflow: hidden;\n}\n\n.pass-rate-fill {\n position: absolute;\n left: 0;\n top: 0;\n height: 100%;\n background: linear-gradient(90deg, var(--test-success) 0%, #34d399 100%);\n border-radius: 12px;\n transition: width 0.5s ease-out;\n}\n\n.pass-rate-text {\n position: absolute;\n left: 50%;\n top: 50%;\n transform: translate(-50%, -50%);\n font-size: 11px;\n font-weight: 700;\n color: var(--test-text);\n z-index: 1;\n}\n\n/* Suite Performance List */\n.suite-performance-list {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.suite-perf-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 18px;\n background: var(--test-bg);\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-md);\n cursor: pointer;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover {\n background: rgba(37, 99, 235, 0.05);\n border-color: var(--test-primary-light);\n transform: translateX(4px);\n}\n\n.suite-perf-header {\n flex: 1;\n min-width: 0;\n}\n\n.suite-perf-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--test-text);\n margin-bottom: 6px;\n}\n\n.suite-perf-tags {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n}\n\n.tag-mini {\n display: inline-flex;\n padding: 2px 8px;\n background: rgba(37, 99, 235, 0.1);\n color: var(--test-primary);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.tag-more {\n display: inline-flex;\n padding: 2px 8px;\n background: var(--test-border);\n color: var(--test-text-muted);\n border-radius: 10px;\n font-size: 10px;\n font-weight: 600;\n}\n\n.suite-perf-stats {\n display: flex;\n gap: 24px;\n flex-wrap: wrap;\n}\n\n.suite-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 2px;\n}\n\n.suite-stat .stat-value {\n font-size: 16px;\n font-weight: 700;\n color: var(--test-text);\n}\n\n.suite-stat .stat-value.pass-rate.high {\n color: var(--test-success);\n}\n\n.suite-stat .stat-value.pass-rate.low {\n color: var(--test-error);\n}\n\n.suite-stat .stat-label {\n font-size: 10px;\n color: var(--test-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n.suite-perf-arrow {\n color: var(--test-text-muted);\n font-size: 14px;\n transition: var(--test-transition);\n}\n\n.suite-perf-card:hover .suite-perf-arrow {\n color: var(--test-primary);\n transform: translateX(2px);\n}\n\n/* ===========================\n Print Styles\n =========================== */\n@media print {\n .test-form {\n background: white;\n height: auto;\n }\n\n .header-actions,\n .tabs-container,\n .keyboard-shortcuts {\n display: none !important;\n }\n\n .tab-content {\n overflow: visible;\n padding: 0;\n }\n\n .info-section,\n .json-section,\n .config-section,\n .empty-state {\n break-inside: avoid;\n box-shadow: none;\n border: 1px solid #ddd;\n }\n}\n"] }]
|
|
1965
|
+
}], () => [{ type: i0.ElementRef }, { type: i1.SharedService }, { type: i2.Router }, { type: i2.ActivatedRoute }, { type: i0.ChangeDetectorRef }, { type: i3.TestingDialogService }, { type: i3.EvaluationPreferencesService }, { type: i0.ViewContainerRef }], { handleKeyboardShortcut: [{
|
|
1966
|
+
type: HostListener,
|
|
1967
|
+
args: ['document:keydown', ['$event']]
|
|
1968
|
+
}] }); })();
|
|
1969
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestFormComponentExtended, { className: "TestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber: 61 }); })();
|
|
718
1970
|
export function LoadTestFormComponentExtended() {
|
|
719
1971
|
// Prevents tree-shaking
|
|
720
1972
|
}
|