@silasfmartins/testhub 1.0.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/.github/copilot-instructions.md +520 -0
- package/biome.json +37 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +169 -0
- package/dist/scripts/consumer-postinstall.d.ts +15 -0
- package/dist/scripts/consumer-postinstall.js +785 -0
- package/dist/scripts/generate-docs.d.ts +16 -0
- package/dist/scripts/generate-docs.js +1363 -0
- package/dist/scripts/generate-index.d.ts +2 -0
- package/dist/scripts/generate-index.js +314 -0
- package/dist/scripts/init-api.d.ts +2 -0
- package/dist/scripts/init-api.js +525 -0
- package/dist/scripts/init-banco.d.ts +2 -0
- package/dist/scripts/init-banco.js +347 -0
- package/dist/scripts/init-frontend.d.ts +2 -0
- package/dist/scripts/init-frontend.js +627 -0
- package/dist/scripts/init-mobile.d.ts +2 -0
- package/dist/scripts/init-mobile.js +481 -0
- package/dist/scripts/init-scenarios.d.ts +2 -0
- package/dist/scripts/init-scenarios.js +846 -0
- package/dist/scripts/init-ssh.d.ts +2 -0
- package/dist/scripts/init-ssh.js +639 -0
- package/dist/scripts/package-versions.d.ts +57 -0
- package/dist/scripts/package-versions.js +768 -0
- package/dist/scripts/postinstall.d.ts +1 -0
- package/dist/scripts/postinstall.js +527 -0
- package/dist/scripts/robust-build.d.ts +7 -0
- package/dist/scripts/robust-build.js +88 -0
- package/dist/scripts/setup-local-packages.d.ts +31 -0
- package/dist/scripts/setup-local-packages.js +237 -0
- package/dist/scripts/smart-override.d.ts +2 -0
- package/dist/scripts/smart-override.js +1360 -0
- package/dist/scripts/sync-configs.d.ts +27 -0
- package/dist/scripts/sync-configs.js +248 -0
- package/dist/scripts/test-biome-parse.d.ts +5 -0
- package/dist/scripts/test-biome-parse.js +84 -0
- package/dist/scripts/ultracite-setup.d.ts +4 -0
- package/dist/scripts/ultracite-setup.js +310 -0
- package/dist/scripts/update-all-init-scripts.d.ts +2 -0
- package/dist/scripts/update-all-init-scripts.js +52 -0
- package/dist/scripts/update-biome-schema.d.ts +15 -0
- package/dist/scripts/update-biome-schema.js +124 -0
- package/dist/src/AutoCoreFacade.d.ts +145 -0
- package/dist/src/AutoCoreFacade.js +217 -0
- package/dist/src/api/ApiActions.d.ts +297 -0
- package/dist/src/api/ApiActions.js +1905 -0
- package/dist/src/api/Certificate.d.ts +60 -0
- package/dist/src/api/Certificate.js +79 -0
- package/dist/src/api/JsonResponse.d.ts +116 -0
- package/dist/src/api/JsonResponse.js +206 -0
- package/dist/src/appium/DeviceFarmViewer.d.ts +79 -0
- package/dist/src/appium/DeviceFarmViewer.js +1083 -0
- package/dist/src/appium/MobileActions.d.ts +347 -0
- package/dist/src/appium/MobileActions.js +1632 -0
- package/dist/src/appium/MobileConnection.d.ts +160 -0
- package/dist/src/appium/MobileConnection.js +772 -0
- package/dist/src/config/envLoader.d.ts +123 -0
- package/dist/src/config/envLoader.js +361 -0
- package/dist/src/config/jest-safe-setup.d.ts +19 -0
- package/dist/src/config/jest-safe-setup.js +369 -0
- package/dist/src/config/timeouts.d.ts +32 -0
- package/dist/src/config/timeouts.js +38 -0
- package/dist/src/desktop/DesktopActions.d.ts +46 -0
- package/dist/src/desktop/DesktopActions.js +398 -0
- package/dist/src/desktop/DesktopConnection.d.ts +32 -0
- package/dist/src/desktop/DesktopConnection.js +84 -0
- package/dist/src/domain/entities/TestExecution.d.ts +117 -0
- package/dist/src/domain/entities/TestExecution.js +150 -0
- package/dist/src/domain/entities/TestReport.d.ts +114 -0
- package/dist/src/domain/entities/TestReport.js +179 -0
- package/dist/src/domain/repositories/ITestRepository.d.ts +196 -0
- package/dist/src/domain/repositories/ITestRepository.js +14 -0
- package/dist/src/domain/schemas/ValidationSchemas.d.ts +159 -0
- package/dist/src/domain/schemas/ValidationSchemas.js +181 -0
- package/dist/src/functions/errors/BaseError.d.ts +78 -0
- package/dist/src/functions/errors/BaseError.js +245 -0
- package/dist/src/functions/errors/ConfigurationError.d.ts +16 -0
- package/dist/src/functions/errors/ConfigurationError.js +48 -0
- package/dist/src/functions/errors/ErrorCatalog.d.ts +148 -0
- package/dist/src/functions/errors/ErrorCatalog.js +157 -0
- package/dist/src/functions/errors/GlobalErrorHandler.d.ts +101 -0
- package/dist/src/functions/errors/GlobalErrorHandler.js +281 -0
- package/dist/src/functions/errors/IntegrationError.d.ts +17 -0
- package/dist/src/functions/errors/IntegrationError.js +51 -0
- package/dist/src/functions/errors/SecurityError.d.ts +14 -0
- package/dist/src/functions/errors/SecurityError.js +42 -0
- package/dist/src/functions/errors/SystemError.d.ts +12 -0
- package/dist/src/functions/errors/SystemError.js +36 -0
- package/dist/src/functions/errors/ValidationError.d.ts +14 -0
- package/dist/src/functions/errors/ValidationError.js +61 -0
- package/dist/src/functions/errors/index.d.ts +12 -0
- package/dist/src/functions/errors/index.js +13 -0
- package/dist/src/global-setup.d.ts +1 -0
- package/dist/src/global-setup.js +1037 -0
- package/dist/src/helpers/BancoActions.d.ts +188 -0
- package/dist/src/helpers/BancoActions.js +581 -0
- package/dist/src/helpers/EnviromentHelper.d.ts +17 -0
- package/dist/src/helpers/EnviromentHelper.js +66 -0
- package/dist/src/helpers/ParallelExecutionHelper.d.ts +183 -0
- package/dist/src/helpers/ParallelExecutionHelper.js +375 -0
- package/dist/src/helpers/SyncSignal.d.ts +15 -0
- package/dist/src/helpers/SyncSignal.js +44 -0
- package/dist/src/hubdocs/CategoryDetector.d.ts +83 -0
- package/dist/src/hubdocs/CategoryDetector.js +401 -0
- package/dist/src/hubdocs/DirectStatementInterceptor.d.ts +54 -0
- package/dist/src/hubdocs/DirectStatementInterceptor.js +243 -0
- package/dist/src/hubdocs/ExecutionTracker.d.ts +107 -0
- package/dist/src/hubdocs/ExecutionTracker.js +702 -0
- package/dist/src/hubdocs/HubDocs.d.ts +395 -0
- package/dist/src/hubdocs/HubDocs.js +3586 -0
- package/dist/src/hubdocs/StatementMethodFilter.d.ts +71 -0
- package/dist/src/hubdocs/StatementMethodFilter.js +618 -0
- package/dist/src/hubdocs/StatementTracker.d.ts +417 -0
- package/dist/src/hubdocs/StatementTracker.js +2419 -0
- package/dist/src/hubdocs/SwaggerGenerator.d.ts +59 -0
- package/dist/src/hubdocs/SwaggerGenerator.js +405 -0
- package/dist/src/hubdocs/index.d.ts +9 -0
- package/dist/src/hubdocs/index.js +9 -0
- package/dist/src/hubdocs/types.d.ts +114 -0
- package/dist/src/hubdocs/types.js +5 -0
- package/dist/src/infrastructure/DependencyContainer.d.ts +142 -0
- package/dist/src/infrastructure/DependencyContainer.js +250 -0
- package/dist/src/infrastructure/adapters/AppiumAdapter.d.ts +168 -0
- package/dist/src/infrastructure/adapters/AppiumAdapter.js +468 -0
- package/dist/src/infrastructure/adapters/OracleAdapter.d.ts +150 -0
- package/dist/src/infrastructure/adapters/OracleAdapter.js +388 -0
- package/dist/src/infrastructure/adapters/PlaywrightAdapter.d.ts +192 -0
- package/dist/src/infrastructure/adapters/PlaywrightAdapter.js +382 -0
- package/dist/src/infrastructure/adapters/SSHAdapter.d.ts +141 -0
- package/dist/src/infrastructure/adapters/SSHAdapter.js +428 -0
- package/dist/src/interfaces.d.ts +501 -0
- package/dist/src/interfaces.js +25 -0
- package/dist/src/internal/fakes/__fake-actions__.d.ts +17 -0
- package/dist/src/internal/fakes/__fake-actions__.js +21 -0
- package/dist/src/internal/fakes/__forbidden__.d.ts +10 -0
- package/dist/src/internal/fakes/__forbidden__.js +18 -0
- package/dist/src/internal/fakes/__honeypot__.d.ts +15 -0
- package/dist/src/internal/fakes/__honeypot__.js +24 -0
- package/dist/src/octane/OctaneReporter.d.ts +13 -0
- package/dist/src/octane/OctaneReporter.js +61 -0
- package/dist/src/playwright/CryptoActions.d.ts +20 -0
- package/dist/src/playwright/CryptoActions.js +75 -0
- package/dist/src/playwright/EnhancedWebActions.d.ts +7 -0
- package/dist/src/playwright/EnhancedWebActions.js +65 -0
- package/dist/src/playwright/WebActions.d.ts +1599 -0
- package/dist/src/playwright/WebActions.js +11788 -0
- package/dist/src/playwright/actions/ActionTimeline.d.ts +36 -0
- package/dist/src/playwright/actions/ActionTimeline.js +101 -0
- package/dist/src/playwright/actions/RecoveryQueue.d.ts +82 -0
- package/dist/src/playwright/actions/RecoveryQueue.js +130 -0
- package/dist/src/playwright/actions/SelectorCache.d.ts +53 -0
- package/dist/src/playwright/actions/SelectorCache.js +96 -0
- package/dist/src/playwright/actions/index.d.ts +13 -0
- package/dist/src/playwright/actions/index.js +14 -0
- package/dist/src/playwright/actions/types.d.ts +147 -0
- package/dist/src/playwright/actions/types.js +5 -0
- package/dist/src/playwright/fixtures.d.ts +112 -0
- package/dist/src/playwright/fixtures.js +718 -0
- package/dist/src/playwright/network-logs-reporter.d.ts +7 -0
- package/dist/src/playwright/network-logs-reporter.js +66 -0
- package/dist/src/playwright/registerRecoveryWrappers.d.ts +1 -0
- package/dist/src/playwright/registerRecoveryWrappers.js +54 -0
- package/dist/src/security/BuildSecurity.d.ts +12 -0
- package/dist/src/security/BuildSecurity.js +138 -0
- package/dist/src/security/EulaProtection.d.ts +70 -0
- package/dist/src/security/EulaProtection.js +155 -0
- package/dist/src/security/HoneypotManager.d.ts +46 -0
- package/dist/src/security/HoneypotManager.js +234 -0
- package/dist/src/security/KeysManager.d.ts +36 -0
- package/dist/src/security/KeysManager.js +158 -0
- package/dist/src/security/ProofOfWorkIntegration.d.ts +64 -0
- package/dist/src/security/ProofOfWorkIntegration.js +206 -0
- package/dist/src/security/SecurityValidation.d.ts +21 -0
- package/dist/src/security/SecurityValidation.js +163 -0
- package/dist/src/security/SourceMapProtection.d.ts +55 -0
- package/dist/src/security/SourceMapProtection.js +220 -0
- package/dist/src/security/protector.d.ts +1 -0
- package/dist/src/security/protector.js +97 -0
- package/dist/src/ssh/SSHActions.d.ts +262 -0
- package/dist/src/ssh/SSHActions.js +790 -0
- package/dist/src/ssh/SSHClient.d.ts +99 -0
- package/dist/src/ssh/SSHClient.js +409 -0
- package/dist/src/statements/BaseStatement.d.ts +38 -0
- package/dist/src/statements/BaseStatement.js +78 -0
- package/dist/src/testContext/AuthStateManager.d.ts +93 -0
- package/dist/src/testContext/AuthStateManager.js +256 -0
- package/dist/src/testContext/CoverageManager.d.ts +198 -0
- package/dist/src/testContext/CoverageManager.js +917 -0
- package/dist/src/testContext/TestAnnotations.d.ts +476 -0
- package/dist/src/testContext/TestAnnotations.js +2647 -0
- package/dist/src/testContext/TestContext.d.ts +138 -0
- package/dist/src/testContext/TestContext.js +369 -0
- package/dist/src/testContext/UnifiedHtmlGenerator.d.ts +7 -0
- package/dist/src/testContext/UnifiedHtmlGenerator.js +264 -0
- package/dist/src/testContext/UnifiedReportManager.d.ts +211 -0
- package/dist/src/testContext/UnifiedReportManager.js +1206 -0
- package/dist/src/testhub/DynamicConfigManager.d.ts +121 -0
- package/dist/src/testhub/DynamicConfigManager.js +320 -0
- package/dist/src/testhub/SystemsManager.d.ts +119 -0
- package/dist/src/testhub/SystemsManager.js +365 -0
- package/dist/src/testhub/TestHubClient.d.ts +335 -0
- package/dist/src/testhub/TestHubClient.js +1215 -0
- package/dist/src/testhub/TestHubReporter.d.ts +62 -0
- package/dist/src/testhub/TestHubReporter.js +576 -0
- package/dist/src/testhub/TestHubVars.d.ts +116 -0
- package/dist/src/testhub/TestHubVars.js +273 -0
- package/dist/src/utils/ActionInterceptor.d.ts +59 -0
- package/dist/src/utils/ActionInterceptor.js +741 -0
- package/dist/src/utils/ArtifactsCompressor.d.ts +43 -0
- package/dist/src/utils/ArtifactsCompressor.js +181 -0
- package/dist/src/utils/AutoLogsFinal.d.ts +47 -0
- package/dist/src/utils/AutoLogsFinal.js +148 -0
- package/dist/src/utils/CodeGenSession.d.ts +114 -0
- package/dist/src/utils/CodeGenSession.js +264 -0
- package/dist/src/utils/ConfigLogger.d.ts +133 -0
- package/dist/src/utils/ConfigLogger.js +611 -0
- package/dist/src/utils/CustomReporter.d.ts +22 -0
- package/dist/src/utils/CustomReporter.js +352 -0
- package/dist/src/utils/DataStore.d.ts +171 -0
- package/dist/src/utils/DataStore.js +484 -0
- package/dist/src/utils/DatabaseInterceptor.d.ts +19 -0
- package/dist/src/utils/DatabaseInterceptor.js +295 -0
- package/dist/src/utils/DateHelper.d.ts +16 -0
- package/dist/src/utils/DateHelper.js +120 -0
- package/dist/src/utils/DateValidator.d.ts +4 -0
- package/dist/src/utils/DateValidator.js +51 -0
- package/dist/src/utils/DocumentGenerator.d.ts +35 -0
- package/dist/src/utils/DocumentGenerator.js +129 -0
- package/dist/src/utils/EvidenceCapture.d.ts +90 -0
- package/dist/src/utils/EvidenceCapture.js +600 -0
- package/dist/src/utils/EvidenceReportGenerator.d.ts +70 -0
- package/dist/src/utils/EvidenceReportGenerator.js +799 -0
- package/dist/src/utils/FrameManagementUtil.d.ts +42 -0
- package/dist/src/utils/FrameManagementUtil.js +75 -0
- package/dist/src/utils/GlobalStatementsInterceptor.d.ts +1 -0
- package/dist/src/utils/GlobalStatementsInterceptor.js +1 -0
- package/dist/src/utils/HTMLTemplate.d.ts +1 -0
- package/dist/src/utils/HTMLTemplate.js +1034 -0
- package/dist/src/utils/InterceptacaoMagica.d.ts +23 -0
- package/dist/src/utils/InterceptacaoMagica.js +365 -0
- package/dist/src/utils/LogSanitizer.d.ts +35 -0
- package/dist/src/utils/LogSanitizer.js +110 -0
- package/dist/src/utils/Logger.d.ts +65 -0
- package/dist/src/utils/Logger.js +284 -0
- package/dist/src/utils/McpLocalClient.d.ts +141 -0
- package/dist/src/utils/McpLocalClient.js +871 -0
- package/dist/src/utils/PDFEvidenceGenerator.d.ts +20 -0
- package/dist/src/utils/PDFEvidenceGenerator.js +156 -0
- package/dist/src/utils/SpecFileAnalyzer.d.ts +35 -0
- package/dist/src/utils/SpecFileAnalyzer.js +209 -0
- package/dist/src/utils/StatementInterceptor.d.ts +18 -0
- package/dist/src/utils/StatementInterceptor.js +87 -0
- package/dist/src/utils/StatementLogger.d.ts +33 -0
- package/dist/src/utils/StatementLogger.js +113 -0
- package/dist/src/utils/StatementsInterceptor.d.ts +1 -0
- package/dist/src/utils/StatementsInterceptor.js +1 -0
- package/dist/src/utils/TeamsFlushHook.d.ts +17 -0
- package/dist/src/utils/TeamsFlushHook.js +168 -0
- package/dist/src/utils/TerminalLogCapture.d.ts +158 -0
- package/dist/src/utils/TerminalLogCapture.js +531 -0
- package/dist/src/utils/TestMethodLogger.d.ts +70 -0
- package/dist/src/utils/TestMethodLogger.js +95 -0
- package/dist/src/utils/UnifiedTeardown.d.ts +4 -0
- package/dist/src/utils/UnifiedTeardown.js +400 -0
- package/dist/src/utils/XPathCatalog.d.ts +152 -0
- package/dist/src/utils/XPathCatalog.js +350 -0
- package/dist/src/utils/generators.d.ts +90 -0
- package/dist/src/utils/generators.js +167 -0
- package/dist/src/utils/testRecovery/ResilientPlaywright.d.ts +152 -0
- package/dist/src/utils/testRecovery/ResilientPlaywright.js +715 -0
- package/dist/src/utils/testRecovery/TestRecoveryClient.d.ts +801 -0
- package/dist/src/utils/testRecovery/TestRecoveryClient.js +1415 -0
- package/dist/src/utils/testRecovery/autoFixCode.d.ts +65 -0
- package/dist/src/utils/testRecovery/autoFixCode.js +32 -0
- package/dist/vitest.config.d.ts +2 -0
- package/dist/vitest.config.js +59 -0
- package/dist/wdio.conf.d.ts +1 -0
- package/dist/wdio.conf.js +420 -0
- package/package.json +137 -0
- package/protect-loader.mjs +643 -0
- package/scripts/consumer-postinstall.ts +975 -0
- package/scripts/generate-index.ts +343 -0
- package/scripts/init-api.ts +613 -0
- package/scripts/init-banco.ts +437 -0
- package/scripts/init-frontend.ts +727 -0
- package/scripts/init-mobile.ts +558 -0
- package/scripts/init-scenarios.ts +925 -0
- package/scripts/init-ssh.ts +734 -0
- package/scripts/package-versions.ts +978 -0
- package/scripts/postinstall.ts +605 -0
- package/scripts/smart-override.ts +1675 -0
- package/scripts/sync-configs.ts +302 -0
- package/scripts/ultracite-setup.ts +370 -0
- package/src/types/globals.d.ts +48 -0
- package/tsconfig.json +29 -0
- package/types/autocore-sync-signal.d.ts +10 -0
|
@@ -0,0 +1,715 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import { analyzeFailure, markSuccess, reportResult, inferActionType, getRecoveryMetadata, } from "./TestRecoveryClient.js";
|
|
3
|
+
import { TestContext } from "../../testContext/TestContext.js";
|
|
4
|
+
import { Logger } from "../Logger.js";
|
|
5
|
+
const HUB_MIN_CONFIDENCE = Number(process.env.AUTOCORE_RECOVERY_MIN_CONFIDENCE || "0.7");
|
|
6
|
+
const HUB_MAX_RETRIES = Number(process.env.AUTOCORE_RECOVERY_MAX_RETRIES || "8");
|
|
7
|
+
/**
|
|
8
|
+
* Captura o HTML completo da página atual
|
|
9
|
+
* Use como fallback quando MCP não está disponível
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const html = await capturePageHtml(page)
|
|
14
|
+
* // Enviar para analyzeFailure({ ..., pageHtml: html })
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export async function capturePageHtml(page) {
|
|
18
|
+
try {
|
|
19
|
+
return await page.content();
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Captura screenshot da página em Base64
|
|
27
|
+
* Útil para auditoria visual
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const screenshot = await captureScreenshotBase64(page)
|
|
32
|
+
* // Enviar para analyzeFailure({ ..., screenshot })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export async function captureScreenshotBase64(page) {
|
|
36
|
+
try {
|
|
37
|
+
const buffer = await page.screenshot({ type: "png" });
|
|
38
|
+
return buffer.toString("base64");
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return "";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Extrai contexto do elemento para melhorar inferência de actionType
|
|
46
|
+
*/
|
|
47
|
+
async function extractElementContext(page, xpath) {
|
|
48
|
+
try {
|
|
49
|
+
return await page.evaluate((selector) => {
|
|
50
|
+
const el = document.evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
|
51
|
+
if (!el)
|
|
52
|
+
return {};
|
|
53
|
+
return {
|
|
54
|
+
text: el.textContent?.trim().slice(0, 100) || undefined,
|
|
55
|
+
role: el.getAttribute("role") || undefined,
|
|
56
|
+
ariaLabel: el.getAttribute("aria-label") || undefined,
|
|
57
|
+
type: el.type || el.tagName.toLowerCase(),
|
|
58
|
+
};
|
|
59
|
+
}, xpath);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return {};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Extrai estrutura básica da página para envio ao Test Recovery
|
|
67
|
+
* (fallback quando MCP não está disponível)
|
|
68
|
+
*/
|
|
69
|
+
async function extractBasicPageStructure(page) {
|
|
70
|
+
try {
|
|
71
|
+
return await page.evaluate(() => {
|
|
72
|
+
// Helper: coleta elementos de um root (document ou shadowRoot)
|
|
73
|
+
const collectFromRoot = (root, depth = 0) => {
|
|
74
|
+
const btns = [];
|
|
75
|
+
const inps = [];
|
|
76
|
+
const lks = [];
|
|
77
|
+
if (depth > 3)
|
|
78
|
+
return { btns, inps, lks }; // Limitar profundidade de Shadow DOM
|
|
79
|
+
const queryAll = (sel) => {
|
|
80
|
+
try {
|
|
81
|
+
return Array.from(root.querySelectorAll(sel));
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
queryAll('button, [role="button"], input[type="button"], input[type="submit"]')
|
|
88
|
+
.slice(0, 30)
|
|
89
|
+
.forEach((el) => {
|
|
90
|
+
btns.push({
|
|
91
|
+
tag: el.tagName.toLowerCase(),
|
|
92
|
+
text: el.textContent?.trim().slice(0, 100) || "",
|
|
93
|
+
id: el.id || undefined,
|
|
94
|
+
className: el.className || undefined,
|
|
95
|
+
ariaLabel: el.getAttribute("aria-label") || undefined,
|
|
96
|
+
dataTestId: el.getAttribute("data-testid") || undefined,
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
queryAll("input, textarea, select")
|
|
100
|
+
.slice(0, 30)
|
|
101
|
+
.forEach((el) => {
|
|
102
|
+
const input = el;
|
|
103
|
+
inps.push({
|
|
104
|
+
tag: el.tagName.toLowerCase(),
|
|
105
|
+
type: input.type || undefined,
|
|
106
|
+
name: input.name || undefined,
|
|
107
|
+
id: el.id || undefined,
|
|
108
|
+
placeholder: input.placeholder || undefined,
|
|
109
|
+
ariaLabel: el.getAttribute("aria-label") || undefined,
|
|
110
|
+
dataTestId: el.getAttribute("data-testid") || undefined,
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
queryAll("a[href]")
|
|
114
|
+
.slice(0, 30)
|
|
115
|
+
.forEach((el) => {
|
|
116
|
+
lks.push({
|
|
117
|
+
text: el.textContent?.trim().slice(0, 100) || "",
|
|
118
|
+
href: el.href || undefined,
|
|
119
|
+
id: el.id || undefined,
|
|
120
|
+
ariaLabel: el.getAttribute("aria-label") || undefined,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
// 🔄 Pierce Shadow DOM: buscar em shadowRoots de Custom Elements
|
|
124
|
+
queryAll("*").forEach((el) => {
|
|
125
|
+
if (el.shadowRoot) {
|
|
126
|
+
const shadow = collectFromRoot(el.shadowRoot, depth + 1);
|
|
127
|
+
btns.push(...shadow.btns);
|
|
128
|
+
inps.push(...shadow.inps);
|
|
129
|
+
lks.push(...shadow.lks);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return { btns, inps, lks };
|
|
133
|
+
};
|
|
134
|
+
const result = collectFromRoot(document);
|
|
135
|
+
return {
|
|
136
|
+
buttons: result.btns.slice(0, 50),
|
|
137
|
+
inputs: result.inps.slice(0, 50),
|
|
138
|
+
links: result.lks.slice(0, 50),
|
|
139
|
+
elements: [],
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return { buttons: [], inputs: [], links: [], elements: [] };
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
class PlaywrightTestRecovery {
|
|
148
|
+
sessionId;
|
|
149
|
+
systemName;
|
|
150
|
+
environment;
|
|
151
|
+
constructor(sessionId, systemName, environment) {
|
|
152
|
+
this.sessionId = sessionId || TestContext.getOrCreateSessionId();
|
|
153
|
+
this.systemName = systemName;
|
|
154
|
+
this.environment = environment;
|
|
155
|
+
}
|
|
156
|
+
async recoverFromFailure(page, xpath, errorType, actionType, context) {
|
|
157
|
+
const url = page.url();
|
|
158
|
+
try {
|
|
159
|
+
// Captura estrutura da página para envio ao backend
|
|
160
|
+
const pageStructure = await extractBasicPageStructure(page);
|
|
161
|
+
// Captura HTML como fallback caso pageStructure esteja vazio
|
|
162
|
+
let pageHtml;
|
|
163
|
+
const hasContent = pageStructure &&
|
|
164
|
+
((pageStructure.buttons?.length ?? 0) > 0 ||
|
|
165
|
+
(pageStructure.inputs?.length ?? 0) > 0 ||
|
|
166
|
+
(pageStructure.links?.length ?? 0) > 0);
|
|
167
|
+
if (!hasContent) {
|
|
168
|
+
try {
|
|
169
|
+
const html = await page.content();
|
|
170
|
+
pageHtml =
|
|
171
|
+
html.length > 50000
|
|
172
|
+
? html.substring(0, 50000) + "... (truncated)"
|
|
173
|
+
: html;
|
|
174
|
+
}
|
|
175
|
+
catch { }
|
|
176
|
+
}
|
|
177
|
+
// Enriquecer contexto com informações do elemento
|
|
178
|
+
const elementContext = await extractElementContext(page, xpath);
|
|
179
|
+
const enrichedContext = {
|
|
180
|
+
...elementContext,
|
|
181
|
+
...context,
|
|
182
|
+
originalMethod: context?.originalMethod || actionType,
|
|
183
|
+
};
|
|
184
|
+
// Se actionType não foi fornecido ou é genérico, tenta inferir
|
|
185
|
+
const finalActionType = actionType ||
|
|
186
|
+
inferActionType(xpath, enrichedContext);
|
|
187
|
+
const meta = getRecoveryMetadata();
|
|
188
|
+
const body = {
|
|
189
|
+
url,
|
|
190
|
+
failedXPath: xpath,
|
|
191
|
+
errorType,
|
|
192
|
+
actionType: finalActionType,
|
|
193
|
+
sessionId: this.sessionId,
|
|
194
|
+
systemName: this.systemName,
|
|
195
|
+
environment: this.environment,
|
|
196
|
+
executionSource: meta.executionSource,
|
|
197
|
+
testCaseName: meta.testCaseName,
|
|
198
|
+
testCaseId: meta.testCaseId,
|
|
199
|
+
testMethodName: meta.testMethodName,
|
|
200
|
+
context: enrichedContext,
|
|
201
|
+
pageStructure,
|
|
202
|
+
pageHtml,
|
|
203
|
+
};
|
|
204
|
+
const result = await analyzeFailure(body);
|
|
205
|
+
return result;
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
async markSuccess(attempt) {
|
|
212
|
+
await markSuccess(this.sessionId, attempt);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export class ResilientPlaywright {
|
|
216
|
+
recovery;
|
|
217
|
+
page;
|
|
218
|
+
constructor(page, sessionId, systemName, environment) {
|
|
219
|
+
this.page = page;
|
|
220
|
+
const sid = sessionId || TestContext.getOrCreateSessionId();
|
|
221
|
+
this.recovery = new PlaywrightTestRecovery(sid, systemName, environment);
|
|
222
|
+
}
|
|
223
|
+
detectErrorType(error) {
|
|
224
|
+
const message = error?.message?.toLowerCase() ?? "";
|
|
225
|
+
if (message.includes("strict mode violation") ||
|
|
226
|
+
message.includes("multiple elements")) {
|
|
227
|
+
return "multiple_elements";
|
|
228
|
+
}
|
|
229
|
+
if (message.includes("timeout") || message.includes("timed out")) {
|
|
230
|
+
return "timeout";
|
|
231
|
+
}
|
|
232
|
+
if (message.includes("detached") || message.includes("stale")) {
|
|
233
|
+
return "stale_element";
|
|
234
|
+
}
|
|
235
|
+
return "not_found";
|
|
236
|
+
}
|
|
237
|
+
/** Tracks whether we switched into a frame during recovery */
|
|
238
|
+
_inRecoveryFrame = false;
|
|
239
|
+
async executeAction(action) {
|
|
240
|
+
if (!action)
|
|
241
|
+
return;
|
|
242
|
+
switch (action.type) {
|
|
243
|
+
case "wait":
|
|
244
|
+
await this.page.waitForTimeout(action.waitMs || 2000);
|
|
245
|
+
break;
|
|
246
|
+
case "click_other":
|
|
247
|
+
if (action.selector)
|
|
248
|
+
await this.page.locator(action.selector).click();
|
|
249
|
+
break;
|
|
250
|
+
case "navigate":
|
|
251
|
+
if (action.url)
|
|
252
|
+
await this.page.goto(action.url);
|
|
253
|
+
break;
|
|
254
|
+
case "refresh":
|
|
255
|
+
await this.page.reload();
|
|
256
|
+
break;
|
|
257
|
+
case "switch_frame":
|
|
258
|
+
if (action.selector) {
|
|
259
|
+
try {
|
|
260
|
+
// Try using WebActions.switchTo if available
|
|
261
|
+
const mod = await import("../../playwright/WebActions.js").catch(() => null);
|
|
262
|
+
const WebActions = mod ? mod.WebActions : null;
|
|
263
|
+
if (WebActions && typeof WebActions.switchTo === "function") {
|
|
264
|
+
await WebActions.switchTo(action.selector, "Recovery frame switch", false);
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
// Fallback: use Playwright frameLocator directly
|
|
268
|
+
const frame = this.page.frameLocator(action.selector);
|
|
269
|
+
// Validate frame exists by trying to locate something inside it
|
|
270
|
+
await frame
|
|
271
|
+
.locator("body")
|
|
272
|
+
.waitFor({ timeout: 5000 })
|
|
273
|
+
.catch(() => { });
|
|
274
|
+
}
|
|
275
|
+
this._inRecoveryFrame = true;
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
Logger.warning(`[ResilientPlaywright] switch_frame falhou para ${action.selector}: ${e}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
break;
|
|
282
|
+
default:
|
|
283
|
+
break;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Retorna ao contexto default após switch_frame de recovery
|
|
288
|
+
*/
|
|
289
|
+
async switchToDefaultIfNeeded() {
|
|
290
|
+
if (!this._inRecoveryFrame)
|
|
291
|
+
return;
|
|
292
|
+
try {
|
|
293
|
+
const mod = await import("../../playwright/WebActions.js").catch(() => null);
|
|
294
|
+
const WebActions = mod ? mod.WebActions : null;
|
|
295
|
+
if (WebActions && typeof WebActions.switchToDefault === "function") {
|
|
296
|
+
await WebActions.switchToDefault("Recovery frame switch back", false);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch { }
|
|
300
|
+
this._inRecoveryFrame = false;
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* 🔄 Força atualização/estabilização do DOM antes de re-tentar localizar elementos.
|
|
304
|
+
* Essencial para SPAs (ex.: Salesforce Lightning/Vlocity) que recriam Shadow DOM a cada interação.
|
|
305
|
+
*
|
|
306
|
+
* 1. waitForLoadState('domcontentloaded')
|
|
307
|
+
* 2. Force reflow + aguardar Custom Elements pendentes (whenDefined)
|
|
308
|
+
* 3. requestAnimationFrame + microtask cycle
|
|
309
|
+
*
|
|
310
|
+
* @param {number} [stabilityMs=300] Tempo mínimo em ms para considerar o DOM estável.
|
|
311
|
+
*/
|
|
312
|
+
/** Timestamp do último refreshDOM (debounce) */
|
|
313
|
+
_lastDOMRefreshTime = 0;
|
|
314
|
+
async refreshDOM(stabilityMs = 300) {
|
|
315
|
+
// 🛡️ Feature toggle
|
|
316
|
+
if (process.env.AUTOCORE_REFRESH_DOM_BEFORE_ACTION === "false")
|
|
317
|
+
return;
|
|
318
|
+
// 🛡️ Debounce: pular se chamado dentro do cooldown
|
|
319
|
+
const now = Date.now();
|
|
320
|
+
const cooldown = Number(process.env.AUTOCORE_DOM_REFRESH_COOLDOWN_MS || "200");
|
|
321
|
+
if (now - this._lastDOMRefreshTime < cooldown)
|
|
322
|
+
return;
|
|
323
|
+
this._lastDOMRefreshTime = now;
|
|
324
|
+
try {
|
|
325
|
+
await this.page
|
|
326
|
+
.waitForLoadState("domcontentloaded", { timeout: 3000 })
|
|
327
|
+
.catch(() => { });
|
|
328
|
+
await this.page
|
|
329
|
+
.evaluate((waitMs) => {
|
|
330
|
+
return new Promise((resolve) => {
|
|
331
|
+
void document.body?.offsetHeight;
|
|
332
|
+
const waitForCustomElements = () => {
|
|
333
|
+
return new Promise((res) => {
|
|
334
|
+
if (typeof customElements !== "undefined" &&
|
|
335
|
+
customElements.whenDefined) {
|
|
336
|
+
const customTags = new Set();
|
|
337
|
+
document.querySelectorAll("*").forEach((el) => {
|
|
338
|
+
const tag = el.tagName.toLowerCase();
|
|
339
|
+
if (tag.includes("-"))
|
|
340
|
+
customTags.add(tag);
|
|
341
|
+
});
|
|
342
|
+
const promises = [];
|
|
343
|
+
let count = 0;
|
|
344
|
+
for (const tag of customTags) {
|
|
345
|
+
if (count++ >= 10)
|
|
346
|
+
break;
|
|
347
|
+
promises.push(customElements
|
|
348
|
+
.whenDefined(tag)
|
|
349
|
+
.catch(() => class {
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
if (promises.length) {
|
|
353
|
+
Promise.race([
|
|
354
|
+
Promise.all(promises),
|
|
355
|
+
new Promise((r) => setTimeout(r, 500)),
|
|
356
|
+
]).then(() => res());
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
res();
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
else {
|
|
363
|
+
res();
|
|
364
|
+
}
|
|
365
|
+
});
|
|
366
|
+
};
|
|
367
|
+
waitForCustomElements().then(() => {
|
|
368
|
+
requestAnimationFrame(() => {
|
|
369
|
+
void document.body?.offsetHeight;
|
|
370
|
+
setTimeout(() => resolve(), Math.max(50, waitMs));
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
}, stabilityMs)
|
|
375
|
+
.catch(() => { });
|
|
376
|
+
await this.page
|
|
377
|
+
.waitForTimeout(Math.max(100, stabilityMs - 200))
|
|
378
|
+
.catch(() => { });
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
// refreshDOM é best-effort
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Loop genérico de recovery com ordem recomendada (v2.7.43):
|
|
386
|
+
* 1. Executar actions[] (incluindo switch_frame)
|
|
387
|
+
* 2. Tentar suggestedXPath
|
|
388
|
+
* 3. Tentar alternatives[] (todas, até o limite de retries)
|
|
389
|
+
* 4. Chamar report-result com sucesso/falha (prioriza logId)
|
|
390
|
+
* 5. Se sucesso com confiança >= 0.7, chamar auto-fix-local
|
|
391
|
+
* 6. Gerenciar contexto de frame (switchToDefault quando necessário)
|
|
392
|
+
*/
|
|
393
|
+
async withRecoveryLoop(initialXPath, actionType, executor, options, extraContext) {
|
|
394
|
+
const maxRetries = options?.retries ?? HUB_MAX_RETRIES;
|
|
395
|
+
const meta = getRecoveryMetadata();
|
|
396
|
+
let xpath = initialXPath;
|
|
397
|
+
let lastError = null;
|
|
398
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
399
|
+
try {
|
|
400
|
+
// 🔄 Refresh DOM antes de CADA tentativa (Shadow DOM/LWC pode ter recriado elementos)
|
|
401
|
+
await this.refreshDOM(attempt === 1 ? 150 : 300);
|
|
402
|
+
// Garantir contexto de frame limpo a cada tentativa
|
|
403
|
+
await this.switchToDefaultIfNeeded();
|
|
404
|
+
await executor(xpath);
|
|
405
|
+
if (attempt > 1)
|
|
406
|
+
await this.recovery.markSuccess(attempt);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
catch (err) {
|
|
410
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
411
|
+
if (attempt >= maxRetries)
|
|
412
|
+
break;
|
|
413
|
+
// 🔄 Refresh DOM imediato após falha — capturar estado mais recente do Shadow DOM
|
|
414
|
+
await this.refreshDOM(200);
|
|
415
|
+
const errorType = this.detectErrorType(lastError);
|
|
416
|
+
const suggestion = await this.recovery.recoverFromFailure(this.page, xpath, errorType, actionType, extraContext);
|
|
417
|
+
if (!suggestion?.analysis)
|
|
418
|
+
continue;
|
|
419
|
+
const { suggestedXPath, confidence, alternatives } = suggestion.analysis;
|
|
420
|
+
if (!suggestedXPath || confidence < HUB_MIN_CONFIDENCE) {
|
|
421
|
+
// Reportar falha mesmo com baixa confiança
|
|
422
|
+
try {
|
|
423
|
+
await reportResult({
|
|
424
|
+
logId: suggestion.logId,
|
|
425
|
+
sessionId: TestContext.getOrCreateSessionId(),
|
|
426
|
+
xpath: suggestedXPath,
|
|
427
|
+
attemptNumber: attempt,
|
|
428
|
+
success: false,
|
|
429
|
+
recoverySource: "framework",
|
|
430
|
+
executionSource: meta.executionSource,
|
|
431
|
+
testCaseName: meta.testCaseName,
|
|
432
|
+
testCaseId: meta.testCaseId,
|
|
433
|
+
testMethodName: meta.testMethodName,
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
catch { }
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
// 1) Executar actions[] (incluindo switch_frame se vier)
|
|
440
|
+
if (suggestion.actions?.length) {
|
|
441
|
+
for (const a of suggestion.actions)
|
|
442
|
+
await this.executeAction(a);
|
|
443
|
+
}
|
|
444
|
+
// 2) Tentar suggestedXPath
|
|
445
|
+
try {
|
|
446
|
+
await this.refreshDOM(200);
|
|
447
|
+
await executor(suggestedXPath);
|
|
448
|
+
// Sucesso! Reportar e opcionalmente auto-fix
|
|
449
|
+
await this.switchToDefaultIfNeeded();
|
|
450
|
+
try {
|
|
451
|
+
await reportResult({
|
|
452
|
+
logId: suggestion.logId,
|
|
453
|
+
sessionId: TestContext.getOrCreateSessionId(),
|
|
454
|
+
xpath: suggestedXPath,
|
|
455
|
+
attemptNumber: attempt + 1,
|
|
456
|
+
success: true,
|
|
457
|
+
autoFixed: false,
|
|
458
|
+
recoverySource: "framework",
|
|
459
|
+
executionSource: meta.executionSource,
|
|
460
|
+
testCaseName: meta.testCaseName,
|
|
461
|
+
testCaseId: meta.testCaseId,
|
|
462
|
+
testMethodName: meta.testMethodName,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
catch { }
|
|
466
|
+
// Auto-fix local se confiança >= 0.7 e autoFixAvailable
|
|
467
|
+
if (suggestion.analysis.autoFixAvailable && confidence >= 0.7) {
|
|
468
|
+
await this.triggerAutoFixLocal(initialXPath, suggestedXPath, confidence, suggestion.logId).catch(() => { });
|
|
469
|
+
}
|
|
470
|
+
await this.recovery.markSuccess(attempt + 1);
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
// suggestedXPath falhou, tentar alternatives
|
|
475
|
+
}
|
|
476
|
+
// 3) Tentar alternatives[]
|
|
477
|
+
if (alternatives?.length) {
|
|
478
|
+
let found = false;
|
|
479
|
+
for (const alt of alternatives) {
|
|
480
|
+
attempt++;
|
|
481
|
+
if (attempt > maxRetries)
|
|
482
|
+
break;
|
|
483
|
+
try {
|
|
484
|
+
await this.switchToDefaultIfNeeded();
|
|
485
|
+
// 🔄 Refresh DOM antes de cada alternativa
|
|
486
|
+
await this.refreshDOM(200);
|
|
487
|
+
// Re-executar actions de switch_frame antes de cada alternativa
|
|
488
|
+
if (suggestion.actions?.length) {
|
|
489
|
+
for (const a of suggestion.actions) {
|
|
490
|
+
if (a.type === "switch_frame")
|
|
491
|
+
await this.executeAction(a);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
await executor(alt);
|
|
495
|
+
// Sucesso com alternativa!
|
|
496
|
+
await this.switchToDefaultIfNeeded();
|
|
497
|
+
try {
|
|
498
|
+
await reportResult({
|
|
499
|
+
logId: suggestion.logId,
|
|
500
|
+
sessionId: TestContext.getOrCreateSessionId(),
|
|
501
|
+
xpath: alt,
|
|
502
|
+
attemptNumber: attempt,
|
|
503
|
+
success: true,
|
|
504
|
+
autoFixed: false,
|
|
505
|
+
recoverySource: "framework",
|
|
506
|
+
executionSource: meta.executionSource,
|
|
507
|
+
testCaseName: meta.testCaseName,
|
|
508
|
+
testCaseId: meta.testCaseId,
|
|
509
|
+
testMethodName: meta.testMethodName,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
catch { }
|
|
513
|
+
if (suggestion.analysis.autoFixAvailable && confidence >= 0.7) {
|
|
514
|
+
await this.triggerAutoFixLocal(initialXPath, alt, confidence, suggestion.logId).catch(() => { });
|
|
515
|
+
}
|
|
516
|
+
await this.recovery.markSuccess(attempt);
|
|
517
|
+
found = true;
|
|
518
|
+
break;
|
|
519
|
+
}
|
|
520
|
+
catch {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (found)
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
// Nenhum candidato funcionou, reportar falha
|
|
528
|
+
await this.switchToDefaultIfNeeded();
|
|
529
|
+
try {
|
|
530
|
+
await reportResult({
|
|
531
|
+
logId: suggestion.logId,
|
|
532
|
+
sessionId: TestContext.getOrCreateSessionId(),
|
|
533
|
+
xpath: suggestedXPath,
|
|
534
|
+
attemptNumber: attempt + 1,
|
|
535
|
+
success: false,
|
|
536
|
+
recoverySource: "framework",
|
|
537
|
+
executionSource: meta.executionSource,
|
|
538
|
+
testCaseName: meta.testCaseName,
|
|
539
|
+
testCaseId: meta.testCaseId,
|
|
540
|
+
testMethodName: meta.testMethodName,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
catch { }
|
|
544
|
+
// Atualizar xpath para próxima iteração do loop principal
|
|
545
|
+
xpath = suggestedXPath;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
await this.switchToDefaultIfNeeded();
|
|
549
|
+
throw lastError || new Error(`${actionType} failed after all retries`);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Dispara auto-fix-local em background (v2.7.44)
|
|
553
|
+
* Não bloqueia o fluxo de execução
|
|
554
|
+
*
|
|
555
|
+
* Prioriza chamada via MCP local (fix_xpath_in_attributes) quando disponível.
|
|
556
|
+
* Fallback para HTTP backend se MCP não estiver acessível.
|
|
557
|
+
*/
|
|
558
|
+
async triggerAutoFixLocal(oldXPath, newXPath, confidence, _logId) {
|
|
559
|
+
try {
|
|
560
|
+
const { testRecoveryHelper, getRecoveryMetadata: getMeta } = await import("./TestRecoveryClient.js");
|
|
561
|
+
const { fixXPathViaLocalMcp } = await import("../McpLocalClient.js");
|
|
562
|
+
const projectRoot = testRecoveryHelper.getProjectRoot() ||
|
|
563
|
+
process.env.AUTOCORE_PROJECT_ROOT ||
|
|
564
|
+
process.cwd();
|
|
565
|
+
const meta = getMeta();
|
|
566
|
+
// Enriquecer com contexto de Attributes/Page (item 7/12 v2.7.44)
|
|
567
|
+
let attrCtx = {};
|
|
568
|
+
try {
|
|
569
|
+
const mod = await import("../../playwright/WebActions.js").catch(() => null);
|
|
570
|
+
const WA = mod ? mod.WebActions : null;
|
|
571
|
+
if (WA && typeof WA.getCallerAttributesContext === "function") {
|
|
572
|
+
attrCtx = WA.getCallerAttributesContext();
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
catch { }
|
|
576
|
+
const resolvedMethodName = attrCtx.methodName || meta.testMethodName;
|
|
577
|
+
// Tentar MCP local primeiro (executionSource=Local)
|
|
578
|
+
if (meta.executionSource === "LOCAL") {
|
|
579
|
+
const result = await fixXPathViaLocalMcp({
|
|
580
|
+
projectRoot,
|
|
581
|
+
oldXPath,
|
|
582
|
+
newXPath,
|
|
583
|
+
confidence,
|
|
584
|
+
methodName: resolvedMethodName,
|
|
585
|
+
className: attrCtx.className,
|
|
586
|
+
attributesFile: attrCtx.attributesFile,
|
|
587
|
+
executionSource: "Local",
|
|
588
|
+
});
|
|
589
|
+
if (result?.success) {
|
|
590
|
+
// (item 29.1) Verificação em disco antes de logar sucesso final
|
|
591
|
+
if (result.fileUpdated && result.filePath) {
|
|
592
|
+
try {
|
|
593
|
+
if (fs.existsSync(result.filePath)) {
|
|
594
|
+
Logger.success(`[ResilientPlaywright] ✅ Auto-fix aplicado via MCP: ${result.filePath} — verificado em disco`);
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
Logger.warning(`[ResilientPlaywright] ⚠️ Auto-fix reportou sucesso mas arquivo não encontrado: ${result.filePath}`);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
catch {
|
|
601
|
+
Logger.success(`[ResilientPlaywright] ✅ Auto-fix aplicado via MCP: ${result.filePath}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
else {
|
|
605
|
+
Logger.success(`[ResilientPlaywright] ✅ Auto-fix aplicado via MCP: ${result.filePath}`);
|
|
606
|
+
}
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Fallback: chamar backend HTTP (comportamento original)
|
|
611
|
+
await testRecoveryHelper.autoFixLocal({
|
|
612
|
+
projectRoot,
|
|
613
|
+
oldXPath,
|
|
614
|
+
newXPath,
|
|
615
|
+
confidence,
|
|
616
|
+
methodName: resolvedMethodName,
|
|
617
|
+
className: attrCtx.className,
|
|
618
|
+
attributesFile: attrCtx.attributesFile,
|
|
619
|
+
executionSource: "Local",
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
catch {
|
|
623
|
+
// Auto-fix é best-effort, não bloqueia execução
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
async click(xpath, options) {
|
|
627
|
+
await this.withRecoveryLoop(xpath, "click", async (xp) => {
|
|
628
|
+
await this.page.locator(xp).click({ timeout: options?.timeout });
|
|
629
|
+
}, options);
|
|
630
|
+
}
|
|
631
|
+
async fill(xpath, value, options) {
|
|
632
|
+
await this.withRecoveryLoop(xpath, "fill", async (xp) => {
|
|
633
|
+
await this.page.locator(xp).fill(value, { timeout: options?.timeout });
|
|
634
|
+
}, options, { value });
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* Select com auto-recovery (dropdown/combobox)
|
|
638
|
+
*/
|
|
639
|
+
async select(xpath, value, options) {
|
|
640
|
+
const values = Array.isArray(value) ? value : [value];
|
|
641
|
+
await this.withRecoveryLoop(xpath, "select", async (xp) => {
|
|
642
|
+
const locator = this.page.locator(xp);
|
|
643
|
+
// Ensure the element is visible and interactable
|
|
644
|
+
await locator.scrollIntoViewIfNeeded();
|
|
645
|
+
await locator.click(); // Open the dropdown
|
|
646
|
+
// Select the option
|
|
647
|
+
await locator.selectOption(values, { timeout: options?.timeout });
|
|
648
|
+
}, options);
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Hover com auto-recovery
|
|
652
|
+
*/
|
|
653
|
+
async hover(xpath, options) {
|
|
654
|
+
await this.withRecoveryLoop(xpath, "hover", async (xp) => {
|
|
655
|
+
await this.page.locator(xp).hover({ timeout: options?.timeout });
|
|
656
|
+
}, options);
|
|
657
|
+
}
|
|
658
|
+
/**
|
|
659
|
+
* Check com auto-recovery (checkbox/radio)
|
|
660
|
+
*/
|
|
661
|
+
async check(xpath, options) {
|
|
662
|
+
await this.withRecoveryLoop(xpath, "check", async (xp) => {
|
|
663
|
+
await this.page.locator(xp).check({ timeout: options?.timeout });
|
|
664
|
+
}, options);
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Uncheck com auto-recovery (checkbox)
|
|
668
|
+
*/
|
|
669
|
+
async uncheck(xpath, options) {
|
|
670
|
+
await this.withRecoveryLoop(xpath, "uncheck", async (xp) => {
|
|
671
|
+
await this.page.locator(xp).uncheck({ timeout: options?.timeout });
|
|
672
|
+
}, options);
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Press com auto-recovery (teclas como Enter, Tab, Escape)
|
|
676
|
+
*/
|
|
677
|
+
async press(xpath, key, options) {
|
|
678
|
+
await this.withRecoveryLoop(xpath, "press", async (xp) => {
|
|
679
|
+
await this.page.locator(xp).press(key, { timeout: options?.timeout });
|
|
680
|
+
}, options, { text: key });
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* Método genérico para qualquer ação com auto-recovery
|
|
684
|
+
* Útil quando você quer especificar o actionType manualmente
|
|
685
|
+
*/
|
|
686
|
+
async action(actionType, xpath, executor, options) {
|
|
687
|
+
let result;
|
|
688
|
+
await this.withRecoveryLoop(xpath, actionType, async (xp) => {
|
|
689
|
+
result = await executor(this.page.locator(xp));
|
|
690
|
+
}, options);
|
|
691
|
+
return result;
|
|
692
|
+
}
|
|
693
|
+
/**
|
|
694
|
+
* Captura estrutura da página usando extractBasicPageStructure
|
|
695
|
+
* Útil para obter estrutura manualmente antes de chamar analyzeFailure
|
|
696
|
+
*/
|
|
697
|
+
async capturePageStructure() {
|
|
698
|
+
return extractBasicPageStructure(this.page);
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Captura HTML completo da página
|
|
702
|
+
* Fallback quando MCP não está disponível
|
|
703
|
+
*/
|
|
704
|
+
async capturePageHtml() {
|
|
705
|
+
return capturePageHtml(this.page);
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Captura screenshot em Base64
|
|
709
|
+
* Útil para auditoria visual
|
|
710
|
+
*/
|
|
711
|
+
async captureScreenshotBase64() {
|
|
712
|
+
return captureScreenshotBase64(this.page);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
export default ResilientPlaywright;
|