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