@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.
Files changed (95) hide show
  1. package/dist/lib/custom/Tests/entity-link-pill.component.d.ts +44 -0
  2. package/dist/lib/custom/Tests/entity-link-pill.component.d.ts.map +1 -0
  3. package/dist/lib/custom/Tests/entity-link-pill.component.js +124 -0
  4. package/dist/lib/custom/Tests/entity-link-pill.component.js.map +1 -0
  5. package/dist/lib/custom/Tests/test-form.component.d.ts +96 -9
  6. package/dist/lib/custom/Tests/test-form.component.d.ts.map +1 -1
  7. package/dist/lib/custom/Tests/test-form.component.js +1529 -277
  8. package/dist/lib/custom/Tests/test-form.component.js.map +1 -1
  9. package/dist/lib/custom/Tests/test-run-form.component.d.ts +50 -9
  10. package/dist/lib/custom/Tests/test-run-form.component.d.ts.map +1 -1
  11. package/dist/lib/custom/Tests/test-run-form.component.js +1079 -426
  12. package/dist/lib/custom/Tests/test-run-form.component.js.map +1 -1
  13. package/dist/lib/custom/Tests/test-suite-form.component.d.ts +228 -5
  14. package/dist/lib/custom/Tests/test-suite-form.component.d.ts.map +1 -1
  15. package/dist/lib/custom/Tests/test-suite-form.component.js +3309 -201
  16. package/dist/lib/custom/Tests/test-suite-form.component.js.map +1 -1
  17. package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts +88 -3
  18. package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts.map +1 -1
  19. package/dist/lib/custom/Tests/test-suite-run-form.component.js +1976 -262
  20. package/dist/lib/custom/Tests/test-suite-run-form.component.js.map +1 -1
  21. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +9 -2
  22. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
  23. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +275 -244
  24. package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
  25. package/dist/lib/custom/custom-forms.module.d.ts +27 -26
  26. package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
  27. package/dist/lib/custom/custom-forms.module.js +9 -3
  28. package/dist/lib/custom/custom-forms.module.js.map +1 -1
  29. package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.d.ts.map +1 -1
  30. package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js +154 -122
  31. package/dist/lib/generated/Entities/AIAgent/aiagent.form.component.js.map +1 -1
  32. package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.d.ts +11 -0
  33. package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.d.ts.map +1 -0
  34. package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.js +75 -0
  35. package/dist/lib/generated/Entities/AIAgentModality/aiagentmodality.form.component.js.map +1 -0
  36. package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.d.ts +11 -0
  37. package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.d.ts.map +1 -0
  38. package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.js +121 -0
  39. package/dist/lib/generated/Entities/AIArchitecture/aiarchitecture.form.component.js.map +1 -0
  40. package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.d.ts.map +1 -1
  41. package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js +77 -41
  42. package/dist/lib/generated/Entities/AIConfiguration/aiconfiguration.form.component.js.map +1 -1
  43. package/dist/lib/generated/Entities/AIModality/aimodality.form.component.d.ts +11 -0
  44. package/dist/lib/generated/Entities/AIModality/aimodality.form.component.d.ts.map +1 -0
  45. package/dist/lib/generated/Entities/AIModality/aimodality.form.component.js +167 -0
  46. package/dist/lib/generated/Entities/AIModality/aimodality.form.component.js.map +1 -0
  47. package/dist/lib/generated/Entities/AIModel/aimodel.form.component.d.ts.map +1 -1
  48. package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js +160 -102
  49. package/dist/lib/generated/Entities/AIModel/aimodel.form.component.js.map +1 -1
  50. package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.d.ts +11 -0
  51. package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.d.ts.map +1 -0
  52. package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.js +73 -0
  53. package/dist/lib/generated/Entities/AIModelArchitecture/aimodelarchitecture.form.component.js.map +1 -0
  54. package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.d.ts +11 -0
  55. package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.d.ts.map +1 -0
  56. package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.js +89 -0
  57. package/dist/lib/generated/Entities/AIModelModality/aimodelmodality.form.component.js.map +1 -0
  58. package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.d.ts.map +1 -1
  59. package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js +27 -13
  60. package/dist/lib/generated/Entities/AIModelType/aimodeltype.form.component.js.map +1 -1
  61. package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.d.ts.map +1 -1
  62. package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.js +39 -21
  63. package/dist/lib/generated/Entities/ConversationDetail/conversationdetail.form.component.js.map +1 -1
  64. package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.d.ts +11 -0
  65. package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.d.ts.map +1 -0
  66. package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.js +95 -0
  67. package/dist/lib/generated/Entities/ConversationDetailAttachment/conversationdetailattachment.form.component.js.map +1 -0
  68. package/dist/lib/generated/Entities/Entity/entity.form.component.d.ts.map +1 -1
  69. package/dist/lib/generated/Entities/Entity/entity.form.component.js +61 -43
  70. package/dist/lib/generated/Entities/Entity/entity.form.component.js.map +1 -1
  71. package/dist/lib/generated/Entities/File/file.form.component.d.ts.map +1 -1
  72. package/dist/lib/generated/Entities/File/file.form.component.js +22 -4
  73. package/dist/lib/generated/Entities/File/file.form.component.js.map +1 -1
  74. package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.d.ts.map +1 -1
  75. package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.js +40 -4
  76. package/dist/lib/generated/Entities/FileStorageProvider/filestorageprovider.form.component.js.map +1 -1
  77. package/dist/lib/generated/Entities/Test/test.form.component.js +17 -15
  78. package/dist/lib/generated/Entities/Test/test.form.component.js.map +1 -1
  79. package/dist/lib/generated/Entities/TestRun/testrun.form.component.d.ts.map +1 -1
  80. package/dist/lib/generated/Entities/TestRun/testrun.form.component.js +55 -43
  81. package/dist/lib/generated/Entities/TestRun/testrun.form.component.js.map +1 -1
  82. package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.d.ts.map +1 -1
  83. package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.js +9 -15
  84. package/dist/lib/generated/Entities/TestRunFeedback/testrunfeedback.form.component.js.map +1 -1
  85. package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.d.ts.map +1 -1
  86. package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.js +39 -19
  87. package/dist/lib/generated/Entities/TestSuite/testsuite.form.component.js.map +1 -1
  88. package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.d.ts.map +1 -1
  89. package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.js +40 -16
  90. package/dist/lib/generated/Entities/TestSuiteRun/testsuiterun.form.component.js.map +1 -1
  91. package/dist/lib/generated/generated-forms.module.d.ts +145 -134
  92. package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
  93. package/dist/lib/generated/generated-forms.module.js +168 -87
  94. package/dist/lib/generated/generated-forms.module.js.map +1 -1
  95. 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 { CompositeKey, RunView } from '@memberjunction/core';
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
- function TestFormComponentExtended_div_20_Template(rf, ctx) { if (rf & 1) {
23
- i0.ɵɵelementStart(0, "div", 29)(1, "p");
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 TestFormComponentExtended_div_21_Template(rf, ctx) { if (rf & 1) {
32
- i0.ɵɵelementStart(0, "div", 30)(1, "div", 31)(2, "div", 32);
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", 33);
54
+ i0.ɵɵelementStart(4, "div", 41);
36
55
  i0.ɵɵtext(5);
37
56
  i0.ɵɵelementEnd()();
38
- i0.ɵɵelementStart(6, "div", 31)(7, "div", 32);
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", 33);
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(11, "div", 31)(12, "div", 32);
45
- i0.ɵɵtext(13, "Avg Cost");
66
+ i0.ɵɵelementStart(13, "div", 39)(14, "div", 40);
67
+ i0.ɵɵtext(15, "Avg Cost");
46
68
  i0.ɵɵelementEnd();
47
- i0.ɵɵelementStart(14, "div", 33);
48
- i0.ɵɵtext(15);
69
+ i0.ɵɵelementStart(16, "div", 41);
70
+ i0.ɵɵtext(17);
49
71
  i0.ɵɵelementEnd()();
50
- i0.ɵɵelementStart(16, "div", 31)(17, "div", 32);
51
- i0.ɵɵtext(18, "Avg Duration");
72
+ i0.ɵɵelementStart(18, "div", 39)(19, "div", 40);
73
+ i0.ɵɵtext(20, "Avg Duration");
52
74
  i0.ɵɵelementEnd();
53
- i0.ɵɵelementStart(19, "div", 33);
54
- i0.ɵɵtext(20);
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 TestFormComponentExtended_span_36_Template(rf, ctx) { if (rf & 1) {
68
- i0.ɵɵelementStart(0, "span", 34);
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 TestFormComponentExtended_span_41_Template(rf, ctx) { if (rf & 1) {
77
- i0.ɵɵelementStart(0, "span", 34);
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 TestFormComponentExtended_div_43_Template(rf, ctx) { if (rf & 1) {
109
+ function TestFormComponentExtended_div_49_Template(rf, ctx) { if (rf & 1) {
86
110
  const _r2 = i0.ɵɵgetCurrentView();
87
- i0.ɵɵelementStart(0, "div", 35)(1, "div", 36)(2, "h3");
88
- i0.ɵɵtext(3, "Test Information");
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(4, "div", 37)(5, "div", 38)(6, "div", 39);
91
- i0.ɵɵtext(7, "Name");
115
+ i0.ɵɵelementStart(5, "div", 48)(6, "div", 49)(7, "div", 50);
116
+ i0.ɵɵtext(8, "Name");
92
117
  i0.ɵɵelementEnd();
93
- i0.ɵɵelementStart(8, "div", 40);
94
- i0.ɵɵtext(9);
118
+ i0.ɵɵelementStart(9, "div", 51);
119
+ i0.ɵɵtext(10);
95
120
  i0.ɵɵelementEnd()();
96
- i0.ɵɵelementStart(10, "div", 38)(11, "div", 39);
97
- i0.ɵɵtext(12, "Type");
121
+ i0.ɵɵelementStart(11, "div", 49)(12, "div", 50);
122
+ i0.ɵɵtext(13, "Type");
98
123
  i0.ɵɵelementEnd();
99
- i0.ɵɵelementStart(13, "div", 40);
100
- i0.ɵɵtext(14);
124
+ i0.ɵɵelementStart(14, "div", 51);
125
+ i0.ɵɵtext(15);
101
126
  i0.ɵɵelementEnd()();
102
- i0.ɵɵelementStart(15, "div", 38)(16, "div", 39);
103
- i0.ɵɵtext(17, "Status");
127
+ i0.ɵɵelementStart(16, "div", 49)(17, "div", 50);
128
+ i0.ɵɵtext(18, "Status");
104
129
  i0.ɵɵelementEnd();
105
- i0.ɵɵelementStart(18, "div", 40);
106
- i0.ɵɵtext(19);
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(20, "div", 38)(21, "div", 39);
109
- i0.ɵɵtext(22, "Priority");
139
+ i0.ɵɵelementStart(27, "div", 49)(28, "div", 50);
140
+ i0.ɵɵtext(29, "Estimated Duration");
110
141
  i0.ɵɵelementEnd();
111
- i0.ɵɵelementStart(23, "div", 40);
112
- i0.ɵɵtext(24);
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(25, "div", 38)(26, "div", 39);
115
- i0.ɵɵtext(27, "Estimated Duration");
151
+ i0.ɵɵelementStart(37, "div", 49)(38, "div", 50);
152
+ i0.ɵɵtext(39, "Repeat Count");
116
153
  i0.ɵɵelementEnd();
117
- i0.ɵɵelementStart(28, "div", 40);
118
- i0.ɵɵtext(29);
154
+ i0.ɵɵelementStart(40, "div", 51);
155
+ i0.ɵɵtext(41);
119
156
  i0.ɵɵelementEnd()();
120
- i0.ɵɵelementStart(30, "div", 38)(31, "div", 39);
121
- i0.ɵɵtext(32, "Estimated Cost");
157
+ i0.ɵɵelementStart(42, "div", 49)(43, "div", 50);
158
+ i0.ɵɵtext(44, "Max Execution Time");
122
159
  i0.ɵɵelementEnd();
123
- i0.ɵɵelementStart(33, "div", 40);
124
- i0.ɵɵtext(34);
160
+ i0.ɵɵelementStart(45, "div", 51);
161
+ i0.ɵɵtext(46);
125
162
  i0.ɵɵelementEnd()();
126
- i0.ɵɵelementStart(35, "div", 38)(36, "div", 39);
127
- i0.ɵɵtext(37, "Repeat Count");
163
+ i0.ɵɵelementStart(47, "div", 49)(48, "div", 50);
164
+ i0.ɵɵtext(49, "Created");
128
165
  i0.ɵɵelementEnd();
129
- i0.ɵɵelementStart(38, "div", 40);
130
- i0.ɵɵtext(39);
166
+ i0.ɵɵelementStart(50, "div", 51);
167
+ i0.ɵɵtext(51);
168
+ i0.ɵɵpipe(52, "date");
131
169
  i0.ɵɵelementEnd()();
132
- i0.ɵɵelementStart(40, "div", 38)(41, "div", 39);
133
- i0.ɵɵtext(42, "Created");
170
+ i0.ɵɵelementStart(53, "div", 49)(54, "div", 50);
171
+ i0.ɵɵtext(55, "Updated");
134
172
  i0.ɵɵelementEnd();
135
- i0.ɵɵelementStart(43, "div", 40);
136
- i0.ɵɵtext(44);
137
- i0.ɵɵpipe(45, "date");
173
+ i0.ɵɵelementStart(56, "div", 51);
174
+ i0.ɵɵtext(57);
175
+ i0.ɵɵpipe(58, "date");
138
176
  i0.ɵɵelementEnd()()()();
139
- i0.ɵɵelementStart(46, "div", 41)(47, "h3");
140
- i0.ɵɵtext(48, "Test Definition");
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(49, "div", 42)(50, "button", 43);
143
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_43_Template_button_click_50_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("input")); });
144
- i0.ɵɵtext(51, " Input Definition ");
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(52, "button", 43);
147
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_43_Template_button_click_52_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("expected")); });
148
- i0.ɵɵtext(53, " Expected Outcomes ");
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(54, "button", 43);
151
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_43_Template_button_click_54_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("config")); });
152
- i0.ɵɵtext(55, " Configuration ");
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(56, "button", 43);
155
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_43_Template_button_click_56_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.setJsonView("tags")); });
156
- i0.ɵɵtext(57, " Tags ");
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(58, "div", 44)(59, "pre");
159
- i0.ɵɵtext(60);
160
- i0.ɵɵpipe(61, "json");
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(9);
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(i0.ɵɵpipeBind2(45, 17, ctx_r0.record.__mj_CreatedAt, "medium"));
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(2);
230
+ i0.ɵɵadvance(3);
183
231
  i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "expected");
184
- i0.ɵɵadvance(2);
232
+ i0.ɵɵadvance(3);
185
233
  i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "config");
186
- i0.ɵɵadvance(2);
234
+ i0.ɵɵadvance(3);
187
235
  i0.ɵɵclassProp("active", ctx_r0.activeJsonView === "tags");
188
236
  i0.ɵɵadvance(4);
189
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind1(61, 20, ctx_r0.getJsonData()));
237
+ i0.ɵɵproperty("value", ctx_r0.getJsonData())("readonly", true)("toolbar", ctx_r0.jsonToolbar)("lineWrapping", true);
190
238
  } }
191
- function TestFormComponentExtended_div_44_Template(rf, ctx) { if (rf & 1) {
239
+ function TestFormComponentExtended_div_50_Template(rf, ctx) { if (rf & 1) {
192
240
  const _r3 = i0.ɵɵgetCurrentView();
193
- i0.ɵɵelementStart(0, "div", 45)(1, "div", 46)(2, "h3");
194
- i0.ɵɵtext(3, "Execution Settings");
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(4, "div", 47)(5, "div", 48)(6, "label");
197
- i0.ɵɵtext(7, "Priority");
245
+ i0.ɵɵelementStart(5, "div", 66)(6, "div", 67)(7, "label");
246
+ i0.ɵɵtext(8, "Priority");
198
247
  i0.ɵɵelementEnd();
199
- i0.ɵɵelementStart(8, "input", 49);
200
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_input_ngModelChange_8_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); });
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(9, "div", 48)(10, "label");
203
- i0.ɵɵtext(11, "Repeat Count");
251
+ i0.ɵɵelementStart(10, "div", 67)(11, "label");
252
+ i0.ɵɵtext(12, "Repeat Count");
204
253
  i0.ɵɵelementEnd();
205
- i0.ɵɵelementStart(12, "input", 49);
206
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_input_ngModelChange_12_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); });
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(13, "div", 48)(14, "label");
209
- i0.ɵɵtext(15, "Estimated Duration (seconds)");
257
+ i0.ɵɵelementStart(14, "div", 67)(15, "label");
258
+ i0.ɵɵtext(16, "Estimated Duration (seconds)");
210
259
  i0.ɵɵelementEnd();
211
- i0.ɵɵelementStart(16, "input", 49);
212
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_input_ngModelChange_16_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); });
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(17, "div", 48)(18, "label");
215
- i0.ɵɵtext(19, "Estimated Cost (USD)");
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(24, "textarea", 51);
224
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_textarea_ngModelChange_24_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.InputDefinition, $event) || (ctx_r0.record.InputDefinition = $event); return i0.ɵɵresetView($event); });
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(25, "div", 46)(26, "h3");
227
- i0.ɵɵtext(27, "Expected Outcomes (JSON)");
269
+ i0.ɵɵelementStart(22, "div", 72)(23, "label");
270
+ i0.ɵɵtext(24, "Max Execution Time (ms)");
228
271
  i0.ɵɵelementEnd();
229
- i0.ɵɵelementStart(28, "textarea", 51);
230
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_textarea_ngModelChange_28_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.ExpectedOutcomes, $event) || (ctx_r0.record.ExpectedOutcomes = $event); return i0.ɵɵresetView($event); });
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(32, "textarea", 51);
236
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_textarea_ngModelChange_32_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.Configuration, $event) || (ctx_r0.record.Configuration = $event); return i0.ɵɵresetView($event); });
237
- i0.ɵɵelementEnd()();
238
- i0.ɵɵelementStart(33, "div", 46)(34, "h3");
239
- i0.ɵɵtext(35, "Tags (JSON Array)");
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(36, "textarea", 52);
242
- i0.ɵɵtwoWayListener("ngModelChange", function TestFormComponentExtended_div_44_Template_textarea_ngModelChange_36_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.Tags, $event) || (ctx_r0.record.Tags = $event); return i0.ɵɵresetView($event); });
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(8);
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.InputDefinition);
256
- i0.ɵɵadvance(4);
257
- i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.ExpectedOutcomes);
258
- i0.ɵɵadvance(4);
259
- i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.Configuration);
260
- i0.ɵɵadvance(4);
261
- i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.Tags);
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 TestFormComponentExtended_div_45_div_1_div_1_span_13_Template(rf, ctx) { if (rf & 1) {
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.ɵɵtext(1);
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.ɵɵtextInterpolate(ctx_r0.formatDuration(run_r5.DurationSeconds));
350
+ i0.ɵɵadvance(2);
351
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(run_r5.DurationSeconds), "");
272
352
  } }
273
- function TestFormComponentExtended_div_45_div_1_div_1_span_14_Template(rf, ctx) { if (rf & 1) {
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.ɵɵtext(1);
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.ɵɵtextInterpolate(ctx_r0.formatCost(run_r5.CostUSD));
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 TestFormComponentExtended_div_45_div_1_div_1_span_15_Template(rf, ctx) { if (rf & 1) {
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.ɵɵtextInterpolate1("Score: ", run_r5.Score.toFixed(4), "");
478
+ i0.ɵɵproperty("ngIf", ctx_r0.getRunTags(run_r5).length > 3);
291
479
  } }
292
- function TestFormComponentExtended_div_45_div_1_div_1_Template(rf, ctx) { if (rf & 1) {
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", 58);
295
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_45_div_1_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)); });
296
- i0.ɵɵelementStart(1, "div", 59);
297
- i0.ɵɵelement(2, "i", 60);
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", 61)(4, "div", 62)(5, "span", 63);
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", 64);
490
+ i0.ɵɵelementStart(7, "span", 98);
303
491
  i0.ɵɵtext(8);
304
492
  i0.ɵɵelementEnd()();
305
- i0.ɵɵelementStart(9, "div", 65)(10, "span");
306
- i0.ɵɵtext(11);
307
- i0.ɵɵpipe(12, "date");
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, TestFormComponentExtended_div_45_div_1_div_1_span_13_Template, 2, 1, "span", 66)(14, TestFormComponentExtended_div_45_div_1_div_1_span_14_Template, 2, 1, "span", 66)(15, TestFormComponentExtended_div_45_div_1_div_1_span_15_Template, 2, 1, "span", 66);
310
- i0.ɵɵelementEnd()();
311
- i0.ɵɵelement(16, "i", 67);
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", run_r5.Status === "Passed" ? "#4caf50" : run_r5.Status === "Failed" ? "#f44336" : "#ff9800");
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", run_r5.Status === "Passed" ? "#4caf50" : run_r5.Status === "Failed" ? "#f44336" : "#ff9800");
516
+ i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(run_r5.Status));
323
517
  i0.ɵɵadvance();
324
518
  i0.ɵɵtextInterpolate(run_r5.Status);
325
- i0.ɵɵadvance(3);
326
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(12, 16, run_r5.StartedAt, "short"));
327
- i0.ɵɵadvance(2);
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.Score != null);
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 TestFormComponentExtended_div_45_div_1_Template(rf, ctx) { if (rf & 1) {
335
- i0.ɵɵelementStart(0, "div", 56);
336
- i0.ɵɵtemplate(1, TestFormComponentExtended_div_45_div_1_div_1_Template, 17, 19, "div", 57);
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 TestFormComponentExtended_div_45_div_2_Template(rf, ctx) { if (rf & 1) {
344
- i0.ɵɵelementStart(0, "div", 68);
345
- i0.ɵɵelement(1, "i", 69);
346
- i0.ɵɵelementStart(2, "p");
347
- i0.ɵɵtext(3, "No test runs found for this test");
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 TestFormComponentExtended_div_45_Template(rf, ctx) { if (rf & 1) {
351
- i0.ɵɵelementStart(0, "div", 53);
352
- i0.ɵɵtemplate(1, TestFormComponentExtended_div_45_div_1_Template, 2, 1, "div", 54)(2, TestFormComponentExtended_div_45_div_2_Template, 4, 0, "div", 55);
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.testRuns.length > 0);
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 TestFormComponentExtended_div_46_div_1_div_1_Template(rf, ctx) { if (rf & 1) {
362
- const _r6 = i0.ɵɵgetCurrentView();
363
- i0.ɵɵelementStart(0, "div", 74);
364
- i0.ɵɵlistener("click", function TestFormComponentExtended_div_46_div_1_div_1_Template_div_click_0_listener() { const suiteTest_r7 = i0.ɵɵrestoreView(_r6).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestSuite(suiteTest_r7.SuiteID)); });
365
- i0.ɵɵelementStart(1, "div", 75);
366
- i0.ɵɵelement(2, "i", 76);
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", 77)(4, "div", 78);
607
+ i0.ɵɵelementStart(3, "div", 136)(4, "div", 137);
369
608
  i0.ɵɵtext(5);
370
609
  i0.ɵɵelementEnd();
371
- i0.ɵɵelementStart(6, "div", 79)(7, "span");
372
- i0.ɵɵtext(8);
373
- i0.ɵɵelementEnd()()();
374
- i0.ɵɵelement(9, "i", 67);
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 suiteTest_r7 = ctx.$implicit;
619
+ const suiteTest_r10 = ctx.$implicit;
378
620
  i0.ɵɵadvance(5);
379
- i0.ɵɵtextInterpolate(suiteTest_r7.Suite);
380
- i0.ɵɵadvance(3);
381
- i0.ɵɵtextInterpolate1("Sequence: ", suiteTest_r7.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 TestFormComponentExtended_div_46_div_1_Template(rf, ctx) { if (rf & 1) {
384
- i0.ɵɵelementStart(0, "div", 72);
385
- i0.ɵɵtemplate(1, TestFormComponentExtended_div_46_div_1_div_1_Template, 10, 2, "div", 73);
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 TestFormComponentExtended_div_46_div_2_Template(rf, ctx) { if (rf & 1) {
393
- i0.ɵɵelementStart(0, "div", 68);
394
- i0.ɵɵelement(1, "i", 69);
395
- i0.ɵɵelementStart(2, "p");
396
- i0.ɵɵtext(3, "This test is not part of any test suite");
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 TestFormComponentExtended_div_46_Template(rf, ctx) { if (rf & 1) {
400
- i0.ɵɵelementStart(0, "div", 70);
401
- i0.ɵɵtemplate(1, TestFormComponentExtended_div_46_div_1_Template, 2, 1, "div", 71)(2, TestFormComponentExtended_div_46_div_2_Template, 4, 0, "div", 55);
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.suiteTests.length > 0);
1075
+ i0.ɵɵproperty("ngIf", ctx_r0.loadingHistory);
407
1076
  i0.ɵɵadvance();
408
- i0.ɵɵproperty("ngIf", ctx_r0.suiteTestsLoaded && ctx_r0.suiteTests.length === 0);
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
- async loadRelatedData() {
443
- this.loading = true;
444
- try {
445
- this.cdr.markForCheck();
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
- catch (error) {
448
- console.error('Error loading related data:', error);
449
- this.error = 'Failed to load related data';
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
- finally {
452
- this.loading = false;
453
- this.cdr.markForCheck();
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 '#4caf50';
536
- case 'Disabled': return '#9e9e9e';
537
- case 'Pending': return '#ffc107';
538
- default: return '#999';
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
- await this.loadRelatedData();
595
- if (this.testRunsLoaded) {
596
- this.testRunsLoaded = false;
597
- await this.loadTestRuns();
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
- if (this.suiteTestsLoaded) {
600
- this.suiteTestsLoaded = false;
601
- await this.loadSuiteTests();
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': return this.parsedData.inputDefinition;
608
- case 'expected': return this.parsedData.expectedOutcomes;
609
- case 'config': return this.parsedData.configuration;
610
- case 'tags': return this.parsedData.tags;
611
- default: return null;
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
- 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)); }; }
615
- static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: TestFormComponentExtended, selectors: [["mj-test-form"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 47, vars: 24, 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"], [1, "fas", 3, "ngClass"], [1, "test-type"], [1, "header-actions"], ["kendoButton", "", "icon", "play", 3, "click"], ["kendoButton", "", "icon", "sync", 3, "click"], ["class", "test-description", 4, "ngIf"], ["class", "metrics-bar", 4, "ngIf"], [1, "tabs-container"], [1, "tabs"], [1, "tab", 3, "click"], [1, "fas", "fa-th-large"], [1, "fas", "fa-sliders-h"], [1, "fas", "fa-list"], ["class", "tab-badge", 4, "ngIf"], [1, "fas", "fa-layer-group"], [1, "tab-content"], ["class", "overview-tab", 4, "ngIf"], ["class", "config-tab", 4, "ngIf"], ["class", "runs-tab", 4, "ngIf"], ["class", "suites-tab", 4, "ngIf"], [1, "test-description"], [1, "metrics-bar"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "tab-badge"], [1, "overview-tab"], [1, "info-section"], [1, "info-grid"], [1, "info-item"], [1, "info-label"], [1, "info-value"], [1, "json-section"], [1, "json-tabs"], [1, "json-tab", 3, "click"], [1, "json-content"], [1, "config-tab"], [1, "config-section"], [1, "config-grid"], [1, "config-item"], ["type", "number", 1, "config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "step", "0.000001", 1, "config-input", 3, "ngModelChange", "ngModel"], ["rows", "10", 1, "json-editor", 3, "ngModelChange", "ngModel"], ["rows", "5", 1, "json-editor", 3, "ngModelChange", "ngModel"], [1, "runs-tab"], ["class", "runs-list", 4, "ngIf"], ["class", "no-data", 4, "ngIf"], [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"], [4, "ngIf"], [1, "fas", "fa-chevron-right"], [1, "no-data"], [1, "fas", "fa-inbox"], [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, "fas", "fa-folder"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"]], template: function TestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
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.ɵɵelementStart(13, "span", 10);
627
- i0.ɵɵtext(14);
628
- i0.ɵɵelementEnd()()()();
629
- i0.ɵɵelementStart(15, "div", 11)(16, "button", 12);
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.ɵɵtext(17, " Run Test ");
1847
+ i0.ɵɵelement(17, "i", 13);
1848
+ i0.ɵɵtext(18, " Run Test ");
632
1849
  i0.ɵɵelementEnd();
633
- i0.ɵɵelementStart(18, "button", 13);
634
- i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_18_listener() { return ctx.refresh(); });
635
- i0.ɵɵtext(19, " Refresh ");
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(20, TestFormComponentExtended_div_20_Template, 3, 1, "div", 14)(21, TestFormComponentExtended_div_21_Template, 21, 4, "div", 15);
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(22, "div", 16)(23, "div", 17)(24, "button", 18);
640
- i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_24_listener() { return ctx.changeTab("overview"); });
641
- i0.ɵɵelement(25, "i", 19);
642
- i0.ɵɵelementStart(26, "span");
643
- i0.ɵɵtext(27, "Overview");
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(28, "button", 18);
646
- i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_28_listener() { return ctx.changeTab("config"); });
647
- i0.ɵɵelement(29, "i", 20);
648
- i0.ɵɵelementStart(30, "span");
649
- i0.ɵɵtext(31, "Configuration");
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(32, "button", 18);
652
- i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_32_listener() { return ctx.changeTab("runs"); });
653
- i0.ɵɵelement(33, "i", 21);
654
- i0.ɵɵelementStart(34, "span");
655
- i0.ɵɵtext(35, "Test Runs");
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(36, TestFormComponentExtended_span_36_Template, 2, 1, "span", 22);
1875
+ i0.ɵɵtemplate(38, TestFormComponentExtended_span_38_Template, 2, 1, "span", 23);
658
1876
  i0.ɵɵelementEnd();
659
- i0.ɵɵelementStart(37, "button", 18);
660
- i0.ɵɵlistener("click", function TestFormComponentExtended_Template_button_click_37_listener() { return ctx.changeTab("suites"); });
661
- i0.ɵɵelement(38, "i", 23);
662
- i0.ɵɵelementStart(39, "span");
663
- i0.ɵɵtext(40, "Test Suites");
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.ɵɵstyleProp("background-color", ctx.getStatusColor());
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(2);
682
- i0.ɵɵtextInterpolate(ctx.record.Type);
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(2);
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
- } }, dependencies: [i4.NgClass, i4.NgForOf, i4.NgIf, i5.DefaultValueAccessor, i5.NumberValueAccessor, i5.NgControlStatus, i5.NgModel, i6.DialogContainerDirective, i7.ButtonComponent, i4.JsonPipe, i4.DatePipe], styles: [".test-form[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: #f8f9fa;\n}\n\n\n\n.test-header[_ngcontent-%COMP%] {\n background: white;\n border-bottom: 1px solid #e0e0e0;\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}\n\n.header-left[_ngcontent-%COMP%] {\n display: flex;\n gap: 16px;\n}\n\n.test-icon[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] h1[_ngcontent-%COMP%] {\n margin: 0 0 8px 0;\n font-size: 24px;\n font-weight: 600;\n color: #333;\n}\n\n.test-meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.status-badge[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #666;\n}\n\n.header-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n}\n\n.test-description[_ngcontent-%COMP%] {\n padding: 16px;\n background: #f8f9fa;\n border-radius: 8px;\n margin-bottom: 16px;\n}\n\n.test-description[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n color: #666;\n line-height: 1.5;\n}\n\n.metrics-bar[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: 16px;\n}\n\n.metric-card[_ngcontent-%COMP%] {\n background: #f5f7fa;\n border-radius: 8px;\n padding: 12px;\n text-align: center;\n}\n\n.metric-label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 16px;\n font-weight: 600;\n color: #333;\n}\n\n\n\n.tabs-container[_ngcontent-%COMP%] {\n background: white;\n border-bottom: 1px solid #e0e0e0;\n}\n\n.tabs[_ngcontent-%COMP%] {\n display: flex;\n padding: 0 20px;\n overflow-x: auto;\n}\n\n.tab[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n color: #2196f3;\n background: rgba(33, 150, 243, 0.05);\n}\n\n.tab.active[_ngcontent-%COMP%] {\n color: #2196f3;\n border-bottom-color: #2196f3;\n}\n\n.tab[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n\n.tab-badge[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] .tab-badge[_ngcontent-%COMP%] {\n background: #e3f2fd;\n color: #2196f3;\n}\n\n\n\n.tab-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n\n\n.overview-tab[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 20px;\n}\n\n.info-section[_ngcontent-%COMP%], \n.json-section[_ngcontent-%COMP%], \n.config-section[_ngcontent-%COMP%] {\n background: white;\n border-radius: 12px;\n padding: 20px;\n}\n\n.info-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], \n.json-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%], \n.config-section[_ngcontent-%COMP%] h3[_ngcontent-%COMP%] {\n margin: 0 0 16px 0;\n font-size: 18px;\n font-weight: 600;\n color: #333;\n}\n\n.info-grid[_ngcontent-%COMP%], \n.config-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(2, 1fr);\n gap: 16px;\n}\n\n.info-item[_ngcontent-%COMP%], \n.config-item[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.info-label[_ngcontent-%COMP%], \n.config-item[_ngcontent-%COMP%] label[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n font-size: 14px;\n color: #333;\n word-wrap: break-word;\n}\n\n.config-input[_ngcontent-%COMP%] {\n padding: 8px 12px;\n border: 1px solid #ddd;\n border-radius: 4px;\n font-size: 14px;\n}\n\n.config-input[_ngcontent-%COMP%]: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[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n margin-bottom: 16px;\n border-bottom: 2px solid #e0e0e0;\n}\n\n.json-tab[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover {\n color: #2196f3;\n}\n\n.json-tab.active[_ngcontent-%COMP%] {\n color: #2196f3;\n border-bottom-color: #2196f3;\n}\n\n.json-content[_ngcontent-%COMP%] {\n background: #1e1e1e;\n border-radius: 8px;\n padding: 16px;\n max-height: 400px;\n overflow-y: auto;\n}\n\n.json-content[_ngcontent-%COMP%] pre[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:focus {\n outline: none;\n border-color: #2196f3;\n box-shadow: 0 0 0 2px rgba(33, 150, 243, 0.1);\n}\n\n\n\n.runs-list[_ngcontent-%COMP%], \n.suites-list[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n.run-item[_ngcontent-%COMP%], \n.suite-item[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%]:hover, \n.suite-item[_ngcontent-%COMP%]:hover {\n background: #e3f2fd;\n border-color: #90caf9;\n}\n\n.run-icon[_ngcontent-%COMP%], \n.suite-icon[_ngcontent-%COMP%] {\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[_ngcontent-%COMP%] {\n background: #ff9800;\n}\n\n.run-content[_ngcontent-%COMP%], \n.suite-content[_ngcontent-%COMP%] {\n flex: 1;\n}\n\n.run-header[_ngcontent-%COMP%], \n.suite-name[_ngcontent-%COMP%] {\n font-size: 14px;\n font-weight: 600;\n color: #333;\n margin-bottom: 4px;\n}\n\n.run-id[_ngcontent-%COMP%] {\n margin-right: 12px;\n}\n\n.run-status[_ngcontent-%COMP%] {\n font-weight: 700;\n}\n\n.run-meta[_ngcontent-%COMP%], \n.suite-meta[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: #666;\n}\n\n.run-item[_ngcontent-%COMP%] > i[_ngcontent-%COMP%], \n.suite-item[_ngcontent-%COMP%] > i[_ngcontent-%COMP%] {\n color: #999;\n font-size: 14px;\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: #999;\n text-align: center;\n background: white;\n border-radius: 12px;\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@media (max-width: 1200px) {\n .metrics-bar[_ngcontent-%COMP%] {\n grid-template-columns: repeat(2, 1fr);\n }\n\n .info-grid[_ngcontent-%COMP%], \n .config-grid[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n}\n\n@media (max-width: 768px) {\n .metrics-bar[_ngcontent-%COMP%] {\n grid-template-columns: 1fr;\n }\n\n .header-content[_ngcontent-%COMP%] {\n flex-direction: column;\n gap: 16px;\n }\n\n .tabs[_ngcontent-%COMP%] {\n overflow-x: auto;\n }\n}"], changeDetection: 0 }); }
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 }], null); })();
717
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(TestFormComponentExtended, { className: "TestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber: 27 }); })();
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
  }