@memberjunction/ng-core-entity-forms 6.2.0-edge.0 → 6.2.0-edge.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.d.ts +1 -1
- package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.js +2 -2
- package/dist/lib/custom/AIAgentCategories/ai-agent-category-overview.panel.js.map +1 -1
- package/dist/lib/custom/AIAgentSessions/ai-agent-session-form.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/add-action-dialog.component.d.ts +90 -16
- package/dist/lib/custom/AIAgents/add-action-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/add-action-dialog.component.js +218 -70
- package/dist/lib/custom/AIAgents/add-action-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/agent-form-tabs.d.ts +1 -0
- package/dist/lib/custom/AIAgents/agent-form-tabs.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/agent-form-tabs.js +2 -0
- package/dist/lib/custom/AIAgents/agent-form-tabs.js.map +1 -1
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.d.ts +80 -16
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js +146 -46
- package/dist/lib/custom/AIAgents/agent-prompt-advanced-settings-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts +329 -27
- package/dist/lib/custom/AIAgents/ai-agent-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js +1322 -783
- package/dist/lib/custom/AIAgents/ai-agent-form.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-management.service.d.ts +76 -0
- package/dist/lib/custom/AIAgents/ai-agent-management.service.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/ai-agent-management.service.js +77 -33
- package/dist/lib/custom/AIAgents/ai-agent-management.service.js.map +1 -1
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.d.ts +69 -15
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js +182 -74
- package/dist/lib/custom/AIAgents/create-prompt-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.d.ts +105 -25
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js +272 -112
- package/dist/lib/custom/AIAgents/create-sub-agent-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts +32 -6
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.js +71 -19
- package/dist/lib/custom/AIAgents/new-agent-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts +10 -0
- package/dist/lib/custom/AIAgents/new-agent-dialog.service.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/new-agent-dialog.service.js +27 -7
- package/dist/lib/custom/AIAgents/new-agent-dialog.service.js.map +1 -1
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.d.ts +65 -15
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js +152 -52
- package/dist/lib/custom/AIAgents/prompt-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.d.ts +81 -15
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js +145 -45
- package/dist/lib/custom/AIAgents/sub-agent-advanced-settings-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.d.ts +71 -17
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js +175 -67
- package/dist/lib/custom/AIAgents/sub-agent-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts +173 -27
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js +385 -93
- package/dist/lib/custom/AIPromptRuns/ai-prompt-run-form.component.js.map +1 -1
- package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.d.ts +64 -4
- package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.js +129 -29
- package/dist/lib/custom/AIPromptRuns/chat-message-viewer.component.js.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts +260 -27
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js +694 -252
- package/dist/lib/custom/AIPrompts/ai-prompt-form.component.js.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-management.service.d.ts +10 -0
- package/dist/lib/custom/AIPrompts/ai-prompt-management.service.d.ts.map +1 -1
- package/dist/lib/custom/AIPrompts/ai-prompt-management.service.js +17 -9
- package/dist/lib/custom/AIPrompts/ai-prompt-management.service.js.map +1 -1
- package/dist/lib/custom/AIPrompts/pending-records.d.ts +11 -0
- package/dist/lib/custom/AIPrompts/pending-records.d.ts.map +1 -0
- package/dist/lib/custom/AIPrompts/pending-records.js +21 -0
- package/dist/lib/custom/AIPrompts/pending-records.js.map +1 -0
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.d.ts +74 -16
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js +174 -58
- package/dist/lib/custom/AIPrompts/template-selector-dialog.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts +108 -14
- package/dist/lib/custom/Actions/action-execution-log-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-execution-log-form.component.js +258 -70
- package/dist/lib/custom/Actions/action-execution-log-form.component.js.map +1 -1
- package/dist/lib/custom/Actions/action-form.component.d.ts +198 -14
- package/dist/lib/custom/Actions/action-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Actions/action-form.component.js +387 -107
- package/dist/lib/custom/Actions/action-form.component.js.map +1 -1
- package/dist/lib/custom/BridgeProviders/bridge-provider-features.d.ts +12 -6
- package/dist/lib/custom/BridgeProviders/bridge-provider-features.d.ts.map +1 -1
- package/dist/lib/custom/BridgeProviders/bridge-provider-features.js +41 -29
- package/dist/lib/custom/BridgeProviders/bridge-provider-features.js.map +1 -1
- package/dist/lib/custom/BridgeProviders/bridge-provider-form.component.js +14 -14
- package/dist/lib/custom/BridgeProviders/bridge-provider-form.component.js.map +1 -1
- package/dist/lib/custom/Companies/company-overview.panel.d.ts +1 -1
- package/dist/lib/custom/Companies/company-overview.panel.js +2 -2
- package/dist/lib/custom/Companies/company-overview.panel.js.map +1 -1
- package/dist/lib/custom/Conversations/conversation-overview.panel.d.ts +1 -1
- package/dist/lib/custom/Conversations/conversation-overview.panel.js +2 -2
- package/dist/lib/custom/Conversations/conversation-overview.panel.js.map +1 -1
- package/dist/lib/custom/Employees/employee-overview.panel.d.ts +1 -1
- package/dist/lib/custom/Employees/employee-overview.panel.js +2 -2
- package/dist/lib/custom/Employees/employee-overview.panel.js.map +1 -1
- package/dist/lib/custom/Entities/entity-form.component.d.ts +217 -29
- package/dist/lib/custom/Entities/entity-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Entities/entity-form.component.js +734 -285
- package/dist/lib/custom/Entities/entity-form.component.js.map +1 -1
- package/dist/lib/custom/EntityActions/entityaction.form.component.d.ts +5 -1
- package/dist/lib/custom/EntityActions/entityaction.form.component.d.ts.map +1 -1
- package/dist/lib/custom/EntityActions/entityaction.form.component.js +10 -2
- package/dist/lib/custom/EntityActions/entityaction.form.component.js.map +1 -1
- package/dist/lib/custom/Files/file-form.component.d.ts +1 -1
- package/dist/lib/custom/Files/file-form.component.js +2 -2
- package/dist/lib/custom/Files/file-form.component.js.map +1 -1
- package/dist/lib/custom/Lists/list-form.component.d.ts +300 -36
- package/dist/lib/custom/Lists/list-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Lists/list-form.component.js +664 -208
- package/dist/lib/custom/Lists/list-form.component.js.map +1 -1
- package/dist/lib/custom/MLModels/ml-model-view-models.d.ts +31 -0
- package/dist/lib/custom/MLModels/ml-model-view-models.d.ts.map +1 -1
- package/dist/lib/custom/MLModels/ml-model-view-models.js +52 -16
- package/dist/lib/custom/MLModels/ml-model-view-models.js.map +1 -1
- package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts +107 -10
- package/dist/lib/custom/MLModels/ps-model-detail.component.d.ts.map +1 -1
- package/dist/lib/custom/MLModels/ps-model-detail.component.js +291 -101
- package/dist/lib/custom/MLModels/ps-model-detail.component.js.map +1 -1
- package/dist/lib/custom/Queries/query-form.component.d.ts +201 -26
- package/dist/lib/custom/Queries/query-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Queries/query-form.component.js +429 -109
- package/dist/lib/custom/Queries/query-form.component.js.map +1 -1
- package/dist/lib/custom/Queries/query-run-dialog.component.d.ts +107 -14
- package/dist/lib/custom/Queries/query-run-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/Queries/query-run-dialog.component.js +249 -79
- package/dist/lib/custom/Queries/query-run-dialog.component.js.map +1 -1
- package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts +129 -4
- package/dist/lib/custom/RecordProcesses/record-process-form.component.d.ts.map +1 -1
- package/dist/lib/custom/RecordProcesses/record-process-form.component.js +904 -90
- package/dist/lib/custom/RecordProcesses/record-process-form.component.js.map +1 -1
- package/dist/lib/custom/Rubrics/evaluation-form.component.d.ts +37 -0
- package/dist/lib/custom/Rubrics/evaluation-form.component.d.ts.map +1 -0
- package/dist/lib/custom/Rubrics/evaluation-form.component.js +399 -0
- package/dist/lib/custom/Rubrics/evaluation-form.component.js.map +1 -0
- package/dist/lib/custom/Rubrics/record-forms.component.d.ts +69 -0
- package/dist/lib/custom/Rubrics/record-forms.component.d.ts.map +1 -0
- package/dist/lib/custom/Rubrics/record-forms.component.js +587 -0
- package/dist/lib/custom/Rubrics/record-forms.component.js.map +1 -0
- package/dist/lib/custom/Rubrics/rubric-form.component.d.ts +67 -0
- package/dist/lib/custom/Rubrics/rubric-form.component.d.ts.map +1 -0
- package/dist/lib/custom/Rubrics/rubric-form.component.js +534 -0
- package/dist/lib/custom/Rubrics/rubric-form.component.js.map +1 -0
- package/dist/lib/custom/Rubrics/rubric-form.panels.d.ts +25 -0
- package/dist/lib/custom/Rubrics/rubric-form.panels.d.ts.map +1 -0
- package/dist/lib/custom/Rubrics/rubric-form.panels.js +299 -0
- package/dist/lib/custom/Rubrics/rubric-form.panels.js.map +1 -0
- package/dist/lib/custom/Rubrics/scale-form.component.d.ts +31 -0
- package/dist/lib/custom/Rubrics/scale-form.component.d.ts.map +1 -0
- package/dist/lib/custom/Rubrics/scale-form.component.js +395 -0
- package/dist/lib/custom/Rubrics/scale-form.component.js.map +1 -0
- package/dist/lib/custom/SearchScopes/searchscope-form.component.d.ts +1 -1
- package/dist/lib/custom/SearchScopes/searchscope-form.component.js +4 -4
- package/dist/lib/custom/SearchScopes/searchscope-form.component.js.map +1 -1
- package/dist/lib/custom/SessionBridges/session-bridge-form.component.js.map +1 -1
- package/dist/lib/custom/Templates/template-param-dialog.component.d.ts +77 -13
- package/dist/lib/custom/Templates/template-param-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/template-param-dialog.component.js +196 -78
- package/dist/lib/custom/Templates/template-param-dialog.component.js.map +1 -1
- package/dist/lib/custom/Templates/template-params-grid.component.d.ts +49 -7
- package/dist/lib/custom/Templates/template-params-grid.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/template-params-grid.component.js +132 -44
- package/dist/lib/custom/Templates/template-params-grid.component.js.map +1 -1
- package/dist/lib/custom/Templates/templates-form.component.d.ts +153 -19
- package/dist/lib/custom/Templates/templates-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Templates/templates-form.component.js +434 -217
- package/dist/lib/custom/Templates/templates-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/entity-link-pill.component.d.ts +24 -4
- package/dist/lib/custom/Tests/entity-link-pill.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/entity-link-pill.component.js +64 -20
- package/dist/lib/custom/Tests/entity-link-pill.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-form.component.d.ts +223 -27
- package/dist/lib/custom/Tests/test-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-form.component.js +951 -530
- package/dist/lib/custom/Tests/test-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-run-checks.d.ts +2 -0
- package/dist/lib/custom/Tests/test-run-checks.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-run-checks.js +5 -1
- package/dist/lib/custom/Tests/test-run-checks.js.map +1 -1
- package/dist/lib/custom/Tests/test-run-feedback-form.component.d.ts +2 -0
- package/dist/lib/custom/Tests/test-run-feedback-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-run-feedback-form.component.js +5 -1
- package/dist/lib/custom/Tests/test-run-feedback-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-run-form.component.d.ts +146 -18
- package/dist/lib/custom/Tests/test-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-run-form.component.js +362 -106
- package/dist/lib/custom/Tests/test-run-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-suite-form.component.d.ts +395 -47
- package/dist/lib/custom/Tests/test-suite-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-suite-form.component.js +1719 -1007
- package/dist/lib/custom/Tests/test-suite-form.component.js.map +1 -1
- package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts +214 -26
- package/dist/lib/custom/Tests/test-suite-run-form.component.d.ts.map +1 -1
- package/dist/lib/custom/Tests/test-suite-run-form.component.js +535 -159
- package/dist/lib/custom/Tests/test-suite-run-form.component.js.map +1 -1
- package/dist/lib/custom/Users/user-overview.panel.d.ts +1 -1
- package/dist/lib/custom/Users/user-overview.panel.js +3 -3
- package/dist/lib/custom/Users/user-overview.panel.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts +208 -29
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js +446 -138
- package/dist/lib/custom/ai-agent-run/ai-agent-run-analytics.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts +6 -0
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js +16 -4
- package/dist/lib/custom/ai-agent-run/ai-agent-run-cost.service.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts +50 -6
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js +84 -12
- package/dist/lib/custom/ai-agent-run/ai-agent-run-data.service.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts +89 -5
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js +216 -70
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-detail.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.d.ts +62 -3
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js +140 -42
- package/dist/lib/custom/ai-agent-run/ai-agent-run-step-node.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts +81 -10
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js +165 -37
- package/dist/lib/custom/ai-agent-run/ai-agent-run-timeline.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts +140 -15
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js +326 -100
- package/dist/lib/custom/ai-agent-run/ai-agent-run.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.d.ts +101 -15
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.js +281 -97
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.d.ts +43 -25
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.js +100 -64
- package/dist/lib/custom/ai-agent-run/flow/agent-run-flow.model.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/constellation.component.d.ts +14 -2
- package/dist/lib/custom/ai-agent-run/flow/constellation.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/constellation.component.js +71 -53
- package/dist/lib/custom/ai-agent-run/flow/constellation.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.d.ts +14 -2
- package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.js +70 -52
- package/dist/lib/custom/ai-agent-run/flow/flame-cascade.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.d.ts +8 -0
- package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.js +22 -6
- package/dist/lib/custom/ai-agent-run/flow/flow-svg.util.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flowchart.component.d.ts +24 -2
- package/dist/lib/custom/ai-agent-run/flow/flowchart.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/flowchart.component.js +91 -59
- package/dist/lib/custom/ai-agent-run/flow/flowchart.component.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/pan-zoom.d.ts +10 -2
- package/dist/lib/custom/ai-agent-run/flow/pan-zoom.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/pan-zoom.js +33 -17
- package/dist/lib/custom/ai-agent-run/flow/pan-zoom.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.d.ts +2 -0
- package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.js +34 -30
- package/dist/lib/custom/ai-agent-run/flow/run-tree-flow-projection.js.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.d.ts +14 -2
- package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.d.ts.map +1 -1
- package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.js +69 -51
- package/dist/lib/custom/ai-agent-run/flow/subway-lines.component.js.map +1 -1
- package/dist/lib/custom/custom-forms.module.d.ts +56 -48
- package/dist/lib/custom/custom-forms.module.d.ts.map +1 -1
- package/dist/lib/custom/custom-forms.module.js +60 -0
- package/dist/lib/custom/custom-forms.module.js.map +1 -1
- package/dist/lib/custom/shared/entity-selector-dialog.component.d.ts +38 -12
- package/dist/lib/custom/shared/entity-selector-dialog.component.d.ts.map +1 -1
- package/dist/lib/custom/shared/entity-selector-dialog.component.js +103 -51
- package/dist/lib/custom/shared/entity-selector-dialog.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js +61 -43
- package/dist/lib/generated/Entities/MJAIAgent/mjaiagent.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.js +74 -0
- package/dist/lib/generated/Entities/MJAIAgentRubric/mjaiagentrubric.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js +37 -19
- package/dist/lib/generated/Entities/MJAIAgentRun/mjaiagentrun.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.js +78 -52
- package/dist/lib/generated/Entities/MJAIPromptRun/mjaipromptrun.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJAIVendor/mjaivendor.form.component.js +76 -74
- package/dist/lib/generated/Entities/MJAIVendor/mjaivendor.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js +156 -84
- package/dist/lib/generated/Entities/MJEntity/mjentity.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.js +62 -0
- package/dist/lib/generated/Entities/MJFeaturePipelineType/mjfeaturepipelinetype.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js +24 -6
- package/dist/lib/generated/Entities/MJProcessRun/mjprocessrun.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJProject/mjproject.form.component.js +27 -25
- package/dist/lib/generated/Entities/MJProject/mjproject.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.js +24 -16
- package/dist/lib/generated/Entities/MJRecordChange/mjrecordchange.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.js +107 -0
- package/dist/lib/generated/Entities/MJRecordCloneLog/mjrecordclonelog.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.js +74 -0
- package/dist/lib/generated/Entities/MJRecordCloneLogItem/mjrecordclonelogitem.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.js +135 -0
- package/dist/lib/generated/Entities/MJRubric/mjrubric.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.js +87 -0
- package/dist/lib/generated/Entities/MJRubricBand/mjrubricband.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.js +107 -0
- package/dist/lib/generated/Entities/MJRubricCategory/mjrubriccategory.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.js +155 -0
- package/dist/lib/generated/Entities/MJRubricCriterion/mjrubriccriterion.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.js +62 -0
- package/dist/lib/generated/Entities/MJRubricCriterionLevel/mjrubriccriterionlevel.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.js +183 -0
- package/dist/lib/generated/Entities/MJRubricEvaluation/mjrubricevaluation.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.js +120 -0
- package/dist/lib/generated/Entities/MJRubricEvaluationScore/mjrubricevaluationscore.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.js +107 -0
- package/dist/lib/generated/Entities/MJRubricScale/mjrubricscale.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.js +103 -0
- package/dist/lib/generated/Entities/MJRubricScaleLevel/mjrubricscalelevel.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.d.ts +10 -0
- package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.d.ts.map +1 -0
- package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.js +171 -0
- package/dist/lib/generated/Entities/MJRubricVersion/mjrubricversion.form.component.js.map +1 -0
- package/dist/lib/generated/Entities/MJTest/mjtest.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js +24 -16
- package/dist/lib/generated/Entities/MJTest/mjtest.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJTestSuite/mjtestsuite.form.component.js +27 -25
- package/dist/lib/generated/Entities/MJTestSuite/mjtestsuite.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.js +19 -11
- package/dist/lib/generated/Entities/MJTestSuiteRun/mjtestsuiterun.form.component.js.map +1 -1
- package/dist/lib/generated/Entities/MJUser/mjuser.form.component.d.ts.map +1 -1
- package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js +209 -119
- package/dist/lib/generated/Entities/MJUser/mjuser.form.component.js.map +1 -1
- package/dist/lib/generated/generated-forms.module.d.ts +436 -422
- package/dist/lib/generated/generated-forms.module.d.ts.map +1 -1
- package/dist/lib/generated/generated-forms.module.js +44 -2
- package/dist/lib/generated/generated-forms.module.js.map +1 -1
- package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.d.ts +5 -1
- package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.d.ts.map +1 -1
- package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.js +11 -3
- package/dist/lib/panels/ai-skill-sharing/ai-skill-sharing-panel.component.js.map +1 -1
- package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.d.ts +2 -0
- package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.d.ts.map +1 -1
- package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.js +16 -12
- package/dist/lib/panels/content-sources/tag-pipeline-configuration.panel.js.map +1 -1
- package/dist/lib/panels/content-sources/website-crawler-settings.panel.d.ts +1 -1
- package/dist/lib/panels/content-sources/website-crawler-settings.panel.js +2 -2
- package/dist/lib/panels/content-sources/website-crawler-settings.panel.js.map +1 -1
- package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts +45 -19
- package/dist/lib/panels/model-predictions/model-prediction.logic.d.ts.map +1 -1
- package/dist/lib/panels/model-predictions/model-prediction.logic.js +95 -43
- package/dist/lib/panels/model-predictions/model-prediction.logic.js.map +1 -1
- package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts +20 -16
- package/dist/lib/panels/model-predictions/model-prediction.panel.d.ts.map +1 -1
- package/dist/lib/panels/model-predictions/model-prediction.panel.js +73 -65
- package/dist/lib/panels/model-predictions/model-prediction.panel.js.map +1 -1
- package/dist/lib/panels/record-clone/entity-clone-config-editor.component.d.ts +116 -0
- package/dist/lib/panels/record-clone/entity-clone-config-editor.component.d.ts.map +1 -0
- package/dist/lib/panels/record-clone/entity-clone-config-editor.component.js +820 -0
- package/dist/lib/panels/record-clone/entity-clone-config-editor.component.js.map +1 -0
- package/dist/lib/shared/components/template-editor.component.d.ts +158 -15
- package/dist/lib/shared/components/template-editor.component.d.ts.map +1 -1
- package/dist/lib/shared/components/template-editor.component.js +389 -127
- package/dist/lib/shared/components/template-editor.component.js.map +1 -1
- package/dist/public-api.d.ts +5 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +5 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +47 -42
|
@@ -14,7 +14,7 @@ import { RegisterClass } from '@memberjunction/global';
|
|
|
14
14
|
import { SharedService, NavigationService } from '@memberjunction/ng-shared';
|
|
15
15
|
import { ApplicationManager } from '@memberjunction/ng-base-application';
|
|
16
16
|
import { MJTestFormComponent } from '../../generated/Entities/MJTest/mjtest.form.component';
|
|
17
|
-
import { TestingDialogService, TagsHelper, EvaluationPreferencesService } from '@memberjunction/ng-testing';
|
|
17
|
+
import { TestingDialogService, TagsHelper, EvaluationPreferencesService, RubricPickerOptions } from '@memberjunction/ng-testing';
|
|
18
18
|
import { createCopyOnlyToolbar } from '@memberjunction/ng-code-editor';
|
|
19
19
|
import { MJConfirmService } from '@memberjunction/ng-ui-components';
|
|
20
20
|
import * as i0 from "@angular/core";
|
|
@@ -27,7 +27,8 @@ import * as i6 from "@memberjunction/ng-shared-generic";
|
|
|
27
27
|
import * as i7 from "./entity-link-pill.component";
|
|
28
28
|
const _c0 = () => [1, 2, 3, 4, 5];
|
|
29
29
|
const _c1 = () => [1, 2, 3];
|
|
30
|
-
const _forTrack0 = ($index, $item) => $item.
|
|
30
|
+
const _forTrack0 = ($index, $item) => $item.id;
|
|
31
|
+
const _forTrack1 = ($index, $item) => $item.ID;
|
|
31
32
|
function MJTestFormComponentExtended_Conditional_21_Template(rf, ctx) { if (rf & 1) {
|
|
32
33
|
i0.ɵɵelementStart(0, "span", 14);
|
|
33
34
|
i0.ɵɵtext(1, "\u25CF");
|
|
@@ -257,30 +258,40 @@ function MJTestFormComponentExtended_Conditional_64_For_25_Template(rf, ctx) { i
|
|
|
257
258
|
i0.ɵɵadvance();
|
|
258
259
|
i0.ɵɵtextInterpolate(opt_r4);
|
|
259
260
|
} }
|
|
260
|
-
function
|
|
261
|
+
function MJTestFormComponentExtended_Conditional_64_For_33_Template(rf, ctx) { if (rf & 1) {
|
|
261
262
|
i0.ɵɵelementStart(0, "option", 93);
|
|
262
263
|
i0.ɵɵtext(1);
|
|
263
264
|
i0.ɵɵelementEnd();
|
|
264
265
|
} if (rf & 2) {
|
|
265
|
-
const
|
|
266
|
-
i0.ɵɵproperty("ngValue",
|
|
266
|
+
const rubric_r5 = ctx.$implicit;
|
|
267
|
+
i0.ɵɵproperty("ngValue", rubric_r5.id);
|
|
267
268
|
i0.ɵɵadvance();
|
|
268
|
-
i0.ɵɵtextInterpolate(
|
|
269
|
+
i0.ɵɵtextInterpolate(rubric_r5.name);
|
|
269
270
|
} }
|
|
270
|
-
function
|
|
271
|
-
|
|
272
|
-
i0.ɵɵelementStart(0, "span", 98);
|
|
271
|
+
function MJTestFormComponentExtended_Conditional_64_For_39_Template(rf, ctx) { if (rf & 1) {
|
|
272
|
+
i0.ɵɵelementStart(0, "option", 93);
|
|
273
273
|
i0.ɵɵtext(1);
|
|
274
|
-
i0.ɵɵ
|
|
275
|
-
|
|
276
|
-
|
|
274
|
+
i0.ɵɵelementEnd();
|
|
275
|
+
} if (rf & 2) {
|
|
276
|
+
const t_r6 = ctx.$implicit;
|
|
277
|
+
i0.ɵɵproperty("ngValue", t_r6.ID);
|
|
278
|
+
i0.ɵɵadvance();
|
|
279
|
+
i0.ɵɵtextInterpolate(t_r6.Name);
|
|
280
|
+
} }
|
|
281
|
+
function MJTestFormComponentExtended_Conditional_64_For_45_Template(rf, ctx) { if (rf & 1) {
|
|
282
|
+
const _r8 = i0.ɵɵgetCurrentView();
|
|
283
|
+
i0.ɵɵelementStart(0, "span", 100);
|
|
284
|
+
i0.ɵɵtext(1);
|
|
285
|
+
i0.ɵɵelementStart(2, "button", 123);
|
|
286
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_For_45_Template_button_click_2_listener($event) { const tag_r9 = i0.ɵɵrestoreView(_r8).$implicit; const ctx_r0 = i0.ɵɵnextContext(2); ctx_r0.removeTag(tag_r9); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
287
|
+
i0.ɵɵelement(3, "i", 124);
|
|
277
288
|
i0.ɵɵelementEnd()();
|
|
278
289
|
} if (rf & 2) {
|
|
279
|
-
const
|
|
290
|
+
const tag_r9 = ctx.$implicit;
|
|
280
291
|
i0.ɵɵadvance();
|
|
281
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
292
|
+
i0.ɵɵtextInterpolate1(" ", tag_r9, " ");
|
|
282
293
|
i0.ɵɵadvance();
|
|
283
|
-
i0.ɵɵattribute("aria-label", "Remove tag " +
|
|
294
|
+
i0.ɵɵattribute("aria-label", "Remove tag " + tag_r9);
|
|
284
295
|
} }
|
|
285
296
|
function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf & 1) {
|
|
286
297
|
const _r3 = i0.ɵɵgetCurrentView();
|
|
@@ -313,108 +324,118 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
|
|
|
313
324
|
i0.ɵɵrepeaterCreate(24, MJTestFormComponentExtended_Conditional_64_For_25_Template, 2, 2, "option", 93, i0.ɵɵrepeaterTrackByIdentity);
|
|
314
325
|
i0.ɵɵelementEnd()()();
|
|
315
326
|
i0.ɵɵelementStart(26, "div", 89)(27, "label", 94);
|
|
316
|
-
i0.ɵɵtext(28, "
|
|
327
|
+
i0.ɵɵtext(28, "Rubric");
|
|
317
328
|
i0.ɵɵelementEnd();
|
|
318
329
|
i0.ɵɵelementStart(29, "select", 95);
|
|
319
|
-
i0.ɵɵ
|
|
320
|
-
i0.ɵɵ
|
|
330
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_select_ngModelChange_29_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.RubricID = $event || null); });
|
|
331
|
+
i0.ɵɵelementStart(30, "option", 93);
|
|
332
|
+
i0.ɵɵtext(31, "\u2014 None \u2014");
|
|
333
|
+
i0.ɵɵelementEnd();
|
|
334
|
+
i0.ɵɵrepeaterCreate(32, MJTestFormComponentExtended_Conditional_64_For_33_Template, 2, 2, "option", 93, _forTrack0);
|
|
321
335
|
i0.ɵɵelementEnd()();
|
|
322
|
-
i0.ɵɵelementStart(
|
|
323
|
-
i0.ɵɵtext(
|
|
324
|
-
i0.ɵɵelementEnd();
|
|
325
|
-
i0.ɵɵelementStart(
|
|
326
|
-
i0.ɵɵ
|
|
327
|
-
i0.ɵɵrepeaterCreate(
|
|
328
|
-
i0.ɵɵ
|
|
329
|
-
i0.ɵɵ
|
|
330
|
-
i0.ɵɵ
|
|
336
|
+
i0.ɵɵelementStart(34, "div", 89)(35, "label", 96);
|
|
337
|
+
i0.ɵɵtext(36, "Test Type");
|
|
338
|
+
i0.ɵɵelementEnd();
|
|
339
|
+
i0.ɵɵelementStart(37, "select", 97);
|
|
340
|
+
i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_select_ngModelChange_37_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.record.TypeID, $event) || (ctx_r0.record.TypeID = $event); return i0.ɵɵresetView($event); });
|
|
341
|
+
i0.ɵɵrepeaterCreate(38, MJTestFormComponentExtended_Conditional_64_For_39_Template, 2, 2, "option", 93, _forTrack1);
|
|
342
|
+
i0.ɵɵelementEnd()();
|
|
343
|
+
i0.ɵɵelementStart(40, "div", 83)(41, "label", 98);
|
|
344
|
+
i0.ɵɵtext(42, "Tags");
|
|
345
|
+
i0.ɵɵelementEnd();
|
|
346
|
+
i0.ɵɵelementStart(43, "div", 99);
|
|
347
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_64_Template_div_click_43_listener() { i0.ɵɵrestoreView(_r3); const tagInput_r7 = i0.ɵɵreference(47); return i0.ɵɵresetView(tagInput_r7.focus()); });
|
|
348
|
+
i0.ɵɵrepeaterCreate(44, MJTestFormComponentExtended_Conditional_64_For_45_Template, 4, 2, "span", 100, i0.ɵɵrepeaterTrackByIdentity);
|
|
349
|
+
i0.ɵɵelementStart(46, "input", 101, 0);
|
|
350
|
+
i0.ɵɵtwoWayListener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r0.tagDraft, $event) || (ctx_r0.tagDraft = $event); return i0.ɵɵresetView($event); });
|
|
351
|
+
i0.ɵɵlistener("keydown", function MJTestFormComponentExtended_Conditional_64_Template_input_keydown_46_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.onTagInputKeydown($event)); })("blur", function MJTestFormComponentExtended_Conditional_64_Template_input_blur_46_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.addTagFromDraft()); });
|
|
331
352
|
i0.ɵɵelementEnd()();
|
|
332
|
-
i0.ɵɵelementStart(
|
|
333
|
-
i0.ɵɵtext(
|
|
353
|
+
i0.ɵɵelementStart(48, "span", 102);
|
|
354
|
+
i0.ɵɵtext(49, "Press Enter or comma to add a tag. Backspace on empty input removes the last tag.");
|
|
334
355
|
i0.ɵɵelementEnd()()()();
|
|
335
|
-
i0.ɵɵelementStart(
|
|
336
|
-
i0.ɵɵelement(
|
|
337
|
-
i0.ɵɵtext(
|
|
356
|
+
i0.ɵɵelementStart(50, "section", 78)(51, "header", 79)(52, "h3");
|
|
357
|
+
i0.ɵɵelement(53, "i", 103);
|
|
358
|
+
i0.ɵɵtext(54, " Execution Settings");
|
|
338
359
|
i0.ɵɵelementEnd()();
|
|
339
|
-
i0.ɵɵelementStart(
|
|
340
|
-
i0.ɵɵtext(
|
|
360
|
+
i0.ɵɵelementStart(55, "div", 82)(56, "div", 89)(57, "label", 104);
|
|
361
|
+
i0.ɵɵtext(58, "Priority");
|
|
341
362
|
i0.ɵɵelementEnd();
|
|
342
|
-
i0.ɵɵelementStart(
|
|
343
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
363
|
+
i0.ɵɵelementStart(59, "input", 105);
|
|
364
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_59_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Priority = $event === "" || $event === null ? null : +$event); });
|
|
344
365
|
i0.ɵɵelementEnd();
|
|
345
|
-
i0.ɵɵelementStart(
|
|
346
|
-
i0.ɵɵtext(
|
|
366
|
+
i0.ɵɵelementStart(60, "span", 102);
|
|
367
|
+
i0.ɵɵtext(61, "Lower numbers run first.");
|
|
347
368
|
i0.ɵɵelementEnd()();
|
|
348
|
-
i0.ɵɵelementStart(
|
|
349
|
-
i0.ɵɵtext(
|
|
369
|
+
i0.ɵɵelementStart(62, "div", 89)(63, "label", 106);
|
|
370
|
+
i0.ɵɵtext(64, "Repeat Count");
|
|
350
371
|
i0.ɵɵelementEnd();
|
|
351
|
-
i0.ɵɵelementStart(
|
|
352
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
372
|
+
i0.ɵɵelementStart(65, "input", 107);
|
|
373
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_65_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.RepeatCount = $event === "" || $event === null ? null : +$event); });
|
|
353
374
|
i0.ɵɵelementEnd();
|
|
354
|
-
i0.ɵɵelementStart(
|
|
355
|
-
i0.ɵɵtext(
|
|
375
|
+
i0.ɵɵelementStart(66, "span", 102);
|
|
376
|
+
i0.ɵɵtext(67, "Number of times to repeat for statistical analysis.");
|
|
356
377
|
i0.ɵɵelementEnd()();
|
|
357
|
-
i0.ɵɵelementStart(
|
|
358
|
-
i0.ɵɵtext(
|
|
378
|
+
i0.ɵɵelementStart(68, "div", 89)(69, "label", 108);
|
|
379
|
+
i0.ɵɵtext(70, "Estimated Duration (seconds)");
|
|
359
380
|
i0.ɵɵelementEnd();
|
|
360
|
-
i0.ɵɵelementStart(
|
|
361
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
381
|
+
i0.ɵɵelementStart(71, "input", 109);
|
|
382
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_71_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedDurationSeconds = $event === "" || $event === null ? null : +$event); });
|
|
362
383
|
i0.ɵɵelementEnd()();
|
|
363
|
-
i0.ɵɵelementStart(
|
|
364
|
-
i0.ɵɵtext(
|
|
384
|
+
i0.ɵɵelementStart(72, "div", 89)(73, "label", 110);
|
|
385
|
+
i0.ɵɵtext(74, "Estimated Cost (USD)");
|
|
365
386
|
i0.ɵɵelementEnd();
|
|
366
|
-
i0.ɵɵelementStart(
|
|
367
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
387
|
+
i0.ɵɵelementStart(75, "input", 111);
|
|
388
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_75_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.EstimatedCostUSD = $event === "" || $event === null ? null : +$event); });
|
|
368
389
|
i0.ɵɵelementEnd()();
|
|
369
|
-
i0.ɵɵelementStart(
|
|
370
|
-
i0.ɵɵtext(
|
|
390
|
+
i0.ɵɵelementStart(76, "div", 83)(77, "label", 112);
|
|
391
|
+
i0.ɵɵtext(78, "Max Execution Time (ms)");
|
|
371
392
|
i0.ɵɵelementEnd();
|
|
372
|
-
i0.ɵɵelementStart(
|
|
373
|
-
i0.ɵɵlistener("ngModelChange", function
|
|
393
|
+
i0.ɵɵelementStart(79, "input", 113);
|
|
394
|
+
i0.ɵɵlistener("ngModelChange", function MJTestFormComponentExtended_Conditional_64_Template_input_ngModelChange_79_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.MaxExecutionTimeMS = $event === "" || $event === null ? null : +$event); });
|
|
374
395
|
i0.ɵɵelementEnd();
|
|
375
|
-
i0.ɵɵelementStart(
|
|
376
|
-
i0.ɵɵtext(
|
|
396
|
+
i0.ɵɵelementStart(80, "span", 102);
|
|
397
|
+
i0.ɵɵtext(81);
|
|
377
398
|
i0.ɵɵelementEnd()()()();
|
|
378
|
-
i0.ɵɵelementStart(
|
|
379
|
-
i0.ɵɵelement(
|
|
380
|
-
i0.ɵɵtext(
|
|
399
|
+
i0.ɵɵelementStart(82, "div", 114)(83, "h3");
|
|
400
|
+
i0.ɵɵelement(84, "i", 72);
|
|
401
|
+
i0.ɵɵtext(85, " Input Definition (JSON)");
|
|
381
402
|
i0.ɵɵelementEnd();
|
|
382
|
-
i0.ɵɵelementStart(
|
|
383
|
-
i0.ɵɵlistener("change", function
|
|
403
|
+
i0.ɵɵelementStart(86, "div", 115)(87, "mj-code-editor", 116);
|
|
404
|
+
i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_87_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.InputDefinition = $event); });
|
|
384
405
|
i0.ɵɵelementEnd()()();
|
|
385
|
-
i0.ɵɵelementStart(
|
|
386
|
-
i0.ɵɵelement(
|
|
387
|
-
i0.ɵɵtext(
|
|
406
|
+
i0.ɵɵelementStart(88, "div", 114)(89, "h3");
|
|
407
|
+
i0.ɵɵelement(90, "i", 73);
|
|
408
|
+
i0.ɵɵtext(91, " Expected Outcomes (JSON)");
|
|
388
409
|
i0.ɵɵelementEnd();
|
|
389
|
-
i0.ɵɵelementStart(
|
|
390
|
-
i0.ɵɵlistener("change", function
|
|
410
|
+
i0.ɵɵelementStart(92, "div", 115)(93, "mj-code-editor", 116);
|
|
411
|
+
i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_93_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.ExpectedOutcomes = $event); });
|
|
391
412
|
i0.ɵɵelementEnd()()();
|
|
392
|
-
i0.ɵɵelementStart(
|
|
393
|
-
i0.ɵɵelement(
|
|
394
|
-
i0.ɵɵtext(
|
|
413
|
+
i0.ɵɵelementStart(94, "div", 114)(95, "h3");
|
|
414
|
+
i0.ɵɵelement(96, "i", 74);
|
|
415
|
+
i0.ɵɵtext(97, " Configuration (JSON)");
|
|
395
416
|
i0.ɵɵelementEnd();
|
|
396
|
-
i0.ɵɵelementStart(
|
|
397
|
-
i0.ɵɵlistener("change", function
|
|
417
|
+
i0.ɵɵelementStart(98, "div", 115)(99, "mj-code-editor", 116);
|
|
418
|
+
i0.ɵɵlistener("change", function MJTestFormComponentExtended_Conditional_64_Template_mj_code_editor_change_99_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.record.Configuration = $event); });
|
|
398
419
|
i0.ɵɵelementEnd()()();
|
|
399
|
-
i0.ɵɵelementStart(
|
|
400
|
-
i0.ɵɵtext(
|
|
420
|
+
i0.ɵɵelementStart(100, "section", 117)(101, "div", 118)(102, "div", 119)(103, "div", 120);
|
|
421
|
+
i0.ɵɵtext(104, "Created");
|
|
401
422
|
i0.ɵɵelementEnd();
|
|
402
|
-
i0.ɵɵelementStart(
|
|
403
|
-
i0.ɵɵtext(
|
|
404
|
-
i0.ɵɵpipe(
|
|
423
|
+
i0.ɵɵelementStart(105, "div", 121);
|
|
424
|
+
i0.ɵɵtext(106);
|
|
425
|
+
i0.ɵɵpipe(107, "date");
|
|
405
426
|
i0.ɵɵelementEnd()();
|
|
406
|
-
i0.ɵɵelementStart(
|
|
407
|
-
i0.ɵɵtext(
|
|
427
|
+
i0.ɵɵelementStart(108, "div", 119)(109, "div", 120);
|
|
428
|
+
i0.ɵɵtext(110, "Last updated");
|
|
408
429
|
i0.ɵɵelementEnd();
|
|
409
|
-
i0.ɵɵelementStart(
|
|
410
|
-
i0.ɵɵtext(
|
|
411
|
-
i0.ɵɵpipe(
|
|
430
|
+
i0.ɵɵelementStart(111, "div", 121);
|
|
431
|
+
i0.ɵɵtext(112);
|
|
432
|
+
i0.ɵɵpipe(113, "date");
|
|
412
433
|
i0.ɵɵelementEnd()();
|
|
413
|
-
i0.ɵɵelementStart(
|
|
414
|
-
i0.ɵɵtext(
|
|
434
|
+
i0.ɵɵelementStart(114, "div", 119)(115, "div", 120);
|
|
435
|
+
i0.ɵɵtext(116, "Test ID");
|
|
415
436
|
i0.ɵɵelementEnd();
|
|
416
|
-
i0.ɵɵelementStart(
|
|
417
|
-
i0.ɵɵtext(
|
|
437
|
+
i0.ɵɵelementStart(117, "div", 122);
|
|
438
|
+
i0.ɵɵtext(118);
|
|
418
439
|
i0.ɵɵelementEnd()()()()();
|
|
419
440
|
} if (rf & 2) {
|
|
420
441
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -427,6 +448,12 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
|
|
|
427
448
|
i0.ɵɵadvance();
|
|
428
449
|
i0.ɵɵrepeater(ctx_r0.statusOptions);
|
|
429
450
|
i0.ɵɵadvance(5);
|
|
451
|
+
i0.ɵɵproperty("ngModel", ctx_r0.record.RubricID);
|
|
452
|
+
i0.ɵɵadvance();
|
|
453
|
+
i0.ɵɵproperty("ngValue", null);
|
|
454
|
+
i0.ɵɵadvance(2);
|
|
455
|
+
i0.ɵɵrepeater(ctx_r0.RubricOptions);
|
|
456
|
+
i0.ɵɵadvance(5);
|
|
430
457
|
i0.ɵɵtwoWayProperty("ngModel", ctx_r0.record.TypeID);
|
|
431
458
|
i0.ɵɵadvance();
|
|
432
459
|
i0.ɵɵrepeater(ctx_r0.testTypeOptions);
|
|
@@ -454,22 +481,22 @@ function MJTestFormComponentExtended_Conditional_64_Template(rf, ctx) { if (rf &
|
|
|
454
481
|
i0.ɵɵadvance(6);
|
|
455
482
|
i0.ɵɵproperty("value", ctx_r0.record.Configuration || "")("readonly", false)("lineWrapping", true);
|
|
456
483
|
i0.ɵɵadvance(7);
|
|
457
|
-
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(
|
|
484
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(107, 26, ctx_r0.record.__mj_CreatedAt, "medium"));
|
|
458
485
|
i0.ɵɵadvance(6);
|
|
459
|
-
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(
|
|
486
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(113, 29, ctx_r0.record.__mj_UpdatedAt, "medium"));
|
|
460
487
|
i0.ɵɵadvance(6);
|
|
461
488
|
i0.ɵɵtextInterpolate(ctx_r0.record.ID);
|
|
462
489
|
} }
|
|
463
490
|
function MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
464
|
-
i0.ɵɵelementStart(0, "div",
|
|
465
|
-
i0.ɵɵelement(1, "div",
|
|
466
|
-
i0.ɵɵelementStart(2, "div",
|
|
467
|
-
i0.ɵɵelement(3, "div",
|
|
491
|
+
i0.ɵɵelementStart(0, "div", 129);
|
|
492
|
+
i0.ɵɵelement(1, "div", 130);
|
|
493
|
+
i0.ɵɵelementStart(2, "div", 131);
|
|
494
|
+
i0.ɵɵelement(3, "div", 132)(4, "div", 133);
|
|
468
495
|
i0.ɵɵelementEnd()();
|
|
469
496
|
} }
|
|
470
497
|
function MJTestFormComponentExtended_Conditional_65_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
471
|
-
i0.ɵɵelementStart(0, "div",
|
|
472
|
-
i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template, 5, 0, "div",
|
|
498
|
+
i0.ɵɵelementStart(0, "div", 125)(1, "div", 128);
|
|
499
|
+
i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_1_For_3_Template, 5, 0, "div", 129, i0.ɵɵrepeaterTrackByIdentity);
|
|
473
500
|
i0.ɵɵelementEnd()();
|
|
474
501
|
} if (rf & 2) {
|
|
475
502
|
i0.ɵɵadvance(2);
|
|
@@ -477,190 +504,190 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_1_Template(rf, c
|
|
|
477
504
|
} }
|
|
478
505
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_13_Template(rf, ctx) { if (rf & 1) {
|
|
479
506
|
i0.ɵɵelementStart(0, "span");
|
|
480
|
-
i0.ɵɵelement(1, "i",
|
|
507
|
+
i0.ɵɵelement(1, "i", 149);
|
|
481
508
|
i0.ɵɵtext(2);
|
|
482
509
|
i0.ɵɵelementEnd();
|
|
483
510
|
} if (rf & 2) {
|
|
484
|
-
const
|
|
511
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
485
512
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
486
513
|
i0.ɵɵadvance(2);
|
|
487
|
-
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(
|
|
514
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatDuration(run_r11.DurationSeconds));
|
|
488
515
|
} }
|
|
489
516
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
490
517
|
i0.ɵɵelementStart(0, "span");
|
|
491
|
-
i0.ɵɵelement(1, "i",
|
|
518
|
+
i0.ɵɵelement(1, "i", 150);
|
|
492
519
|
i0.ɵɵtext(2);
|
|
493
520
|
i0.ɵɵelementEnd();
|
|
494
521
|
} if (rf & 2) {
|
|
495
|
-
const
|
|
522
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
496
523
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
497
524
|
i0.ɵɵadvance(2);
|
|
498
|
-
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatCost(
|
|
525
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.formatCost(run_r11.CostUSD));
|
|
499
526
|
} }
|
|
500
527
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
501
|
-
i0.ɵɵelement(0, "mj-entity-link-pill",
|
|
528
|
+
i0.ɵɵelement(0, "mj-entity-link-pill", 144);
|
|
502
529
|
} if (rf & 2) {
|
|
503
|
-
const
|
|
504
|
-
i0.ɵɵproperty("entityName",
|
|
530
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
531
|
+
i0.ɵɵproperty("entityName", run_r11.TargetLogEntity)("recordId", run_r11.TargetLogID);
|
|
505
532
|
} }
|
|
506
533
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
507
|
-
i0.ɵɵelementStart(0, "span",
|
|
508
|
-
i0.ɵɵelement(1, "i",
|
|
534
|
+
i0.ɵɵelementStart(0, "span", 146);
|
|
535
|
+
i0.ɵɵelement(1, "i", 137);
|
|
509
536
|
i0.ɵɵelementStart(2, "span");
|
|
510
537
|
i0.ɵɵtext(3);
|
|
511
538
|
i0.ɵɵelementEnd()();
|
|
512
539
|
} if (rf & 2) {
|
|
513
|
-
const
|
|
540
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
514
541
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
515
|
-
i0.ɵɵproperty("ngClass", "status-" +
|
|
542
|
+
i0.ɵɵproperty("ngClass", "status-" + run_r11.Status.toLowerCase())("title", ctx_r0.getStatusTooltip(run_r11.Status));
|
|
516
543
|
i0.ɵɵadvance();
|
|
517
|
-
i0.ɵɵclassProp("fa-check",
|
|
544
|
+
i0.ɵɵclassProp("fa-check", run_r11.Status === "Passed")("fa-times", run_r11.Status === "Failed")("fa-exclamation", run_r11.Status === "Error")("fa-hourglass-end", run_r11.Status === "Timeout")("fa-forward", run_r11.Status === "Skipped")("fa-spinner", run_r11.Status === "Running")("fa-clock", run_r11.Status === "Pending");
|
|
518
545
|
i0.ɵɵadvance(2);
|
|
519
|
-
i0.ɵɵtextInterpolate(
|
|
546
|
+
i0.ɵɵtextInterpolate(run_r11.Status);
|
|
520
547
|
} }
|
|
521
548
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
522
|
-
i0.ɵɵelementStart(0, "span",
|
|
523
|
-
i0.ɵɵelement(1, "i",
|
|
549
|
+
i0.ɵɵelementStart(0, "span", 151);
|
|
550
|
+
i0.ɵɵelement(1, "i", 153);
|
|
524
551
|
i0.ɵɵelementEnd();
|
|
525
552
|
} }
|
|
526
553
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
527
|
-
i0.ɵɵelementStart(0, "span",
|
|
528
|
-
i0.ɵɵelement(1, "i",
|
|
554
|
+
i0.ɵɵelementStart(0, "span", 154);
|
|
555
|
+
i0.ɵɵelement(1, "i", 155);
|
|
529
556
|
i0.ɵɵelementStart(2, "span");
|
|
530
557
|
i0.ɵɵtext(3);
|
|
531
558
|
i0.ɵɵelementEnd()();
|
|
532
559
|
} if (rf & 2) {
|
|
533
560
|
let tmp_19_0;
|
|
534
|
-
const
|
|
535
|
-
const
|
|
561
|
+
const rating_r12 = ctx;
|
|
562
|
+
const run_r11 = i0.ɵɵnextContext(2).$implicit;
|
|
536
563
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
537
|
-
i0.ɵɵclassProp("rating-low",
|
|
538
|
-
i0.ɵɵproperty("title", ctx_r0.getHumanTooltip(
|
|
564
|
+
i0.ɵɵclassProp("rating-low", rating_r12 <= 4)("rating-medium", rating_r12 >= 5 && rating_r12 <= 6)("rating-good", rating_r12 >= 7 && rating_r12 <= 8)("rating-excellent", rating_r12 >= 9);
|
|
565
|
+
i0.ɵɵproperty("title", ctx_r0.getHumanTooltip(rating_r12, ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_19_0.CorrectionSummary) || ((tmp_19_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_19_0.Comments) || null));
|
|
539
566
|
i0.ɵɵadvance(3);
|
|
540
|
-
i0.ɵɵtextInterpolate(
|
|
567
|
+
i0.ɵɵtextInterpolate(rating_r12);
|
|
541
568
|
} }
|
|
542
569
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
543
|
-
i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template, 2, 0, "span",
|
|
544
|
-
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template, 4, 10, "span",
|
|
570
|
+
i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_0_Template, 2, 0, "span", 151);
|
|
571
|
+
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Conditional_1_Template, 4, 10, "span", 152);
|
|
545
572
|
} if (rf & 2) {
|
|
546
573
|
let tmp_13_0;
|
|
547
574
|
let tmp_14_0;
|
|
548
|
-
const
|
|
575
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
549
576
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
550
|
-
i0.ɵɵconditional(!((tmp_13_0 = ctx_r0.getFeedbackForRun(
|
|
577
|
+
i0.ɵɵconditional(!((tmp_13_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_13_0.Rating) ? 0 : -1);
|
|
551
578
|
i0.ɵɵadvance();
|
|
552
|
-
i0.ɵɵconditional((tmp_14_0 = (tmp_14_0 = ctx_r0.getFeedbackForRun(
|
|
579
|
+
i0.ɵɵconditional((tmp_14_0 = (tmp_14_0 = ctx_r0.getFeedbackForRun(run_r11.ID)) == null ? null : tmp_14_0.Rating) ? 1 : -1, tmp_14_0);
|
|
553
580
|
} }
|
|
554
581
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
555
|
-
i0.ɵɵelementStart(0, "span",
|
|
556
|
-
i0.ɵɵelement(1, "i",
|
|
582
|
+
i0.ɵɵelementStart(0, "span", 156);
|
|
583
|
+
i0.ɵɵelement(1, "i", 158);
|
|
557
584
|
i0.ɵɵelementEnd();
|
|
558
585
|
} }
|
|
559
586
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
560
|
-
i0.ɵɵelementStart(0, "span",
|
|
561
|
-
i0.ɵɵelement(1, "i",
|
|
587
|
+
i0.ɵɵelementStart(0, "span", 159);
|
|
588
|
+
i0.ɵɵelement(1, "i", 158);
|
|
562
589
|
i0.ɵɵelementStart(2, "span");
|
|
563
590
|
i0.ɵɵtext(3);
|
|
564
591
|
i0.ɵɵelementEnd()();
|
|
565
592
|
} if (rf & 2) {
|
|
566
|
-
const
|
|
567
|
-
i0.ɵɵclassProp("score-low",
|
|
568
|
-
i0.ɵɵproperty("title", "Auto Score: " + (
|
|
593
|
+
const run_r11 = i0.ɵɵnextContext(2).$implicit;
|
|
594
|
+
i0.ɵɵclassProp("score-low", run_r11.Score < 0.5)("score-medium", run_r11.Score >= 0.5 && run_r11.Score < 0.7)("score-good", run_r11.Score >= 0.7 && run_r11.Score < 0.85)("score-excellent", run_r11.Score >= 0.85);
|
|
595
|
+
i0.ɵɵproperty("title", "Auto Score: " + (run_r11.Score * 100).toFixed(0) + "% automated evaluation");
|
|
569
596
|
i0.ɵɵadvance(3);
|
|
570
|
-
i0.ɵɵtextInterpolate1("", (
|
|
597
|
+
i0.ɵɵtextInterpolate1("", (run_r11.Score * 100).toFixed(0), "%");
|
|
571
598
|
} }
|
|
572
599
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
573
|
-
i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template, 2, 0, "span",
|
|
574
|
-
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template, 4, 10, "span",
|
|
600
|
+
i0.ɵɵconditionalCreate(0, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_0_Template, 2, 0, "span", 156);
|
|
601
|
+
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Conditional_1_Template, 4, 10, "span", 157);
|
|
575
602
|
} if (rf & 2) {
|
|
576
|
-
const
|
|
577
|
-
i0.ɵɵconditional(
|
|
603
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
604
|
+
i0.ɵɵconditional(run_r11.Score == null ? 0 : -1);
|
|
578
605
|
i0.ɵɵadvance();
|
|
579
|
-
i0.ɵɵconditional(
|
|
606
|
+
i0.ɵɵconditional(run_r11.Score != null ? 1 : -1);
|
|
580
607
|
} }
|
|
581
608
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
582
|
-
i0.ɵɵelementStart(0, "span",
|
|
609
|
+
i0.ɵɵelementStart(0, "span", 160);
|
|
583
610
|
i0.ɵɵtext(1);
|
|
584
611
|
i0.ɵɵelementEnd();
|
|
585
612
|
} if (rf & 2) {
|
|
586
|
-
const
|
|
613
|
+
const tag_r13 = ctx.$implicit;
|
|
587
614
|
i0.ɵɵadvance();
|
|
588
|
-
i0.ɵɵtextInterpolate(
|
|
615
|
+
i0.ɵɵtextInterpolate(tag_r13);
|
|
589
616
|
} }
|
|
590
617
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
591
|
-
i0.ɵɵelementStart(0, "span",
|
|
618
|
+
i0.ɵɵelementStart(0, "span", 161);
|
|
592
619
|
i0.ɵɵtext(1);
|
|
593
620
|
i0.ɵɵelementEnd();
|
|
594
621
|
} if (rf & 2) {
|
|
595
|
-
const
|
|
622
|
+
const run_r11 = i0.ɵɵnextContext(2).$implicit;
|
|
596
623
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
597
624
|
i0.ɵɵadvance();
|
|
598
|
-
i0.ɵɵtextInterpolate1("+", ctx_r0.getRunTags(
|
|
625
|
+
i0.ɵɵtextInterpolate1("+", ctx_r0.getRunTags(run_r11).length - 3);
|
|
599
626
|
} }
|
|
600
627
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
601
|
-
i0.ɵɵelementStart(0, "div",
|
|
602
|
-
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template, 2, 1, "span",
|
|
603
|
-
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template, 2, 1, "span",
|
|
628
|
+
i0.ɵɵelementStart(0, "div", 147);
|
|
629
|
+
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_For_2_Template, 2, 1, "span", 160, i0.ɵɵrepeaterTrackByIdentity);
|
|
630
|
+
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Conditional_3_Template, 2, 1, "span", 161);
|
|
604
631
|
i0.ɵɵelementEnd();
|
|
605
632
|
} if (rf & 2) {
|
|
606
|
-
const
|
|
633
|
+
const run_r11 = i0.ɵɵnextContext().$implicit;
|
|
607
634
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
608
635
|
i0.ɵɵadvance();
|
|
609
|
-
i0.ɵɵrepeater(ctx_r0.getRunTags(
|
|
636
|
+
i0.ɵɵrepeater(ctx_r0.getRunTags(run_r11).slice(0, 3));
|
|
610
637
|
i0.ɵɵadvance(2);
|
|
611
|
-
i0.ɵɵconditional(ctx_r0.getRunTags(
|
|
638
|
+
i0.ɵɵconditional(ctx_r0.getRunTags(run_r11).length > 3 ? 3 : -1);
|
|
612
639
|
} }
|
|
613
640
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
614
|
-
const
|
|
615
|
-
i0.ɵɵelementStart(0, "div",
|
|
616
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template_div_click_0_listener() { const
|
|
617
|
-
i0.ɵɵelementStart(1, "div",
|
|
618
|
-
i0.ɵɵelement(2, "i",
|
|
641
|
+
const _r10 = i0.ɵɵgetCurrentView();
|
|
642
|
+
i0.ɵɵelementStart(0, "div", 135);
|
|
643
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template_div_click_0_listener() { const run_r11 = i0.ɵɵrestoreView(_r10).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestRun(run_r11.ID)); });
|
|
644
|
+
i0.ɵɵelementStart(1, "div", 136);
|
|
645
|
+
i0.ɵɵelement(2, "i", 137);
|
|
619
646
|
i0.ɵɵelementEnd();
|
|
620
|
-
i0.ɵɵelementStart(3, "div",
|
|
647
|
+
i0.ɵɵelementStart(3, "div", 138)(4, "div", 139)(5, "span", 140);
|
|
621
648
|
i0.ɵɵtext(6);
|
|
622
649
|
i0.ɵɵelementEnd();
|
|
623
|
-
i0.ɵɵelementStart(7, "span",
|
|
650
|
+
i0.ɵɵelementStart(7, "span", 141);
|
|
624
651
|
i0.ɵɵtext(8);
|
|
625
652
|
i0.ɵɵelementEnd()();
|
|
626
|
-
i0.ɵɵelementStart(9, "div",
|
|
627
|
-
i0.ɵɵelement(11, "i",
|
|
653
|
+
i0.ɵɵelementStart(9, "div", 142)(10, "span");
|
|
654
|
+
i0.ɵɵelement(11, "i", 143);
|
|
628
655
|
i0.ɵɵtext(12);
|
|
629
656
|
i0.ɵɵelementEnd();
|
|
630
657
|
i0.ɵɵconditionalCreate(13, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_13_Template, 3, 1, "span");
|
|
631
658
|
i0.ɵɵconditionalCreate(14, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_14_Template, 3, 1, "span");
|
|
632
|
-
i0.ɵɵconditionalCreate(15, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template, 1, 2, "mj-entity-link-pill",
|
|
659
|
+
i0.ɵɵconditionalCreate(15, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_15_Template, 1, 2, "mj-entity-link-pill", 144);
|
|
633
660
|
i0.ɵɵelementEnd();
|
|
634
|
-
i0.ɵɵelementStart(16, "div",
|
|
635
|
-
i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template, 4, 17, "span",
|
|
661
|
+
i0.ɵɵelementStart(16, "div", 145);
|
|
662
|
+
i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_17_Template, 4, 17, "span", 146);
|
|
636
663
|
i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_18_Template, 2, 2);
|
|
637
664
|
i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_19_Template, 2, 2);
|
|
638
665
|
i0.ɵɵelementEnd();
|
|
639
|
-
i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template, 4, 1, "div",
|
|
666
|
+
i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Conditional_20_Template, 4, 1, "div", 147);
|
|
640
667
|
i0.ɵɵelementEnd();
|
|
641
|
-
i0.ɵɵelement(21, "i",
|
|
668
|
+
i0.ɵɵelement(21, "i", 148);
|
|
642
669
|
i0.ɵɵelementEnd();
|
|
643
670
|
} if (rf & 2) {
|
|
644
|
-
const
|
|
671
|
+
const run_r11 = ctx.$implicit;
|
|
645
672
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
646
673
|
i0.ɵɵadvance();
|
|
647
|
-
i0.ɵɵstyleProp("background-color", ctx_r0.getRunStatusColor(
|
|
674
|
+
i0.ɵɵstyleProp("background-color", ctx_r0.getRunStatusColor(run_r11.Status));
|
|
648
675
|
i0.ɵɵadvance();
|
|
649
|
-
i0.ɵɵclassProp("fa-check",
|
|
676
|
+
i0.ɵɵclassProp("fa-check", run_r11.Status === "Passed")("fa-times", run_r11.Status === "Failed")("fa-exclamation", run_r11.Status === "Error")("fa-stopwatch", run_r11.Status === "Timeout")("fa-spinner", run_r11.Status === "Running")("fa-clock", run_r11.Status === "Pending");
|
|
650
677
|
i0.ɵɵadvance(4);
|
|
651
|
-
i0.ɵɵtextInterpolate1("Run #",
|
|
678
|
+
i0.ɵɵtextInterpolate1("Run #", run_r11.ID.substring(0, 8));
|
|
652
679
|
i0.ɵɵadvance();
|
|
653
|
-
i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(
|
|
680
|
+
i0.ɵɵstyleProp("color", ctx_r0.getRunStatusColor(run_r11.Status));
|
|
654
681
|
i0.ɵɵadvance();
|
|
655
|
-
i0.ɵɵtextInterpolate(
|
|
682
|
+
i0.ɵɵtextInterpolate(run_r11.Status);
|
|
656
683
|
i0.ɵɵadvance(4);
|
|
657
|
-
i0.ɵɵtextInterpolate1(" ", ctx_r0.getRelativeTime(
|
|
684
|
+
i0.ɵɵtextInterpolate1(" ", ctx_r0.getRelativeTime(run_r11.StartedAt));
|
|
658
685
|
i0.ɵɵadvance();
|
|
659
|
-
i0.ɵɵconditional(
|
|
686
|
+
i0.ɵɵconditional(run_r11.DurationSeconds ? 13 : -1);
|
|
660
687
|
i0.ɵɵadvance();
|
|
661
|
-
i0.ɵɵconditional(
|
|
688
|
+
i0.ɵɵconditional(run_r11.CostUSD ? 14 : -1);
|
|
662
689
|
i0.ɵɵadvance();
|
|
663
|
-
i0.ɵɵconditional(
|
|
690
|
+
i0.ɵɵconditional(run_r11.TargetLogEntityID && run_r11.TargetLogID ? 15 : -1);
|
|
664
691
|
i0.ɵɵadvance(2);
|
|
665
692
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 17 : -1);
|
|
666
693
|
i0.ɵɵadvance();
|
|
@@ -668,11 +695,11 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template
|
|
|
668
695
|
i0.ɵɵadvance();
|
|
669
696
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 19 : -1);
|
|
670
697
|
i0.ɵɵadvance();
|
|
671
|
-
i0.ɵɵconditional(ctx_r0.getRunTags(
|
|
698
|
+
i0.ɵɵconditional(ctx_r0.getRunTags(run_r11).length > 0 ? 20 : -1);
|
|
672
699
|
} }
|
|
673
700
|
function MJTestFormComponentExtended_Conditional_65_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
674
|
-
i0.ɵɵelementStart(0, "div",
|
|
675
|
-
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template, 22, 26, "div",
|
|
701
|
+
i0.ɵɵelementStart(0, "div", 126);
|
|
702
|
+
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_2_For_2_Template, 22, 26, "div", 134, i0.ɵɵrepeaterTrackByIdentity);
|
|
676
703
|
i0.ɵɵelementEnd();
|
|
677
704
|
} if (rf & 2) {
|
|
678
705
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -680,16 +707,16 @@ function MJTestFormComponentExtended_Conditional_65_Conditional_2_Template(rf, c
|
|
|
680
707
|
i0.ɵɵrepeater(ctx_r0.testRuns);
|
|
681
708
|
} }
|
|
682
709
|
function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
683
|
-
const
|
|
684
|
-
i0.ɵɵelementStart(0, "mj-empty-state",
|
|
685
|
-
i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(
|
|
710
|
+
const _r14 = i0.ɵɵgetCurrentView();
|
|
711
|
+
i0.ɵɵelementStart(0, "mj-empty-state", 162);
|
|
712
|
+
i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_65_Conditional_3_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r14); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.runTest()); });
|
|
686
713
|
i0.ɵɵelementEnd();
|
|
687
714
|
} }
|
|
688
715
|
function MJTestFormComponentExtended_Conditional_65_Template(rf, ctx) { if (rf & 1) {
|
|
689
716
|
i0.ɵɵelementStart(0, "div", 38);
|
|
690
|
-
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_1_Template, 4, 1, "div",
|
|
691
|
-
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_2_Template, 3, 0, "div",
|
|
692
|
-
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_3_Template, 1, 0, "mj-empty-state",
|
|
717
|
+
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_65_Conditional_1_Template, 4, 1, "div", 125);
|
|
718
|
+
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_65_Conditional_2_Template, 3, 0, "div", 126);
|
|
719
|
+
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_65_Conditional_3_Template, 1, 0, "mj-empty-state", 127);
|
|
693
720
|
i0.ɵɵelementEnd();
|
|
694
721
|
} if (rf & 2) {
|
|
695
722
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -701,15 +728,15 @@ function MJTestFormComponentExtended_Conditional_65_Template(rf, ctx) { if (rf &
|
|
|
701
728
|
i0.ɵɵconditional(ctx_r0.testRunsLoaded && !ctx_r0.loadingRuns && ctx_r0.testRuns.length === 0 ? 3 : -1);
|
|
702
729
|
} }
|
|
703
730
|
function MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template(rf, ctx) { if (rf & 1) {
|
|
704
|
-
i0.ɵɵelementStart(0, "div",
|
|
705
|
-
i0.ɵɵelement(1, "div",
|
|
706
|
-
i0.ɵɵelementStart(2, "div",
|
|
707
|
-
i0.ɵɵelement(3, "div",
|
|
731
|
+
i0.ɵɵelementStart(0, "div", 129);
|
|
732
|
+
i0.ɵɵelement(1, "div", 130);
|
|
733
|
+
i0.ɵɵelementStart(2, "div", 131);
|
|
734
|
+
i0.ɵɵelement(3, "div", 132)(4, "div", 133);
|
|
708
735
|
i0.ɵɵelementEnd()();
|
|
709
736
|
} }
|
|
710
737
|
function MJTestFormComponentExtended_Conditional_66_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
711
|
-
i0.ɵɵelementStart(0, "div",
|
|
712
|
-
i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template, 5, 0, "div",
|
|
738
|
+
i0.ɵɵelementStart(0, "div", 125)(1, "div", 128);
|
|
739
|
+
i0.ɵɵrepeaterCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_1_For_3_Template, 5, 0, "div", 129, i0.ɵɵrepeaterTrackByIdentity);
|
|
713
740
|
i0.ɵɵelementEnd()();
|
|
714
741
|
} if (rf & 2) {
|
|
715
742
|
i0.ɵɵadvance(2);
|
|
@@ -721,40 +748,40 @@ function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Conditio
|
|
|
721
748
|
i0.ɵɵtext(2);
|
|
722
749
|
i0.ɵɵelementEnd();
|
|
723
750
|
} if (rf & 2) {
|
|
724
|
-
const
|
|
751
|
+
const suiteTest_r16 = i0.ɵɵnextContext().$implicit;
|
|
725
752
|
i0.ɵɵadvance(2);
|
|
726
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
753
|
+
i0.ɵɵtextInterpolate1(" ", suiteTest_r16.Status);
|
|
727
754
|
} }
|
|
728
755
|
function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
729
|
-
const
|
|
730
|
-
i0.ɵɵelementStart(0, "div",
|
|
731
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template_div_click_0_listener() { const
|
|
732
|
-
i0.ɵɵelementStart(1, "div",
|
|
756
|
+
const _r15 = i0.ɵɵgetCurrentView();
|
|
757
|
+
i0.ɵɵelementStart(0, "div", 166);
|
|
758
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template_div_click_0_listener() { const suiteTest_r16 = i0.ɵɵrestoreView(_r15).$implicit; const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.openTestSuite(suiteTest_r16.SuiteID)); });
|
|
759
|
+
i0.ɵɵelementStart(1, "div", 167);
|
|
733
760
|
i0.ɵɵelement(2, "i", 33);
|
|
734
761
|
i0.ɵɵelementEnd();
|
|
735
|
-
i0.ɵɵelementStart(3, "div",
|
|
762
|
+
i0.ɵɵelementStart(3, "div", 168)(4, "div", 169);
|
|
736
763
|
i0.ɵɵtext(5);
|
|
737
764
|
i0.ɵɵelementEnd();
|
|
738
|
-
i0.ɵɵelementStart(6, "div",
|
|
739
|
-
i0.ɵɵelement(8, "i",
|
|
765
|
+
i0.ɵɵelementStart(6, "div", 170)(7, "span");
|
|
766
|
+
i0.ɵɵelement(8, "i", 171);
|
|
740
767
|
i0.ɵɵtext(9);
|
|
741
768
|
i0.ɵɵelementEnd();
|
|
742
769
|
i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Conditional_10_Template, 3, 1, "span");
|
|
743
770
|
i0.ɵɵelementEnd()();
|
|
744
|
-
i0.ɵɵelement(11, "i",
|
|
771
|
+
i0.ɵɵelement(11, "i", 148);
|
|
745
772
|
i0.ɵɵelementEnd();
|
|
746
773
|
} if (rf & 2) {
|
|
747
|
-
const
|
|
774
|
+
const suiteTest_r16 = ctx.$implicit;
|
|
748
775
|
i0.ɵɵadvance(5);
|
|
749
|
-
i0.ɵɵtextInterpolate(
|
|
776
|
+
i0.ɵɵtextInterpolate(suiteTest_r16.Suite);
|
|
750
777
|
i0.ɵɵadvance(4);
|
|
751
|
-
i0.ɵɵtextInterpolate1(" Sequence: ",
|
|
778
|
+
i0.ɵɵtextInterpolate1(" Sequence: ", suiteTest_r16.Sequence);
|
|
752
779
|
i0.ɵɵadvance();
|
|
753
|
-
i0.ɵɵconditional(
|
|
780
|
+
i0.ɵɵconditional(suiteTest_r16.Status ? 10 : -1);
|
|
754
781
|
} }
|
|
755
782
|
function MJTestFormComponentExtended_Conditional_66_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
756
|
-
i0.ɵɵelementStart(0, "div",
|
|
757
|
-
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template, 12, 3, "div",
|
|
783
|
+
i0.ɵɵelementStart(0, "div", 163);
|
|
784
|
+
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_2_For_2_Template, 12, 3, "div", 165, i0.ɵɵrepeaterTrackByIdentity);
|
|
758
785
|
i0.ɵɵelementEnd();
|
|
759
786
|
} if (rf & 2) {
|
|
760
787
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -762,13 +789,13 @@ function MJTestFormComponentExtended_Conditional_66_Conditional_2_Template(rf, c
|
|
|
762
789
|
i0.ɵɵrepeater(ctx_r0.suiteTests);
|
|
763
790
|
} }
|
|
764
791
|
function MJTestFormComponentExtended_Conditional_66_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
765
|
-
i0.ɵɵelement(0, "mj-empty-state",
|
|
792
|
+
i0.ɵɵelement(0, "mj-empty-state", 164);
|
|
766
793
|
} }
|
|
767
794
|
function MJTestFormComponentExtended_Conditional_66_Template(rf, ctx) { if (rf & 1) {
|
|
768
795
|
i0.ɵɵelementStart(0, "div", 39);
|
|
769
|
-
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_1_Template, 4, 1, "div",
|
|
770
|
-
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_2_Template, 3, 0, "div",
|
|
771
|
-
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_66_Conditional_3_Template, 1, 0, "mj-empty-state",
|
|
796
|
+
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_66_Conditional_1_Template, 4, 1, "div", 125);
|
|
797
|
+
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_66_Conditional_2_Template, 3, 0, "div", 163);
|
|
798
|
+
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_66_Conditional_3_Template, 1, 0, "mj-empty-state", 164);
|
|
772
799
|
i0.ɵɵelementEnd();
|
|
773
800
|
} if (rf & 2) {
|
|
774
801
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -780,19 +807,19 @@ function MJTestFormComponentExtended_Conditional_66_Template(rf, ctx) { if (rf &
|
|
|
780
807
|
i0.ɵɵconditional(ctx_r0.suiteTestsLoaded && !ctx_r0.loadingSuites && ctx_r0.suiteTests.length === 0 ? 3 : -1);
|
|
781
808
|
} }
|
|
782
809
|
function MJTestFormComponentExtended_Conditional_67_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
783
|
-
i0.ɵɵelementStart(0, "div",
|
|
784
|
-
i0.ɵɵelement(1, "mj-loading",
|
|
810
|
+
i0.ɵɵelementStart(0, "div", 125);
|
|
811
|
+
i0.ɵɵelement(1, "mj-loading", 173);
|
|
785
812
|
i0.ɵɵelementEnd();
|
|
786
813
|
} }
|
|
787
814
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
788
|
-
i0.ɵɵelementStart(0, "div",
|
|
789
|
-
i0.ɵɵelement(2, "i",
|
|
815
|
+
i0.ɵɵelementStart(0, "div", 183)(1, "div", 189);
|
|
816
|
+
i0.ɵɵelement(2, "i", 190);
|
|
790
817
|
i0.ɵɵelementEnd();
|
|
791
|
-
i0.ɵɵelementStart(3, "div",
|
|
818
|
+
i0.ɵɵelementStart(3, "div", 186)(4, "div", 187);
|
|
792
819
|
i0.ɵɵtext(5);
|
|
793
|
-
i0.ɵɵelement(6, "i",
|
|
820
|
+
i0.ɵɵelement(6, "i", 191);
|
|
794
821
|
i0.ɵɵelementEnd();
|
|
795
|
-
i0.ɵɵelementStart(7, "div",
|
|
822
|
+
i0.ɵɵelementStart(7, "div", 188);
|
|
796
823
|
i0.ɵɵtext(8, "Pass Rate");
|
|
797
824
|
i0.ɵɵelementEnd()()();
|
|
798
825
|
} if (rf & 2) {
|
|
@@ -803,13 +830,13 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17
|
|
|
803
830
|
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");
|
|
804
831
|
} }
|
|
805
832
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
806
|
-
i0.ɵɵelementStart(0, "div",
|
|
807
|
-
i0.ɵɵelement(2, "i",
|
|
833
|
+
i0.ɵɵelementStart(0, "div", 183)(1, "div", 184);
|
|
834
|
+
i0.ɵɵelement(2, "i", 192);
|
|
808
835
|
i0.ɵɵelementEnd();
|
|
809
|
-
i0.ɵɵelementStart(3, "div",
|
|
836
|
+
i0.ɵɵelementStart(3, "div", 186)(4, "div", 187);
|
|
810
837
|
i0.ɵɵtext(5);
|
|
811
838
|
i0.ɵɵelementEnd();
|
|
812
|
-
i0.ɵɵelementStart(6, "div",
|
|
839
|
+
i0.ɵɵelementStart(6, "div", 188);
|
|
813
840
|
i0.ɵɵtext(7, "Avg Score");
|
|
814
841
|
i0.ɵɵelementEnd()()();
|
|
815
842
|
} if (rf & 2) {
|
|
@@ -818,33 +845,33 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17
|
|
|
818
845
|
i0.ɵɵtextInterpolate1("", (ctx_r0.getOverallAvgScore() * 100).toFixed(1), "%");
|
|
819
846
|
} }
|
|
820
847
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
821
|
-
i0.ɵɵelementStart(0, "div",
|
|
822
|
-
i0.ɵɵelement(3, "i",
|
|
848
|
+
i0.ɵɵelementStart(0, "div", 180)(1, "div", 183)(2, "div", 184);
|
|
849
|
+
i0.ɵɵelement(3, "i", 185);
|
|
823
850
|
i0.ɵɵelementEnd();
|
|
824
|
-
i0.ɵɵelementStart(4, "div",
|
|
851
|
+
i0.ɵɵelementStart(4, "div", 186)(5, "div", 187);
|
|
825
852
|
i0.ɵɵtext(6);
|
|
826
853
|
i0.ɵɵelementEnd();
|
|
827
|
-
i0.ɵɵelementStart(7, "div",
|
|
854
|
+
i0.ɵɵelementStart(7, "div", 188);
|
|
828
855
|
i0.ɵɵtext(8, "Total Runs");
|
|
829
856
|
i0.ɵɵelementEnd()()();
|
|
830
|
-
i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template, 9, 11, "div",
|
|
831
|
-
i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template, 8, 1, "div",
|
|
832
|
-
i0.ɵɵelementStart(11, "div",
|
|
833
|
-
i0.ɵɵelement(13, "i",
|
|
857
|
+
i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_9_Template, 9, 11, "div", 183);
|
|
858
|
+
i0.ɵɵconditionalCreate(10, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Conditional_10_Template, 8, 1, "div", 183);
|
|
859
|
+
i0.ɵɵelementStart(11, "div", 183)(12, "div", 184);
|
|
860
|
+
i0.ɵɵelement(13, "i", 149);
|
|
834
861
|
i0.ɵɵelementEnd();
|
|
835
|
-
i0.ɵɵelementStart(14, "div",
|
|
862
|
+
i0.ɵɵelementStart(14, "div", 186)(15, "div", 187);
|
|
836
863
|
i0.ɵɵtext(16);
|
|
837
864
|
i0.ɵɵelementEnd();
|
|
838
|
-
i0.ɵɵelementStart(17, "div",
|
|
865
|
+
i0.ɵɵelementStart(17, "div", 188);
|
|
839
866
|
i0.ɵɵtext(18, "Avg Duration");
|
|
840
867
|
i0.ɵɵelementEnd()()();
|
|
841
|
-
i0.ɵɵelementStart(19, "div",
|
|
842
|
-
i0.ɵɵelement(21, "i",
|
|
868
|
+
i0.ɵɵelementStart(19, "div", 183)(20, "div", 184);
|
|
869
|
+
i0.ɵɵelement(21, "i", 150);
|
|
843
870
|
i0.ɵɵelementEnd();
|
|
844
|
-
i0.ɵɵelementStart(22, "div",
|
|
871
|
+
i0.ɵɵelementStart(22, "div", 186)(23, "div", 187);
|
|
845
872
|
i0.ɵɵtext(24);
|
|
846
873
|
i0.ɵɵelementEnd();
|
|
847
|
-
i0.ɵɵelementStart(25, "div",
|
|
874
|
+
i0.ɵɵelementStart(25, "div", 188);
|
|
848
875
|
i0.ɵɵtext(26, "Avg Cost");
|
|
849
876
|
i0.ɵɵelementEnd()()()();
|
|
850
877
|
} if (rf & 2) {
|
|
@@ -881,70 +908,70 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
|
|
|
881
908
|
i0.ɵɵelementEnd();
|
|
882
909
|
} }
|
|
883
910
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
884
|
-
i0.ɵɵelementStart(0, "td",
|
|
911
|
+
i0.ɵɵelementStart(0, "td", 198);
|
|
885
912
|
i0.ɵɵtext(1);
|
|
886
913
|
i0.ɵɵelementEnd();
|
|
887
914
|
} if (rf & 2) {
|
|
888
|
-
const
|
|
915
|
+
const day_r18 = i0.ɵɵnextContext().$implicit;
|
|
889
916
|
i0.ɵɵadvance();
|
|
890
|
-
i0.ɵɵtextInterpolate(
|
|
917
|
+
i0.ɵɵtextInterpolate(day_r18.passCount);
|
|
891
918
|
} }
|
|
892
919
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
893
|
-
i0.ɵɵelementStart(0, "td",
|
|
920
|
+
i0.ɵɵelementStart(0, "td", 199);
|
|
894
921
|
i0.ɵɵtext(1);
|
|
895
922
|
i0.ɵɵelementEnd();
|
|
896
923
|
} if (rf & 2) {
|
|
897
|
-
const
|
|
924
|
+
const day_r18 = i0.ɵɵnextContext().$implicit;
|
|
898
925
|
i0.ɵɵadvance();
|
|
899
|
-
i0.ɵɵtextInterpolate(
|
|
926
|
+
i0.ɵɵtextInterpolate(day_r18.failCount);
|
|
900
927
|
} }
|
|
901
928
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
902
|
-
i0.ɵɵelementStart(0, "td",
|
|
903
|
-
i0.ɵɵelement(2, "div",
|
|
904
|
-
i0.ɵɵelementStart(3, "span",
|
|
929
|
+
i0.ɵɵelementStart(0, "td", 200)(1, "div", 204);
|
|
930
|
+
i0.ɵɵelement(2, "div", 205);
|
|
931
|
+
i0.ɵɵelementStart(3, "span", 206);
|
|
905
932
|
i0.ɵɵtext(4);
|
|
906
933
|
i0.ɵɵelementEnd()()();
|
|
907
934
|
} if (rf & 2) {
|
|
908
|
-
const
|
|
935
|
+
const day_r18 = i0.ɵɵnextContext().$implicit;
|
|
909
936
|
i0.ɵɵadvance(2);
|
|
910
|
-
i0.ɵɵstyleProp("width",
|
|
937
|
+
i0.ɵɵstyleProp("width", day_r18.passRate, "%");
|
|
911
938
|
i0.ɵɵadvance(2);
|
|
912
|
-
i0.ɵɵtextInterpolate1("",
|
|
939
|
+
i0.ɵɵtextInterpolate1("", day_r18.passRate.toFixed(1), "%");
|
|
913
940
|
} }
|
|
914
941
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
915
|
-
i0.ɵɵelementStart(0, "td",
|
|
942
|
+
i0.ɵɵelementStart(0, "td", 201);
|
|
916
943
|
i0.ɵɵtext(1);
|
|
917
944
|
i0.ɵɵelementEnd();
|
|
918
945
|
} if (rf & 2) {
|
|
919
|
-
const
|
|
946
|
+
const day_r18 = i0.ɵɵnextContext().$implicit;
|
|
920
947
|
i0.ɵɵadvance();
|
|
921
|
-
i0.ɵɵtextInterpolate1("", (
|
|
948
|
+
i0.ɵɵtextInterpolate1("", (day_r18.avgScore * 100).toFixed(1), "%");
|
|
922
949
|
} }
|
|
923
950
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Template(rf, ctx) { if (rf & 1) {
|
|
924
|
-
i0.ɵɵelementStart(0, "tr")(1, "td",
|
|
951
|
+
i0.ɵɵelementStart(0, "tr")(1, "td", 196);
|
|
925
952
|
i0.ɵɵtext(2);
|
|
926
953
|
i0.ɵɵpipe(3, "date");
|
|
927
954
|
i0.ɵɵelementEnd();
|
|
928
|
-
i0.ɵɵelementStart(4, "td",
|
|
955
|
+
i0.ɵɵelementStart(4, "td", 197);
|
|
929
956
|
i0.ɵɵtext(5);
|
|
930
957
|
i0.ɵɵelementEnd();
|
|
931
|
-
i0.ɵɵconditionalCreate(6, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template, 2, 1, "td",
|
|
932
|
-
i0.ɵɵconditionalCreate(7, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template, 2, 1, "td",
|
|
933
|
-
i0.ɵɵconditionalCreate(8, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template, 5, 3, "td",
|
|
934
|
-
i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template, 2, 1, "td",
|
|
935
|
-
i0.ɵɵelementStart(10, "td",
|
|
958
|
+
i0.ɵɵconditionalCreate(6, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_6_Template, 2, 1, "td", 198);
|
|
959
|
+
i0.ɵɵconditionalCreate(7, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_7_Template, 2, 1, "td", 199);
|
|
960
|
+
i0.ɵɵconditionalCreate(8, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_8_Template, 5, 3, "td", 200);
|
|
961
|
+
i0.ɵɵconditionalCreate(9, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_For_22_Conditional_9_Template, 2, 1, "td", 201);
|
|
962
|
+
i0.ɵɵelementStart(10, "td", 202);
|
|
936
963
|
i0.ɵɵtext(11);
|
|
937
964
|
i0.ɵɵelementEnd();
|
|
938
|
-
i0.ɵɵelementStart(12, "td",
|
|
965
|
+
i0.ɵɵelementStart(12, "td", 203);
|
|
939
966
|
i0.ɵɵtext(13);
|
|
940
967
|
i0.ɵɵelementEnd()();
|
|
941
968
|
} if (rf & 2) {
|
|
942
|
-
const
|
|
969
|
+
const day_r18 = ctx.$implicit;
|
|
943
970
|
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
944
971
|
i0.ɵɵadvance(2);
|
|
945
|
-
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8,
|
|
972
|
+
i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(3, 8, day_r18.date, "mediumDate"));
|
|
946
973
|
i0.ɵɵadvance(3);
|
|
947
|
-
i0.ɵɵtextInterpolate(
|
|
974
|
+
i0.ɵɵtextInterpolate(day_r18.runCount);
|
|
948
975
|
i0.ɵɵadvance();
|
|
949
976
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 6 : -1);
|
|
950
977
|
i0.ɵɵadvance();
|
|
@@ -954,16 +981,16 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
|
|
|
954
981
|
i0.ɵɵadvance();
|
|
955
982
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 9 : -1);
|
|
956
983
|
i0.ɵɵadvance(2);
|
|
957
|
-
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(
|
|
984
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(day_r18.avgDuration));
|
|
958
985
|
i0.ɵɵadvance(2);
|
|
959
|
-
i0.ɵɵtextInterpolate(ctx_r0.formatCost(
|
|
986
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatCost(day_r18.avgCost));
|
|
960
987
|
} }
|
|
961
988
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
962
|
-
i0.ɵɵelementStart(0, "div",
|
|
963
|
-
i0.ɵɵelement(2, "i",
|
|
989
|
+
i0.ɵɵelementStart(0, "div", 181)(1, "h3");
|
|
990
|
+
i0.ɵɵelement(2, "i", 193);
|
|
964
991
|
i0.ɵɵtext(3, " Daily Performance");
|
|
965
992
|
i0.ɵɵelementEnd();
|
|
966
|
-
i0.ɵɵelementStart(4, "div",
|
|
993
|
+
i0.ɵɵelementStart(4, "div", 194)(5, "table", 195)(6, "thead")(7, "tr")(8, "th");
|
|
967
994
|
i0.ɵɵtext(9, "Date");
|
|
968
995
|
i0.ɵɵelementEnd();
|
|
969
996
|
i0.ɵɵelementStart(10, "th");
|
|
@@ -996,134 +1023,134 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18
|
|
|
996
1023
|
i0.ɵɵrepeater(ctx_r0.historyData);
|
|
997
1024
|
} }
|
|
998
1025
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template(rf, ctx) { if (rf & 1) {
|
|
999
|
-
i0.ɵɵelementStart(0, "span",
|
|
1026
|
+
i0.ɵɵelementStart(0, "span", 218);
|
|
1000
1027
|
i0.ɵɵtext(1);
|
|
1001
1028
|
i0.ɵɵelementEnd();
|
|
1002
1029
|
} if (rf & 2) {
|
|
1003
|
-
const
|
|
1030
|
+
const tag_r21 = ctx.$implicit;
|
|
1004
1031
|
i0.ɵɵadvance();
|
|
1005
|
-
i0.ɵɵtextInterpolate(
|
|
1032
|
+
i0.ɵɵtextInterpolate(tag_r21);
|
|
1006
1033
|
} }
|
|
1007
1034
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template(rf, ctx) { if (rf & 1) {
|
|
1008
|
-
i0.ɵɵelementStart(0, "span",
|
|
1035
|
+
i0.ɵɵelementStart(0, "span", 219);
|
|
1009
1036
|
i0.ɵɵtext(1);
|
|
1010
1037
|
i0.ɵɵelementEnd();
|
|
1011
1038
|
} if (rf & 2) {
|
|
1012
|
-
const
|
|
1039
|
+
const suite_r20 = i0.ɵɵnextContext(2).$implicit;
|
|
1013
1040
|
i0.ɵɵadvance();
|
|
1014
|
-
i0.ɵɵtextInterpolate1("+",
|
|
1041
|
+
i0.ɵɵtextInterpolate1("+", suite_r20.tags.length - 3);
|
|
1015
1042
|
} }
|
|
1016
1043
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
1017
|
-
i0.ɵɵelementStart(0, "div",
|
|
1018
|
-
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template, 2, 1, "span",
|
|
1019
|
-
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template, 2, 1, "span",
|
|
1044
|
+
i0.ɵɵelementStart(0, "div", 212);
|
|
1045
|
+
i0.ɵɵrepeaterCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_For_2_Template, 2, 1, "span", 218, i0.ɵɵrepeaterTrackByIdentity);
|
|
1046
|
+
i0.ɵɵconditionalCreate(3, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Conditional_3_Template, 2, 1, "span", 219);
|
|
1020
1047
|
i0.ɵɵelementEnd();
|
|
1021
1048
|
} if (rf & 2) {
|
|
1022
|
-
const
|
|
1049
|
+
const suite_r20 = i0.ɵɵnextContext().$implicit;
|
|
1023
1050
|
i0.ɵɵadvance();
|
|
1024
|
-
i0.ɵɵrepeater(
|
|
1051
|
+
i0.ɵɵrepeater(suite_r20.tags.slice(0, 3));
|
|
1025
1052
|
i0.ɵɵadvance(2);
|
|
1026
|
-
i0.ɵɵconditional(
|
|
1053
|
+
i0.ɵɵconditional(suite_r20.tags.length > 3 ? 3 : -1);
|
|
1027
1054
|
} }
|
|
1028
1055
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template(rf, ctx) { if (rf & 1) {
|
|
1029
|
-
i0.ɵɵelementStart(0, "div",
|
|
1056
|
+
i0.ɵɵelementStart(0, "div", 214)(1, "span", 220);
|
|
1030
1057
|
i0.ɵɵtext(2);
|
|
1031
1058
|
i0.ɵɵelementEnd();
|
|
1032
|
-
i0.ɵɵelementStart(3, "span",
|
|
1059
|
+
i0.ɵɵelementStart(3, "span", 216);
|
|
1033
1060
|
i0.ɵɵtext(4, "Pass Rate");
|
|
1034
1061
|
i0.ɵɵelementEnd()();
|
|
1035
1062
|
} if (rf & 2) {
|
|
1036
|
-
const
|
|
1063
|
+
const suite_r20 = i0.ɵɵnextContext().$implicit;
|
|
1037
1064
|
i0.ɵɵadvance();
|
|
1038
|
-
i0.ɵɵclassProp("high",
|
|
1065
|
+
i0.ɵɵclassProp("high", suite_r20.passRate >= 80)("low", suite_r20.passRate < 50);
|
|
1039
1066
|
i0.ɵɵadvance();
|
|
1040
|
-
i0.ɵɵtextInterpolate1(" ",
|
|
1067
|
+
i0.ɵɵtextInterpolate1(" ", suite_r20.passRate.toFixed(1), "% ");
|
|
1041
1068
|
} }
|
|
1042
1069
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template(rf, ctx) { if (rf & 1) {
|
|
1043
|
-
i0.ɵɵelementStart(0, "div",
|
|
1070
|
+
i0.ɵɵelementStart(0, "div", 214)(1, "span", 215);
|
|
1044
1071
|
i0.ɵɵtext(2);
|
|
1045
1072
|
i0.ɵɵelementEnd();
|
|
1046
|
-
i0.ɵɵelementStart(3, "span",
|
|
1073
|
+
i0.ɵɵelementStart(3, "span", 216);
|
|
1047
1074
|
i0.ɵɵtext(4, "Avg Score");
|
|
1048
1075
|
i0.ɵɵelementEnd()();
|
|
1049
1076
|
} if (rf & 2) {
|
|
1050
|
-
const
|
|
1077
|
+
const suite_r20 = i0.ɵɵnextContext().$implicit;
|
|
1051
1078
|
i0.ɵɵadvance(2);
|
|
1052
|
-
i0.ɵɵtextInterpolate1("", (
|
|
1079
|
+
i0.ɵɵtextInterpolate1("", (suite_r20.avgScore * 100).toFixed(1), "%");
|
|
1053
1080
|
} }
|
|
1054
1081
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
1055
|
-
i0.ɵɵelementStart(0, "div",
|
|
1082
|
+
i0.ɵɵelementStart(0, "div", 214)(1, "span", 215);
|
|
1056
1083
|
i0.ɵɵtext(2);
|
|
1057
1084
|
i0.ɵɵelementEnd();
|
|
1058
|
-
i0.ɵɵelementStart(3, "span",
|
|
1085
|
+
i0.ɵɵelementStart(3, "span", 216);
|
|
1059
1086
|
i0.ɵɵtext(4, "Last Run");
|
|
1060
1087
|
i0.ɵɵelementEnd()();
|
|
1061
1088
|
} if (rf & 2) {
|
|
1062
|
-
const
|
|
1089
|
+
const suite_r20 = i0.ɵɵnextContext().$implicit;
|
|
1063
1090
|
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
1064
1091
|
i0.ɵɵadvance(2);
|
|
1065
|
-
i0.ɵɵtextInterpolate(ctx_r0.getRelativeTime(
|
|
1092
|
+
i0.ɵɵtextInterpolate(ctx_r0.getRelativeTime(suite_r20.lastRun));
|
|
1066
1093
|
} }
|
|
1067
1094
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template(rf, ctx) { if (rf & 1) {
|
|
1068
|
-
const
|
|
1069
|
-
i0.ɵɵelementStart(0, "div",
|
|
1070
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template_div_click_0_listener() { const
|
|
1071
|
-
i0.ɵɵelementStart(1, "div",
|
|
1095
|
+
const _r19 = i0.ɵɵgetCurrentView();
|
|
1096
|
+
i0.ɵɵelementStart(0, "div", 209);
|
|
1097
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template_div_click_0_listener() { const suite_r20 = i0.ɵɵrestoreView(_r19).$implicit; const ctx_r0 = i0.ɵɵnextContext(4); return i0.ɵɵresetView(ctx_r0.openSuiteFromHistory(suite_r20.suiteId)); });
|
|
1098
|
+
i0.ɵɵelementStart(1, "div", 210)(2, "div", 211);
|
|
1072
1099
|
i0.ɵɵtext(3);
|
|
1073
1100
|
i0.ɵɵelementEnd();
|
|
1074
|
-
i0.ɵɵconditionalCreate(4, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template, 4, 1, "div",
|
|
1101
|
+
i0.ɵɵconditionalCreate(4, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_4_Template, 4, 1, "div", 212);
|
|
1075
1102
|
i0.ɵɵelementEnd();
|
|
1076
|
-
i0.ɵɵelementStart(5, "div",
|
|
1103
|
+
i0.ɵɵelementStart(5, "div", 213)(6, "div", 214)(7, "span", 215);
|
|
1077
1104
|
i0.ɵɵtext(8);
|
|
1078
1105
|
i0.ɵɵelementEnd();
|
|
1079
|
-
i0.ɵɵelementStart(9, "span",
|
|
1106
|
+
i0.ɵɵelementStart(9, "span", 216);
|
|
1080
1107
|
i0.ɵɵtext(10, "Runs");
|
|
1081
1108
|
i0.ɵɵelementEnd()();
|
|
1082
|
-
i0.ɵɵconditionalCreate(11, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template, 5, 5, "div",
|
|
1083
|
-
i0.ɵɵconditionalCreate(12, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template, 5, 1, "div",
|
|
1084
|
-
i0.ɵɵelementStart(13, "div",
|
|
1109
|
+
i0.ɵɵconditionalCreate(11, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_11_Template, 5, 5, "div", 214);
|
|
1110
|
+
i0.ɵɵconditionalCreate(12, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_12_Template, 5, 1, "div", 214);
|
|
1111
|
+
i0.ɵɵelementStart(13, "div", 214)(14, "span", 215);
|
|
1085
1112
|
i0.ɵɵtext(15);
|
|
1086
1113
|
i0.ɵɵelementEnd();
|
|
1087
|
-
i0.ɵɵelementStart(16, "span",
|
|
1114
|
+
i0.ɵɵelementStart(16, "span", 216);
|
|
1088
1115
|
i0.ɵɵtext(17, "Avg Duration");
|
|
1089
1116
|
i0.ɵɵelementEnd()();
|
|
1090
|
-
i0.ɵɵelementStart(18, "div",
|
|
1117
|
+
i0.ɵɵelementStart(18, "div", 214)(19, "span", 215);
|
|
1091
1118
|
i0.ɵɵtext(20);
|
|
1092
1119
|
i0.ɵɵelementEnd();
|
|
1093
|
-
i0.ɵɵelementStart(21, "span",
|
|
1120
|
+
i0.ɵɵelementStart(21, "span", 216);
|
|
1094
1121
|
i0.ɵɵtext(22, "Avg Cost");
|
|
1095
1122
|
i0.ɵɵelementEnd()();
|
|
1096
|
-
i0.ɵɵconditionalCreate(23, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template, 5, 1, "div",
|
|
1123
|
+
i0.ɵɵconditionalCreate(23, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Conditional_23_Template, 5, 1, "div", 214);
|
|
1097
1124
|
i0.ɵɵelementEnd();
|
|
1098
|
-
i0.ɵɵelement(24, "i",
|
|
1125
|
+
i0.ɵɵelement(24, "i", 217);
|
|
1099
1126
|
i0.ɵɵelementEnd();
|
|
1100
1127
|
} if (rf & 2) {
|
|
1101
|
-
const
|
|
1128
|
+
const suite_r20 = ctx.$implicit;
|
|
1102
1129
|
const ctx_r0 = i0.ɵɵnextContext(4);
|
|
1103
1130
|
i0.ɵɵadvance(3);
|
|
1104
|
-
i0.ɵɵtextInterpolate(
|
|
1131
|
+
i0.ɵɵtextInterpolate(suite_r20.suiteName);
|
|
1105
1132
|
i0.ɵɵadvance();
|
|
1106
|
-
i0.ɵɵconditional(
|
|
1133
|
+
i0.ɵɵconditional(suite_r20.tags.length > 0 ? 4 : -1);
|
|
1107
1134
|
i0.ɵɵadvance(4);
|
|
1108
|
-
i0.ɵɵtextInterpolate(
|
|
1135
|
+
i0.ɵɵtextInterpolate(suite_r20.totalRuns);
|
|
1109
1136
|
i0.ɵɵadvance(3);
|
|
1110
1137
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showExecution ? 11 : -1);
|
|
1111
1138
|
i0.ɵɵadvance();
|
|
1112
1139
|
i0.ɵɵconditional(ctx_r0.evalPreferences.showAuto ? 12 : -1);
|
|
1113
1140
|
i0.ɵɵadvance(3);
|
|
1114
|
-
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(
|
|
1141
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatDuration(suite_r20.avgDuration));
|
|
1115
1142
|
i0.ɵɵadvance(5);
|
|
1116
|
-
i0.ɵɵtextInterpolate(ctx_r0.formatCost(
|
|
1143
|
+
i0.ɵɵtextInterpolate(ctx_r0.formatCost(suite_r20.avgCost));
|
|
1117
1144
|
i0.ɵɵadvance(3);
|
|
1118
|
-
i0.ɵɵconditional(
|
|
1145
|
+
i0.ɵɵconditional(suite_r20.lastRun ? 23 : -1);
|
|
1119
1146
|
} }
|
|
1120
1147
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template(rf, ctx) { if (rf & 1) {
|
|
1121
|
-
i0.ɵɵelementStart(0, "div",
|
|
1148
|
+
i0.ɵɵelementStart(0, "div", 181)(1, "h3");
|
|
1122
1149
|
i0.ɵɵelement(2, "i", 33);
|
|
1123
1150
|
i0.ɵɵtext(3, " Performance by Suite");
|
|
1124
1151
|
i0.ɵɵelementEnd();
|
|
1125
|
-
i0.ɵɵelementStart(4, "div",
|
|
1126
|
-
i0.ɵɵrepeaterCreate(5, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template, 25, 8, "div",
|
|
1152
|
+
i0.ɵɵelementStart(4, "div", 207);
|
|
1153
|
+
i0.ɵɵrepeaterCreate(5, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_For_6_Template, 25, 8, "div", 208, i0.ɵɵrepeaterTrackByIdentity);
|
|
1127
1154
|
i0.ɵɵelementEnd()();
|
|
1128
1155
|
} if (rf & 2) {
|
|
1129
1156
|
const ctx_r0 = i0.ɵɵnextContext(3);
|
|
@@ -1131,41 +1158,41 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19
|
|
|
1131
1158
|
i0.ɵɵrepeater(ctx_r0.suitePerformance);
|
|
1132
1159
|
} }
|
|
1133
1160
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template(rf, ctx) { if (rf & 1) {
|
|
1134
|
-
const
|
|
1135
|
-
i0.ɵɵelementStart(0, "mj-empty-state",
|
|
1136
|
-
i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(
|
|
1161
|
+
const _r22 = i0.ɵɵgetCurrentView();
|
|
1162
|
+
i0.ɵɵelementStart(0, "mj-empty-state", 221);
|
|
1163
|
+
i0.ɵɵlistener("Action", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template_mj_empty_state_Action_0_listener() { i0.ɵɵrestoreView(_r22); const ctx_r0 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r0.runTest()); });
|
|
1137
1164
|
i0.ɵɵelementEnd();
|
|
1138
1165
|
} }
|
|
1139
1166
|
function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
1140
|
-
const
|
|
1141
|
-
i0.ɵɵelementStart(0, "div",
|
|
1167
|
+
const _r17 = i0.ɵɵgetCurrentView();
|
|
1168
|
+
i0.ɵɵelementStart(0, "div", 172)(1, "div", 174)(2, "div", 175)(3, "span", 176);
|
|
1142
1169
|
i0.ɵɵtext(4, "Time Range:");
|
|
1143
1170
|
i0.ɵɵelementEnd();
|
|
1144
|
-
i0.ɵɵelementStart(5, "button",
|
|
1145
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(
|
|
1171
|
+
i0.ɵɵelementStart(5, "button", 177);
|
|
1172
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("7d")); });
|
|
1146
1173
|
i0.ɵɵtext(6, "7 Days");
|
|
1147
1174
|
i0.ɵɵelementEnd();
|
|
1148
|
-
i0.ɵɵelementStart(7, "button",
|
|
1149
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(
|
|
1175
|
+
i0.ɵɵelementStart(7, "button", 177);
|
|
1176
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_7_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("30d")); });
|
|
1150
1177
|
i0.ɵɵtext(8, "30 Days");
|
|
1151
1178
|
i0.ɵɵelementEnd();
|
|
1152
|
-
i0.ɵɵelementStart(9, "button",
|
|
1153
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(
|
|
1179
|
+
i0.ɵɵelementStart(9, "button", 177);
|
|
1180
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("90d")); });
|
|
1154
1181
|
i0.ɵɵtext(10, "90 Days");
|
|
1155
1182
|
i0.ɵɵelementEnd();
|
|
1156
|
-
i0.ɵɵelementStart(11, "button",
|
|
1157
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(
|
|
1183
|
+
i0.ɵɵelementStart(11, "button", 177);
|
|
1184
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_11_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.setHistoryTimeRange("all")); });
|
|
1158
1185
|
i0.ɵɵtext(12, "All Time");
|
|
1159
1186
|
i0.ɵɵelementEnd()();
|
|
1160
|
-
i0.ɵɵelementStart(13, "div",
|
|
1161
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_14_listener() { i0.ɵɵrestoreView(
|
|
1162
|
-
i0.ɵɵelement(15, "i",
|
|
1187
|
+
i0.ɵɵelementStart(13, "div", 178)(14, "button", 23);
|
|
1188
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template_button_click_14_listener() { i0.ɵɵrestoreView(_r17); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.exportHistoryToCSV()); });
|
|
1189
|
+
i0.ɵɵelement(15, "i", 179);
|
|
1163
1190
|
i0.ɵɵtext(16, " Export CSV ");
|
|
1164
1191
|
i0.ɵɵelementEnd()()();
|
|
1165
|
-
i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template, 27, 5, "div",
|
|
1166
|
-
i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template, 23, 4, "div",
|
|
1167
|
-
i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template, 7, 0, "div",
|
|
1168
|
-
i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template, 1, 0, "mj-empty-state",
|
|
1192
|
+
i0.ɵɵconditionalCreate(17, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_17_Template, 27, 5, "div", 180);
|
|
1193
|
+
i0.ɵɵconditionalCreate(18, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_18_Template, 23, 4, "div", 181);
|
|
1194
|
+
i0.ɵɵconditionalCreate(19, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_19_Template, 7, 0, "div", 181);
|
|
1195
|
+
i0.ɵɵconditionalCreate(20, MJTestFormComponentExtended_Conditional_67_Conditional_2_Conditional_20_Template, 1, 0, "mj-empty-state", 182);
|
|
1169
1196
|
i0.ɵɵelementEnd();
|
|
1170
1197
|
} if (rf & 2) {
|
|
1171
1198
|
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
@@ -1190,8 +1217,8 @@ function MJTestFormComponentExtended_Conditional_67_Conditional_2_Template(rf, c
|
|
|
1190
1217
|
} }
|
|
1191
1218
|
function MJTestFormComponentExtended_Conditional_67_Template(rf, ctx) { if (rf & 1) {
|
|
1192
1219
|
i0.ɵɵelementStart(0, "div", 40);
|
|
1193
|
-
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_1_Template, 2, 0, "div",
|
|
1194
|
-
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_67_Conditional_2_Template, 21, 13, "div",
|
|
1220
|
+
i0.ɵɵconditionalCreate(1, MJTestFormComponentExtended_Conditional_67_Conditional_1_Template, 2, 0, "div", 125);
|
|
1221
|
+
i0.ɵɵconditionalCreate(2, MJTestFormComponentExtended_Conditional_67_Conditional_2_Template, 21, 13, "div", 172);
|
|
1195
1222
|
i0.ɵɵelementEnd();
|
|
1196
1223
|
} if (rf & 2) {
|
|
1197
1224
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -1201,45 +1228,45 @@ function MJTestFormComponentExtended_Conditional_67_Template(rf, ctx) { if (rf &
|
|
|
1201
1228
|
i0.ɵɵconditional(!ctx_r0.loadingHistory && ctx_r0.historyLoaded ? 2 : -1);
|
|
1202
1229
|
} }
|
|
1203
1230
|
function MJTestFormComponentExtended_Conditional_70_Template(rf, ctx) { if (rf & 1) {
|
|
1204
|
-
const
|
|
1205
|
-
i0.ɵɵelementStart(0, "div", 43)(1, "div",
|
|
1231
|
+
const _r23 = i0.ɵɵgetCurrentView();
|
|
1232
|
+
i0.ɵɵelementStart(0, "div", 43)(1, "div", 222);
|
|
1206
1233
|
i0.ɵɵelement(2, "i", 42);
|
|
1207
1234
|
i0.ɵɵtext(3, " Shortcuts ");
|
|
1208
|
-
i0.ɵɵelementStart(4, "button",
|
|
1209
|
-
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_70_Template_button_click_4_listener() { i0.ɵɵrestoreView(
|
|
1210
|
-
i0.ɵɵelement(5, "i",
|
|
1235
|
+
i0.ɵɵelementStart(4, "button", 223);
|
|
1236
|
+
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Conditional_70_Template_button_click_4_listener() { i0.ɵɵrestoreView(_r23); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.toggleShortcuts()); });
|
|
1237
|
+
i0.ɵɵelement(5, "i", 124);
|
|
1211
1238
|
i0.ɵɵelementEnd()();
|
|
1212
|
-
i0.ɵɵelementStart(6, "div",
|
|
1239
|
+
i0.ɵɵelementStart(6, "div", 224)(7, "div", 225)(8, "span");
|
|
1213
1240
|
i0.ɵɵtext(9, "Save changes");
|
|
1214
1241
|
i0.ɵɵelementEnd();
|
|
1215
|
-
i0.ɵɵelementStart(10, "span",
|
|
1242
|
+
i0.ɵɵelementStart(10, "span", 226)(11, "kbd");
|
|
1216
1243
|
i0.ɵɵtext(12, "Cmd");
|
|
1217
1244
|
i0.ɵɵelementEnd();
|
|
1218
1245
|
i0.ɵɵelementStart(13, "kbd");
|
|
1219
1246
|
i0.ɵɵtext(14, "S");
|
|
1220
1247
|
i0.ɵɵelementEnd()()();
|
|
1221
|
-
i0.ɵɵelementStart(15, "div",
|
|
1248
|
+
i0.ɵɵelementStart(15, "div", 225)(16, "span");
|
|
1222
1249
|
i0.ɵɵtext(17, "Refresh");
|
|
1223
1250
|
i0.ɵɵelementEnd();
|
|
1224
|
-
i0.ɵɵelementStart(18, "span",
|
|
1251
|
+
i0.ɵɵelementStart(18, "span", 226)(19, "kbd");
|
|
1225
1252
|
i0.ɵɵtext(20, "Cmd");
|
|
1226
1253
|
i0.ɵɵelementEnd();
|
|
1227
1254
|
i0.ɵɵelementStart(21, "kbd");
|
|
1228
1255
|
i0.ɵɵtext(22, "R");
|
|
1229
1256
|
i0.ɵɵelementEnd()()();
|
|
1230
|
-
i0.ɵɵelementStart(23, "div",
|
|
1257
|
+
i0.ɵɵelementStart(23, "div", 225)(24, "span");
|
|
1231
1258
|
i0.ɵɵtext(25, "Run Test");
|
|
1232
1259
|
i0.ɵɵelementEnd();
|
|
1233
|
-
i0.ɵɵelementStart(26, "span",
|
|
1260
|
+
i0.ɵɵelementStart(26, "span", 226)(27, "kbd");
|
|
1234
1261
|
i0.ɵɵtext(28, "Cmd");
|
|
1235
1262
|
i0.ɵɵelementEnd();
|
|
1236
1263
|
i0.ɵɵelementStart(29, "kbd");
|
|
1237
1264
|
i0.ɵɵtext(30, "Enter");
|
|
1238
1265
|
i0.ɵɵelementEnd()()();
|
|
1239
|
-
i0.ɵɵelementStart(31, "div",
|
|
1266
|
+
i0.ɵɵelementStart(31, "div", 225)(32, "span");
|
|
1240
1267
|
i0.ɵɵtext(33, "Switch Tabs");
|
|
1241
1268
|
i0.ɵɵelementEnd();
|
|
1242
|
-
i0.ɵɵelementStart(34, "span",
|
|
1269
|
+
i0.ɵɵelementStart(34, "span", 226)(35, "kbd");
|
|
1243
1270
|
i0.ɵɵtext(36, "1");
|
|
1244
1271
|
i0.ɵɵelementEnd();
|
|
1245
1272
|
i0.ɵɵtext(37, "-");
|
|
@@ -1248,11 +1275,11 @@ function MJTestFormComponentExtended_Conditional_70_Template(rf, ctx) { if (rf &
|
|
|
1248
1275
|
i0.ɵɵelementEnd()()()()();
|
|
1249
1276
|
} }
|
|
1250
1277
|
function MJTestFormComponentExtended_Conditional_71_Template(rf, ctx) { if (rf & 1) {
|
|
1251
|
-
const
|
|
1252
|
-
i0.ɵɵelementStart(0, "mj-slide-panel",
|
|
1253
|
-
i0.ɵɵlistener("Closed", function MJTestFormComponentExtended_Conditional_71_Template_mj_slide_panel_Closed_0_listener() { i0.ɵɵrestoreView(
|
|
1254
|
-
i0.ɵɵelementStart(1, "app-test-run-dialog",
|
|
1255
|
-
i0.ɵɵlistener("PanelClose", function MJTestFormComponentExtended_Conditional_71_Template_app_test_run_dialog_PanelClose_1_listener() { i0.ɵɵrestoreView(
|
|
1278
|
+
const _r24 = i0.ɵɵgetCurrentView();
|
|
1279
|
+
i0.ɵɵelementStart(0, "mj-slide-panel", 227);
|
|
1280
|
+
i0.ɵɵlistener("Closed", function MJTestFormComponentExtended_Conditional_71_Template_mj_slide_panel_Closed_0_listener() { i0.ɵɵrestoreView(_r24); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
|
|
1281
|
+
i0.ɵɵelementStart(1, "app-test-run-dialog", 228);
|
|
1282
|
+
i0.ɵɵlistener("PanelClose", function MJTestFormComponentExtended_Conditional_71_Template_app_test_run_dialog_PanelClose_1_listener() { i0.ɵɵrestoreView(_r24); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.OnPanelClosed()); });
|
|
1256
1283
|
i0.ɵɵelementEnd()();
|
|
1257
1284
|
} if (rf & 2) {
|
|
1258
1285
|
const ctx_r0 = i0.ɵɵnextContext();
|
|
@@ -1273,13 +1300,13 @@ function MJTestFormComponentExtended_Conditional_81_Template(rf, ctx) { if (rf &
|
|
|
1273
1300
|
i0.ɵɵtextInterpolate1(" ", ctx_r0.dirtyFieldNames.length, " fields edited ");
|
|
1274
1301
|
} }
|
|
1275
1302
|
function MJTestFormComponentExtended_Conditional_84_Template(rf, ctx) { if (rf & 1) {
|
|
1276
|
-
i0.ɵɵelement(0, "i",
|
|
1303
|
+
i0.ɵɵelement(0, "i", 229);
|
|
1277
1304
|
i0.ɵɵtext(1, " Saving\u2026 ");
|
|
1278
1305
|
} }
|
|
1279
1306
|
function MJTestFormComponentExtended_Conditional_85_Template(rf, ctx) { if (rf & 1) {
|
|
1280
|
-
i0.ɵɵelement(0, "i",
|
|
1307
|
+
i0.ɵɵelement(0, "i", 230);
|
|
1281
1308
|
i0.ɵɵtext(1, " Save changes ");
|
|
1282
|
-
i0.ɵɵelementStart(2, "span",
|
|
1309
|
+
i0.ɵɵelementStart(2, "span", 231)(3, "kbd");
|
|
1283
1310
|
i0.ɵɵtext(4, "\u2318");
|
|
1284
1311
|
i0.ɵɵelementEnd();
|
|
1285
1312
|
i0.ɵɵelementStart(5, "kbd");
|
|
@@ -1293,67 +1320,281 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1293
1320
|
super(...arguments);
|
|
1294
1321
|
this.destroy$ = new Subject();
|
|
1295
1322
|
// UI state
|
|
1296
|
-
this.
|
|
1297
|
-
this.
|
|
1298
|
-
this.
|
|
1299
|
-
this.
|
|
1323
|
+
this.ActiveTab = 'overview';
|
|
1324
|
+
this.Loading = false;
|
|
1325
|
+
this.LoadingRuns = false;
|
|
1326
|
+
this.LoadingSuites = false;
|
|
1300
1327
|
this.error = null;
|
|
1301
|
-
this.
|
|
1302
|
-
this.
|
|
1303
|
-
this.isRefreshing = false;
|
|
1328
|
+
this.TestRunsLoaded = false;
|
|
1329
|
+
this.SuiteTestsLoaded = false;
|
|
1330
|
+
this.isRefreshing = false; // case-violation-ok-legacy-back-compat: an ancestor class already declares the PascalCase name
|
|
1304
1331
|
// Related data
|
|
1305
|
-
this.
|
|
1306
|
-
this.
|
|
1332
|
+
this.TestRuns = [];
|
|
1333
|
+
this.SuiteTests = [];
|
|
1307
1334
|
// Human feedback map: testRunId -> feedback entity
|
|
1308
|
-
this.
|
|
1335
|
+
this.FeedbackMap = new Map();
|
|
1309
1336
|
// History tab data
|
|
1310
|
-
this.
|
|
1311
|
-
this.
|
|
1312
|
-
this.
|
|
1313
|
-
this.
|
|
1314
|
-
this.
|
|
1315
|
-
this.
|
|
1316
|
-
this.
|
|
1337
|
+
this.HistoryLoaded = false;
|
|
1338
|
+
this.LoadingHistory = false;
|
|
1339
|
+
this.HistoryTimeRange = '30d';
|
|
1340
|
+
this.HistoryData = [];
|
|
1341
|
+
this.SuitePerformance = [];
|
|
1342
|
+
this.UniqueTags = [];
|
|
1343
|
+
this.SelectedTagFilter = null;
|
|
1317
1344
|
// Parsed JSON fields
|
|
1318
|
-
this.
|
|
1345
|
+
this.ParsedData = {};
|
|
1319
1346
|
// Active JSON view
|
|
1320
|
-
this.
|
|
1347
|
+
this.ActiveJsonView = 'input';
|
|
1321
1348
|
// Code editor configuration
|
|
1322
|
-
this.
|
|
1349
|
+
this.JsonToolbar = createCopyOnlyToolbar();
|
|
1323
1350
|
// Keyboard shortcuts
|
|
1324
|
-
this.
|
|
1325
|
-
this.
|
|
1351
|
+
this.KeyboardShortcutsEnabled = true;
|
|
1352
|
+
this.ShowShortcuts = false;
|
|
1326
1353
|
this.shortcutsSettingEntity = null;
|
|
1327
1354
|
// Evaluation preferences
|
|
1328
|
-
this.
|
|
1355
|
+
this.EvalPreferences = { showExecution: true, showHuman: true, showAuto: false };
|
|
1329
1356
|
// Service injections
|
|
1330
1357
|
this.navigationService = inject(NavigationService);
|
|
1331
|
-
this.
|
|
1358
|
+
this.TestingDialogService = inject(TestingDialogService);
|
|
1332
1359
|
this.evalPrefsService = inject(EvaluationPreferencesService);
|
|
1333
1360
|
this.viewContainerRef = inject(ViewContainerRef);
|
|
1334
1361
|
this.appManager = inject(ApplicationManager);
|
|
1335
1362
|
this.confirmService = inject(MJConfirmService);
|
|
1336
1363
|
// Edit state
|
|
1337
|
-
this.
|
|
1338
|
-
this.
|
|
1339
|
-
this.
|
|
1340
|
-
this.
|
|
1364
|
+
this.IsSaving = false;
|
|
1365
|
+
this.TestTypeOptions = [];
|
|
1366
|
+
this.RubricOptions = [];
|
|
1367
|
+
this.TagDraft = '';
|
|
1368
|
+
this.StatusOptions = ['Active', 'Pending', 'Disabled'];
|
|
1369
|
+
}
|
|
1370
|
+
/** @deprecated Use {@link ActiveTab}. */
|
|
1371
|
+
get activeTab() {
|
|
1372
|
+
return this.ActiveTab;
|
|
1373
|
+
}
|
|
1374
|
+
/** @deprecated Use {@link ActiveTab}. */
|
|
1375
|
+
set activeTab(value) {
|
|
1376
|
+
this.ActiveTab = value;
|
|
1377
|
+
}
|
|
1378
|
+
/** @deprecated Use {@link Loading}. */
|
|
1379
|
+
get loading() {
|
|
1380
|
+
return this.Loading;
|
|
1381
|
+
}
|
|
1382
|
+
/** @deprecated Use {@link Loading}. */
|
|
1383
|
+
set loading(value) {
|
|
1384
|
+
this.Loading = value;
|
|
1385
|
+
}
|
|
1386
|
+
/** @deprecated Use {@link LoadingRuns}. */
|
|
1387
|
+
get loadingRuns() {
|
|
1388
|
+
return this.LoadingRuns;
|
|
1389
|
+
}
|
|
1390
|
+
/** @deprecated Use {@link LoadingRuns}. */
|
|
1391
|
+
set loadingRuns(value) {
|
|
1392
|
+
this.LoadingRuns = value;
|
|
1393
|
+
}
|
|
1394
|
+
/** @deprecated Use {@link LoadingSuites}. */
|
|
1395
|
+
get loadingSuites() {
|
|
1396
|
+
return this.LoadingSuites;
|
|
1397
|
+
}
|
|
1398
|
+
/** @deprecated Use {@link LoadingSuites}. */
|
|
1399
|
+
set loadingSuites(value) {
|
|
1400
|
+
this.LoadingSuites = value;
|
|
1401
|
+
}
|
|
1402
|
+
/** @deprecated Use {@link TestRunsLoaded}. */
|
|
1403
|
+
get testRunsLoaded() {
|
|
1404
|
+
return this.TestRunsLoaded;
|
|
1405
|
+
}
|
|
1406
|
+
/** @deprecated Use {@link TestRunsLoaded}. */
|
|
1407
|
+
set testRunsLoaded(value) {
|
|
1408
|
+
this.TestRunsLoaded = value;
|
|
1409
|
+
}
|
|
1410
|
+
/** @deprecated Use {@link SuiteTestsLoaded}. */
|
|
1411
|
+
get suiteTestsLoaded() {
|
|
1412
|
+
return this.SuiteTestsLoaded;
|
|
1413
|
+
}
|
|
1414
|
+
/** @deprecated Use {@link SuiteTestsLoaded}. */
|
|
1415
|
+
set suiteTestsLoaded(value) {
|
|
1416
|
+
this.SuiteTestsLoaded = value;
|
|
1417
|
+
}
|
|
1418
|
+
/** @deprecated Use {@link TestRuns}. */
|
|
1419
|
+
get testRuns() {
|
|
1420
|
+
return this.TestRuns;
|
|
1421
|
+
}
|
|
1422
|
+
/** @deprecated Use {@link TestRuns}. */
|
|
1423
|
+
set testRuns(value) {
|
|
1424
|
+
this.TestRuns = value;
|
|
1425
|
+
}
|
|
1426
|
+
/** @deprecated Use {@link SuiteTests}. */
|
|
1427
|
+
get suiteTests() {
|
|
1428
|
+
return this.SuiteTests;
|
|
1429
|
+
}
|
|
1430
|
+
/** @deprecated Use {@link SuiteTests}. */
|
|
1431
|
+
set suiteTests(value) {
|
|
1432
|
+
this.SuiteTests = value;
|
|
1433
|
+
}
|
|
1434
|
+
/** @deprecated Use {@link FeedbackMap}. */
|
|
1435
|
+
get feedbackMap() {
|
|
1436
|
+
return this.FeedbackMap;
|
|
1437
|
+
}
|
|
1438
|
+
/** @deprecated Use {@link FeedbackMap}. */
|
|
1439
|
+
set feedbackMap(value) {
|
|
1440
|
+
this.FeedbackMap = value;
|
|
1441
|
+
}
|
|
1442
|
+
/** @deprecated Use {@link HistoryLoaded}. */
|
|
1443
|
+
get historyLoaded() {
|
|
1444
|
+
return this.HistoryLoaded;
|
|
1445
|
+
}
|
|
1446
|
+
/** @deprecated Use {@link HistoryLoaded}. */
|
|
1447
|
+
set historyLoaded(value) {
|
|
1448
|
+
this.HistoryLoaded = value;
|
|
1449
|
+
}
|
|
1450
|
+
/** @deprecated Use {@link LoadingHistory}. */
|
|
1451
|
+
get loadingHistory() {
|
|
1452
|
+
return this.LoadingHistory;
|
|
1341
1453
|
}
|
|
1454
|
+
/** @deprecated Use {@link LoadingHistory}. */
|
|
1455
|
+
set loadingHistory(value) {
|
|
1456
|
+
this.LoadingHistory = value;
|
|
1457
|
+
}
|
|
1458
|
+
/** @deprecated Use {@link HistoryTimeRange}. */
|
|
1459
|
+
get historyTimeRange() {
|
|
1460
|
+
return this.HistoryTimeRange;
|
|
1461
|
+
}
|
|
1462
|
+
/** @deprecated Use {@link HistoryTimeRange}. */
|
|
1463
|
+
set historyTimeRange(value) {
|
|
1464
|
+
this.HistoryTimeRange = value;
|
|
1465
|
+
}
|
|
1466
|
+
/** @deprecated Use {@link HistoryData}. */
|
|
1467
|
+
get historyData() {
|
|
1468
|
+
return this.HistoryData;
|
|
1469
|
+
}
|
|
1470
|
+
/** @deprecated Use {@link HistoryData}. */
|
|
1471
|
+
set historyData(value) {
|
|
1472
|
+
this.HistoryData = value;
|
|
1473
|
+
}
|
|
1474
|
+
/** @deprecated Use {@link SuitePerformance}. */
|
|
1475
|
+
get suitePerformance() {
|
|
1476
|
+
return this.SuitePerformance;
|
|
1477
|
+
}
|
|
1478
|
+
/** @deprecated Use {@link SuitePerformance}. */
|
|
1479
|
+
set suitePerformance(value) {
|
|
1480
|
+
this.SuitePerformance = value;
|
|
1481
|
+
}
|
|
1482
|
+
/** @deprecated Use {@link UniqueTags}. */
|
|
1483
|
+
get uniqueTags() {
|
|
1484
|
+
return this.UniqueTags;
|
|
1485
|
+
}
|
|
1486
|
+
/** @deprecated Use {@link UniqueTags}. */
|
|
1487
|
+
set uniqueTags(value) {
|
|
1488
|
+
this.UniqueTags = value;
|
|
1489
|
+
}
|
|
1490
|
+
/** @deprecated Use {@link SelectedTagFilter}. */
|
|
1491
|
+
get selectedTagFilter() {
|
|
1492
|
+
return this.SelectedTagFilter;
|
|
1493
|
+
}
|
|
1494
|
+
/** @deprecated Use {@link SelectedTagFilter}. */
|
|
1495
|
+
set selectedTagFilter(value) {
|
|
1496
|
+
this.SelectedTagFilter = value;
|
|
1497
|
+
}
|
|
1498
|
+
/** @deprecated Use {@link ParsedData}. */
|
|
1499
|
+
get parsedData() {
|
|
1500
|
+
return this.ParsedData;
|
|
1501
|
+
}
|
|
1502
|
+
/** @deprecated Use {@link ParsedData}. */
|
|
1503
|
+
set parsedData(value) {
|
|
1504
|
+
this.ParsedData = value;
|
|
1505
|
+
}
|
|
1506
|
+
/** @deprecated Use {@link ActiveJsonView}. */
|
|
1507
|
+
get activeJsonView() {
|
|
1508
|
+
return this.ActiveJsonView;
|
|
1509
|
+
}
|
|
1510
|
+
/** @deprecated Use {@link ActiveJsonView}. */
|
|
1511
|
+
set activeJsonView(value) {
|
|
1512
|
+
this.ActiveJsonView = value;
|
|
1513
|
+
}
|
|
1514
|
+
/** @deprecated Use {@link JsonToolbar}. */
|
|
1515
|
+
get jsonToolbar() {
|
|
1516
|
+
return this.JsonToolbar;
|
|
1517
|
+
}
|
|
1518
|
+
/** @deprecated Use {@link JsonToolbar}. */
|
|
1519
|
+
set jsonToolbar(value) {
|
|
1520
|
+
this.JsonToolbar = value;
|
|
1521
|
+
}
|
|
1522
|
+
/** @deprecated Use {@link KeyboardShortcutsEnabled}. */
|
|
1523
|
+
get keyboardShortcutsEnabled() {
|
|
1524
|
+
return this.KeyboardShortcutsEnabled;
|
|
1525
|
+
}
|
|
1526
|
+
/** @deprecated Use {@link KeyboardShortcutsEnabled}. */
|
|
1527
|
+
set keyboardShortcutsEnabled(value) {
|
|
1528
|
+
this.KeyboardShortcutsEnabled = value;
|
|
1529
|
+
}
|
|
1530
|
+
/** @deprecated Use {@link ShowShortcuts}. */
|
|
1531
|
+
get showShortcuts() {
|
|
1532
|
+
return this.ShowShortcuts;
|
|
1533
|
+
}
|
|
1534
|
+
/** @deprecated Use {@link ShowShortcuts}. */
|
|
1535
|
+
set showShortcuts(value) {
|
|
1536
|
+
this.ShowShortcuts = value;
|
|
1537
|
+
} // Hidden by default
|
|
1342
1538
|
get metadata() { return this.ProviderToUse; }
|
|
1539
|
+
/** @deprecated Use {@link EvalPreferences}. */
|
|
1540
|
+
get evalPreferences() {
|
|
1541
|
+
return this.EvalPreferences;
|
|
1542
|
+
}
|
|
1543
|
+
/** @deprecated Use {@link EvalPreferences}. */
|
|
1544
|
+
set evalPreferences(value) {
|
|
1545
|
+
this.EvalPreferences = value;
|
|
1546
|
+
}
|
|
1547
|
+
/** @deprecated Use {@link TestingDialogService}. */
|
|
1548
|
+
get testingDialogService() {
|
|
1549
|
+
return this.TestingDialogService;
|
|
1550
|
+
}
|
|
1551
|
+
/** @deprecated Use {@link TestingDialogService}. */
|
|
1552
|
+
set testingDialogService(value) {
|
|
1553
|
+
this.TestingDialogService = value;
|
|
1554
|
+
}
|
|
1555
|
+
/** @deprecated Use {@link IsSaving}. */
|
|
1556
|
+
get isSaving() {
|
|
1557
|
+
return this.IsSaving;
|
|
1558
|
+
}
|
|
1559
|
+
/** @deprecated Use {@link IsSaving}. */
|
|
1560
|
+
set isSaving(value) {
|
|
1561
|
+
this.IsSaving = value;
|
|
1562
|
+
}
|
|
1563
|
+
/** @deprecated Use {@link TestTypeOptions}. */
|
|
1564
|
+
get testTypeOptions() {
|
|
1565
|
+
return this.TestTypeOptions;
|
|
1566
|
+
}
|
|
1567
|
+
/** @deprecated Use {@link TestTypeOptions}. */
|
|
1568
|
+
set testTypeOptions(value) {
|
|
1569
|
+
this.TestTypeOptions = value;
|
|
1570
|
+
}
|
|
1571
|
+
/** @deprecated Use {@link TagDraft}. */
|
|
1572
|
+
get tagDraft() {
|
|
1573
|
+
return this.TagDraft;
|
|
1574
|
+
}
|
|
1575
|
+
/** @deprecated Use {@link TagDraft}. */
|
|
1576
|
+
set tagDraft(value) {
|
|
1577
|
+
this.TagDraft = value;
|
|
1578
|
+
}
|
|
1579
|
+
/** @deprecated Use {@link StatusOptions}. */
|
|
1580
|
+
get statusOptions() {
|
|
1581
|
+
return this.StatusOptions;
|
|
1582
|
+
}
|
|
1343
1583
|
async ngOnInit() {
|
|
1344
1584
|
await super.ngOnInit();
|
|
1345
1585
|
this.loadShortcutsSetting();
|
|
1346
1586
|
// Fire-and-forget: test type list for the edit form
|
|
1347
1587
|
this.loadTestTypeOptions();
|
|
1588
|
+
void this.loadRubricOptions();
|
|
1348
1589
|
// Subscribe to evaluation preferences
|
|
1349
1590
|
this.evalPrefsService.preferences$
|
|
1350
1591
|
.pipe(takeUntil(this.destroy$))
|
|
1351
1592
|
.subscribe(prefs => {
|
|
1352
|
-
this.
|
|
1593
|
+
this.EvalPreferences = prefs;
|
|
1353
1594
|
this.cdr.markForCheck();
|
|
1354
1595
|
});
|
|
1355
1596
|
// Subscribe to panel state changes so the slide panel renders in this form
|
|
1356
|
-
this.
|
|
1597
|
+
this.TestingDialogService.PanelStateChanged$
|
|
1357
1598
|
.pipe(takeUntil(this.destroy$))
|
|
1358
1599
|
.subscribe(() => {
|
|
1359
1600
|
this.cdr.detectChanges();
|
|
@@ -1368,52 +1609,52 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1368
1609
|
}
|
|
1369
1610
|
// Keyboard shortcuts
|
|
1370
1611
|
handleKeyboardShortcut(event) {
|
|
1371
|
-
if (!this.
|
|
1612
|
+
if (!this.KeyboardShortcutsEnabled)
|
|
1372
1613
|
return;
|
|
1373
1614
|
// Cmd/Ctrl + S: Save (if dirty)
|
|
1374
1615
|
if ((event.metaKey || event.ctrlKey) && event.key === 's' && !event.shiftKey) {
|
|
1375
|
-
if (this.
|
|
1616
|
+
if (this.IsDirty) {
|
|
1376
1617
|
event.preventDefault();
|
|
1377
|
-
this.
|
|
1618
|
+
this.SaveChanges();
|
|
1378
1619
|
}
|
|
1379
1620
|
return;
|
|
1380
1621
|
}
|
|
1381
1622
|
// Cmd/Ctrl + R: Refresh
|
|
1382
1623
|
if ((event.metaKey || event.ctrlKey) && event.key === 'r' && !event.shiftKey) {
|
|
1383
1624
|
event.preventDefault();
|
|
1384
|
-
this.
|
|
1625
|
+
this.Refresh();
|
|
1385
1626
|
return;
|
|
1386
1627
|
}
|
|
1387
1628
|
// Cmd/Ctrl + Enter: Run test
|
|
1388
1629
|
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
|
1389
1630
|
event.preventDefault();
|
|
1390
|
-
this.
|
|
1631
|
+
this.RunTest();
|
|
1391
1632
|
return;
|
|
1392
1633
|
}
|
|
1393
1634
|
// Number keys for tabs (1-5) — skip when typing into form inputs
|
|
1394
1635
|
if (!event.metaKey && !event.ctrlKey && !event.altKey && !this.isTextInputFocused()) {
|
|
1395
1636
|
switch (event.key) {
|
|
1396
1637
|
case '1':
|
|
1397
|
-
this.
|
|
1638
|
+
this.ChangeTab('overview');
|
|
1398
1639
|
break;
|
|
1399
1640
|
case '2':
|
|
1400
|
-
this.
|
|
1641
|
+
this.ChangeTab('config');
|
|
1401
1642
|
break;
|
|
1402
1643
|
case '3':
|
|
1403
|
-
this.
|
|
1644
|
+
this.ChangeTab('runs');
|
|
1404
1645
|
break;
|
|
1405
1646
|
case '4':
|
|
1406
|
-
this.
|
|
1647
|
+
this.ChangeTab('suites');
|
|
1407
1648
|
break;
|
|
1408
1649
|
case '5':
|
|
1409
|
-
this.
|
|
1650
|
+
this.ChangeTab('analytics');
|
|
1410
1651
|
break;
|
|
1411
1652
|
}
|
|
1412
1653
|
}
|
|
1413
1654
|
}
|
|
1414
1655
|
// Warn before tab close / hard navigation when there are unsaved changes
|
|
1415
1656
|
handleBeforeUnload(event) {
|
|
1416
|
-
if (this.
|
|
1657
|
+
if (this.IsDirty) {
|
|
1417
1658
|
event.preventDefault();
|
|
1418
1659
|
event.returnValue = '';
|
|
1419
1660
|
}
|
|
@@ -1426,9 +1667,9 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1426
1667
|
return tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || el.isContentEditable;
|
|
1427
1668
|
}
|
|
1428
1669
|
async loadTestRuns() {
|
|
1429
|
-
if (this.
|
|
1670
|
+
if (this.TestRunsLoaded)
|
|
1430
1671
|
return;
|
|
1431
|
-
this.
|
|
1672
|
+
this.LoadingRuns = true;
|
|
1432
1673
|
this.cdr.markForCheck();
|
|
1433
1674
|
try {
|
|
1434
1675
|
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
@@ -1440,20 +1681,20 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1440
1681
|
ResultType: 'entity_object'
|
|
1441
1682
|
});
|
|
1442
1683
|
if (result.Success) {
|
|
1443
|
-
this.
|
|
1684
|
+
this.TestRuns = result.Results || [];
|
|
1444
1685
|
// Load feedbacks for all test runs
|
|
1445
|
-
if (this.
|
|
1446
|
-
await this.loadFeedbacksForTestRuns(this.
|
|
1686
|
+
if (this.TestRuns.length > 0) {
|
|
1687
|
+
await this.loadFeedbacksForTestRuns(this.TestRuns.map(r => r.ID));
|
|
1447
1688
|
}
|
|
1448
1689
|
}
|
|
1449
|
-
this.
|
|
1690
|
+
this.TestRunsLoaded = true;
|
|
1450
1691
|
}
|
|
1451
1692
|
catch (error) {
|
|
1452
1693
|
console.error('Error loading test runs:', error);
|
|
1453
1694
|
SharedService.Instance.CreateSimpleNotification('Failed to load test runs', 'error', 3000);
|
|
1454
1695
|
}
|
|
1455
1696
|
finally {
|
|
1456
|
-
this.
|
|
1697
|
+
this.LoadingRuns = false;
|
|
1457
1698
|
this.cdr.markForCheck();
|
|
1458
1699
|
}
|
|
1459
1700
|
}
|
|
@@ -1476,7 +1717,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1476
1717
|
});
|
|
1477
1718
|
if (result.Success && result.Results) {
|
|
1478
1719
|
for (const feedback of result.Results) {
|
|
1479
|
-
this.
|
|
1720
|
+
this.FeedbackMap.set(feedback.TestRunID, feedback);
|
|
1480
1721
|
}
|
|
1481
1722
|
}
|
|
1482
1723
|
}
|
|
@@ -1488,13 +1729,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1488
1729
|
/**
|
|
1489
1730
|
* Get feedback for a specific test run
|
|
1490
1731
|
*/
|
|
1732
|
+
GetFeedbackForRun(testRunId) {
|
|
1733
|
+
return this.FeedbackMap.get(testRunId);
|
|
1734
|
+
}
|
|
1735
|
+
/** @deprecated Use {@link GetFeedbackForRun}. */
|
|
1491
1736
|
getFeedbackForRun(testRunId) {
|
|
1492
|
-
return this.
|
|
1737
|
+
return this.GetFeedbackForRun(testRunId);
|
|
1493
1738
|
}
|
|
1494
1739
|
/**
|
|
1495
1740
|
* Get tooltip for status indicator
|
|
1496
1741
|
*/
|
|
1497
|
-
|
|
1742
|
+
GetStatusTooltip(status) {
|
|
1498
1743
|
switch (status) {
|
|
1499
1744
|
case 'Passed': return 'Status: Passed - Test completed without error';
|
|
1500
1745
|
case 'Failed': return 'Status: Failed - Test assertions did not pass';
|
|
@@ -1506,10 +1751,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1506
1751
|
default: return `Status: ${status}`;
|
|
1507
1752
|
}
|
|
1508
1753
|
}
|
|
1754
|
+
/** @deprecated Use {@link GetStatusTooltip}. */
|
|
1755
|
+
getStatusTooltip(status) {
|
|
1756
|
+
return this.GetStatusTooltip(status);
|
|
1757
|
+
}
|
|
1509
1758
|
/**
|
|
1510
1759
|
* Get tooltip for human review with rating and optional comments
|
|
1511
1760
|
*/
|
|
1512
|
-
|
|
1761
|
+
GetHumanTooltip(rating, comments) {
|
|
1513
1762
|
let tooltip = `Human Review: ${rating}/10 rating`;
|
|
1514
1763
|
if (comments) {
|
|
1515
1764
|
const truncated = comments.length > 200 ? comments.substring(0, 200) + '...' : comments;
|
|
@@ -1517,10 +1766,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1517
1766
|
}
|
|
1518
1767
|
return tooltip;
|
|
1519
1768
|
}
|
|
1769
|
+
/** @deprecated Use {@link GetHumanTooltip}. */
|
|
1770
|
+
getHumanTooltip(rating, comments) {
|
|
1771
|
+
return this.GetHumanTooltip(rating, comments);
|
|
1772
|
+
}
|
|
1520
1773
|
async loadSuiteTests() {
|
|
1521
|
-
if (this.
|
|
1774
|
+
if (this.SuiteTestsLoaded)
|
|
1522
1775
|
return;
|
|
1523
|
-
this.
|
|
1776
|
+
this.LoadingSuites = true;
|
|
1524
1777
|
this.cdr.markForCheck();
|
|
1525
1778
|
try {
|
|
1526
1779
|
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
@@ -1531,57 +1784,65 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1531
1784
|
ResultType: 'entity_object'
|
|
1532
1785
|
});
|
|
1533
1786
|
if (result.Success) {
|
|
1534
|
-
this.
|
|
1787
|
+
this.SuiteTests = result.Results || [];
|
|
1535
1788
|
}
|
|
1536
|
-
this.
|
|
1789
|
+
this.SuiteTestsLoaded = true;
|
|
1537
1790
|
}
|
|
1538
1791
|
catch (error) {
|
|
1539
1792
|
console.error('Error loading suite tests:', error);
|
|
1540
1793
|
SharedService.Instance.CreateSimpleNotification('Failed to load test suites', 'error', 3000);
|
|
1541
1794
|
}
|
|
1542
1795
|
finally {
|
|
1543
|
-
this.
|
|
1796
|
+
this.LoadingSuites = false;
|
|
1544
1797
|
this.cdr.markForCheck();
|
|
1545
1798
|
}
|
|
1546
1799
|
}
|
|
1547
1800
|
parseJsonFields() {
|
|
1548
1801
|
try {
|
|
1549
1802
|
if (this.record.InputDefinition) {
|
|
1550
|
-
this.
|
|
1803
|
+
this.ParsedData.inputDefinition = JSON.parse(this.record.InputDefinition);
|
|
1551
1804
|
}
|
|
1552
1805
|
if (this.record.ExpectedOutcomes) {
|
|
1553
|
-
this.
|
|
1806
|
+
this.ParsedData.expectedOutcomes = JSON.parse(this.record.ExpectedOutcomes);
|
|
1554
1807
|
}
|
|
1555
1808
|
if (this.record.Configuration) {
|
|
1556
|
-
this.
|
|
1809
|
+
this.ParsedData.configuration = JSON.parse(this.record.Configuration);
|
|
1557
1810
|
}
|
|
1558
1811
|
if (this.record.Tags) {
|
|
1559
|
-
this.
|
|
1812
|
+
this.ParsedData.tags = JSON.parse(this.record.Tags);
|
|
1560
1813
|
}
|
|
1561
1814
|
}
|
|
1562
1815
|
catch (error) {
|
|
1563
1816
|
console.error('Error parsing JSON fields:', error);
|
|
1564
1817
|
}
|
|
1565
1818
|
}
|
|
1566
|
-
|
|
1567
|
-
this.
|
|
1819
|
+
ChangeTab(tab) {
|
|
1820
|
+
this.ActiveTab = tab;
|
|
1568
1821
|
// Lazy load tabs
|
|
1569
|
-
if (tab === 'runs' && !this.
|
|
1822
|
+
if (tab === 'runs' && !this.TestRunsLoaded) {
|
|
1570
1823
|
this.loadTestRuns();
|
|
1571
1824
|
}
|
|
1572
|
-
if (tab === 'suites' && !this.
|
|
1825
|
+
if (tab === 'suites' && !this.SuiteTestsLoaded) {
|
|
1573
1826
|
this.loadSuiteTests();
|
|
1574
1827
|
}
|
|
1575
|
-
if (tab === 'analytics' && !this.
|
|
1576
|
-
this.
|
|
1828
|
+
if (tab === 'analytics' && !this.HistoryLoaded) {
|
|
1829
|
+
this.LoadHistory();
|
|
1577
1830
|
}
|
|
1578
1831
|
this.cdr.markForCheck();
|
|
1579
1832
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1833
|
+
/** @deprecated Use {@link ChangeTab}. */
|
|
1834
|
+
changeTab(tab) {
|
|
1835
|
+
return this.ChangeTab(tab);
|
|
1836
|
+
}
|
|
1837
|
+
SetJsonView(view) {
|
|
1838
|
+
this.ActiveJsonView = view;
|
|
1582
1839
|
this.cdr.markForCheck();
|
|
1583
1840
|
}
|
|
1584
|
-
|
|
1841
|
+
/** @deprecated Use {@link SetJsonView}. */
|
|
1842
|
+
setJsonView(view) {
|
|
1843
|
+
return this.SetJsonView(view);
|
|
1844
|
+
}
|
|
1845
|
+
GetStatusColor() {
|
|
1585
1846
|
switch (this.record.Status) {
|
|
1586
1847
|
case 'Active': return '#10b981';
|
|
1587
1848
|
case 'Disabled': return '#6b7280';
|
|
@@ -1589,7 +1850,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1589
1850
|
default: return '#9ca3af';
|
|
1590
1851
|
}
|
|
1591
1852
|
}
|
|
1592
|
-
|
|
1853
|
+
/** @deprecated Use {@link GetStatusColor}. */
|
|
1854
|
+
getStatusColor() {
|
|
1855
|
+
return this.GetStatusColor();
|
|
1856
|
+
}
|
|
1857
|
+
GetStatusIcon() {
|
|
1593
1858
|
switch (this.record.Status) {
|
|
1594
1859
|
case 'Active': return 'fa-circle-check';
|
|
1595
1860
|
case 'Disabled': return 'fa-circle-stop';
|
|
@@ -1597,10 +1862,18 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1597
1862
|
default: return 'fa-circle-question';
|
|
1598
1863
|
}
|
|
1599
1864
|
}
|
|
1600
|
-
|
|
1865
|
+
/** @deprecated Use {@link GetStatusIcon}. */
|
|
1866
|
+
getStatusIcon() {
|
|
1867
|
+
return this.GetStatusIcon();
|
|
1868
|
+
}
|
|
1869
|
+
GetStatusClass() {
|
|
1601
1870
|
return `status-${this.record.Status?.toLowerCase() || 'unknown'}`;
|
|
1602
1871
|
}
|
|
1603
|
-
|
|
1872
|
+
/** @deprecated Use {@link GetStatusClass}. */
|
|
1873
|
+
getStatusClass() {
|
|
1874
|
+
return this.GetStatusClass();
|
|
1875
|
+
}
|
|
1876
|
+
GetRunStatusColor(status) {
|
|
1604
1877
|
switch (status) {
|
|
1605
1878
|
case 'Passed': return '#10b981';
|
|
1606
1879
|
case 'Failed': return '#ef4444';
|
|
@@ -1611,23 +1884,39 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1611
1884
|
default: return '#6b7280';
|
|
1612
1885
|
}
|
|
1613
1886
|
}
|
|
1887
|
+
/** @deprecated Use {@link GetRunStatusColor}. */
|
|
1888
|
+
getRunStatusColor(status) {
|
|
1889
|
+
return this.GetRunStatusColor(status);
|
|
1890
|
+
}
|
|
1891
|
+
GetPassRate() {
|
|
1892
|
+
if (this.TestRuns.length === 0)
|
|
1893
|
+
return 0;
|
|
1894
|
+
const passed = this.TestRuns.filter(r => r.Status === 'Passed').length;
|
|
1895
|
+
return (passed / this.TestRuns.length) * 100;
|
|
1896
|
+
}
|
|
1897
|
+
/** @deprecated Use {@link GetPassRate}. */
|
|
1614
1898
|
getPassRate() {
|
|
1615
|
-
|
|
1899
|
+
return this.GetPassRate();
|
|
1900
|
+
}
|
|
1901
|
+
GetAverageCost() {
|
|
1902
|
+
if (this.TestRuns.length === 0)
|
|
1616
1903
|
return 0;
|
|
1617
|
-
const
|
|
1618
|
-
return
|
|
1904
|
+
const totalCost = this.TestRuns.reduce((sum, r) => sum + (r.CostUSD || 0), 0);
|
|
1905
|
+
return totalCost / this.TestRuns.length;
|
|
1619
1906
|
}
|
|
1907
|
+
/** @deprecated Use {@link GetAverageCost}. */
|
|
1620
1908
|
getAverageCost() {
|
|
1621
|
-
|
|
1909
|
+
return this.GetAverageCost();
|
|
1910
|
+
}
|
|
1911
|
+
GetAverageDuration() {
|
|
1912
|
+
if (this.TestRuns.length === 0)
|
|
1622
1913
|
return 0;
|
|
1623
|
-
const
|
|
1624
|
-
return
|
|
1914
|
+
const totalDuration = this.TestRuns.reduce((sum, r) => sum + (r.DurationSeconds || 0), 0);
|
|
1915
|
+
return totalDuration / this.TestRuns.length;
|
|
1625
1916
|
}
|
|
1917
|
+
/** @deprecated Use {@link GetAverageDuration}. */
|
|
1626
1918
|
getAverageDuration() {
|
|
1627
|
-
|
|
1628
|
-
return 0;
|
|
1629
|
-
const totalDuration = this.testRuns.reduce((sum, r) => sum + (r.DurationSeconds || 0), 0);
|
|
1630
|
-
return totalDuration / this.testRuns.length;
|
|
1919
|
+
return this.GetAverageDuration();
|
|
1631
1920
|
}
|
|
1632
1921
|
formatDuration(seconds) {
|
|
1633
1922
|
if (seconds < 60)
|
|
@@ -1641,10 +1930,14 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1641
1930
|
const mins = Math.floor((seconds % 3600) / 60);
|
|
1642
1931
|
return `${hours}h ${mins}m`;
|
|
1643
1932
|
}
|
|
1644
|
-
|
|
1933
|
+
FormatCost(cost) {
|
|
1645
1934
|
return `$${cost.toFixed(6)}`;
|
|
1646
1935
|
}
|
|
1647
|
-
|
|
1936
|
+
/** @deprecated Use {@link FormatCost}. */
|
|
1937
|
+
formatCost(cost) {
|
|
1938
|
+
return this.FormatCost(cost);
|
|
1939
|
+
}
|
|
1940
|
+
FormatTimeout(ms) {
|
|
1648
1941
|
if (ms === null || ms === undefined)
|
|
1649
1942
|
return 'Default (5 min)';
|
|
1650
1943
|
if (ms < 1000)
|
|
@@ -1660,45 +1953,69 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1660
1953
|
const mins = Math.floor((ms % 3600000) / 60000);
|
|
1661
1954
|
return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
|
|
1662
1955
|
}
|
|
1663
|
-
|
|
1956
|
+
/** @deprecated Use {@link FormatTimeout}. */
|
|
1957
|
+
formatTimeout(ms) {
|
|
1958
|
+
return this.FormatTimeout(ms);
|
|
1959
|
+
}
|
|
1960
|
+
OpenTestRun(runId) {
|
|
1664
1961
|
SharedService.Instance.OpenEntityRecord('MJ: Test Runs', CompositeKey.FromID(runId));
|
|
1665
1962
|
}
|
|
1666
|
-
|
|
1963
|
+
/** @deprecated Use {@link OpenTestRun}. */
|
|
1964
|
+
openTestRun(runId) {
|
|
1965
|
+
return this.OpenTestRun(runId);
|
|
1966
|
+
}
|
|
1967
|
+
GetRunTags(run) {
|
|
1667
1968
|
return TagsHelper.parseTags(run.Tags);
|
|
1668
1969
|
}
|
|
1669
|
-
|
|
1970
|
+
/** @deprecated Use {@link GetRunTags}. */
|
|
1971
|
+
getRunTags(run) {
|
|
1972
|
+
return this.GetRunTags(run);
|
|
1973
|
+
}
|
|
1974
|
+
OpenTestSuite(suiteId) {
|
|
1670
1975
|
SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
|
|
1671
1976
|
}
|
|
1672
|
-
|
|
1977
|
+
/** @deprecated Use {@link OpenTestSuite}. */
|
|
1978
|
+
openTestSuite(suiteId) {
|
|
1979
|
+
return this.OpenTestSuite(suiteId);
|
|
1980
|
+
}
|
|
1981
|
+
NavigateToTestingDashboard() {
|
|
1673
1982
|
const testingApp = this.appManager.GetAppByName('Testing');
|
|
1674
1983
|
if (testingApp) {
|
|
1675
1984
|
this.navigationService.SwitchToApp(testingApp.ID);
|
|
1676
1985
|
}
|
|
1677
1986
|
}
|
|
1678
|
-
|
|
1987
|
+
/** @deprecated Use {@link NavigateToTestingDashboard}. */
|
|
1988
|
+
navigateToTestingDashboard() {
|
|
1989
|
+
return this.NavigateToTestingDashboard();
|
|
1990
|
+
}
|
|
1991
|
+
async RunTest() {
|
|
1679
1992
|
if (this.record?.ID) {
|
|
1680
|
-
this.
|
|
1993
|
+
this.TestingDialogService.OpenTestPanel(this.record.ID);
|
|
1681
1994
|
}
|
|
1682
1995
|
}
|
|
1996
|
+
/** @deprecated Use {@link RunTest}. */
|
|
1997
|
+
async runTest() {
|
|
1998
|
+
return this.RunTest();
|
|
1999
|
+
}
|
|
1683
2000
|
OnPanelClosed() {
|
|
1684
|
-
this.
|
|
2001
|
+
this.TestingDialogService.ClosePanel();
|
|
1685
2002
|
this.cdr.markForCheck();
|
|
1686
2003
|
}
|
|
1687
|
-
async
|
|
2004
|
+
async Refresh() {
|
|
1688
2005
|
this.isRefreshing = true;
|
|
1689
2006
|
this.cdr.markForCheck();
|
|
1690
2007
|
try {
|
|
1691
2008
|
await this.record.Load(this.record.ID);
|
|
1692
2009
|
this.parseJsonFields();
|
|
1693
2010
|
// Reset lazy-loaded data to force reload
|
|
1694
|
-
if (this.
|
|
1695
|
-
this.
|
|
1696
|
-
this.
|
|
2011
|
+
if (this.TestRunsLoaded) {
|
|
2012
|
+
this.TestRunsLoaded = false;
|
|
2013
|
+
this.TestRuns = [];
|
|
1697
2014
|
await this.loadTestRuns();
|
|
1698
2015
|
}
|
|
1699
|
-
if (this.
|
|
1700
|
-
this.
|
|
1701
|
-
this.
|
|
2016
|
+
if (this.SuiteTestsLoaded) {
|
|
2017
|
+
this.SuiteTestsLoaded = false;
|
|
2018
|
+
this.SuiteTests = [];
|
|
1702
2019
|
await this.loadSuiteTests();
|
|
1703
2020
|
}
|
|
1704
2021
|
SharedService.Instance.CreateSimpleNotification('Refreshed successfully', 'success', 2000);
|
|
@@ -1711,25 +2028,33 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1711
2028
|
this.cdr.markForCheck();
|
|
1712
2029
|
}
|
|
1713
2030
|
}
|
|
1714
|
-
|
|
2031
|
+
/** @deprecated Use {@link Refresh}. */
|
|
2032
|
+
async refresh() {
|
|
2033
|
+
return this.Refresh();
|
|
2034
|
+
}
|
|
2035
|
+
GetJsonData() {
|
|
1715
2036
|
let data;
|
|
1716
|
-
switch (this.
|
|
2037
|
+
switch (this.ActiveJsonView) {
|
|
1717
2038
|
case 'input':
|
|
1718
|
-
data = this.
|
|
2039
|
+
data = this.ParsedData.inputDefinition;
|
|
1719
2040
|
break;
|
|
1720
2041
|
case 'expected':
|
|
1721
|
-
data = this.
|
|
2042
|
+
data = this.ParsedData.expectedOutcomes;
|
|
1722
2043
|
break;
|
|
1723
2044
|
case 'config':
|
|
1724
|
-
data = this.
|
|
2045
|
+
data = this.ParsedData.configuration;
|
|
1725
2046
|
break;
|
|
1726
2047
|
case 'tags':
|
|
1727
|
-
data = this.
|
|
2048
|
+
data = this.ParsedData.tags;
|
|
1728
2049
|
break;
|
|
1729
2050
|
}
|
|
1730
2051
|
return data ? JSON.stringify(data, null, 2) : '// No data available';
|
|
1731
2052
|
}
|
|
1732
|
-
|
|
2053
|
+
/** @deprecated Use {@link GetJsonData}. */
|
|
2054
|
+
getJsonData() {
|
|
2055
|
+
return this.GetJsonData();
|
|
2056
|
+
}
|
|
2057
|
+
GetRelativeTime(date) {
|
|
1733
2058
|
if (!date)
|
|
1734
2059
|
return 'N/A';
|
|
1735
2060
|
const d = new Date(date);
|
|
@@ -1748,13 +2073,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1748
2073
|
return `${diffDays}d ago`;
|
|
1749
2074
|
return d.toLocaleDateString();
|
|
1750
2075
|
}
|
|
2076
|
+
/** @deprecated Use {@link GetRelativeTime}. */
|
|
2077
|
+
getRelativeTime(date) {
|
|
2078
|
+
return this.GetRelativeTime(date);
|
|
2079
|
+
}
|
|
1751
2080
|
// ===========================
|
|
1752
2081
|
// History Tab Methods
|
|
1753
2082
|
// ===========================
|
|
1754
|
-
async
|
|
1755
|
-
if (this.
|
|
2083
|
+
async LoadHistory() {
|
|
2084
|
+
if (this.HistoryLoaded)
|
|
1756
2085
|
return;
|
|
1757
|
-
this.
|
|
2086
|
+
this.LoadingHistory = true;
|
|
1758
2087
|
this.cdr.markForCheck();
|
|
1759
2088
|
try {
|
|
1760
2089
|
// Load all test runs for this test
|
|
@@ -1768,23 +2097,27 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1768
2097
|
if (runsResult.Success && runsResult.Results) {
|
|
1769
2098
|
const allRuns = runsResult.Results;
|
|
1770
2099
|
// Extract unique tags from all runs
|
|
1771
|
-
this.
|
|
2100
|
+
this.UniqueTags = TagsHelper.getUniqueTags(allRuns.map(r => r.Tags));
|
|
1772
2101
|
// Build history data (aggregated by date)
|
|
1773
|
-
this.
|
|
2102
|
+
this.HistoryData = this.buildHistoryData(allRuns);
|
|
1774
2103
|
// Build suite performance data
|
|
1775
2104
|
await this.buildSuitePerformance(allRuns);
|
|
1776
2105
|
}
|
|
1777
|
-
this.
|
|
2106
|
+
this.HistoryLoaded = true;
|
|
1778
2107
|
}
|
|
1779
2108
|
catch (error) {
|
|
1780
2109
|
console.error('Error loading history:', error);
|
|
1781
2110
|
SharedService.Instance.CreateSimpleNotification('Failed to load history', 'error', 3000);
|
|
1782
2111
|
}
|
|
1783
2112
|
finally {
|
|
1784
|
-
this.
|
|
2113
|
+
this.LoadingHistory = false;
|
|
1785
2114
|
this.cdr.markForCheck();
|
|
1786
2115
|
}
|
|
1787
2116
|
}
|
|
2117
|
+
/** @deprecated Use {@link LoadHistory}. */
|
|
2118
|
+
async loadHistory() {
|
|
2119
|
+
return this.LoadHistory();
|
|
2120
|
+
}
|
|
1788
2121
|
buildHistoryData(runs) {
|
|
1789
2122
|
// Filter by time range
|
|
1790
2123
|
const filteredRuns = this.filterRunsByTimeRange(runs);
|
|
@@ -1822,11 +2155,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1822
2155
|
return dataPoints.sort((a, b) => b.date.getTime() - a.date.getTime());
|
|
1823
2156
|
}
|
|
1824
2157
|
filterRunsByTimeRange(runs) {
|
|
1825
|
-
if (this.
|
|
2158
|
+
if (this.HistoryTimeRange === 'all')
|
|
1826
2159
|
return runs;
|
|
1827
2160
|
const now = new Date();
|
|
1828
2161
|
let cutoff;
|
|
1829
|
-
switch (this.
|
|
2162
|
+
switch (this.HistoryTimeRange) {
|
|
1830
2163
|
case '7d':
|
|
1831
2164
|
cutoff = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
|
|
1832
2165
|
break;
|
|
@@ -1874,7 +2207,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1874
2207
|
suiteIdMap.get(suiteId).runs.push(...testRuns);
|
|
1875
2208
|
}
|
|
1876
2209
|
// Build performance data for each suite
|
|
1877
|
-
this.
|
|
2210
|
+
this.SuitePerformance = [];
|
|
1878
2211
|
for (const [suiteId, data] of suiteIdMap) {
|
|
1879
2212
|
const suiteName = data.suiteRuns[0]?.Suite || 'Unknown Suite';
|
|
1880
2213
|
const totalRuns = data.runs.length;
|
|
@@ -1889,7 +2222,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1889
2222
|
const lastRun = data.runs
|
|
1890
2223
|
.filter(r => r.StartedAt)
|
|
1891
2224
|
.sort((a, b) => new Date(b.StartedAt).getTime() - new Date(a.StartedAt).getTime())[0];
|
|
1892
|
-
this.
|
|
2225
|
+
this.SuitePerformance.push({
|
|
1893
2226
|
suiteId,
|
|
1894
2227
|
suiteName,
|
|
1895
2228
|
totalRuns,
|
|
@@ -1904,57 +2237,89 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1904
2237
|
});
|
|
1905
2238
|
}
|
|
1906
2239
|
// Sort by total runs descending
|
|
1907
|
-
this.
|
|
2240
|
+
this.SuitePerformance.sort((a, b) => b.totalRuns - a.totalRuns);
|
|
1908
2241
|
}
|
|
1909
2242
|
}
|
|
1910
2243
|
}
|
|
2244
|
+
SetHistoryTimeRange(range) {
|
|
2245
|
+
this.HistoryTimeRange = range;
|
|
2246
|
+
this.HistoryLoaded = false;
|
|
2247
|
+
this.LoadHistory();
|
|
2248
|
+
}
|
|
2249
|
+
/** @deprecated Use {@link SetHistoryTimeRange}. */
|
|
1911
2250
|
setHistoryTimeRange(range) {
|
|
1912
|
-
this.
|
|
1913
|
-
this.historyLoaded = false;
|
|
1914
|
-
this.loadHistory();
|
|
2251
|
+
return this.SetHistoryTimeRange(range);
|
|
1915
2252
|
}
|
|
1916
|
-
|
|
1917
|
-
this.
|
|
2253
|
+
SetTagFilter(tag) {
|
|
2254
|
+
this.SelectedTagFilter = tag;
|
|
1918
2255
|
this.cdr.markForCheck();
|
|
1919
2256
|
}
|
|
2257
|
+
/** @deprecated Use {@link SetTagFilter}. */
|
|
2258
|
+
setTagFilter(tag) {
|
|
2259
|
+
return this.SetTagFilter(tag);
|
|
2260
|
+
}
|
|
2261
|
+
GetFilteredHistoryData() {
|
|
2262
|
+
return this.HistoryData;
|
|
2263
|
+
}
|
|
2264
|
+
/** @deprecated Use {@link GetFilteredHistoryData}. */
|
|
1920
2265
|
getFilteredHistoryData() {
|
|
1921
|
-
return this.
|
|
2266
|
+
return this.GetFilteredHistoryData();
|
|
1922
2267
|
}
|
|
1923
|
-
|
|
1924
|
-
if (this.
|
|
2268
|
+
GetOverallPassRate() {
|
|
2269
|
+
if (this.HistoryData.length === 0)
|
|
1925
2270
|
return 0;
|
|
1926
|
-
const totalRuns = this.
|
|
1927
|
-
const totalPassed = this.
|
|
2271
|
+
const totalRuns = this.HistoryData.reduce((sum, d) => sum + d.runCount, 0);
|
|
2272
|
+
const totalPassed = this.HistoryData.reduce((sum, d) => sum + d.passCount, 0);
|
|
1928
2273
|
return totalRuns > 0 ? (totalPassed / totalRuns) * 100 : 0;
|
|
1929
2274
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
2275
|
+
/** @deprecated Use {@link GetOverallPassRate}. */
|
|
2276
|
+
getOverallPassRate() {
|
|
2277
|
+
return this.GetOverallPassRate();
|
|
2278
|
+
}
|
|
2279
|
+
GetOverallAvgScore() {
|
|
2280
|
+
if (this.HistoryData.length === 0)
|
|
1932
2281
|
return 0;
|
|
1933
|
-
const scores = this.
|
|
2282
|
+
const scores = this.HistoryData.filter(d => d.avgScore > 0).map(d => d.avgScore);
|
|
1934
2283
|
return scores.length > 0 ? scores.reduce((a, b) => a + b, 0) / scores.length : 0;
|
|
1935
2284
|
}
|
|
1936
|
-
|
|
1937
|
-
|
|
2285
|
+
/** @deprecated Use {@link GetOverallAvgScore}. */
|
|
2286
|
+
getOverallAvgScore() {
|
|
2287
|
+
return this.GetOverallAvgScore();
|
|
2288
|
+
}
|
|
2289
|
+
GetOverallAvgDuration() {
|
|
2290
|
+
if (this.HistoryData.length === 0)
|
|
1938
2291
|
return 0;
|
|
1939
|
-
const durations = this.
|
|
2292
|
+
const durations = this.HistoryData.filter(d => d.avgDuration > 0).map(d => d.avgDuration);
|
|
1940
2293
|
return durations.length > 0 ? durations.reduce((a, b) => a + b, 0) / durations.length : 0;
|
|
1941
2294
|
}
|
|
1942
|
-
|
|
1943
|
-
|
|
2295
|
+
/** @deprecated Use {@link GetOverallAvgDuration}. */
|
|
2296
|
+
getOverallAvgDuration() {
|
|
2297
|
+
return this.GetOverallAvgDuration();
|
|
2298
|
+
}
|
|
2299
|
+
GetOverallAvgCost() {
|
|
2300
|
+
if (this.HistoryData.length === 0)
|
|
1944
2301
|
return 0;
|
|
1945
|
-
const costs = this.
|
|
2302
|
+
const costs = this.HistoryData.filter(d => d.avgCost > 0).map(d => d.avgCost);
|
|
1946
2303
|
return costs.length > 0 ? costs.reduce((a, b) => a + b, 0) / costs.length : 0;
|
|
1947
2304
|
}
|
|
2305
|
+
/** @deprecated Use {@link GetOverallAvgCost}. */
|
|
2306
|
+
getOverallAvgCost() {
|
|
2307
|
+
return this.GetOverallAvgCost();
|
|
2308
|
+
}
|
|
2309
|
+
GetTotalRuns() {
|
|
2310
|
+
return this.HistoryData.reduce((sum, d) => sum + d.runCount, 0);
|
|
2311
|
+
}
|
|
2312
|
+
/** @deprecated Use {@link GetTotalRuns}. */
|
|
1948
2313
|
getTotalRuns() {
|
|
1949
|
-
return this.
|
|
2314
|
+
return this.GetTotalRuns();
|
|
1950
2315
|
}
|
|
1951
|
-
|
|
1952
|
-
if (this.
|
|
2316
|
+
GetPassRateTrend() {
|
|
2317
|
+
if (this.HistoryData.length < 2)
|
|
1953
2318
|
return 'stable';
|
|
1954
2319
|
// Compare recent half to older half
|
|
1955
|
-
const mid = Math.floor(this.
|
|
1956
|
-
const recentData = this.
|
|
1957
|
-
const olderData = this.
|
|
2320
|
+
const mid = Math.floor(this.HistoryData.length / 2);
|
|
2321
|
+
const recentData = this.HistoryData.slice(0, mid);
|
|
2322
|
+
const olderData = this.HistoryData.slice(mid);
|
|
1958
2323
|
const recentRate = recentData.reduce((sum, d) => sum + d.passRate, 0) / recentData.length;
|
|
1959
2324
|
const olderRate = olderData.reduce((sum, d) => sum + d.passRate, 0) / olderData.length;
|
|
1960
2325
|
const diff = recentRate - olderRate;
|
|
@@ -1964,9 +2329,13 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1964
2329
|
return 'down';
|
|
1965
2330
|
return 'stable';
|
|
1966
2331
|
}
|
|
1967
|
-
|
|
2332
|
+
/** @deprecated Use {@link GetPassRateTrend}. */
|
|
2333
|
+
getPassRateTrend() {
|
|
2334
|
+
return this.GetPassRateTrend();
|
|
2335
|
+
}
|
|
2336
|
+
ExportHistoryToCSV() {
|
|
1968
2337
|
const headers = ['Date', 'Run Count', 'Passed', 'Failed', 'Pass Rate (%)', 'Avg Score', 'Avg Duration (s)', 'Avg Cost (USD)'];
|
|
1969
|
-
const rows = this.
|
|
2338
|
+
const rows = this.HistoryData.map(d => [
|
|
1970
2339
|
d.date.toISOString().split('T')[0],
|
|
1971
2340
|
d.runCount.toString(),
|
|
1972
2341
|
d.passCount.toString(),
|
|
@@ -1987,9 +2356,17 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
1987
2356
|
URL.revokeObjectURL(link.href);
|
|
1988
2357
|
SharedService.Instance.CreateSimpleNotification('Export complete', 'success', 2000);
|
|
1989
2358
|
}
|
|
1990
|
-
|
|
2359
|
+
/** @deprecated Use {@link ExportHistoryToCSV}. */
|
|
2360
|
+
exportHistoryToCSV() {
|
|
2361
|
+
return this.ExportHistoryToCSV();
|
|
2362
|
+
}
|
|
2363
|
+
OpenSuiteFromHistory(suiteId) {
|
|
1991
2364
|
SharedService.Instance.OpenEntityRecord('MJ: Test Suites', CompositeKey.FromID(suiteId));
|
|
1992
2365
|
}
|
|
2366
|
+
/** @deprecated Use {@link OpenSuiteFromHistory}. */
|
|
2367
|
+
openSuiteFromHistory(suiteId) {
|
|
2368
|
+
return this.OpenSuiteFromHistory(suiteId);
|
|
2369
|
+
}
|
|
1993
2370
|
// ===========================
|
|
1994
2371
|
// Keyboard Shortcuts Settings
|
|
1995
2372
|
// ===========================
|
|
@@ -2002,11 +2379,11 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2002
2379
|
const setting = engine.UserSettings.find(s => s.Setting === SHORTCUTS_SETTINGS_KEY);
|
|
2003
2380
|
if (setting) {
|
|
2004
2381
|
this.shortcutsSettingEntity = setting;
|
|
2005
|
-
this.
|
|
2382
|
+
this.ShowShortcuts = setting.Value === 'true';
|
|
2006
2383
|
}
|
|
2007
2384
|
else {
|
|
2008
2385
|
// Default to hidden
|
|
2009
|
-
this.
|
|
2386
|
+
this.ShowShortcuts = false;
|
|
2010
2387
|
}
|
|
2011
2388
|
this.cdr.markForCheck();
|
|
2012
2389
|
}
|
|
@@ -2017,8 +2394,8 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2017
2394
|
/**
|
|
2018
2395
|
* Toggle keyboard shortcuts visibility and save preference
|
|
2019
2396
|
*/
|
|
2020
|
-
async
|
|
2021
|
-
this.
|
|
2397
|
+
async ToggleShortcuts() {
|
|
2398
|
+
this.ShowShortcuts = !this.ShowShortcuts;
|
|
2022
2399
|
this.cdr.markForCheck();
|
|
2023
2400
|
try {
|
|
2024
2401
|
const userId = this.metadata.CurrentUser?.ID;
|
|
@@ -2036,30 +2413,54 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2036
2413
|
this.shortcutsSettingEntity.Setting = SHORTCUTS_SETTINGS_KEY;
|
|
2037
2414
|
}
|
|
2038
2415
|
}
|
|
2039
|
-
this.shortcutsSettingEntity.Value = this.
|
|
2416
|
+
this.shortcutsSettingEntity.Value = this.ShowShortcuts ? 'true' : 'false';
|
|
2040
2417
|
await this.shortcutsSettingEntity.Save();
|
|
2041
2418
|
}
|
|
2042
2419
|
catch (error) {
|
|
2043
2420
|
console.warn('Failed to save shortcuts setting:', error);
|
|
2044
2421
|
}
|
|
2045
2422
|
}
|
|
2423
|
+
/** @deprecated Use {@link ToggleShortcuts}. */
|
|
2424
|
+
async toggleShortcuts() {
|
|
2425
|
+
return this.ToggleShortcuts();
|
|
2426
|
+
}
|
|
2046
2427
|
// ==========================================
|
|
2047
2428
|
// Edit / Save Methods
|
|
2048
2429
|
// ==========================================
|
|
2049
2430
|
/** True if the test record has any unsaved field changes. */
|
|
2050
|
-
get
|
|
2431
|
+
get IsDirty() {
|
|
2051
2432
|
return this.record?.Dirty === true;
|
|
2052
2433
|
}
|
|
2434
|
+
/** @deprecated Use {@link IsDirty}. */
|
|
2435
|
+
get isDirty() {
|
|
2436
|
+
return this.IsDirty;
|
|
2437
|
+
}
|
|
2053
2438
|
/** Names of fields with pending edits, used by the save bar. */
|
|
2054
|
-
get
|
|
2439
|
+
get DirtyFieldNames() {
|
|
2055
2440
|
if (!this.record?.Fields)
|
|
2056
2441
|
return [];
|
|
2057
2442
|
return this.record.Fields.filter(f => f.Dirty).map(f => f.Name);
|
|
2058
2443
|
}
|
|
2444
|
+
/** @deprecated Use {@link DirtyFieldNames}. */
|
|
2445
|
+
get dirtyFieldNames() {
|
|
2446
|
+
return this.DirtyFieldNames;
|
|
2447
|
+
}
|
|
2059
2448
|
/** Parsed tags as a plain array — derived from record.Tags JSON. */
|
|
2060
2449
|
get tags() {
|
|
2061
2450
|
return TagsHelper.parseTags(this.record?.Tags);
|
|
2062
2451
|
}
|
|
2452
|
+
/** Active rubrics for the RubricID picker. */
|
|
2453
|
+
async loadRubricOptions() {
|
|
2454
|
+
try {
|
|
2455
|
+
const rv = RunView.FromMetadataProvider(this.ProviderToUse);
|
|
2456
|
+
const result = await rv.RunView({ EntityName: 'MJ: Rubrics', ExtraFilter: `Status='Active'`, OrderBy: 'Name', ResultType: 'simple', MaxRows: 500 });
|
|
2457
|
+
this.RubricOptions = RubricPickerOptions((result.Results ?? []));
|
|
2458
|
+
this.cdr.markForCheck();
|
|
2459
|
+
}
|
|
2460
|
+
catch (error) {
|
|
2461
|
+
console.warn('Failed to load rubric options:', error);
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2063
2464
|
/** Load test types for the Type dropdown. */
|
|
2064
2465
|
async loadTestTypeOptions() {
|
|
2065
2466
|
try {
|
|
@@ -2070,7 +2471,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2070
2471
|
ResultType: 'entity_object'
|
|
2071
2472
|
});
|
|
2072
2473
|
if (result.Success) {
|
|
2073
|
-
this.
|
|
2474
|
+
this.TestTypeOptions = result.Results || [];
|
|
2074
2475
|
this.cdr.markForCheck();
|
|
2075
2476
|
}
|
|
2076
2477
|
}
|
|
@@ -2079,10 +2480,10 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2079
2480
|
}
|
|
2080
2481
|
}
|
|
2081
2482
|
/** Save all pending changes on the test record. */
|
|
2082
|
-
async
|
|
2083
|
-
if (!this.
|
|
2483
|
+
async SaveChanges() {
|
|
2484
|
+
if (!this.IsDirty || this.IsSaving)
|
|
2084
2485
|
return;
|
|
2085
|
-
this.
|
|
2486
|
+
this.IsSaving = true;
|
|
2086
2487
|
this.cdr.markForCheck();
|
|
2087
2488
|
try {
|
|
2088
2489
|
const ok = await this.SaveRecord(false);
|
|
@@ -2101,24 +2502,32 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2101
2502
|
SharedService.Instance.CreateSimpleNotification(msg, 'error', 4000);
|
|
2102
2503
|
}
|
|
2103
2504
|
finally {
|
|
2104
|
-
this.
|
|
2505
|
+
this.IsSaving = false;
|
|
2105
2506
|
this.cdr.markForCheck();
|
|
2106
2507
|
}
|
|
2107
2508
|
}
|
|
2509
|
+
/** @deprecated Use {@link SaveChanges}. */
|
|
2510
|
+
async saveChanges() {
|
|
2511
|
+
return this.SaveChanges();
|
|
2512
|
+
}
|
|
2108
2513
|
/** Discard all pending field changes on the test. */
|
|
2109
|
-
async
|
|
2110
|
-
if (!this.
|
|
2514
|
+
async DiscardChanges() {
|
|
2515
|
+
if (!this.IsDirty)
|
|
2111
2516
|
return;
|
|
2112
2517
|
if (!(await this.confirmService.Confirm('Discard your unsaved changes?')))
|
|
2113
2518
|
return;
|
|
2114
2519
|
this.record.Revert();
|
|
2115
|
-
this.
|
|
2520
|
+
this.TagDraft = '';
|
|
2116
2521
|
this.parseJsonFields();
|
|
2117
2522
|
this.cdr.markForCheck();
|
|
2118
2523
|
}
|
|
2524
|
+
/** @deprecated Use {@link DiscardChanges}. */
|
|
2525
|
+
async discardChanges() {
|
|
2526
|
+
return this.DiscardChanges();
|
|
2527
|
+
}
|
|
2119
2528
|
/** Add a tag from tagDraft (called on Enter / comma / blur). */
|
|
2120
|
-
|
|
2121
|
-
const raw = this.
|
|
2529
|
+
AddTagFromDraft() {
|
|
2530
|
+
const raw = this.TagDraft.trim();
|
|
2122
2531
|
if (!raw)
|
|
2123
2532
|
return;
|
|
2124
2533
|
const incoming = raw.split(',').map(t => t.trim()).filter(t => t.length > 0);
|
|
@@ -2129,33 +2538,45 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2129
2538
|
merged.push(t);
|
|
2130
2539
|
}
|
|
2131
2540
|
this.record.Tags = TagsHelper.toJson(merged);
|
|
2132
|
-
this.
|
|
2541
|
+
this.TagDraft = '';
|
|
2133
2542
|
this.cdr.markForCheck();
|
|
2134
2543
|
}
|
|
2544
|
+
/** @deprecated Use {@link AddTagFromDraft}. */
|
|
2545
|
+
addTagFromDraft() {
|
|
2546
|
+
return this.AddTagFromDraft();
|
|
2547
|
+
}
|
|
2135
2548
|
/** Remove a single tag. */
|
|
2136
|
-
|
|
2549
|
+
RemoveTag(tag) {
|
|
2137
2550
|
this.record.Tags = TagsHelper.removeTag(this.record.Tags, tag);
|
|
2138
2551
|
this.cdr.markForCheck();
|
|
2139
2552
|
}
|
|
2553
|
+
/** @deprecated Use {@link RemoveTag}. */
|
|
2554
|
+
removeTag(tag) {
|
|
2555
|
+
return this.RemoveTag(tag);
|
|
2556
|
+
}
|
|
2140
2557
|
/** Handle Enter / comma in the tag input to commit the draft tag. */
|
|
2141
|
-
|
|
2558
|
+
OnTagInputKeydown(event) {
|
|
2142
2559
|
if (event.key === 'Enter' || event.key === ',') {
|
|
2143
2560
|
event.preventDefault();
|
|
2144
|
-
this.
|
|
2561
|
+
this.AddTagFromDraft();
|
|
2145
2562
|
return;
|
|
2146
2563
|
}
|
|
2147
|
-
if (event.key === 'Backspace' && !this.
|
|
2564
|
+
if (event.key === 'Backspace' && !this.TagDraft) {
|
|
2148
2565
|
const all = this.tags;
|
|
2149
2566
|
if (all.length > 0) {
|
|
2150
2567
|
event.preventDefault();
|
|
2151
|
-
this.
|
|
2568
|
+
this.RemoveTag(all[all.length - 1]);
|
|
2152
2569
|
}
|
|
2153
2570
|
}
|
|
2154
2571
|
}
|
|
2572
|
+
/** @deprecated Use {@link OnTagInputKeydown}. */
|
|
2573
|
+
onTagInputKeydown(event) {
|
|
2574
|
+
return this.OnTagInputKeydown(event);
|
|
2575
|
+
}
|
|
2155
2576
|
static { this.ɵfac = /*@__PURE__*/ (() => { let ɵMJTestFormComponentExtended_BaseFactory; return function MJTestFormComponentExtended_Factory(__ngFactoryType__) { return (ɵMJTestFormComponentExtended_BaseFactory || (ɵMJTestFormComponentExtended_BaseFactory = i0.ɵɵgetInheritedFactory(MJTestFormComponentExtended)))(__ngFactoryType__ || MJTestFormComponentExtended); }; })(); }
|
|
2156
2577
|
static { this.ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJTestFormComponentExtended, selectors: [["mj-test-form"]], hostBindings: function MJTestFormComponentExtended_HostBindings(rf, ctx) { if (rf & 1) {
|
|
2157
2578
|
i0.ɵɵlistener("keydown", function MJTestFormComponentExtended_keydown_HostBindingHandler($event) { return ctx.handleKeyboardShortcut($event); }, i0.ɵɵresolveDocument)("beforeunload", function MJTestFormComponentExtended_beforeunload_HostBindingHandler($event) { return ctx.handleBeforeUnload($event); }, i0.ɵɵresolveWindow);
|
|
2158
|
-
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 89, vars: 51, consts: [["tagInput", ""], [1, "test-form"], [1, "test-header"], ["aria-label", "Breadcrumb", 1, "breadcrumb"], ["href", "javascript:void(0)", 3, "click"], [1, "fas", "fa-vial"], [1, "breadcrumb-text"], [1, "current"], [1, "fas", "fa-chevron-right", "separator"], [1, "fas", "fa-flask"], [1, "header-content"], [1, "header-left"], [1, "test-icon"], [1, "test-info"], ["title", "You have unsaved changes", "aria-label", "Unsaved changes", 1, "dirty-indicator"], [1, "test-meta"], [1, "status-badge", 3, "ngClass"], [1, "fas", 3, "ngClass"], [1, "test-type"], ["title", "You have unsaved changes", 1, "unsaved-pill"], [1, "header-actions"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fas", "fa-play"], ["mjButton", "", 3, "click", "disabled"], [1, "test-description"], [1, "metrics-bar"], [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"], [1, "tab-badge"], [1, "fas", "fa-layer-group"], [1, "fas", "fa-chart-line"], [1, "tab-content"], [1, "overview-tab"], [1, "config-tab"], [1, "runs-tab"], [1, "suites-tab"], [1, "history-tab"], [1, "shortcuts-toggle", 3, "click", "title"], [1, "fas", "fa-keyboard"], [1, "keyboard-shortcuts"], ["Mode", "slide", 3, "Title", "Resizable"], ["role", "region", "aria-label", "Unsaved changes", 1, "save-bar"], [1, "save-bar-message"], ["aria-hidden", "true", 1, "save-bar-dot"], [1, "save-bar-text"], [1, "save-bar-fields"], [1, "save-bar-actions"], ["type", "button", "title", "Save changes (\u2318S)", 1, "save-bar-btn", "save-bar-btn--primary", 3, "click", "disabled"], ["type", "button", "title", "Discard changes", 1, "save-bar-btn", "save-bar-btn--ghost", 3, "click", "disabled"], [1, "fas", "fa-undo"], [1, "fas", "fa-tag"], [1, "fas", "fa-circle"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "metric-progress"], [1, "metric-progress-fill"], [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, "edit-section"], [1, "edit-section-header"], [1, "fas", "fa-pen-to-square"], [1, "edit-section-sub"], [1, "edit-grid"], [1, "edit-field", "edit-field--full"], ["for", "test-name"], [1, "required"], ["id", "test-name", "type", "text", "placeholder", "e.g., Agent Response Quality Test", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-description"], ["id", "test-description", "rows", "3", "placeholder", "What does this test evaluate?", 1, "config-input", "config-textarea", 3, "ngModelChange", "ngModel"], [1, "edit-field"], ["for", "test-status"], [1, "select-wrapper"], ["id", "test-status", 1, "config-input", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["for", "test-type"], ["id", "test-type", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-tags"], [1, "tag-editor", 3, "click"], [1, "tag-chip-editable"], ["id", "test-tags", "type", "text", 1, "tag-input-inline", 3, "ngModelChange", "keydown", "blur", "placeholder", "ngModel"], [1, "config-hint"], [1, "fas", "fa-cogs"], ["for", "test-priority"], ["id", "test-priority", "type", "number", "placeholder", "0 (lower runs first)", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-repeat"], ["id", "test-repeat", "type", "number", "min", "1", "placeholder", "1", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-duration"], ["id", "test-duration", "type", "number", "placeholder", "0", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-cost"], ["id", "test-cost", "type", "number", "step", "0.000001", "placeholder", "0.00", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-timeout"], ["id", "test-timeout", "type", "number", "placeholder", "Default: 300000 (5 min)", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-section"], [1, "config-editor-container"], ["language", "json", 3, "change", "value", "readonly", "lineWrapping"], [1, "meta-section"], [1, "meta-grid"], [1, "meta-item"], [1, "meta-label"], [1, "meta-value"], [1, "meta-value", "meta-mono"], ["type", "button", 1, "tag-remove", 3, "click"], [1, "fas", "fa-times"], [1, "loading-state"], [1, "runs-list"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "skeleton-list"], [1, "skeleton-card"], [1, "skeleton-icon"], [1, "skeleton-content"], [1, "skeleton-line", "wide"], [1, "skeleton-line", "narrow"], [1, "run-item"], [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"], [3, "entityName", "recordId"], [1, "run-eval-stack"], [1, "eval-pill", "status-pill", 3, "ngClass", "title"], [1, "run-tags"], [1, "fas", "fa-chevron-right"], [1, "fas", "fa-clock"], [1, "fas", "fa-dollar-sign"], ["title", "Human Review: No rating submitted yet", 1, "eval-pill", "human-pill", "no-feedback"], [1, "eval-pill", "human-pill", "has-feedback", 3, "rating-low", "rating-medium", "rating-good", "rating-excellent", "title"], [1, "fas", "fa-user-slash"], [1, "eval-pill", "human-pill", "has-feedback", 3, "title"], [1, "fas", "fa-user"], ["title", "Auto Score: No automated score available", 1, "eval-pill", "auto-pill", "no-score"], [1, "eval-pill", "auto-pill", "has-score", 3, "score-low", "score-medium", "score-good", "score-excellent", "title"], [1, "fas", "fa-robot"], [1, "eval-pill", "auto-pill", "has-score", 3, "title"], [1, "run-tag"], [1, "run-tag-more"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "suites-list"], ["Icon", "fa-solid fa-folder-open", "Title", "Not Part of Any Suite", "Message", "This test is not included in any test suites. Add it to a suite to run it with other tests."], [1, "suite-item"], [1, "suite-item", 3, "click"], [1, "suite-icon"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"], [1, "fas", "fa-sort-numeric-up"], [1, "history-content"], ["text", "Loading history..."], [1, "history-filters"], [1, "time-range-filters"], [1, "filter-label"], [1, "filter-btn", 3, "click"], [1, "history-actions"], [1, "fas", "fa-download"], [1, "history-kpi-cards"], [1, "history-section"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "kpi-card"], [1, "kpi-icon"], [1, "fas", "fa-play-circle"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-icon", "pass-rate"], [1, "fas", "fa-percentage"], [1, "fas", "trend-icon"], [1, "fas", "fa-star"], [1, "fas", "fa-calendar-alt"], [1, "history-table-container"], [1, "history-table"], [1, "date-cell"], [1, "runs-cell"], [1, "passed-cell"], [1, "failed-cell"], [1, "pass-rate-cell"], [1, "score-cell"], [1, "duration-cell"], [1, "cost-cell"], [1, "pass-rate-bar"], [1, "pass-rate-fill"], [1, "pass-rate-text"], [1, "suite-performance-list"], [1, "suite-perf-card"], [1, "suite-perf-card", 3, "click"], [1, "suite-perf-header"], [1, "suite-perf-name"], [1, "suite-perf-tags"], [1, "suite-perf-stats"], [1, "suite-stat"], [1, "stat-value"], [1, "stat-label"], [1, "fas", "fa-chevron-right", "suite-perf-arrow"], [1, "tag-mini"], [1, "tag-more"], [1, "stat-value", "pass-rate"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "shortcuts-header"], ["title", "Hide shortcuts", 1, "shortcuts-close", 3, "click"], [1, "shortcut-list"], [1, "shortcut-item"], [1, "shortcut-keys"], ["Mode", "slide", 3, "Closed", "Title", "Resizable"], [3, "PanelClose", "PanelMode", "selectedTestId", "selectedSuiteId", "runMode"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-check"], [1, "save-bar-kbd"]], template: function MJTestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
|
|
2579
|
+
} }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 89, vars: 51, consts: [["tagInput", ""], [1, "test-form"], [1, "test-header"], ["aria-label", "Breadcrumb", 1, "breadcrumb"], ["href", "javascript:void(0)", 3, "click"], [1, "fas", "fa-vial"], [1, "breadcrumb-text"], [1, "current"], [1, "fas", "fa-chevron-right", "separator"], [1, "fas", "fa-flask"], [1, "header-content"], [1, "header-left"], [1, "test-icon"], [1, "test-info"], ["title", "You have unsaved changes", "aria-label", "Unsaved changes", 1, "dirty-indicator"], [1, "test-meta"], [1, "status-badge", 3, "ngClass"], [1, "fas", 3, "ngClass"], [1, "test-type"], ["title", "You have unsaved changes", 1, "unsaved-pill"], [1, "header-actions"], ["mjButton", "", "variant", "primary", 3, "click"], [1, "fas", "fa-play"], ["mjButton", "", 3, "click", "disabled"], [1, "test-description"], [1, "metrics-bar"], [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"], [1, "tab-badge"], [1, "fas", "fa-layer-group"], [1, "fas", "fa-chart-line"], [1, "tab-content"], [1, "overview-tab"], [1, "config-tab"], [1, "runs-tab"], [1, "suites-tab"], [1, "history-tab"], [1, "shortcuts-toggle", 3, "click", "title"], [1, "fas", "fa-keyboard"], [1, "keyboard-shortcuts"], ["Mode", "slide", 3, "Title", "Resizable"], ["role", "region", "aria-label", "Unsaved changes", 1, "save-bar"], [1, "save-bar-message"], ["aria-hidden", "true", 1, "save-bar-dot"], [1, "save-bar-text"], [1, "save-bar-fields"], [1, "save-bar-actions"], ["type", "button", "title", "Save changes (\u2318S)", 1, "save-bar-btn", "save-bar-btn--primary", 3, "click", "disabled"], ["type", "button", "title", "Discard changes", 1, "save-bar-btn", "save-bar-btn--ghost", 3, "click", "disabled"], [1, "fas", "fa-undo"], [1, "fas", "fa-tag"], [1, "fas", "fa-circle"], [1, "metric-card"], [1, "metric-label"], [1, "metric-value"], [1, "metric-progress"], [1, "metric-progress-fill"], [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, "edit-section"], [1, "edit-section-header"], [1, "fas", "fa-pen-to-square"], [1, "edit-section-sub"], [1, "edit-grid"], [1, "edit-field", "edit-field--full"], ["for", "test-name"], [1, "required"], ["id", "test-name", "type", "text", "placeholder", "e.g., Agent Response Quality Test", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-description"], ["id", "test-description", "rows", "3", "placeholder", "What does this test evaluate?", 1, "config-input", "config-textarea", 3, "ngModelChange", "ngModel"], [1, "edit-field"], ["for", "test-status"], [1, "select-wrapper"], ["id", "test-status", 1, "config-input", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["for", "test-rubric"], ["id", "test-rubric", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-type"], ["id", "test-type", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-tags"], [1, "tag-editor", 3, "click"], [1, "tag-chip-editable"], ["id", "test-tags", "type", "text", 1, "tag-input-inline", 3, "ngModelChange", "keydown", "blur", "placeholder", "ngModel"], [1, "config-hint"], [1, "fas", "fa-cogs"], ["for", "test-priority"], ["id", "test-priority", "type", "number", "placeholder", "0 (lower runs first)", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-repeat"], ["id", "test-repeat", "type", "number", "min", "1", "placeholder", "1", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-duration"], ["id", "test-duration", "type", "number", "placeholder", "0", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-cost"], ["id", "test-cost", "type", "number", "step", "0.000001", "placeholder", "0.00", 1, "config-input", 3, "ngModelChange", "ngModel"], ["for", "test-timeout"], ["id", "test-timeout", "type", "number", "placeholder", "Default: 300000 (5 min)", 1, "config-input", 3, "ngModelChange", "ngModel"], [1, "config-section"], [1, "config-editor-container"], ["language", "json", 3, "change", "value", "readonly", "lineWrapping"], [1, "meta-section"], [1, "meta-grid"], [1, "meta-item"], [1, "meta-label"], [1, "meta-value"], [1, "meta-value", "meta-mono"], ["type", "button", 1, "tag-remove", 3, "click"], [1, "fas", "fa-times"], [1, "loading-state"], [1, "runs-list"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "skeleton-list"], [1, "skeleton-card"], [1, "skeleton-icon"], [1, "skeleton-content"], [1, "skeleton-line", "wide"], [1, "skeleton-line", "narrow"], [1, "run-item"], [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"], [3, "entityName", "recordId"], [1, "run-eval-stack"], [1, "eval-pill", "status-pill", 3, "ngClass", "title"], [1, "run-tags"], [1, "fas", "fa-chevron-right"], [1, "fas", "fa-clock"], [1, "fas", "fa-dollar-sign"], ["title", "Human Review: No rating submitted yet", 1, "eval-pill", "human-pill", "no-feedback"], [1, "eval-pill", "human-pill", "has-feedback", 3, "rating-low", "rating-medium", "rating-good", "rating-excellent", "title"], [1, "fas", "fa-user-slash"], [1, "eval-pill", "human-pill", "has-feedback", 3, "title"], [1, "fas", "fa-user"], ["title", "Auto Score: No automated score available", 1, "eval-pill", "auto-pill", "no-score"], [1, "eval-pill", "auto-pill", "has-score", 3, "score-low", "score-medium", "score-good", "score-excellent", "title"], [1, "fas", "fa-robot"], [1, "eval-pill", "auto-pill", "has-score", 3, "title"], [1, "run-tag"], [1, "run-tag-more"], ["Icon", "fa-solid fa-circle-play", "Title", "No Test Runs Yet", "Message", "Run this test to see execution history and results here.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "suites-list"], ["Icon", "fa-solid fa-folder-open", "Title", "Not Part of Any Suite", "Message", "This test is not included in any test suites. Add it to a suite to run it with other tests."], [1, "suite-item"], [1, "suite-item", 3, "click"], [1, "suite-icon"], [1, "suite-content"], [1, "suite-name"], [1, "suite-meta"], [1, "fas", "fa-sort-numeric-up"], [1, "history-content"], ["text", "Loading history..."], [1, "history-filters"], [1, "time-range-filters"], [1, "filter-label"], [1, "filter-btn", 3, "click"], [1, "history-actions"], [1, "fas", "fa-download"], [1, "history-kpi-cards"], [1, "history-section"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play"], [1, "kpi-card"], [1, "kpi-icon"], [1, "fas", "fa-play-circle"], [1, "kpi-content"], [1, "kpi-value"], [1, "kpi-label"], [1, "kpi-icon", "pass-rate"], [1, "fas", "fa-percentage"], [1, "fas", "trend-icon"], [1, "fas", "fa-star"], [1, "fas", "fa-calendar-alt"], [1, "history-table-container"], [1, "history-table"], [1, "date-cell"], [1, "runs-cell"], [1, "passed-cell"], [1, "failed-cell"], [1, "pass-rate-cell"], [1, "score-cell"], [1, "duration-cell"], [1, "cost-cell"], [1, "pass-rate-bar"], [1, "pass-rate-fill"], [1, "pass-rate-text"], [1, "suite-performance-list"], [1, "suite-perf-card"], [1, "suite-perf-card", 3, "click"], [1, "suite-perf-header"], [1, "suite-perf-name"], [1, "suite-perf-tags"], [1, "suite-perf-stats"], [1, "suite-stat"], [1, "stat-value"], [1, "stat-label"], [1, "fas", "fa-chevron-right", "suite-perf-arrow"], [1, "tag-mini"], [1, "tag-more"], [1, "stat-value", "pass-rate"], ["Icon", "fa-solid fa-chart-line", "Title", "No History Available", "Message", "Run this test to start building history and analytics data.", "ActionText", "Run Test Now", "ActionIcon", "fa-solid fa-play", 3, "Action"], [1, "shortcuts-header"], ["title", "Hide shortcuts", 1, "shortcuts-close", 3, "click"], [1, "shortcut-list"], [1, "shortcut-item"], [1, "shortcut-keys"], ["Mode", "slide", 3, "Closed", "Title", "Resizable"], [3, "PanelClose", "PanelMode", "selectedTestId", "selectedSuiteId", "runMode"], [1, "fas", "fa-spinner", "fa-spin"], [1, "fas", "fa-check"], [1, "save-bar-kbd"]], template: function MJTestFormComponentExtended_Template(rf, ctx) { if (rf & 1) {
|
|
2159
2580
|
i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "nav", 3)(3, "ol")(4, "li")(5, "a", 4);
|
|
2160
2581
|
i0.ɵɵlistener("click", function MJTestFormComponentExtended_Template_a_click_5_listener() { return ctx.navigateToTestingDashboard(); });
|
|
2161
2582
|
i0.ɵɵelement(6, "i", 5);
|
|
@@ -2232,7 +2653,7 @@ let MJTestFormComponentExtended = class MJTestFormComponentExtended extends MJTe
|
|
|
2232
2653
|
i0.ɵɵelementEnd()()()();
|
|
2233
2654
|
i0.ɵɵelementStart(62, "div", 35);
|
|
2234
2655
|
i0.ɵɵconditionalCreate(63, MJTestFormComponentExtended_Conditional_63_Template, 78, 29, "div", 36);
|
|
2235
|
-
i0.ɵɵconditionalCreate(64, MJTestFormComponentExtended_Conditional_64_Template,
|
|
2656
|
+
i0.ɵɵconditionalCreate(64, MJTestFormComponentExtended_Conditional_64_Template, 119, 32, "div", 37);
|
|
2236
2657
|
i0.ɵɵconditionalCreate(65, MJTestFormComponentExtended_Conditional_65_Template, 4, 3, "div", 38);
|
|
2237
2658
|
i0.ɵɵconditionalCreate(66, MJTestFormComponentExtended_Conditional_66_Template, 4, 3, "div", 39);
|
|
2238
2659
|
i0.ɵɵconditionalCreate(67, MJTestFormComponentExtended_Conditional_67_Template, 3, 2, "div", 40);
|
|
@@ -2348,7 +2769,7 @@ MJTestFormComponentExtended = __decorate([
|
|
|
2348
2769
|
export { MJTestFormComponentExtended };
|
|
2349
2770
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJTestFormComponentExtended, [{
|
|
2350
2771
|
type: Component,
|
|
2351
|
-
args: [{ standalone: false, selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\">\n <!-- Header Section -->\n <div class=\"test-header\">\n <!-- Breadcrumb Navigation -->\n <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li>\n <a href=\"javascript:void(0)\" (click)=\"navigateToTestingDashboard()\">\n <i class=\"fas fa-vial\"></i>\n <span class=\"breadcrumb-text\">Testing</span>\n </a>\n </li>\n <li class=\"current\">\n <i class=\"fas fa-chevron-right separator\"></i>\n <i class=\"fas fa-flask\"></i>\n <span>{{ record.Name }}</span>\n </li>\n </ol>\n </nav>\n\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>\n {{ record.Name }}\n @if (isDirty) {\n <span class=\"dirty-indicator\" title=\"You have unsaved changes\" aria-label=\"Unsaved changes\">\u25CF</span>\n }\n </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 @if (record.Type) {\n <span class=\"test-type\">\n <i class=\"fas fa-tag\"></i>\n {{ record.Type }}\n </span>\n }\n @if (isDirty) {\n <span class=\"unsaved-pill\" title=\"You have unsaved changes\">\n <i class=\"fas fa-circle\"></i> Unsaved\n </span>\n }\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n <button mjButton (click)=\"runTest()\" variant=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test\n </button>\n <button mjButton (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 @if (record.Description) {\n <div class=\"test-description\">\n <p>{{ record.Description }}</p>\n </div>\n }\n\n <!-- Metrics Bar -->\n @if (testRunsLoaded && testRuns.length > 0) {\n <div class=\"metrics-bar\">\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 }\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 @if (testRunsLoaded) {\n <span class=\"tab-badge\">{{ testRuns.length }}</span>\n }\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 @if (suiteTestsLoaded) {\n <span class=\"tab-badge\">{{ suiteTests.length }}</span>\n }\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\" [class.has-savebar]=\"isDirty\">\n <!-- Overview Tab -->\n @if (activeTab === 'overview') {\n <div class=\"overview-tab\">\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 <!-- 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\n <!-- Configuration Tab -->\n @if (activeTab === 'config') {\n <div class=\"config-tab\">\n <!-- Details -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-pen-to-square\"></i> Details</h3>\n <p class=\"edit-section-sub\">Edit any field \u2014 changes are saved with the bar at the bottom.</p>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-name\">Name <span class=\"required\">*</span></label>\n <input\n id=\"test-name\"\n type=\"text\"\n class=\"config-input\"\n placeholder=\"e.g., Agent Response Quality Test\"\n [(ngModel)]=\"record.Name\"\n />\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-description\">Description</label>\n <textarea\n id=\"test-description\"\n class=\"config-input config-textarea\"\n rows=\"3\"\n placeholder=\"What does this test evaluate?\"\n [ngModel]=\"record.Description\"\n (ngModelChange)=\"record.Description = $event || null\"\n ></textarea>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-status\">Status</label>\n <div class=\"select-wrapper\">\n <select\n id=\"test-status\"\n class=\"config-input\"\n [(ngModel)]=\"record.Status\"\n >\n @for (opt of statusOptions; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-type\">Test Type</label>\n <select\n id=\"test-type\"\n class=\"config-input\"\n [(ngModel)]=\"record.TypeID\"\n >\n @for (t of testTypeOptions; track t.ID) {\n <option [ngValue]=\"t.ID\">{{ t.Name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-tags\">Tags</label>\n <div class=\"tag-editor\" (click)=\"tagInput.focus()\">\n @for (tag of tags; track tag) {\n <span class=\"tag-chip-editable\">\n {{ tag }}\n <button type=\"button\" class=\"tag-remove\" (click)=\"removeTag(tag); $event.stopPropagation()\" [attr.aria-label]=\"'Remove tag ' + tag\">\n <i class=\"fas fa-times\"></i>\n </button>\n </span>\n }\n <input\n #tagInput\n id=\"test-tags\"\n type=\"text\"\n class=\"tag-input-inline\"\n [placeholder]=\"tags.length === 0 ? 'Add tags, press Enter or comma...' : ''\"\n [(ngModel)]=\"tagDraft\"\n (keydown)=\"onTagInputKeydown($event)\"\n (blur)=\"addTagFromDraft()\"\n />\n </div>\n <span class=\"config-hint\">Press Enter or comma to add a tag. Backspace on empty input removes the last tag.</span>\n </div>\n </div>\n </section>\n\n <!-- Execution Settings -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-cogs\"></i> Execution Settings</h3>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field\">\n <label for=\"test-priority\">Priority</label>\n <input\n id=\"test-priority\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0 (lower runs first)\"\n [ngModel]=\"record.Priority\"\n (ngModelChange)=\"record.Priority = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Lower numbers run first.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-repeat\">Repeat Count</label>\n <input\n id=\"test-repeat\"\n type=\"number\"\n class=\"config-input\"\n min=\"1\"\n placeholder=\"1\"\n [ngModel]=\"record.RepeatCount\"\n (ngModelChange)=\"record.RepeatCount = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Number of times to repeat for statistical analysis.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-duration\">Estimated Duration (seconds)</label>\n <input\n id=\"test-duration\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0\"\n [ngModel]=\"record.EstimatedDurationSeconds\"\n (ngModelChange)=\"record.EstimatedDurationSeconds = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field\">\n <label for=\"test-cost\">Estimated Cost (USD)</label>\n <input\n id=\"test-cost\"\n type=\"number\"\n step=\"0.000001\"\n class=\"config-input\"\n placeholder=\"0.00\"\n [ngModel]=\"record.EstimatedCostUSD\"\n (ngModelChange)=\"record.EstimatedCostUSD = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-timeout\">Max Execution Time (ms)</label>\n <input\n id=\"test-timeout\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"Default: 300000 (5 min)\"\n [ngModel]=\"record.MaxExecutionTimeMS\"\n (ngModelChange)=\"record.MaxExecutionTimeMS = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Leave empty for the default 5 minute timeout. Currently: {{ formatTimeout(record.MaxExecutionTimeMS) }}</span>\n </div>\n </div>\n </section>\n\n <!-- JSON editors (preserved from original layout) -->\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 <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 <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 <!-- Metadata (read-only) -->\n <section class=\"meta-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-item\">\n <div class=\"meta-label\">Created</div>\n <div class=\"meta-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Last updated</div>\n <div class=\"meta-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Test ID</div>\n <div class=\"meta-value meta-mono\">{{ record.ID }}</div>\n </div>\n </div>\n </section>\n </div>\n }\n\n <!-- Test Runs Tab -->\n @if (activeTab === 'runs') {\n <div class=\"runs-tab\">\n <!-- Loading State -->\n @if (loadingRuns) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3,4,5]; track i) {\n <div class=\"skeleton-card\">\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 }\n </div>\n </div>\n }\n <!-- Runs List -->\n @if (!loadingRuns && testRuns.length > 0) {\n <div class=\"runs-list\">\n @for (run of testRuns; track run) {\n <div class=\"run-item\" (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 @if (run.DurationSeconds) {\n <span><i class=\"fas fa-clock\"></i> {{ formatDuration(run.DurationSeconds) }}</span>\n }\n @if (run.CostUSD) {\n <span><i class=\"fas fa-dollar-sign\"></i> {{ formatCost(run.CostUSD) }}</span>\n }\n @if (run.TargetLogEntityID && run.TargetLogID) {\n <mj-entity-link-pill\n [entityName]=\"run.TargetLogEntity\"\n [recordId]=\"run.TargetLogID\">\n </mj-entity-link-pill>\n }\n </div>\n <!-- Evaluation indicators -->\n <div class=\"run-eval-stack\">\n <!-- Status indicator -->\n @if (evalPreferences.showExecution) {\n <span class=\"eval-pill status-pill\"\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 }\n <!-- Human feedback indicator -->\n @if (evalPreferences.showHuman) {\n @if (!getFeedbackForRun(run.ID)?.Rating) {\n <span class=\"eval-pill human-pill no-feedback\"\n title=\"Human Review: No rating submitted yet\">\n <i class=\"fas fa-user-slash\"></i>\n </span>\n }\n @if (getFeedbackForRun(run.ID)?.Rating; as rating) {\n <span class=\"eval-pill human-pill has-feedback\"\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 }\n }\n <!-- Auto score indicator -->\n @if (evalPreferences.showAuto) {\n @if (run.Score == null) {\n <span class=\"eval-pill auto-pill no-score\"\n title=\"Auto Score: No automated score available\">\n <i class=\"fas fa-robot\"></i>\n </span>\n }\n @if (run.Score != null) {\n <span class=\"eval-pill auto-pill has-score\"\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 }\n }\n </div>\n @if (getRunTags(run).length > 0) {\n <div class=\"run-tags\">\n @for (tag of getRunTags(run).slice(0, 3); track tag) {\n <span class=\"run-tag\">{{ tag }}</span>\n }\n @if (getRunTags(run).length > 3) {\n <span class=\"run-tag-more\">+{{ getRunTags(run).length - 3 }}</span>\n }\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (testRunsLoaded && !loadingRuns && testRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-circle-play\"\n Title=\"No Test Runs Yet\"\n Message=\"Run this test to see execution history and results here.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n\n <!-- Test Suites Tab -->\n @if (activeTab === 'suites') {\n <div class=\"suites-tab\">\n <!-- Loading State -->\n @if (loadingSuites) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3]; track i) {\n <div class=\"skeleton-card\">\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 }\n </div>\n </div>\n }\n <!-- Suites List -->\n @if (!loadingSuites && suiteTests.length > 0) {\n <div class=\"suites-list\">\n @for (suiteTest of suiteTests; track suiteTest) {\n <div class=\"suite-item\" (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 @if (suiteTest.Status) {\n <span><i class=\"fas fa-info-circle\"></i> {{ suiteTest.Status }}</span>\n }\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (suiteTestsLoaded && !loadingSuites && suiteTests.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"Not Part of Any Suite\"\n Message=\"This test is not included in any test suites. Add it to a suite to run it with other tests.\" />\n }\n </div>\n }\n\n <!-- Analytics Tab -->\n @if (activeTab === 'analytics') {\n <div class=\"history-tab\">\n <!-- Loading State -->\n @if (loadingHistory) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading history...\"></mj-loading>\n </div>\n }\n <!-- History Content -->\n @if (!loadingHistory && historyLoaded) {\n <div class=\"history-content\">\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 mjButton (click)=\"exportHistoryToCSV()\" [disabled]=\"historyData.length === 0\">\n <i class=\"fas fa-download\"></i> Export CSV\n </button>\n </div>\n </div>\n <!-- KPI Cards -->\n @if (historyData.length > 0) {\n <div class=\"history-kpi-cards\">\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 @if (evalPreferences.showExecution) {\n <div class=\"kpi-card\">\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 }\n @if (evalPreferences.showAuto) {\n <div class=\"kpi-card\">\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 }\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 @if (historyData.length > 0) {\n <div class=\"history-section\">\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 @if (evalPreferences.showExecution) {\n <th>Passed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Failed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Pass Rate</th>\n }\n @if (evalPreferences.showAuto) {\n <th>Avg Score</th>\n }\n <th>Avg Duration</th>\n <th>Avg Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (day of historyData; track day) {\n <tr>\n <td class=\"date-cell\">{{ day.date | date:'mediumDate' }}</td>\n <td class=\"runs-cell\">{{ day.runCount }}</td>\n @if (evalPreferences.showExecution) {\n <td class=\"passed-cell\">{{ day.passCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"failed-cell\">{{ day.failCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"pass-rate-cell\">\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 }\n @if (evalPreferences.showAuto) {\n <td class=\"score-cell\">{{ (day.avgScore * 100).toFixed(1) }}%</td>\n }\n <td class=\"duration-cell\">{{ formatDuration(day.avgDuration) }}</td>\n <td class=\"cost-cell\">{{ formatCost(day.avgCost) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n <!-- Suite Performance -->\n @if (suitePerformance.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-layer-group\"></i> Performance by Suite</h3>\n <div class=\"suite-performance-list\">\n @for (suite of suitePerformance; track suite) {\n <div class=\"suite-perf-card\" (click)=\"openSuiteFromHistory(suite.suiteId)\">\n <div class=\"suite-perf-header\">\n <div class=\"suite-perf-name\">{{ suite.suiteName }}</div>\n @if (suite.tags.length > 0) {\n <div class=\"suite-perf-tags\">\n @for (tag of suite.tags.slice(0, 3); track tag) {\n <span class=\"tag-mini\">{{ tag }}</span>\n }\n @if (suite.tags.length > 3) {\n <span class=\"tag-more\">+{{ suite.tags.length - 3 }}</span>\n }\n </div>\n }\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 @if (evalPreferences.showExecution) {\n <div class=\"suite-stat\">\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 }\n @if (evalPreferences.showAuto) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ (suite.avgScore * 100).toFixed(1) }}%</span>\n <span class=\"stat-label\">Avg Score</span>\n </div>\n }\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 @if (suite.lastRun) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ getRelativeTime(suite.lastRun) }}</span>\n <span class=\"stat-label\">Last Run</span>\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right suite-perf-arrow\"></i>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty State -->\n @if (historyData.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-chart-line\"\n Title=\"No History Available\"\n Message=\"Run this test to start building history and analytics data.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n </div>\n }\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 @if (showShortcuts) {\n <div class=\"keyboard-shortcuts\">\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>Save changes</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>S</kbd></span>\n </div>\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 }\n\n <!-- Slide Panel for Test Execution -->\n @if (testingDialogService.IsPanelOpen) {\n <mj-slide-panel\n Mode=\"slide\"\n [Title]=\"testingDialogService.PanelOptions?.testId ? 'Test Execution' : 'Run Test'\"\n [Resizable]=\"true\"\n (Closed)=\"OnPanelClosed()\">\n <app-test-run-dialog\n [PanelMode]=\"true\"\n [selectedTestId]=\"testingDialogService.PanelOptions?.testId ?? null\"\n [selectedSuiteId]=\"testingDialogService.PanelOptions?.suiteId ?? null\"\n [runMode]=\"testingDialogService.PanelOptions?.mode ?? 'test'\"\n (PanelClose)=\"OnPanelClosed()\">\n </app-test-run-dialog>\n </mj-slide-panel>\n }\n\n <!-- Sticky Save Bar \u2014 always in the DOM so CSS transitions can fire reliably.\n Visibility is controlled by the .save-bar--visible class, driven by isDirty. -->\n <div\n class=\"save-bar\"\n [class.save-bar--visible]=\"isDirty\"\n [attr.aria-hidden]=\"!isDirty\"\n role=\"region\"\n aria-label=\"Unsaved changes\"\n >\n <div class=\"save-bar-message\">\n <span class=\"save-bar-dot\" aria-hidden=\"true\">\u25CF</span>\n <span class=\"save-bar-text\">\n <strong>Unsaved changes</strong>\n <span class=\"save-bar-fields\">\n @if (dirtyFieldNames.length === 1) {\n {{ dirtyFieldNames[0] }} edited\n } @else if (dirtyFieldNames.length > 1) {\n {{ dirtyFieldNames.length }} fields edited\n }\n </span>\n </span>\n </div>\n <div class=\"save-bar-actions\">\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--primary\"\n (click)=\"saveChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Save changes (\u2318S)\"\n >\n @if (isSaving) {\n <i class=\"fas fa-spinner fa-spin\"></i> Saving\u2026\n } @else {\n <i class=\"fas fa-check\"></i> Save changes\n <span class=\"save-bar-kbd\"><kbd>\u2318</kbd><kbd>S</kbd></span>\n }\n </button>\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--ghost\"\n (click)=\"discardChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Discard changes\"\n >\n <i class=\"fas fa-undo\"></i> Discard\n </button>\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: var(--mj-brand-primary);\n --test-primary-light: var(--mj-brand-primary);\n --test-primary-dark: var(--mj-brand-primary-hover);\n --test-success: var(--mj-status-success);\n --test-success-light: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n --test-error: var(--mj-status-error);\n --test-error-light: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n --test-warning: var(--mj-status-warning);\n --test-warning-light: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n --test-disabled: var(--mj-text-muted);\n --test-bg: var(--mj-bg-surface-card);\n --test-surface: var(--mj-bg-surface);\n --test-border: var(--mj-border-default);\n --test-text: var(--mj-text-primary);\n --test-text-secondary: var(--mj-text-muted);\n --test-text-muted: var(--mj-text-disabled);\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: var(--mj-shadow-sm);\n --test-shadow-md: var(--mj-shadow-md);\n --test-shadow-lg: var(--mj-shadow-lg);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n/* Breadcrumb */\n.breadcrumb {\n margin-bottom: 16px;\n}\n\n.breadcrumb ol {\n display: flex;\n align-items: center;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n font-size: 13px;\n}\n\n.breadcrumb li {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.breadcrumb a {\n color: var(--test-primary);\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.breadcrumb a:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n text-decoration: none;\n}\n\n.breadcrumb .separator {\n font-size: 10px;\n color: var(--test-text-muted);\n margin: 0 4px;\n}\n\n.breadcrumb .current {\n color: var(--test-text-secondary);\n font-weight: 500;\n}\n\n.breadcrumb-text {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Base Container */\n.test-form {\n position: relative;\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: var(--mj-text-inverse);\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: var(--mj-text-inverse);\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: var(--mj-status-success); }\n.status-badge.status-disabled { background: var(--mj-text-secondary); }\n.status-badge.status-pending { background: var(--mj-status-warning); }\n\n.status-badge-inline {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: var(--mj-text-inverse);\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: var(--mj-bg-surface-sunken);\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: var(--mj-bg-surface-sunken);\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: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\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: var(--mj-bg-overlay);\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 color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 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, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: var(--mj-text-inverse);\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon {\n background: var(--mj-status-warning);\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: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.status-pill.status-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.status-pill.status-error {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-timeout {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-skipped,\n.eval-pill.status-pill.status-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.eval-pill.status-pill.status-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Human feedback pills */\n.eval-pill.human-pill.no-feedback {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.human-pill.has-feedback.rating-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Auto score pills */\n.eval-pill.auto-pill.no-score {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.auto-pill.has-score.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.auto-pill.has-score.score-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.auto-pill.has-score.score-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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 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}\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n transform: scale(0.98);\n }\n\n .tab:active,\n .json-tab:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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: var(--mj-border-default);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.filter-btn.active {\n background: var(--test-primary);\n color: var(--mj-text-inverse);\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: var(--mj-brand-primary);\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate {\n background: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 3%, transparent);\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: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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: var(--mj-bg-surface);\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 break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n}\n\n/* ===========================\n Editable Form Sections (Config tab)\n =========================== */\n\n.edit-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.edit-section-header {\n margin: 0 0 18px 0;\n}\n\n.edit-section-header h3 {\n margin: 0 0 4px 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.edit-section-header h3 i {\n color: var(--test-primary);\n}\n\n.edit-section-sub {\n margin: 0;\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.edit-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 18px;\n}\n\n@media (max-width: 720px) {\n .edit-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.edit-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.edit-field--full {\n grid-column: 1 / -1;\n}\n\n.edit-field 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.edit-field .required {\n color: var(--mj-status-error);\n margin-left: 2px;\n}\n\n.config-textarea {\n resize: vertical;\n min-height: 72px;\n font-family: inherit;\n line-height: 1.5;\n}\n\n.select-wrapper {\n position: relative;\n}\n\n.select-wrapper select.config-input {\n padding-right: 36px;\n appearance: none;\n background-color: var(--test-surface);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2364748b' d='M1.41 0L6 4.59 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 14px center;\n background-size: 10px 7px;\n}\n\n/* Tag editor (chips + inline input) */\n.tag-editor {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n transition: var(--test-transition);\n cursor: text;\n min-height: 42px;\n align-items: center;\n}\n\n.tag-editor:focus-within {\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tag-chip-editable {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 4px 3px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary-hover);\n border-radius: 999px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n}\n\n.tag-chip-editable .tag-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 10px;\n transition: background 0.15s;\n}\n\n.tag-chip-editable .tag-remove:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n color: var(--mj-brand-primary-hover);\n}\n\n.tag-input-inline {\n flex: 1;\n min-width: 140px;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--test-text);\n padding: 4px 2px;\n}\n\n.tag-input-inline::placeholder {\n color: var(--test-text-muted);\n}\n\n/* Read-only metadata block at bottom of Config tab */\n.meta-section {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n padding: 14px 18px;\n border: 1px solid var(--test-border);\n}\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 14px;\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.meta-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}\n\n.meta-value {\n font-size: 12px;\n color: var(--test-text-secondary);\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.meta-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n font-size: 11px;\n}\n\n/* Dirty indicator next to the title */\n.dirty-indicator {\n display: inline-block;\n margin-left: 8px;\n font-size: 14px;\n color: var(--mj-status-warning);\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n vertical-align: middle;\n}\n\n@keyframes pulseDot {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.85); }\n}\n\n.unsaved-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 30%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.unsaved-pill i {\n font-size: 6px;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n/* Pad the bottom of the tab content when the save bar is visible\n so the bar doesn't cover the last form field. */\n.tab-content.has-savebar {\n padding-bottom: 84px;\n}\n\n/* ===========================\n Sticky Save Bar\n =========================== */\n\n.save-bar {\n position: absolute;\n left: 16px;\n right: 96px; /* leaves room for the bottom-right chat launcher */\n bottom: 16px;\n z-index: 50;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 12px 16px 12px 20px;\n background: var(--mj-bg-surface-elevated, var(--test-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, var(--test-border));\n border-radius: 14px;\n box-shadow: 0 12px 32px color-mix(in srgb, var(--mj-status-warning) 10%, rgba(0, 0, 0, 0.18));\n backdrop-filter: blur(8px);\n /* Hidden / off-screen state by default. The .save-bar--visible class\n animates it up into view. Using `transition` (not `animation`)\n guarantees the change runs every time isDirty toggles. */\n opacity: 0;\n transform: translateY(calc(100% + 32px));\n pointer-events: none;\n transition:\n transform 420ms cubic-bezier(0.16, 1, 0.3, 1),\n opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);\n will-change: transform, opacity;\n}\n\n.save-bar--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .save-bar {\n transition: opacity 180ms ease-out;\n transform: none;\n }\n .save-bar--visible {\n transform: none;\n }\n}\n\n.save-bar-message {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.save-bar-dot {\n color: var(--mj-status-warning);\n font-size: 12px;\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n.save-bar-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.save-bar-text strong {\n font-size: 14px;\n color: var(--test-text);\n font-weight: 700;\n}\n\n.save-bar-fields {\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.save-bar-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.save-bar-btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 9px 16px;\n font-size: 13px;\n font-weight: 600;\n border-radius: 8px;\n border: 1px solid transparent;\n cursor: pointer;\n transition: var(--test-transition);\n font-family: inherit;\n}\n\n.save-bar-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-bar-btn--primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.save-bar-btn--primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n\n.save-bar-btn--primary:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.save-bar-btn--ghost {\n background: transparent;\n color: var(--test-text-secondary);\n border-color: var(--test-border);\n}\n\n.save-bar-btn--ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card));\n color: var(--test-text);\n border-color: var(--test-text-muted);\n}\n\n.save-bar-kbd {\n display: inline-flex;\n gap: 3px;\n margin-left: 6px;\n opacity: 0.85;\n}\n\n.save-bar-kbd kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n background: color-mix(in srgb, white 18%, transparent);\n border: 1px solid color-mix(in srgb, white 35%, transparent);\n border-radius: 4px;\n color: inherit;\n}\n\n@media (max-width: 640px) {\n .save-bar {\n left: 8px;\n right: 8px;\n bottom: 88px; /* push above the chat launcher on small screens */\n flex-direction: column;\n align-items: stretch;\n padding: 12px;\n }\n .save-bar-actions {\n justify-content: space-between;\n }\n .save-bar-kbd {\n display: none;\n }\n}\n\n"] }]
|
|
2772
|
+
args: [{ standalone: false, selector: 'mj-test-form', changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"test-form\">\n <!-- Header Section -->\n <div class=\"test-header\">\n <!-- Breadcrumb Navigation -->\n <nav class=\"breadcrumb\" aria-label=\"Breadcrumb\">\n <ol>\n <li>\n <a href=\"javascript:void(0)\" (click)=\"navigateToTestingDashboard()\">\n <i class=\"fas fa-vial\"></i>\n <span class=\"breadcrumb-text\">Testing</span>\n </a>\n </li>\n <li class=\"current\">\n <i class=\"fas fa-chevron-right separator\"></i>\n <i class=\"fas fa-flask\"></i>\n <span>{{ record.Name }}</span>\n </li>\n </ol>\n </nav>\n\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>\n {{ record.Name }}\n @if (isDirty) {\n <span class=\"dirty-indicator\" title=\"You have unsaved changes\" aria-label=\"Unsaved changes\">\u25CF</span>\n }\n </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 @if (record.Type) {\n <span class=\"test-type\">\n <i class=\"fas fa-tag\"></i>\n {{ record.Type }}\n </span>\n }\n @if (isDirty) {\n <span class=\"unsaved-pill\" title=\"You have unsaved changes\">\n <i class=\"fas fa-circle\"></i> Unsaved\n </span>\n }\n </div>\n </div>\n </div>\n <div class=\"header-actions\">\n <app-evaluation-mode-toggle></app-evaluation-mode-toggle>\n <button mjButton (click)=\"runTest()\" variant=\"primary\">\n <i class=\"fas fa-play\"></i> Run Test\n </button>\n <button mjButton (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 @if (record.Description) {\n <div class=\"test-description\">\n <p>{{ record.Description }}</p>\n </div>\n }\n\n <!-- Metrics Bar -->\n @if (testRunsLoaded && testRuns.length > 0) {\n <div class=\"metrics-bar\">\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 }\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 @if (testRunsLoaded) {\n <span class=\"tab-badge\">{{ testRuns.length }}</span>\n }\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 @if (suiteTestsLoaded) {\n <span class=\"tab-badge\">{{ suiteTests.length }}</span>\n }\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\" [class.has-savebar]=\"isDirty\">\n <!-- Overview Tab -->\n @if (activeTab === 'overview') {\n <div class=\"overview-tab\">\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 <!-- 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\n <!-- Configuration Tab -->\n @if (activeTab === 'config') {\n <div class=\"config-tab\">\n <!-- Details -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-pen-to-square\"></i> Details</h3>\n <p class=\"edit-section-sub\">Edit any field \u2014 changes are saved with the bar at the bottom.</p>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-name\">Name <span class=\"required\">*</span></label>\n <input\n id=\"test-name\"\n type=\"text\"\n class=\"config-input\"\n placeholder=\"e.g., Agent Response Quality Test\"\n [(ngModel)]=\"record.Name\"\n />\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-description\">Description</label>\n <textarea\n id=\"test-description\"\n class=\"config-input config-textarea\"\n rows=\"3\"\n placeholder=\"What does this test evaluate?\"\n [ngModel]=\"record.Description\"\n (ngModelChange)=\"record.Description = $event || null\"\n ></textarea>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-status\">Status</label>\n <div class=\"select-wrapper\">\n <select\n id=\"test-status\"\n class=\"config-input\"\n [(ngModel)]=\"record.Status\"\n >\n @for (opt of statusOptions; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n </div>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-rubric\">Rubric</label>\n <select\n id=\"test-rubric\"\n class=\"config-input\"\n [ngModel]=\"record.RubricID\"\n (ngModelChange)=\"record.RubricID = $event || null\"\n >\n <option [ngValue]=\"null\">\u2014 None \u2014</option>\n @for (rubric of RubricOptions; track rubric.id) {\n <option [ngValue]=\"rubric.id\">{{ rubric.name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field\">\n <label for=\"test-type\">Test Type</label>\n <select\n id=\"test-type\"\n class=\"config-input\"\n [(ngModel)]=\"record.TypeID\"\n >\n @for (t of testTypeOptions; track t.ID) {\n <option [ngValue]=\"t.ID\">{{ t.Name }}</option>\n }\n </select>\n </div>\n\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-tags\">Tags</label>\n <div class=\"tag-editor\" (click)=\"tagInput.focus()\">\n @for (tag of tags; track tag) {\n <span class=\"tag-chip-editable\">\n {{ tag }}\n <button type=\"button\" class=\"tag-remove\" (click)=\"removeTag(tag); $event.stopPropagation()\" [attr.aria-label]=\"'Remove tag ' + tag\">\n <i class=\"fas fa-times\"></i>\n </button>\n </span>\n }\n <input\n #tagInput\n id=\"test-tags\"\n type=\"text\"\n class=\"tag-input-inline\"\n [placeholder]=\"tags.length === 0 ? 'Add tags, press Enter or comma...' : ''\"\n [(ngModel)]=\"tagDraft\"\n (keydown)=\"onTagInputKeydown($event)\"\n (blur)=\"addTagFromDraft()\"\n />\n </div>\n <span class=\"config-hint\">Press Enter or comma to add a tag. Backspace on empty input removes the last tag.</span>\n </div>\n </div>\n </section>\n\n <!-- Execution Settings -->\n <section class=\"edit-section\">\n <header class=\"edit-section-header\">\n <h3><i class=\"fas fa-cogs\"></i> Execution Settings</h3>\n </header>\n <div class=\"edit-grid\">\n <div class=\"edit-field\">\n <label for=\"test-priority\">Priority</label>\n <input\n id=\"test-priority\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0 (lower runs first)\"\n [ngModel]=\"record.Priority\"\n (ngModelChange)=\"record.Priority = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Lower numbers run first.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-repeat\">Repeat Count</label>\n <input\n id=\"test-repeat\"\n type=\"number\"\n class=\"config-input\"\n min=\"1\"\n placeholder=\"1\"\n [ngModel]=\"record.RepeatCount\"\n (ngModelChange)=\"record.RepeatCount = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Number of times to repeat for statistical analysis.</span>\n </div>\n <div class=\"edit-field\">\n <label for=\"test-duration\">Estimated Duration (seconds)</label>\n <input\n id=\"test-duration\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"0\"\n [ngModel]=\"record.EstimatedDurationSeconds\"\n (ngModelChange)=\"record.EstimatedDurationSeconds = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field\">\n <label for=\"test-cost\">Estimated Cost (USD)</label>\n <input\n id=\"test-cost\"\n type=\"number\"\n step=\"0.000001\"\n class=\"config-input\"\n placeholder=\"0.00\"\n [ngModel]=\"record.EstimatedCostUSD\"\n (ngModelChange)=\"record.EstimatedCostUSD = $event === '' || $event === null ? null : +$event\"\n />\n </div>\n <div class=\"edit-field edit-field--full\">\n <label for=\"test-timeout\">Max Execution Time (ms)</label>\n <input\n id=\"test-timeout\"\n type=\"number\"\n class=\"config-input\"\n placeholder=\"Default: 300000 (5 min)\"\n [ngModel]=\"record.MaxExecutionTimeMS\"\n (ngModelChange)=\"record.MaxExecutionTimeMS = $event === '' || $event === null ? null : +$event\"\n />\n <span class=\"config-hint\">Leave empty for the default 5 minute timeout. Currently: {{ formatTimeout(record.MaxExecutionTimeMS) }}</span>\n </div>\n </div>\n </section>\n\n <!-- JSON editors (preserved from original layout) -->\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 <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 <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 <!-- Metadata (read-only) -->\n <section class=\"meta-section\">\n <div class=\"meta-grid\">\n <div class=\"meta-item\">\n <div class=\"meta-label\">Created</div>\n <div class=\"meta-value\">{{ record.__mj_CreatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Last updated</div>\n <div class=\"meta-value\">{{ record.__mj_UpdatedAt | date:'medium' }}</div>\n </div>\n <div class=\"meta-item\">\n <div class=\"meta-label\">Test ID</div>\n <div class=\"meta-value meta-mono\">{{ record.ID }}</div>\n </div>\n </div>\n </section>\n </div>\n }\n\n <!-- Test Runs Tab -->\n @if (activeTab === 'runs') {\n <div class=\"runs-tab\">\n <!-- Loading State -->\n @if (loadingRuns) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3,4,5]; track i) {\n <div class=\"skeleton-card\">\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 }\n </div>\n </div>\n }\n <!-- Runs List -->\n @if (!loadingRuns && testRuns.length > 0) {\n <div class=\"runs-list\">\n @for (run of testRuns; track run) {\n <div class=\"run-item\" (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 @if (run.DurationSeconds) {\n <span><i class=\"fas fa-clock\"></i> {{ formatDuration(run.DurationSeconds) }}</span>\n }\n @if (run.CostUSD) {\n <span><i class=\"fas fa-dollar-sign\"></i> {{ formatCost(run.CostUSD) }}</span>\n }\n @if (run.TargetLogEntityID && run.TargetLogID) {\n <mj-entity-link-pill\n [entityName]=\"run.TargetLogEntity\"\n [recordId]=\"run.TargetLogID\">\n </mj-entity-link-pill>\n }\n </div>\n <!-- Evaluation indicators -->\n <div class=\"run-eval-stack\">\n <!-- Status indicator -->\n @if (evalPreferences.showExecution) {\n <span class=\"eval-pill status-pill\"\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 }\n <!-- Human feedback indicator -->\n @if (evalPreferences.showHuman) {\n @if (!getFeedbackForRun(run.ID)?.Rating) {\n <span class=\"eval-pill human-pill no-feedback\"\n title=\"Human Review: No rating submitted yet\">\n <i class=\"fas fa-user-slash\"></i>\n </span>\n }\n @if (getFeedbackForRun(run.ID)?.Rating; as rating) {\n <span class=\"eval-pill human-pill has-feedback\"\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 }\n }\n <!-- Auto score indicator -->\n @if (evalPreferences.showAuto) {\n @if (run.Score == null) {\n <span class=\"eval-pill auto-pill no-score\"\n title=\"Auto Score: No automated score available\">\n <i class=\"fas fa-robot\"></i>\n </span>\n }\n @if (run.Score != null) {\n <span class=\"eval-pill auto-pill has-score\"\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 }\n }\n </div>\n @if (getRunTags(run).length > 0) {\n <div class=\"run-tags\">\n @for (tag of getRunTags(run).slice(0, 3); track tag) {\n <span class=\"run-tag\">{{ tag }}</span>\n }\n @if (getRunTags(run).length > 3) {\n <span class=\"run-tag-more\">+{{ getRunTags(run).length - 3 }}</span>\n }\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (testRunsLoaded && !loadingRuns && testRuns.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-circle-play\"\n Title=\"No Test Runs Yet\"\n Message=\"Run this test to see execution history and results here.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n\n <!-- Test Suites Tab -->\n @if (activeTab === 'suites') {\n <div class=\"suites-tab\">\n <!-- Loading State -->\n @if (loadingSuites) {\n <div class=\"loading-state\">\n <div class=\"skeleton-list\">\n @for (i of [1,2,3]; track i) {\n <div class=\"skeleton-card\">\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 }\n </div>\n </div>\n }\n <!-- Suites List -->\n @if (!loadingSuites && suiteTests.length > 0) {\n <div class=\"suites-list\">\n @for (suiteTest of suiteTests; track suiteTest) {\n <div class=\"suite-item\" (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 @if (suiteTest.Status) {\n <span><i class=\"fas fa-info-circle\"></i> {{ suiteTest.Status }}</span>\n }\n </div>\n </div>\n <i class=\"fas fa-chevron-right\"></i>\n </div>\n }\n </div>\n }\n <!-- Empty State -->\n @if (suiteTestsLoaded && !loadingSuites && suiteTests.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-folder-open\"\n Title=\"Not Part of Any Suite\"\n Message=\"This test is not included in any test suites. Add it to a suite to run it with other tests.\" />\n }\n </div>\n }\n\n <!-- Analytics Tab -->\n @if (activeTab === 'analytics') {\n <div class=\"history-tab\">\n <!-- Loading State -->\n @if (loadingHistory) {\n <div class=\"loading-state\">\n <mj-loading text=\"Loading history...\"></mj-loading>\n </div>\n }\n <!-- History Content -->\n @if (!loadingHistory && historyLoaded) {\n <div class=\"history-content\">\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 mjButton (click)=\"exportHistoryToCSV()\" [disabled]=\"historyData.length === 0\">\n <i class=\"fas fa-download\"></i> Export CSV\n </button>\n </div>\n </div>\n <!-- KPI Cards -->\n @if (historyData.length > 0) {\n <div class=\"history-kpi-cards\">\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 @if (evalPreferences.showExecution) {\n <div class=\"kpi-card\">\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 }\n @if (evalPreferences.showAuto) {\n <div class=\"kpi-card\">\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 }\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 @if (historyData.length > 0) {\n <div class=\"history-section\">\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 @if (evalPreferences.showExecution) {\n <th>Passed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Failed</th>\n }\n @if (evalPreferences.showExecution) {\n <th>Pass Rate</th>\n }\n @if (evalPreferences.showAuto) {\n <th>Avg Score</th>\n }\n <th>Avg Duration</th>\n <th>Avg Cost</th>\n </tr>\n </thead>\n <tbody>\n @for (day of historyData; track day) {\n <tr>\n <td class=\"date-cell\">{{ day.date | date:'mediumDate' }}</td>\n <td class=\"runs-cell\">{{ day.runCount }}</td>\n @if (evalPreferences.showExecution) {\n <td class=\"passed-cell\">{{ day.passCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"failed-cell\">{{ day.failCount }}</td>\n }\n @if (evalPreferences.showExecution) {\n <td class=\"pass-rate-cell\">\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 }\n @if (evalPreferences.showAuto) {\n <td class=\"score-cell\">{{ (day.avgScore * 100).toFixed(1) }}%</td>\n }\n <td class=\"duration-cell\">{{ formatDuration(day.avgDuration) }}</td>\n <td class=\"cost-cell\">{{ formatCost(day.avgCost) }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n <!-- Suite Performance -->\n @if (suitePerformance.length > 0) {\n <div class=\"history-section\">\n <h3><i class=\"fas fa-layer-group\"></i> Performance by Suite</h3>\n <div class=\"suite-performance-list\">\n @for (suite of suitePerformance; track suite) {\n <div class=\"suite-perf-card\" (click)=\"openSuiteFromHistory(suite.suiteId)\">\n <div class=\"suite-perf-header\">\n <div class=\"suite-perf-name\">{{ suite.suiteName }}</div>\n @if (suite.tags.length > 0) {\n <div class=\"suite-perf-tags\">\n @for (tag of suite.tags.slice(0, 3); track tag) {\n <span class=\"tag-mini\">{{ tag }}</span>\n }\n @if (suite.tags.length > 3) {\n <span class=\"tag-more\">+{{ suite.tags.length - 3 }}</span>\n }\n </div>\n }\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 @if (evalPreferences.showExecution) {\n <div class=\"suite-stat\">\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 }\n @if (evalPreferences.showAuto) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ (suite.avgScore * 100).toFixed(1) }}%</span>\n <span class=\"stat-label\">Avg Score</span>\n </div>\n }\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 @if (suite.lastRun) {\n <div class=\"suite-stat\">\n <span class=\"stat-value\">{{ getRelativeTime(suite.lastRun) }}</span>\n <span class=\"stat-label\">Last Run</span>\n </div>\n }\n </div>\n <i class=\"fas fa-chevron-right suite-perf-arrow\"></i>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty State -->\n @if (historyData.length === 0) {\n <mj-empty-state\n Icon=\"fa-solid fa-chart-line\"\n Title=\"No History Available\"\n Message=\"Run this test to start building history and analytics data.\"\n ActionText=\"Run Test Now\"\n ActionIcon=\"fa-solid fa-play\"\n (Action)=\"runTest()\" />\n }\n </div>\n }\n </div>\n }\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 @if (showShortcuts) {\n <div class=\"keyboard-shortcuts\">\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>Save changes</span>\n <span class=\"shortcut-keys\"><kbd>Cmd</kbd><kbd>S</kbd></span>\n </div>\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 }\n\n <!-- Slide Panel for Test Execution -->\n @if (testingDialogService.IsPanelOpen) {\n <mj-slide-panel\n Mode=\"slide\"\n [Title]=\"testingDialogService.PanelOptions?.testId ? 'Test Execution' : 'Run Test'\"\n [Resizable]=\"true\"\n (Closed)=\"OnPanelClosed()\">\n <app-test-run-dialog\n [PanelMode]=\"true\"\n [selectedTestId]=\"testingDialogService.PanelOptions?.testId ?? null\"\n [selectedSuiteId]=\"testingDialogService.PanelOptions?.suiteId ?? null\"\n [runMode]=\"testingDialogService.PanelOptions?.mode ?? 'test'\"\n (PanelClose)=\"OnPanelClosed()\">\n </app-test-run-dialog>\n </mj-slide-panel>\n }\n\n <!-- Sticky Save Bar \u2014 always in the DOM so CSS transitions can fire reliably.\n Visibility is controlled by the .save-bar--visible class, driven by isDirty. -->\n <div\n class=\"save-bar\"\n [class.save-bar--visible]=\"isDirty\"\n [attr.aria-hidden]=\"!isDirty\"\n role=\"region\"\n aria-label=\"Unsaved changes\"\n >\n <div class=\"save-bar-message\">\n <span class=\"save-bar-dot\" aria-hidden=\"true\">\u25CF</span>\n <span class=\"save-bar-text\">\n <strong>Unsaved changes</strong>\n <span class=\"save-bar-fields\">\n @if (dirtyFieldNames.length === 1) {\n {{ dirtyFieldNames[0] }} edited\n } @else if (dirtyFieldNames.length > 1) {\n {{ dirtyFieldNames.length }} fields edited\n }\n </span>\n </span>\n </div>\n <div class=\"save-bar-actions\">\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--primary\"\n (click)=\"saveChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Save changes (\u2318S)\"\n >\n @if (isSaving) {\n <i class=\"fas fa-spinner fa-spin\"></i> Saving\u2026\n } @else {\n <i class=\"fas fa-check\"></i> Save changes\n <span class=\"save-bar-kbd\"><kbd>\u2318</kbd><kbd>S</kbd></span>\n }\n </button>\n <button\n type=\"button\"\n class=\"save-bar-btn save-bar-btn--ghost\"\n (click)=\"discardChanges()\"\n [disabled]=\"isSaving || !isDirty\"\n [attr.tabindex]=\"isDirty ? 0 : -1\"\n title=\"Discard changes\"\n >\n <i class=\"fas fa-undo\"></i> Discard\n </button>\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: var(--mj-brand-primary);\n --test-primary-light: var(--mj-brand-primary);\n --test-primary-dark: var(--mj-brand-primary-hover);\n --test-success: var(--mj-status-success);\n --test-success-light: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n --test-error: var(--mj-status-error);\n --test-error-light: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n --test-warning: var(--mj-status-warning);\n --test-warning-light: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n --test-disabled: var(--mj-text-muted);\n --test-bg: var(--mj-bg-surface-card);\n --test-surface: var(--mj-bg-surface);\n --test-border: var(--mj-border-default);\n --test-text: var(--mj-text-primary);\n --test-text-secondary: var(--mj-text-muted);\n --test-text-muted: var(--mj-text-disabled);\n --test-radius-sm: 6px;\n --test-radius-md: 10px;\n --test-radius-lg: 16px;\n --test-shadow-sm: var(--mj-shadow-sm);\n --test-shadow-md: var(--mj-shadow-md);\n --test-shadow-lg: var(--mj-shadow-lg);\n --test-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n display: block;\n height: 100%;\n}\n\n/* Breadcrumb */\n.breadcrumb {\n margin-bottom: 16px;\n}\n\n.breadcrumb ol {\n display: flex;\n align-items: center;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n font-size: 13px;\n}\n\n.breadcrumb li {\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.breadcrumb a {\n color: var(--test-primary);\n text-decoration: none;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 8px;\n border-radius: 6px;\n transition: background 0.15s;\n}\n\n.breadcrumb a:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n text-decoration: none;\n}\n\n.breadcrumb .separator {\n font-size: 10px;\n color: var(--test-text-muted);\n margin: 0 4px;\n}\n\n.breadcrumb .current {\n color: var(--test-text-secondary);\n font-weight: 500;\n}\n\n.breadcrumb-text {\n max-width: 200px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Base Container */\n.test-form {\n position: relative;\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: var(--mj-text-inverse);\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: var(--mj-text-inverse);\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: var(--mj-status-success); }\n.status-badge.status-disabled { background: var(--mj-text-secondary); }\n.status-badge.status-pending { background: var(--mj-status-warning); }\n\n.status-badge-inline {\n display: inline-flex;\n align-items: center;\n padding: 2px 10px;\n border-radius: 10px;\n color: var(--mj-text-inverse);\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: var(--mj-bg-surface-sunken);\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: var(--mj-bg-surface-sunken);\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: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\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: var(--mj-bg-overlay);\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 color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 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, var(--mj-border-default) 25%, var(--mj-bg-surface-sunken) 50%, var(--mj-border-default) 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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: var(--mj-text-inverse);\n font-size: 18px;\n flex-shrink: 0;\n box-shadow: var(--test-shadow-sm);\n}\n\n.suite-icon {\n background: var(--mj-status-warning);\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: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.status-pill.status-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.status-pill.status-error {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-timeout {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.status-pill.status-skipped,\n.eval-pill.status-pill.status-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.eval-pill.status-pill.status-running {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n/* Human feedback pills */\n.eval-pill.human-pill.no-feedback {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.human-pill.has-feedback.rating-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.human-pill.has-feedback.rating-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.human-pill.has-feedback.rating-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.human-pill.has-feedback.rating-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n/* Auto score pills */\n.eval-pill.auto-pill.no-score {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n padding: 4px 8px;\n}\n\n.eval-pill.auto-pill.has-score.score-low {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.eval-pill.auto-pill.has-score.score-medium {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.eval-pill.auto-pill.has-score.score-good {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.eval-pill.auto-pill.has-score.score-excellent {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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 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}\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n transform: scale(0.98);\n }\n\n .tab:active,\n .json-tab:active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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: var(--mj-border-default);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\n}\n\n.filter-btn.active {\n background: var(--test-primary);\n color: var(--mj-text-inverse);\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: var(--mj-brand-primary);\n border-radius: var(--test-radius-md);\n color: var(--mj-text-inverse);\n font-size: 20px;\n flex-shrink: 0;\n}\n\n.kpi-icon.pass-rate {\n background: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 3%, transparent);\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: var(--mj-status-success);\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: color-mix(in srgb, var(--mj-brand-primary) 5%, transparent);\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: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\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: var(--mj-bg-surface);\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 break-inside: avoid;\n box-shadow: none;\n border: 1px solid var(--mj-border-default);\n }\n}\n\n/* ===========================\n Editable Form Sections (Config tab)\n =========================== */\n\n.edit-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.edit-section-header {\n margin: 0 0 18px 0;\n}\n\n.edit-section-header h3 {\n margin: 0 0 4px 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.edit-section-header h3 i {\n color: var(--test-primary);\n}\n\n.edit-section-sub {\n margin: 0;\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.edit-grid {\n display: grid;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: 18px;\n}\n\n@media (max-width: 720px) {\n .edit-grid {\n grid-template-columns: 1fr;\n }\n}\n\n.edit-field {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n}\n\n.edit-field--full {\n grid-column: 1 / -1;\n}\n\n.edit-field 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.edit-field .required {\n color: var(--mj-status-error);\n margin-left: 2px;\n}\n\n.config-textarea {\n resize: vertical;\n min-height: 72px;\n font-family: inherit;\n line-height: 1.5;\n}\n\n.select-wrapper {\n position: relative;\n}\n\n.select-wrapper select.config-input {\n padding-right: 36px;\n appearance: none;\n background-color: var(--test-surface);\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%2364748b' d='M1.41 0L6 4.59 10.59 0 12 1.41l-6 6-6-6z'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 14px center;\n background-size: 10px 7px;\n}\n\n/* Tag editor (chips + inline input) */\n.tag-editor {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n padding: 8px 10px;\n border: 1px solid var(--test-border);\n border-radius: var(--test-radius-sm);\n background: var(--test-surface);\n transition: var(--test-transition);\n cursor: text;\n min-height: 42px;\n align-items: center;\n}\n\n.tag-editor:focus-within {\n border-color: var(--test-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.tag-chip-editable {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 3px 4px 3px 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary-hover);\n border-radius: 999px;\n font-size: 12px;\n font-weight: 600;\n line-height: 1;\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n}\n\n.tag-chip-editable .tag-remove {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n border-radius: 50%;\n border: none;\n background: transparent;\n color: var(--mj-brand-primary);\n cursor: pointer;\n font-size: 10px;\n transition: background 0.15s;\n}\n\n.tag-chip-editable .tag-remove:hover {\n background: color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n color: var(--mj-brand-primary-hover);\n}\n\n.tag-input-inline {\n flex: 1;\n min-width: 140px;\n border: none;\n background: transparent;\n outline: none;\n font-size: 13px;\n color: var(--test-text);\n padding: 4px 2px;\n}\n\n.tag-input-inline::placeholder {\n color: var(--test-text-muted);\n}\n\n/* Read-only metadata block at bottom of Config tab */\n.meta-section {\n background: var(--mj-bg-surface-sunken);\n border-radius: var(--test-radius-md);\n padding: 14px 18px;\n border: 1px solid var(--test-border);\n}\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n gap: 14px;\n}\n\n.meta-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.meta-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}\n\n.meta-value {\n font-size: 12px;\n color: var(--test-text-secondary);\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.meta-mono {\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n font-size: 11px;\n}\n\n/* Dirty indicator next to the title */\n.dirty-indicator {\n display: inline-block;\n margin-left: 8px;\n font-size: 14px;\n color: var(--mj-status-warning);\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n vertical-align: middle;\n}\n\n@keyframes pulseDot {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: 0.5; transform: scale(0.85); }\n}\n\n.unsaved-pill {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 4px 10px;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text, var(--mj-status-warning));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 30%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.4px;\n}\n\n.unsaved-pill i {\n font-size: 6px;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n/* Pad the bottom of the tab content when the save bar is visible\n so the bar doesn't cover the last form field. */\n.tab-content.has-savebar {\n padding-bottom: 84px;\n}\n\n/* ===========================\n Sticky Save Bar\n =========================== */\n\n.save-bar {\n position: absolute;\n left: 16px;\n right: 96px; /* leaves room for the bottom-right chat launcher */\n bottom: 16px;\n z-index: 50;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 12px 16px 12px 20px;\n background: var(--mj-bg-surface-elevated, var(--test-surface));\n border: 1px solid color-mix(in srgb, var(--mj-status-warning) 25%, var(--test-border));\n border-radius: 14px;\n box-shadow: 0 12px 32px color-mix(in srgb, var(--mj-status-warning) 10%, rgba(0, 0, 0, 0.18));\n backdrop-filter: blur(8px);\n /* Hidden / off-screen state by default. The .save-bar--visible class\n animates it up into view. Using `transition` (not `animation`)\n guarantees the change runs every time isDirty toggles. */\n opacity: 0;\n transform: translateY(calc(100% + 32px));\n pointer-events: none;\n transition:\n transform 420ms cubic-bezier(0.16, 1, 0.3, 1),\n opacity 260ms cubic-bezier(0.16, 1, 0.3, 1);\n will-change: transform, opacity;\n}\n\n.save-bar--visible {\n opacity: 1;\n transform: translateY(0);\n pointer-events: auto;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .save-bar {\n transition: opacity 180ms ease-out;\n transform: none;\n }\n .save-bar--visible {\n transform: none;\n }\n}\n\n.save-bar-message {\n display: flex;\n align-items: center;\n gap: 12px;\n min-width: 0;\n}\n\n.save-bar-dot {\n color: var(--mj-status-warning);\n font-size: 12px;\n line-height: 1;\n animation: pulseDot 1.8s ease-in-out infinite;\n}\n\n.save-bar-text {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.save-bar-text strong {\n font-size: 14px;\n color: var(--test-text);\n font-weight: 700;\n}\n\n.save-bar-fields {\n font-size: 12px;\n color: var(--test-text-muted);\n}\n\n.save-bar-actions {\n display: flex;\n align-items: center;\n gap: 8px;\n flex-shrink: 0;\n}\n\n.save-bar-btn {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 9px 16px;\n font-size: 13px;\n font-weight: 600;\n border-radius: 8px;\n border: 1px solid transparent;\n cursor: pointer;\n transition: var(--test-transition);\n font-family: inherit;\n}\n\n.save-bar-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.save-bar-btn--primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse, white);\n border-color: var(--mj-brand-primary);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n}\n\n.save-bar-btn--primary:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover, var(--mj-brand-primary));\n transform: translateY(-1px);\n box-shadow: 0 4px 12px color-mix(in srgb, var(--mj-brand-primary) 45%, transparent);\n}\n\n.save-bar-btn--primary:active:not(:disabled) {\n transform: translateY(0);\n}\n\n.save-bar-btn--ghost {\n background: transparent;\n color: var(--test-text-secondary);\n border-color: var(--test-border);\n}\n\n.save-bar-btn--ghost:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover, var(--mj-bg-surface-card));\n color: var(--test-text);\n border-color: var(--test-text-muted);\n}\n\n.save-bar-kbd {\n display: inline-flex;\n gap: 3px;\n margin-left: 6px;\n opacity: 0.85;\n}\n\n.save-bar-kbd kbd {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 18px;\n height: 18px;\n padding: 0 4px;\n font-size: 10px;\n font-weight: 600;\n font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;\n background: color-mix(in srgb, white 18%, transparent);\n border: 1px solid color-mix(in srgb, white 35%, transparent);\n border-radius: 4px;\n color: inherit;\n}\n\n@media (max-width: 640px) {\n .save-bar {\n left: 8px;\n right: 8px;\n bottom: 88px; /* push above the chat launcher on small screens */\n flex-direction: column;\n align-items: stretch;\n padding: 12px;\n }\n .save-bar-actions {\n justify-content: space-between;\n }\n .save-bar-kbd {\n display: none;\n }\n}\n\n"] }]
|
|
2352
2773
|
}], null, { handleKeyboardShortcut: [{
|
|
2353
2774
|
type: HostListener,
|
|
2354
2775
|
args: ['document:keydown', ['$event']]
|
|
@@ -2356,5 +2777,5 @@ export { MJTestFormComponentExtended };
|
|
|
2356
2777
|
type: HostListener,
|
|
2357
2778
|
args: ['window:beforeunload', ['$event']]
|
|
2358
2779
|
}] }); })();
|
|
2359
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJTestFormComponentExtended, { className: "MJTestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber:
|
|
2780
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJTestFormComponentExtended, { className: "MJTestFormComponentExtended", filePath: "src/lib/custom/Tests/test-form.component.ts", lineNumber: 64 }); })();
|
|
2360
2781
|
//# sourceMappingURL=test-form.component.js.map
|