@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,2647 @@
|
|
|
1
|
+
import { ApiActions } from '../api/ApiActions.js';
|
|
2
|
+
import { MobileConnection } from '../appium/MobileConnection.js';
|
|
3
|
+
import { BancoActions } from '../helpers/BancoActions.js';
|
|
4
|
+
import { StatementTracker } from '../hubdocs/StatementTracker.js';
|
|
5
|
+
import { SSHActions } from '../ssh/SSHActions.js';
|
|
6
|
+
import { ativarInterceptacaoMagica } from '../utils/InterceptacaoMagica.js';
|
|
7
|
+
import { interceptAllStatements } from '../utils/StatementInterceptor.js';
|
|
8
|
+
import { ativarStatementLogs } from '../utils/StatementLogger.js';
|
|
9
|
+
import { TerminalLogCapture } from '../utils/TerminalLogCapture.js';
|
|
10
|
+
import { CoverageManager } from './CoverageManager.js';
|
|
11
|
+
import { TestContext } from './TestContext.js';
|
|
12
|
+
import { DynamicConfigManager } from '../testhub/DynamicConfigManager.js';
|
|
13
|
+
import { UnifiedReportManager, } from './UnifiedReportManager.js';
|
|
14
|
+
import SystemsManager from '../testhub/SystemsManager.js';
|
|
15
|
+
import DesktopConnection from '../desktop/DesktopConnection.js';
|
|
16
|
+
/**
|
|
17
|
+
* 🆕 HELPER CRÍTICO: Garante que TerminalLogCapture está ativo no worker atual
|
|
18
|
+
* Em multi-worker do Playwright, cada worker é um processo separado
|
|
19
|
+
* e precisa iniciar sua própria instância de captura de logs.
|
|
20
|
+
* @param testName Nome do teste (CN) para associar logs
|
|
21
|
+
* @param ctName Nome do CT atual (opcional)
|
|
22
|
+
*/
|
|
23
|
+
function ensureTerminalLogCaptureActive(testName, ctName) {
|
|
24
|
+
try {
|
|
25
|
+
// Sempre chamar start() - ele tem verificação interna para evitar duplicação
|
|
26
|
+
TerminalLogCapture.start();
|
|
27
|
+
globalThis.TerminalLogCapture = TerminalLogCapture;
|
|
28
|
+
// Definir o teste atual para associar logs
|
|
29
|
+
TerminalLogCapture.setCurrentTest(testName);
|
|
30
|
+
// Definir o CT atual se fornecido
|
|
31
|
+
if (ctName) {
|
|
32
|
+
TerminalLogCapture.setCurrentCT(ctName);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// Silenciosamente ignorar erro
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Map para rastrear início dos testes
|
|
41
|
+
*/
|
|
42
|
+
const testStartTimes = new Map();
|
|
43
|
+
/**
|
|
44
|
+
* Tracking de CT ativo atual
|
|
45
|
+
*/
|
|
46
|
+
let currentActiveCT = null;
|
|
47
|
+
/**
|
|
48
|
+
* Flags para evitar loop infinito de interceptadores
|
|
49
|
+
*/
|
|
50
|
+
const interceptorsAtivos = new Set();
|
|
51
|
+
/**
|
|
52
|
+
* 🛑 Flag global para parar execução ao falhar
|
|
53
|
+
* Pode ser ativada no escopo do test.describe() para aplicar a todos os CTs
|
|
54
|
+
*/
|
|
55
|
+
let stopOnFailureEnabled = false;
|
|
56
|
+
/**
|
|
57
|
+
* � CRÍTICO: Importar interceptador universal para garantir ativação
|
|
58
|
+
*/
|
|
59
|
+
// Sistema automático ativo - interceptação agora é automática via StatementTracker
|
|
60
|
+
// AutoStatementInterceptor foi removido, tudo é automático agora
|
|
61
|
+
/**
|
|
62
|
+
* �🆕 Sistema de captura de logs de ações de CTs
|
|
63
|
+
*/
|
|
64
|
+
const originalConsoleLog = console.log;
|
|
65
|
+
const originalConsoleInfo = console.info;
|
|
66
|
+
const originalConsoleWarn = console.warn;
|
|
67
|
+
const originalConsoleError = console.error;
|
|
68
|
+
let logsCapturados = [];
|
|
69
|
+
let interceptacaoLogsAtiva = false;
|
|
70
|
+
const LOGS_GLOBAL_KEYS = ['logsCapturados', '__AUTOCORE_LOGS_CAPTURADOS__'];
|
|
71
|
+
function atualizarReferenciaGlobalDosLogs() {
|
|
72
|
+
try {
|
|
73
|
+
for (const key of LOGS_GLOBAL_KEYS) {
|
|
74
|
+
;
|
|
75
|
+
globalThis[key] = logsCapturados;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
// Não deve quebrar fluxo se globalThis não estiver disponível
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
atualizarReferenciaGlobalDosLogs();
|
|
83
|
+
/**
|
|
84
|
+
* 🆕 Interceptar logs do console para capturar ações de CTs
|
|
85
|
+
*/
|
|
86
|
+
function interceptarLogs() {
|
|
87
|
+
if (interceptacaoLogsAtiva)
|
|
88
|
+
return;
|
|
89
|
+
interceptacaoLogsAtiva = true;
|
|
90
|
+
console.log = (...args) => {
|
|
91
|
+
const message = args
|
|
92
|
+
.map((arg) => (typeof arg === 'string' ? arg : JSON.stringify(arg)))
|
|
93
|
+
.join(' ');
|
|
94
|
+
// ✅ CORRIGIDO: Capturar TODOS os logs durante execução de CT (não apenas alguns emojis)
|
|
95
|
+
// Excluir apenas logs internos do sistema que não são relevantes para o usuário
|
|
96
|
+
const isInternalLog = message.includes('[DEBUG]') ||
|
|
97
|
+
message.includes('node_modules') ||
|
|
98
|
+
message.includes('webpack') ||
|
|
99
|
+
message.includes('Module build') ||
|
|
100
|
+
message.startsWith('Failed to load');
|
|
101
|
+
if (!isInternalLog) {
|
|
102
|
+
logsCapturados.push(`[${new Date().toISOString()}] ${message}`);
|
|
103
|
+
atualizarReferenciaGlobalDosLogs();
|
|
104
|
+
// Também registrar no UnifiedReportManager se possível
|
|
105
|
+
try {
|
|
106
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
107
|
+
if (testInfo?.title) {
|
|
108
|
+
UnifiedReportManager.adicionarLog(testInfo.title, message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// Ignorar erro se TestContext não estiver disponível
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// Chamar console.log original
|
|
116
|
+
originalConsoleLog.apply(console, args);
|
|
117
|
+
};
|
|
118
|
+
console.info = (...args) => {
|
|
119
|
+
const message = args
|
|
120
|
+
.map((arg) => (typeof arg === 'string' ? arg : JSON.stringify(arg)))
|
|
121
|
+
.join(' ');
|
|
122
|
+
// ✅ Capturar todos os console.info
|
|
123
|
+
logsCapturados.push(`[${new Date().toISOString()}] INFO: ${message}`);
|
|
124
|
+
atualizarReferenciaGlobalDosLogs();
|
|
125
|
+
try {
|
|
126
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
127
|
+
if (testInfo?.title) {
|
|
128
|
+
UnifiedReportManager.adicionarLog(testInfo.title, `INFO: ${message}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Ignorar erro
|
|
133
|
+
}
|
|
134
|
+
originalConsoleInfo.apply(console, args);
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Classe unificada para configurar contextos de teste e integração com UnifiedReportManager.
|
|
139
|
+
* Permite configurar automaticamente o contexto do teste (API, Frontend, Mobile, SSH, Banco, Mixed),
|
|
140
|
+
* registrar logs, metadados, screenshots e garantir limpeza automática de sessões mobile.
|
|
141
|
+
*
|
|
142
|
+
* ✅ AGORA COM SISTEMA AUTOMÁTICO - Não precisa configurar nada manualmente!
|
|
143
|
+
*/
|
|
144
|
+
export class TestAnnotations {
|
|
145
|
+
/**
|
|
146
|
+
* Opções do runner para testes desktop
|
|
147
|
+
* - attachWindow: se true, tentará iniciar/atacar uma janela da aplicação
|
|
148
|
+
* - closeWindow: se true, fechará a aplicação ao final do CT
|
|
149
|
+
* - app_path: caminho para o executável a ser iniciado
|
|
150
|
+
*/
|
|
151
|
+
static runnerOptions = {};
|
|
152
|
+
/**
|
|
153
|
+
* Define opções do runner (attachWindow, closeWindow, app_path)
|
|
154
|
+
*/
|
|
155
|
+
static async setRunnerOptions(options) {
|
|
156
|
+
this.runnerOptions = { ...(this.runnerOptions || {}), ...(options || {}) };
|
|
157
|
+
// Se attachWindow for true e app_path fornecido, tentar iniciar a app
|
|
158
|
+
try {
|
|
159
|
+
if (this.runnerOptions.attachWindow && this.runnerOptions.app_path) {
|
|
160
|
+
await DesktopConnection.startApp(this.runnerOptions.app_path);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
console.warn('⚠️ Falha ao iniciar aplicação via setRunnerOptions:', e);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* ✅ REMOVIDO: inicializarAutoCore - Agora é automático!
|
|
169
|
+
* O sistema se inicializa sozinho quando o módulo é carregado.
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* 🛑 Ativa o modo "parar ao falhar" globalmente.
|
|
173
|
+
* Quando ativado, se um CT falhar, os próximos CTs não serão executados.
|
|
174
|
+
* Pode ser chamado no escopo do test.describe() para aplicar a todos os CTs do CN.
|
|
175
|
+
* @param enabled - true para ativar, false para desativar (padrão: true)
|
|
176
|
+
*/
|
|
177
|
+
static stopOnFailure(enabled = true) {
|
|
178
|
+
stopOnFailureEnabled = enabled;
|
|
179
|
+
console.log(`🛑 [STOP-ON-FAILURE] ${enabled ? 'ATIVADO' : 'DESATIVADO'} globalmente`);
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 🛑 Verifica se o modo "parar ao falhar" está ativo.
|
|
183
|
+
* @returns true se stopOnFailure está ativo
|
|
184
|
+
*/
|
|
185
|
+
static isStopOnFailureEnabled() {
|
|
186
|
+
return stopOnFailureEnabled;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* 🎯 NOVO: Captura CT diretamente do contexto do Playwright e registra automaticamente
|
|
190
|
+
*/
|
|
191
|
+
static capturarCTDoPlaywright(cnName, ctName, testInfo) {
|
|
192
|
+
try {
|
|
193
|
+
// 🕐 NOVO: Capturar tempo de início para cálculo de duração
|
|
194
|
+
const ctStartTime = Date.now();
|
|
195
|
+
StatementTracker.startCT(ctName);
|
|
196
|
+
testInfo.__autocoreCtName = ctName;
|
|
197
|
+
testInfo.__autocoreCnName = cnName;
|
|
198
|
+
// Registrar relação CT ↔ CN para uso na finalização mesmo após troca de contexto
|
|
199
|
+
UnifiedReportManager.registrarCtParaCn(ctName, cnName);
|
|
200
|
+
// 2. Registrar que o CT foi detectado pelo Playwright
|
|
201
|
+
StatementTracker.recordAction('OTHER', `CT detectado pelo Playwright: ${ctName}`, true, undefined, {
|
|
202
|
+
method: 'Playwright Detection',
|
|
203
|
+
url: testInfo.file,
|
|
204
|
+
});
|
|
205
|
+
// 3. Configurar finalização automática quando o teste terminar
|
|
206
|
+
const originalAttach = testInfo.attach;
|
|
207
|
+
let ctFinalizado = false;
|
|
208
|
+
testInfo.attach = async function (name, options) {
|
|
209
|
+
// Interceptar attach para detectar fim do teste
|
|
210
|
+
const result = await originalAttach.call(this, name, options);
|
|
211
|
+
if (!ctFinalizado &&
|
|
212
|
+
(name.includes('screenshot') || name.includes('trace'))) {
|
|
213
|
+
// ⏱️ NOVO: Calcular duração real do teste
|
|
214
|
+
const ctEndTime = Date.now();
|
|
215
|
+
const ctDuration = ctEndTime - ctStartTime;
|
|
216
|
+
console.log(`⏱️ [DURAÇÃO] CT executado em ${ctDuration}ms`);
|
|
217
|
+
// Determinar status baseado no estado do teste
|
|
218
|
+
const success = testInfo.status === 'passed' || testInfo.status === undefined;
|
|
219
|
+
const errorMessage = testInfo.status === 'failed' ? 'Teste falhou' : undefined;
|
|
220
|
+
console.log(`🏁 [PLAYWRIGHT-CT] Status do teste: ${testInfo.status}`);
|
|
221
|
+
StatementTracker.endCT(ctName, success, errorMessage);
|
|
222
|
+
// Fechar app se configurado no runner
|
|
223
|
+
try {
|
|
224
|
+
if (TestAnnotations.runnerOptions?.closeWindow) {
|
|
225
|
+
await DesktopConnection.closeApp();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
console.warn('⚠️ Falha ao fechar app após CT:', e);
|
|
230
|
+
}
|
|
231
|
+
ctFinalizado = true;
|
|
232
|
+
}
|
|
233
|
+
return result;
|
|
234
|
+
};
|
|
235
|
+
// 4. Configurar timeout para finalizar CT caso não seja finalizado
|
|
236
|
+
setTimeout(() => {
|
|
237
|
+
if (!ctFinalizado) {
|
|
238
|
+
// ⏱️ NOVO: Calcular duração mesmo no timeout
|
|
239
|
+
const ctEndTime = Date.now();
|
|
240
|
+
const ctDuration = ctEndTime - ctStartTime;
|
|
241
|
+
console.log(`⏱️ [DURAÇÃO] CT executado em ${ctDuration}ms (timeout)`);
|
|
242
|
+
const success = testInfo.status === 'passed' || testInfo.status === undefined;
|
|
243
|
+
StatementTracker.endCT(ctName, success);
|
|
244
|
+
// Fechar app se configurado no runner (fire-and-forget)
|
|
245
|
+
try {
|
|
246
|
+
if (TestAnnotations.runnerOptions?.closeWindow) {
|
|
247
|
+
DesktopConnection.closeApp().catch(() => { });
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch { }
|
|
251
|
+
ctFinalizado = true;
|
|
252
|
+
}
|
|
253
|
+
}, 60_000); // 60 segundos
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error(`❌ [PLAYWRIGHT-CT] Erro ao capturar CT: ${error}`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Detecta automaticamente o contexto principal do projeto a partir das variáveis de ambiente.
|
|
261
|
+
* @returns O tipo de contexto principal detectado.
|
|
262
|
+
*/
|
|
263
|
+
static detectProjectContext() {
|
|
264
|
+
// 1. Verificar PROJECT_TYPE explícito
|
|
265
|
+
const projectType = process.env.PROJECT_TYPE?.toUpperCase();
|
|
266
|
+
if (projectType) {
|
|
267
|
+
switch (projectType) {
|
|
268
|
+
case 'API':
|
|
269
|
+
return 'API';
|
|
270
|
+
case 'FRONTEND':
|
|
271
|
+
case 'WEB':
|
|
272
|
+
return 'Frontend';
|
|
273
|
+
case 'MOBILE':
|
|
274
|
+
return 'Mobile';
|
|
275
|
+
case 'SSH':
|
|
276
|
+
return 'SSH';
|
|
277
|
+
case 'BANCO':
|
|
278
|
+
case 'DATABASE':
|
|
279
|
+
return 'Banco';
|
|
280
|
+
case 'SCENARIOS':
|
|
281
|
+
return 'Scenarios';
|
|
282
|
+
default:
|
|
283
|
+
return 'Mixed';
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
// 2. 🔍 NOVA DETECÇÃO: Baseada no contexto real executado
|
|
287
|
+
try {
|
|
288
|
+
// Verificar se tem page Playwright ativo
|
|
289
|
+
const testContextPage = TestContext._page;
|
|
290
|
+
if (testContextPage && typeof testContextPage.goto === 'function') {
|
|
291
|
+
return 'Frontend';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch { }
|
|
295
|
+
try {
|
|
296
|
+
// Verificar se tem driver mobile ativo
|
|
297
|
+
const mobileDriver = TestContext.appiumDriver;
|
|
298
|
+
if (mobileDriver) {
|
|
299
|
+
return 'Mobile';
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
catch { }
|
|
303
|
+
// 3. Detecção baseada em variáveis de ambiente
|
|
304
|
+
if (process.env.PLAYWRIGHT_BROWSERS_PATH || process.env.PWDEBUG) {
|
|
305
|
+
return 'Frontend';
|
|
306
|
+
}
|
|
307
|
+
if (process.env.APPIUM_HOME || process.env.DEVICEFARM_API_KEY) {
|
|
308
|
+
return 'Mobile';
|
|
309
|
+
}
|
|
310
|
+
// 4. Fallback: detectar por estrutura de arquivos
|
|
311
|
+
const projectStructure = process.cwd();
|
|
312
|
+
if (projectStructure.includes('mobile') ||
|
|
313
|
+
projectStructure.includes('appium')) {
|
|
314
|
+
return 'Mobile';
|
|
315
|
+
}
|
|
316
|
+
if (projectStructure.includes('frontend') ||
|
|
317
|
+
projectStructure.includes('e2e') ||
|
|
318
|
+
projectStructure.includes('ui')) {
|
|
319
|
+
return 'Frontend';
|
|
320
|
+
}
|
|
321
|
+
if (projectStructure.includes('api') &&
|
|
322
|
+
!projectStructure.includes('frontend')) {
|
|
323
|
+
return 'API';
|
|
324
|
+
}
|
|
325
|
+
// 5. Default: Mixed para projetos indefinidos
|
|
326
|
+
return 'Mixed';
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Configuração automática do contexto do teste.
|
|
330
|
+
* Detecta o contexto, configura interceptadores e registra logs/metadados.
|
|
331
|
+
*/
|
|
332
|
+
static Auto = {
|
|
333
|
+
set testInfo(testInfo) {
|
|
334
|
+
// 🆕 CRÍTICO: Garantir que TestContext está disponível globalmente
|
|
335
|
+
// Especialmente importante para projetos consumidores
|
|
336
|
+
const TestContext = globalThis.__TEST_CONTEXT__;
|
|
337
|
+
if (TestContext &&
|
|
338
|
+
typeof TestContext.ensureGlobalAvailability === 'function') {
|
|
339
|
+
TestContext.ensureGlobalAvailability();
|
|
340
|
+
}
|
|
341
|
+
TestContext.setTestInfo(testInfo);
|
|
342
|
+
// ✅ CRÍTICO: Detectar CN e CT baseado na estrutura do titlePath
|
|
343
|
+
// titlePath[0] = filename ou describe principal
|
|
344
|
+
// titlePath[1] = describe (CN) ou test (CT) se só 2 níveis
|
|
345
|
+
// titlePath[2] = test (CT) se 3 níveis
|
|
346
|
+
let cnName;
|
|
347
|
+
let ctName;
|
|
348
|
+
if (testInfo.titlePath && testInfo.titlePath.length >= 3) {
|
|
349
|
+
// 🎯 ESTRUTURA: [filename, describe/CN, test/CT]
|
|
350
|
+
cnName = testInfo.titlePath[1];
|
|
351
|
+
ctName = testInfo.titlePath[2];
|
|
352
|
+
}
|
|
353
|
+
else if (testInfo.titlePath && testInfo.titlePath.length === 2) {
|
|
354
|
+
// 🎯 ESTRUTURA: [describe/CN, test/CT] - Caso do arquivo fornecido
|
|
355
|
+
cnName = testInfo.titlePath[0];
|
|
356
|
+
ctName = testInfo.titlePath[1];
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
// 🎯 FALLBACK: usar title como CT
|
|
360
|
+
cnName = testInfo.title;
|
|
361
|
+
ctName = testInfo.title;
|
|
362
|
+
}
|
|
363
|
+
StatementTracker.setTestName(cnName);
|
|
364
|
+
console.log(`🏷️ [AUTO] StatementTracker inicializado para CN: "${cnName}"`);
|
|
365
|
+
console.log(`🎯 [AUTO] CT detectado do Playwright: "${ctName}"`);
|
|
366
|
+
// 🆕 CRÍTICO: Capturar e registrar CT automaticamente do contexto do Playwright
|
|
367
|
+
TestAnnotations.capturarCTDoPlaywright(cnName, ctName, testInfo);
|
|
368
|
+
// 🆕 INICIAR INTERCEPTAÇÃO DE LOGS ANTES DE TUDO
|
|
369
|
+
interceptarLogs();
|
|
370
|
+
// 1. Detectar contexto principal
|
|
371
|
+
const contextoPrincipal = TestAnnotations.detectProjectContext();
|
|
372
|
+
// 2. Detectar se é realmente mixed (múltiplos tipos necessários)
|
|
373
|
+
const isMixed = TestAnnotations.detectIfMixed();
|
|
374
|
+
const contextoFinal = isMixed ? 'Mixed' : contextoPrincipal;
|
|
375
|
+
testStartTimes.set(cnName, Date.now());
|
|
376
|
+
UnifiedReportManager.setTestContext(cnName, contextoFinal);
|
|
377
|
+
// 3. Configurar interceptadores inteligentemente
|
|
378
|
+
if (isMixed) {
|
|
379
|
+
TestAnnotations.setupAllInterceptors(cnName);
|
|
380
|
+
UnifiedReportManager.adicionarLog(cnName, '🔀 Contexto Mixed detectado - Múltiplos tipos habilitados');
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
TestAnnotations.setupSpecificInterceptor(cnName, contextoPrincipal);
|
|
384
|
+
UnifiedReportManager.adicionarLog(cnName, `🤖 Contexto ${contextoPrincipal} detectado automaticamente`);
|
|
385
|
+
}
|
|
386
|
+
// 🆕 ESSENCIAL: Configurar interceptação de statements
|
|
387
|
+
TestAnnotations.setupStatementInterception(cnName);
|
|
388
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
389
|
+
},
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* Detecta se o projeto é realmente mixed (múltiplos contextos).
|
|
393
|
+
* @returns true se for mixed, false caso contrário.
|
|
394
|
+
*/
|
|
395
|
+
static detectIfMixed() {
|
|
396
|
+
const indicators = [
|
|
397
|
+
process.env.USE_MIXED === 'true',
|
|
398
|
+
process.env.PROJECT_TYPE?.toUpperCase() === 'MIXED',
|
|
399
|
+
process.env.PROJECT_TYPE?.toUpperCase() === 'SCENARIOS',
|
|
400
|
+
!!(process.env.BASE_URL && process.env.SSH_HOST),
|
|
401
|
+
!!(process.env.BASE_URL_APIGTW && process.env.DB_HOST),
|
|
402
|
+
process.cwd().includes('-scenarios') ||
|
|
403
|
+
process.cwd().includes('-cenarios'),
|
|
404
|
+
];
|
|
405
|
+
return indicators.some((indicator) => indicator);
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Configura interceptador seguro para um contexto específico.
|
|
409
|
+
* @param testName Nome do teste.
|
|
410
|
+
* @param contexto Contexto do teste.
|
|
411
|
+
*/
|
|
412
|
+
static setupSpecificInterceptor(testName, contexto) {
|
|
413
|
+
switch (contexto) {
|
|
414
|
+
case 'API':
|
|
415
|
+
TestAnnotations.setupSafeInterceptor(testName, 'API', ApiActions.logEvent.bind(ApiActions), ApiActions, 'logEvent');
|
|
416
|
+
break;
|
|
417
|
+
case 'Banco':
|
|
418
|
+
TestAnnotations.setupSafeInterceptor(testName, 'BANCO', BancoActions.logEvent.bind(BancoActions), BancoActions, 'logEvent');
|
|
419
|
+
break;
|
|
420
|
+
case 'SSH':
|
|
421
|
+
TestAnnotations.setupSafeInterceptor(testName, 'SSH', SSHActions.logEvent.bind(SSHActions), SSHActions, 'logEvent');
|
|
422
|
+
break;
|
|
423
|
+
case 'Frontend':
|
|
424
|
+
case 'Mobile':
|
|
425
|
+
// Frontend e Mobile não precisam de interceptadores específicos
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Configura interceptadores para todos os contextos (usado em projetos mixed).
|
|
431
|
+
* @param testName Nome do teste.
|
|
432
|
+
*/
|
|
433
|
+
static setupAllInterceptors(testName) {
|
|
434
|
+
TestAnnotations.setupSafeInterceptor(testName, 'API', ApiActions.logEvent.bind(ApiActions), ApiActions, 'logEvent');
|
|
435
|
+
TestAnnotations.setupSafeInterceptor(testName, 'SSH', SSHActions.logEvent.bind(SSHActions), SSHActions, 'logEvent');
|
|
436
|
+
TestAnnotations.setupSafeInterceptor(testName, 'BANCO', BancoActions.logEvent.bind(BancoActions), BancoActions, 'logEvent');
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Configura finalização automática do teste para registrar duração.
|
|
440
|
+
* @param testInfo TestInfo do Playwright.
|
|
441
|
+
*/
|
|
442
|
+
static setupTestFinalization(testInfo) {
|
|
443
|
+
const testName = testInfo.title;
|
|
444
|
+
// 🔧 NOVO: Detectar se é um test.describe (CN) ou test() (CT)
|
|
445
|
+
// Só configurar finalização para CTs individuais, NÃO para CNs
|
|
446
|
+
const titlePath = testInfo.titlePath || [];
|
|
447
|
+
const isIndividualTest = titlePath.length <= 2; // Arquivo + Test = 2 níveis
|
|
448
|
+
const isCTInsideDescribe = titlePath.length === 3; // Arquivo + Describe + Test = CT
|
|
449
|
+
// 🛡️ PROTEÇÃO: Não configurar finalização múltipla para CTs dentro de test.describe
|
|
450
|
+
// O CustomReporter já gerencia a finalização do CN completo
|
|
451
|
+
if (isCTInsideDescribe) {
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
// ✅ NOVO: Interceptar erros não capturados para detectar falhas
|
|
455
|
+
const originalErrorHandler = process.listeners('uncaughtException');
|
|
456
|
+
const originalRejectionHandler = process.listeners('unhandledRejection');
|
|
457
|
+
const testFailureDetector = (error) => {
|
|
458
|
+
const errorMessage = error.message || error.toString();
|
|
459
|
+
if (errorMessage.includes(testName) ||
|
|
460
|
+
errorMessage.includes('AssertionError') ||
|
|
461
|
+
errorMessage.includes('expect')) {
|
|
462
|
+
console.log(`🔴 Falha detectada no teste ${testName}: ${errorMessage}`);
|
|
463
|
+
UnifiedReportManager.adicionarLog(testName, `❌ Erro capturado: ${errorMessage}`);
|
|
464
|
+
// Marcar teste como falhado imediatamente
|
|
465
|
+
UnifiedReportManager.marcarFimTeste(testName, 'failed').catch((e) => console.warn(`⚠️ Erro ao marcar teste como falhado: ${e}`));
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
// Adicionar listeners temporários para o teste atual
|
|
469
|
+
process.on('uncaughtException', testFailureDetector);
|
|
470
|
+
process.on('unhandledRejection', testFailureDetector);
|
|
471
|
+
// ✅ NOVO: Configurar monitoramento de status do teste
|
|
472
|
+
// ⚠️ PROTEÇÃO: Wrappear em try/catch para evitar unhandled rejections
|
|
473
|
+
// que podem marcar o teste como failed indevidamente
|
|
474
|
+
setTimeout(async () => {
|
|
475
|
+
try {
|
|
476
|
+
// Remover listeners temporários após finalização
|
|
477
|
+
process.removeListener('uncaughtException', testFailureDetector);
|
|
478
|
+
process.removeListener('unhandledRejection', testFailureDetector);
|
|
479
|
+
const startTime = testStartTimes.get(testName);
|
|
480
|
+
if (startTime) {
|
|
481
|
+
const duration = (Date.now() - startTime) / 1000;
|
|
482
|
+
UnifiedReportManager.setTestDuration(testName, duration);
|
|
483
|
+
try {
|
|
484
|
+
// Aguardar um pouco para permitir que o Playwright processe o status
|
|
485
|
+
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
486
|
+
let testStatus = 'passed';
|
|
487
|
+
const playwrightStatus = testInfo.status;
|
|
488
|
+
if (playwrightStatus === 'failed' ||
|
|
489
|
+
playwrightStatus === 'timedOut' ||
|
|
490
|
+
playwrightStatus === 'interrupted') {
|
|
491
|
+
testStatus = 'failed';
|
|
492
|
+
}
|
|
493
|
+
else if (playwrightStatus === 'skipped') {
|
|
494
|
+
testStatus = 'skipped';
|
|
495
|
+
}
|
|
496
|
+
else if (playwrightStatus === 'passed') {
|
|
497
|
+
testStatus = 'passed';
|
|
498
|
+
}
|
|
499
|
+
if (testInfo.errors && testInfo.errors.length > 0) {
|
|
500
|
+
testStatus = 'failed';
|
|
501
|
+
}
|
|
502
|
+
// Fallback: analisar logs apenas se ainda não detectamos falha
|
|
503
|
+
if (testStatus === 'passed' && TestContext.getSafeTestInfo()) {
|
|
504
|
+
const currentTestInfo = TestContext.getSafeTestInfo();
|
|
505
|
+
if (currentTestInfo && currentTestInfo.title === testName) {
|
|
506
|
+
const logs = UnifiedReportManager.getTestLogs(testName);
|
|
507
|
+
const hasErrorLogs = logs.some((log) => log.includes('❌') ||
|
|
508
|
+
log.includes('FAILED') ||
|
|
509
|
+
log.includes('erro') ||
|
|
510
|
+
log.includes('falhou') ||
|
|
511
|
+
log.includes('Error') ||
|
|
512
|
+
log.includes('AssertionError'));
|
|
513
|
+
if (hasErrorLogs) {
|
|
514
|
+
testStatus = 'failed';
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
const detectedCnName = testInfo.__autocoreCnName ||
|
|
519
|
+
testInfo.titlePath?.[1] ||
|
|
520
|
+
testName;
|
|
521
|
+
const detectedCtName = testInfo.__autocoreCtName ||
|
|
522
|
+
testInfo.titlePath?.[2] ||
|
|
523
|
+
testInfo.title;
|
|
524
|
+
const errorDetails = testStatus === 'failed'
|
|
525
|
+
? testInfo.errors?.[0]?.message ||
|
|
526
|
+
testInfo.error?.message ||
|
|
527
|
+
'Playwright test failed'
|
|
528
|
+
: undefined;
|
|
529
|
+
if (detectedCtName) {
|
|
530
|
+
try {
|
|
531
|
+
const executions = StatementTracker.getExecutions();
|
|
532
|
+
const testEntry = executions.get(detectedCnName);
|
|
533
|
+
const runningCT = testEntry?.cts.find((ct) => ct.name === detectedCtName && ct.status === 'running');
|
|
534
|
+
if (runningCT) {
|
|
535
|
+
StatementTracker.endCT(detectedCtName, testStatus !== 'failed', errorDetails);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch (trackerError) {
|
|
539
|
+
console.warn(`⚠️ [PLAYWRIGHT-CT] Erro ao finalizar CT ${detectedCtName}:`, trackerError);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
console.log(`🔍 Status detectado para ${testName}: ${testStatus}`);
|
|
543
|
+
await UnifiedReportManager.marcarFimTeste(testName, testStatus, {
|
|
544
|
+
cnName: detectedCnName,
|
|
545
|
+
ctName: detectedCtName,
|
|
546
|
+
errorMessage: errorDetails,
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
console.warn(`⚠️ Erro ao detectar status do teste ${testName}:`, error);
|
|
551
|
+
await UnifiedReportManager.marcarFimTeste(testName, 'passed', {
|
|
552
|
+
cnName: testInfo.__autocoreCnName ||
|
|
553
|
+
testInfo.titlePath?.[1] ||
|
|
554
|
+
testName,
|
|
555
|
+
ctName: testInfo.__autocoreCtName ||
|
|
556
|
+
testInfo.titlePath?.[2] ||
|
|
557
|
+
testInfo.title,
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
await TestAnnotations.finalizeCoverageForTest(testName, testInfo, duration * 1000);
|
|
561
|
+
testStartTimes.delete(testName);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (fatalError) {
|
|
565
|
+
// ⚠️ PROTEÇÃO: Capturar qualquer erro para evitar unhandled rejection
|
|
566
|
+
// que pode marcar o teste como failed indevidamente
|
|
567
|
+
console.warn(`⚠️ [setupTestFinalization] Erro não-fatal durante finalização de "${testName}":`, fatalError);
|
|
568
|
+
}
|
|
569
|
+
}, 100);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* ✅ NOVO: Configura cobertura automática para teste Frontend
|
|
573
|
+
* @param testInfo TestInfo do Playwright
|
|
574
|
+
*/
|
|
575
|
+
static setupFrontendCoverage(testInfo) {
|
|
576
|
+
const testName = testInfo.title;
|
|
577
|
+
// Aguardar um pouco para página estar disponível
|
|
578
|
+
setTimeout(async () => {
|
|
579
|
+
try {
|
|
580
|
+
// ✅ VERIFICAR SE TESTCONTEXT.PAGE ESTÁ DISPONÍVEL
|
|
581
|
+
if (!TestContext.isPageInitialized()) {
|
|
582
|
+
console.warn(`TestContext.page não está inicializado para ${testName}, pulando cobertura frontend`);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
const currentPage = TestContext.page;
|
|
586
|
+
if (currentPage && currentPage.coverage) {
|
|
587
|
+
await CoverageManager.startFrontendCoverage(currentPage, testName);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch (error) {
|
|
591
|
+
console.warn(`Erro ao configurar cobertura para ${testName}:`, error);
|
|
592
|
+
}
|
|
593
|
+
}, 200);
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* ✅ NOVO: Finaliza e registra cobertura para um teste
|
|
597
|
+
* @param testName Nome do teste
|
|
598
|
+
* @param testInfo TestInfo do teste
|
|
599
|
+
* @param duration Duração do teste em ms
|
|
600
|
+
*/
|
|
601
|
+
static async finalizeCoverageForTest(testName, testInfo, duration) {
|
|
602
|
+
try {
|
|
603
|
+
// 🎯 FINALIZAR CT ATIVO ANTES DE TERMINAR O TESTE
|
|
604
|
+
TestAnnotations.finalizarCTAtivo();
|
|
605
|
+
// Determinar tipo do teste baseado no contexto
|
|
606
|
+
const metrics = UnifiedReportManager.getMetrics();
|
|
607
|
+
const contextType = metrics.testes.find((t) => t.nome === testName)?.tipo || 'Frontend';
|
|
608
|
+
// Obter página se disponível
|
|
609
|
+
let currentPage;
|
|
610
|
+
try {
|
|
611
|
+
currentPage = TestContext.page;
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
// Página não disponível
|
|
615
|
+
}
|
|
616
|
+
// Registrar cobertura
|
|
617
|
+
await CoverageManager.registerTestCoverage(testName, contextType, currentPage, duration);
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
console.warn(`Erro ao finalizar cobertura para ${testName}:`, error);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* 🆕 Configura interceptação automática de todas as classes Statements
|
|
625
|
+
*/
|
|
626
|
+
static setupStatementInterception(testName) {
|
|
627
|
+
try {
|
|
628
|
+
// 1. 🚨 INTERCEPTAÇÃO IMEDIATA - Antes de tudo!
|
|
629
|
+
TestAnnotations.setupImmediateInterception();
|
|
630
|
+
// 2. Configurar interceptação global
|
|
631
|
+
// StatementTracker não precisa setupGlobalInterception
|
|
632
|
+
// 3. Registrar o teste no sistema
|
|
633
|
+
UnifiedReportManager.registrarTeste(testName, 'API');
|
|
634
|
+
// 4. 🚀 DETECTAR AUTOMATICAMENTE classes Statement do projeto
|
|
635
|
+
const detectedStatements = TestAnnotations.detectStatementClassesFromCallStack();
|
|
636
|
+
if (detectedStatements.length > 0) {
|
|
637
|
+
detectedStatements.forEach((className, index) => {
|
|
638
|
+
console.log(` ${index + 1}. ${className}`);
|
|
639
|
+
});
|
|
640
|
+
console.log('');
|
|
641
|
+
// 🔥 INTERCEPTAÇÃO FORÇADA IMEDIATA para projeto real
|
|
642
|
+
TestAnnotations.forceInterceptRealProjectStatements(detectedStatements);
|
|
643
|
+
}
|
|
644
|
+
else {
|
|
645
|
+
// 🚫 NÃO usar fallback hardcoded - confiar na interceptação universal
|
|
646
|
+
}
|
|
647
|
+
// 5. Interceptar constructor global para capturar instanciações
|
|
648
|
+
TestAnnotations.setupConstructorInterception(detectedStatements);
|
|
649
|
+
// 6. 🚨 INTERCEPTAÇÃO UNIVERSAL MAIS AGRESSIVA
|
|
650
|
+
TestAnnotations.setupUniversalStatementInterception();
|
|
651
|
+
// TestAnnotations.setupRealTimeMethodInterception() // DESABILITADO - causa stack overflow
|
|
652
|
+
// 7. Métodos legacy para compatibilidade
|
|
653
|
+
try {
|
|
654
|
+
interceptAllStatements();
|
|
655
|
+
// Interceptação agora é automática via StatementTracker
|
|
656
|
+
// setupModuleInterception()
|
|
657
|
+
// StatementTracker não precisa autoDetectStatements
|
|
658
|
+
}
|
|
659
|
+
catch (legacyError) {
|
|
660
|
+
console.warn(`⚠️ Aviso: Métodos legacy falharam: ${legacyError}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
catch (error) {
|
|
664
|
+
console.error(`❌ Erro ao configurar interceptação de Statements: ${error}`);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* 🚨 Interceptação imediata - Configurada ANTES de qualquer import
|
|
669
|
+
*/
|
|
670
|
+
static setupImmediateInterception() {
|
|
671
|
+
try {
|
|
672
|
+
// 🔥 Interceptar console.info para capturar quando statements são executados
|
|
673
|
+
const originalConsoleInfo = console.info;
|
|
674
|
+
console.info = (...args) => {
|
|
675
|
+
const message = args.join(' ');
|
|
676
|
+
// 🎯 Capturar APENAS métodos que realmente são Statement execution
|
|
677
|
+
if (message.includes('Executando o método:')) {
|
|
678
|
+
// 🔍 FILTRO RIGOROSO: Verificar se realmente é de classe Statement
|
|
679
|
+
const methodMatches = message.match(/Executando o método:\s*(\w+)/i);
|
|
680
|
+
if (methodMatches && methodMatches[1]) {
|
|
681
|
+
const methodName = methodMatches[1];
|
|
682
|
+
// Só interceptar métodos típicos de Statement classes
|
|
683
|
+
const validStatementMethods = [
|
|
684
|
+
'getToken',
|
|
685
|
+
'get_token',
|
|
686
|
+
'token',
|
|
687
|
+
'auth',
|
|
688
|
+
'authenticate',
|
|
689
|
+
'consulta',
|
|
690
|
+
'consultaEndereco',
|
|
691
|
+
'consultaCobertura',
|
|
692
|
+
'validar',
|
|
693
|
+
'validarCNPJ',
|
|
694
|
+
'criarCliente',
|
|
695
|
+
'createContact',
|
|
696
|
+
'create',
|
|
697
|
+
'post',
|
|
698
|
+
'put',
|
|
699
|
+
'delete',
|
|
700
|
+
'request',
|
|
701
|
+
];
|
|
702
|
+
const isValidStatementMethod = validStatementMethods.some((valid) => methodName.toLowerCase().includes(valid.toLowerCase()));
|
|
703
|
+
if (isValidStatementMethod) {
|
|
704
|
+
originalConsoleInfo('📋 === CT EM EXECUÇÃO === [INTERCEPTADO!]');
|
|
705
|
+
originalConsoleInfo(` 🎯 Método Statement interceptado: ${message}`);
|
|
706
|
+
originalConsoleInfo(` ⏰ [${new Date().toISOString()}] Execução detectada`);
|
|
707
|
+
originalConsoleInfo(' 📊 Status: INTERCEPTADO COM SUCESSO');
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
// 🚫 REMOVIDO: Detecção hardcoded - usar apenas métodos dinâmicos
|
|
712
|
+
// Removido fallback com métodos específicos hardcoded
|
|
713
|
+
// Sistema agora só intercepta quando detecta padrões reais de Statement execution
|
|
714
|
+
return originalConsoleInfo.apply(console, args);
|
|
715
|
+
};
|
|
716
|
+
// 🔥 Interceptar também console.log padrão - COM PROTEÇÃO ANTI-LOOP
|
|
717
|
+
const originalConsoleLog = console.log;
|
|
718
|
+
const intercepting = false; // Flag para evitar recursão
|
|
719
|
+
const detectedSteps = new Set(); // Cache para evitar repetições
|
|
720
|
+
console.log = function (...args) {
|
|
721
|
+
const message = args.join(' ');
|
|
722
|
+
// 🚨 PROTEÇÃO ANTI-LOOP: Se já estamos interceptando, apenas execute o original
|
|
723
|
+
if (intercepting) {
|
|
724
|
+
return originalConsoleLog.apply(console, args);
|
|
725
|
+
}
|
|
726
|
+
// 🚀 DETECTAR EXECUÇÃO DE MÉTODOS CT
|
|
727
|
+
if (message.includes('Executando o método:')) {
|
|
728
|
+
const methodMatch = message.match(/Executando o método:\s*(.+)/);
|
|
729
|
+
if (methodMatch) {
|
|
730
|
+
const methodName = methodMatch[1].trim();
|
|
731
|
+
// ✅ DETECTAR CLASSE STATEMENT DO STACK TRACE
|
|
732
|
+
let className = 'Statement';
|
|
733
|
+
try {
|
|
734
|
+
const stack = new Error().stack || '';
|
|
735
|
+
// Procurar por "Statements<Nome>" no stack
|
|
736
|
+
const stackMatch = stack.match(/Statements(\w+)\.(\w+)/);
|
|
737
|
+
if (stackMatch) {
|
|
738
|
+
className = `Statements${stackMatch[1]}`;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
catch {
|
|
742
|
+
// Ignorar erro, usar className padrão
|
|
743
|
+
}
|
|
744
|
+
// ✅ CORREÇÃO: Usar novo sistema de Statements
|
|
745
|
+
StatementTracker.ensureCTCreated();
|
|
746
|
+
StatementTracker.startStatement(className, methodName);
|
|
747
|
+
// ❌ REMOVIDO: handleMethodExecution causava logs duplicados
|
|
748
|
+
// O log já é feito pelo StatementTracker
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
// ✅ DETECTAR FINALIZAÇÃO DE STATEMENT COM SUCESSO
|
|
752
|
+
if (message.includes('[ STEP ]') &&
|
|
753
|
+
message.includes('[ PASSED ]') &&
|
|
754
|
+
!message.includes('Validação do status') &&
|
|
755
|
+
!message.includes('Validação')) {
|
|
756
|
+
const stepMatch = message.match(/\[\s*STEP\s*\]\s*(.+?)\s*\[\s*PASSED\s*\]/);
|
|
757
|
+
if (stepMatch) {
|
|
758
|
+
const stepName = stepMatch[1].trim();
|
|
759
|
+
StatementTracker.finishStatement(Date.now(), true);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
// ❌ DETECTAR FINALIZAÇÃO DE STATEMENT COM ERRO
|
|
763
|
+
if (message.includes('[ STEP ]') && message.includes('[ FAILED ]')) {
|
|
764
|
+
const stepMatch = message.match(/\[\s*STEP\s*\]\s*(.+?)\s*\[\s*FAILED\s*\]/);
|
|
765
|
+
if (stepMatch) {
|
|
766
|
+
const stepName = stepMatch[1].trim();
|
|
767
|
+
// ❌ REMOVIDO: Log duplicado - já aparece no [ STEP ] [ FAILED ]
|
|
768
|
+
StatementTracker.finishStatement(Date.now(), false, stepName);
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
// � DETECTAR ERROS DE VALIDAÇÃO - NOVO!
|
|
772
|
+
if ((message.includes('❌ Validação falhou:') ||
|
|
773
|
+
message.includes('Validação falhou:')) &&
|
|
774
|
+
!message.includes('ERRO DE VALIDAÇÃO DETECTADO') &&
|
|
775
|
+
!message.includes('CT FALHOU:') &&
|
|
776
|
+
!this._processingError) {
|
|
777
|
+
TestAnnotations.handleValidationError(message);
|
|
778
|
+
}
|
|
779
|
+
// �📡 DETECTAR INÍCIO DE REQUESTS
|
|
780
|
+
if (message.includes('🔄 Executando POST:') ||
|
|
781
|
+
message.includes('🔄 Executando GET:')) {
|
|
782
|
+
// ❌ REMOVIDO: Log duplicado de "NOVA REQUEST INICIADA"
|
|
783
|
+
// O log já aparece no ApiActions quando a requisição é feita
|
|
784
|
+
}
|
|
785
|
+
// 🎯 FILTRO ULTRA-RIGOROSO: APENAS logs que realmente indicam execução de CT
|
|
786
|
+
if (message.includes('STEP') &&
|
|
787
|
+
(message.includes('PASSED') || message.includes('FAILED'))) {
|
|
788
|
+
// � PRIMEIRA VERIFICAÇÃO: EXCLUIR validações genéricas que não são CTs
|
|
789
|
+
const isGenericValidation = message.includes('Validação do status') ||
|
|
790
|
+
message.includes('Validação da resposta') ||
|
|
791
|
+
message.includes('Validação de status') ||
|
|
792
|
+
message.includes('Validação de') ||
|
|
793
|
+
message.includes('Validação') ||
|
|
794
|
+
message.includes('Validation of') ||
|
|
795
|
+
message.includes('Validation') ||
|
|
796
|
+
message.includes('Assert') ||
|
|
797
|
+
message.includes('Expect') ||
|
|
798
|
+
message.includes('Check') ||
|
|
799
|
+
message.includes('Verify') ||
|
|
800
|
+
message.includes('status code') ||
|
|
801
|
+
message.includes('response validation') ||
|
|
802
|
+
message.includes('campo obrigatório') ||
|
|
803
|
+
message.includes('campo') ||
|
|
804
|
+
message.includes('Schema') ||
|
|
805
|
+
message.includes('Assertion');
|
|
806
|
+
// 🔍 SEGUNDA VERIFICAÇÃO: Só permitir se realmente é de uma requisição/ação Statement
|
|
807
|
+
const isValidStatementStep = message.includes('Requisição Token') ||
|
|
808
|
+
message.includes('Requisição de') ||
|
|
809
|
+
message.includes('Request Token') ||
|
|
810
|
+
message.includes('Request') ||
|
|
811
|
+
message.includes('POST') ||
|
|
812
|
+
message.includes('GET') ||
|
|
813
|
+
message.includes('PUT') ||
|
|
814
|
+
message.includes('DELETE') ||
|
|
815
|
+
message.includes('Token FTTH') ||
|
|
816
|
+
message.includes('Massa SFA') ||
|
|
817
|
+
message.includes('Auth') ||
|
|
818
|
+
message.includes('API Call') ||
|
|
819
|
+
message.includes('Service Call');
|
|
820
|
+
// ✅ SÓ INTERCEPTAR se é Statement válido E NÃO é validação genérica
|
|
821
|
+
if (isValidStatementStep && !isGenericValidation) {
|
|
822
|
+
const stepHash = message.substring(0, 100); // Usar primeiros 100 chars como hash
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return originalConsoleLog.apply(console, args);
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
catch (error) {
|
|
829
|
+
console.warn(`⚠️ [IMEDIATO] Erro na interceptação imediata: ${error}`);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* 🔥 Interceptação em tempo real de métodos Statement
|
|
834
|
+
*/
|
|
835
|
+
static setupRealTimeMethodInterception() {
|
|
836
|
+
try {
|
|
837
|
+
console.log('🔥 === INTERCEPTAÇÃO EM TEMPO REAL ===');
|
|
838
|
+
// 🎯 Interceptar QUALQUER chamada de método que contenha padrões Statement
|
|
839
|
+
const originalApply = Function.prototype.apply;
|
|
840
|
+
const originalCall = Function.prototype.call;
|
|
841
|
+
Function.prototype.apply = function (thisArg, argsArray) {
|
|
842
|
+
// Verificar se é um método de classe Statement
|
|
843
|
+
if (thisArg &&
|
|
844
|
+
thisArg.constructor &&
|
|
845
|
+
thisArg.constructor.name.includes('Statement')) {
|
|
846
|
+
const className = thisArg.constructor.name;
|
|
847
|
+
const methodName = this.name || 'método_desconhecido';
|
|
848
|
+
console.log(`📋 === CT EM EXECUÇÃO === ${className}.${methodName}()`);
|
|
849
|
+
console.log(` 🎯 [TEMPO REAL] Detectada execução: ${className}.${methodName}()`);
|
|
850
|
+
console.log(` ⏰ [${new Date().toISOString()}] Iniciando execução...`);
|
|
851
|
+
// Executar com tracking
|
|
852
|
+
const startTime = Date.now();
|
|
853
|
+
try {
|
|
854
|
+
const result = originalApply.call(this, thisArg, argsArray);
|
|
855
|
+
const duration = Date.now() - startTime;
|
|
856
|
+
console.log(` ✅ [${new Date().toISOString()}] Finalizada: ${className}.${methodName}() - ${duration}ms`);
|
|
857
|
+
console.log('📋 === CT FINALIZADA ===');
|
|
858
|
+
return result;
|
|
859
|
+
}
|
|
860
|
+
catch (error) {
|
|
861
|
+
const duration = Date.now() - startTime;
|
|
862
|
+
console.log(` ❌ [${new Date().toISOString()}] Erro: ${className}.${methodName}() - ${duration}ms`);
|
|
863
|
+
console.log('📋 === CT COM ERRO ===');
|
|
864
|
+
throw error;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return originalApply.call(this, thisArg, argsArray);
|
|
868
|
+
};
|
|
869
|
+
Function.prototype.call = function (thisArg, ...args) {
|
|
870
|
+
// Verificar se é um método de classe Statement
|
|
871
|
+
if (thisArg &&
|
|
872
|
+
thisArg.constructor &&
|
|
873
|
+
thisArg.constructor.name.includes('Statement')) {
|
|
874
|
+
const className = thisArg.constructor.name;
|
|
875
|
+
const methodName = this.name || 'método_desconhecido';
|
|
876
|
+
console.log(`📋 === CT EM EXECUÇÃO === ${className}.${methodName}()`);
|
|
877
|
+
console.log(` 🎯 [TEMPO REAL] Detectada execução: ${className}.${methodName}()`);
|
|
878
|
+
console.log(` ⏰ [${new Date().toISOString()}] Iniciando execução...`);
|
|
879
|
+
// Executar com tracking
|
|
880
|
+
const startTime = Date.now();
|
|
881
|
+
try {
|
|
882
|
+
const result = originalCall.call(this, thisArg, ...args);
|
|
883
|
+
const duration = Date.now() - startTime;
|
|
884
|
+
console.log(` ✅ [${new Date().toISOString()}] Finalizada: ${className}.${methodName}() - ${duration}ms`);
|
|
885
|
+
console.log('📋 === CT FINALIZADA ===');
|
|
886
|
+
return result;
|
|
887
|
+
}
|
|
888
|
+
catch (error) {
|
|
889
|
+
const duration = Date.now() - startTime;
|
|
890
|
+
console.log(` ❌ [${new Date().toISOString()}] Erro: ${className}.${methodName}() - ${duration}ms`);
|
|
891
|
+
console.log('📋 === CT COM ERRO ===');
|
|
892
|
+
throw error;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
return originalCall.call(this, thisArg, ...args);
|
|
896
|
+
};
|
|
897
|
+
console.log('🔧 [TEMPO REAL] Interceptação de métodos configurada');
|
|
898
|
+
}
|
|
899
|
+
catch (error) {
|
|
900
|
+
console.warn(`⚠️ [TEMPO REAL] Erro na interceptação: ${error}`);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
/**
|
|
904
|
+
* 🆕 Intercepta constructors de classes Statement para capturar instanciações
|
|
905
|
+
*/
|
|
906
|
+
static setupConstructorInterception(statementClasses) {
|
|
907
|
+
try {
|
|
908
|
+
// Interceptar contexto global para capturar imports/instanciações
|
|
909
|
+
const contexts = [globalThis];
|
|
910
|
+
// Adicionar outros contextos se disponíveis
|
|
911
|
+
try {
|
|
912
|
+
const windowObj = globalThis.window;
|
|
913
|
+
if (windowObj)
|
|
914
|
+
contexts.push(windowObj);
|
|
915
|
+
const globalObj = globalThis.global;
|
|
916
|
+
if (globalObj)
|
|
917
|
+
contexts.push(globalObj);
|
|
918
|
+
}
|
|
919
|
+
catch {
|
|
920
|
+
// Ignorar se não disponíveis
|
|
921
|
+
}
|
|
922
|
+
for (const context of contexts) {
|
|
923
|
+
for (const className of statementClasses) {
|
|
924
|
+
try {
|
|
925
|
+
// Verificar se a classe existe no contexto
|
|
926
|
+
const OriginalClass = context[className];
|
|
927
|
+
if (typeof OriginalClass === 'function') {
|
|
928
|
+
console.log(`🔍 Encontrada classe: ${className} - preparando interceptação`);
|
|
929
|
+
// Criar versão interceptada
|
|
930
|
+
const InterceptedClass = StatementTracker.interceptStatement(OriginalClass);
|
|
931
|
+
// Substituir no contexto
|
|
932
|
+
context[className] = InterceptedClass;
|
|
933
|
+
console.log(`✅ Classe ${className} interceptada com sucesso`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
catch (error) {
|
|
937
|
+
// Ignorar erros individuais de classes
|
|
938
|
+
console.warn(`⚠️ Erro ao interceptar ${className}: ${error}`);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// Interceptação dinâmica usando Proxy para capturar instanciações futuras
|
|
943
|
+
TestAnnotations.setupDynamicInterception(statementClasses);
|
|
944
|
+
}
|
|
945
|
+
catch (error) {
|
|
946
|
+
console.error(`❌ Erro na interceptação de constructors: ${error}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* 🚀 Detecta automaticamente classes Statement via análise de arquivo .spec.ts
|
|
951
|
+
*/
|
|
952
|
+
static detectStatementClassesFromCallStack() {
|
|
953
|
+
const detectedClasses = new Set();
|
|
954
|
+
try {
|
|
955
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
956
|
+
if (testInfo?.file) {
|
|
957
|
+
const testFile = testInfo.file;
|
|
958
|
+
console.log(`📁 Arquivo de teste detectado: ${testFile}`);
|
|
959
|
+
// 🎯 NOVA ABORDAGEM: Análise robusta do arquivo .spec.ts
|
|
960
|
+
const newClasses = TestAnnotations.detectStatementClassesFromSpecFile(testFile);
|
|
961
|
+
newClasses.forEach((cls) => detectedClasses.add(cls));
|
|
962
|
+
}
|
|
963
|
+
// 🔄 Fallback: Analisar contexto global - APENAS se realmente necessário
|
|
964
|
+
if (detectedClasses.size === 0) {
|
|
965
|
+
TestAnnotations.detectStatementClassesFromGlobal(detectedClasses);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
catch (error) {
|
|
969
|
+
console.warn(`⚠️ Erro ao detectar classes Statement: ${error}`);
|
|
970
|
+
}
|
|
971
|
+
const classArray = Array.from(detectedClasses);
|
|
972
|
+
return classArray;
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* 🎯 Detecta classes Statement de um arquivo .spec.ts específico
|
|
976
|
+
*/
|
|
977
|
+
static detectStatementClassesFromSpecFile(testFile) {
|
|
978
|
+
const detectedClasses = new Set();
|
|
979
|
+
try {
|
|
980
|
+
// 🔄 COMPATÍVEL COM ESM: Usar dynamic imports
|
|
981
|
+
const fs = globalThis.require?.('fs') ||
|
|
982
|
+
globalThis.fs ||
|
|
983
|
+
TestAnnotations.getFileSystemModule();
|
|
984
|
+
if (!fs) {
|
|
985
|
+
return TestAnnotations.detectStatementClassesFromTestContext();
|
|
986
|
+
}
|
|
987
|
+
const path = globalThis.require?.('path') ||
|
|
988
|
+
globalThis.path ||
|
|
989
|
+
TestAnnotations.getPathModule();
|
|
990
|
+
// 📂 Tentar diferentes caminhos para encontrar o arquivo
|
|
991
|
+
const possiblePaths = [
|
|
992
|
+
testFile,
|
|
993
|
+
testFile.replace('C:\\Users\\A0169497\\PlayWright\\comp-autocore-v1\\', './'),
|
|
994
|
+
'./tests/' + testFile.split('\\').pop(),
|
|
995
|
+
'./test/' + testFile.split('\\').pop(),
|
|
996
|
+
'./' + testFile.split('\\').pop(),
|
|
997
|
+
];
|
|
998
|
+
let content = '';
|
|
999
|
+
for (const filePath of possiblePaths) {
|
|
1000
|
+
try {
|
|
1001
|
+
if (fs.existsSync && fs.existsSync(filePath)) {
|
|
1002
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
1003
|
+
console.log(`📖 Analisando arquivo: ${filePath}`);
|
|
1004
|
+
break;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
catch (fileError) {
|
|
1008
|
+
// Continua tentando outros caminhos
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (!content) {
|
|
1012
|
+
console.warn(`⚠️ Não foi possível ler o arquivo de teste: ${testFile}`);
|
|
1013
|
+
console.log('🔄 Aguardando detecção dinâmica durante execução...');
|
|
1014
|
+
// � REMOVIDO: Fallback hardcoded - retornar vazio para forçar detecção real
|
|
1015
|
+
return [];
|
|
1016
|
+
}
|
|
1017
|
+
// 🔍 PADRÕES DE DETECÇÃO ROBUSTOS
|
|
1018
|
+
// 1. Exports da própria classe: export class StatementsToken
|
|
1019
|
+
const exportClassRegex = /export\s+class\s+(\w*Statement\w*)/g;
|
|
1020
|
+
let exportMatch;
|
|
1021
|
+
while ((exportMatch = exportClassRegex.exec(content)) !== null) {
|
|
1022
|
+
if (exportMatch[1]) {
|
|
1023
|
+
detectedClasses.add(exportMatch[1]);
|
|
1024
|
+
console.log(`🎯 Classe Statement encontrada via export: ${exportMatch[1]}`);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// 2. Imports diretos: import { StatementsToken } from "..."
|
|
1028
|
+
const importDirectRegex = /import\s*{\s*([^}]*)\s*}\s*from\s*['"]/g;
|
|
1029
|
+
let importMatch;
|
|
1030
|
+
while ((importMatch = importDirectRegex.exec(content)) !== null) {
|
|
1031
|
+
const importedItems = importMatch[1].split(',');
|
|
1032
|
+
importedItems.forEach((item) => {
|
|
1033
|
+
const cleanItem = item.trim();
|
|
1034
|
+
if (cleanItem.includes('Statement') ||
|
|
1035
|
+
cleanItem.startsWith('Statements')) {
|
|
1036
|
+
detectedClasses.add(cleanItem);
|
|
1037
|
+
console.log(`🎯 Classe Statement encontrada via import: ${cleanItem}`);
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
// 3. Instanciações: new StatementsToken(), new StatementsConsultaEndereco()
|
|
1042
|
+
const newStatementRegex = /new\s+(\w*Statement\w*)\s*\(/g;
|
|
1043
|
+
let newMatch;
|
|
1044
|
+
while ((newMatch = newStatementRegex.exec(content)) !== null) {
|
|
1045
|
+
if (newMatch[1]) {
|
|
1046
|
+
detectedClasses.add(newMatch[1]);
|
|
1047
|
+
console.log(`🎯 Classe Statement encontrada via new: ${newMatch[1]}`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
// 4. Await new: await new StatementsToken()
|
|
1051
|
+
const awaitNewRegex = /await\s+new\s+(\w*Statement\w*)\s*\(/g;
|
|
1052
|
+
let awaitMatch;
|
|
1053
|
+
while ((awaitMatch = awaitNewRegex.exec(content)) !== null) {
|
|
1054
|
+
if (awaitMatch[1]) {
|
|
1055
|
+
detectedClasses.add(awaitMatch[1]);
|
|
1056
|
+
console.log(`🎯 Classe Statement encontrada via await new: ${awaitMatch[1]}`);
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
catch (error) {
|
|
1061
|
+
console.warn(`⚠️ Erro ao analisar arquivo .spec.ts: ${error}`);
|
|
1062
|
+
// 🚫 REMOVIDO: Fallback - retornar vazio para forçar detecção real
|
|
1063
|
+
return [];
|
|
1064
|
+
}
|
|
1065
|
+
return Array.from(detectedClasses);
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* 🌐 Interceptar classes Statement disponíveis no globalThis
|
|
1069
|
+
*/
|
|
1070
|
+
static interceptGlobalStatements() {
|
|
1071
|
+
try {
|
|
1072
|
+
console.log('🔍 [Global] Procurando classes Statement no globalThis...');
|
|
1073
|
+
const globalObj = globalThis;
|
|
1074
|
+
const foundClasses = [];
|
|
1075
|
+
// Procurar no globalThis por classes que seguem padrões Statement
|
|
1076
|
+
for (const key in globalObj) {
|
|
1077
|
+
try {
|
|
1078
|
+
const value = globalObj[key];
|
|
1079
|
+
if (typeof value === 'function' && value.prototype) {
|
|
1080
|
+
const className = value.name || key;
|
|
1081
|
+
// Detectar padrões de Statement
|
|
1082
|
+
const isStatementClass = className &&
|
|
1083
|
+
(className.startsWith('Statements') ||
|
|
1084
|
+
className.includes('Statement') ||
|
|
1085
|
+
className.endsWith('Statement') ||
|
|
1086
|
+
className.match(/^\w*Statements?\w*$/));
|
|
1087
|
+
if (isStatementClass) {
|
|
1088
|
+
console.log(`🎯 [Global] Classe Statement encontrada: ${className}`);
|
|
1089
|
+
foundClasses.push(className);
|
|
1090
|
+
// Interceptar a classe diretamente
|
|
1091
|
+
try {
|
|
1092
|
+
const InterceptedClass = StatementTracker.interceptStatement(value);
|
|
1093
|
+
globalObj[key] = InterceptedClass;
|
|
1094
|
+
console.log(`✅ [Global] ${className} interceptado com sucesso`);
|
|
1095
|
+
}
|
|
1096
|
+
catch (error) {
|
|
1097
|
+
console.warn(`⚠️ [Global] Erro ao interceptar ${className}:`, error);
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
catch (error) {
|
|
1103
|
+
// Ignorar erros individuais
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1106
|
+
console.log(`🎯 [Global] ${foundClasses.length} classes Statement interceptadas: ${foundClasses.join(', ')}`);
|
|
1107
|
+
// ✅ NOVO: Interceptar construtores dinamicamente usando Proxy
|
|
1108
|
+
TestAnnotations.setupDynamicConstructorInterception();
|
|
1109
|
+
}
|
|
1110
|
+
catch (error) {
|
|
1111
|
+
console.warn('⚠️ [Global] Erro na interceptação global:', error);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
/**
|
|
1115
|
+
* ✨ NOVO: Interceptação dinâmica de construtores Statement
|
|
1116
|
+
*/
|
|
1117
|
+
static setupDynamicConstructorInterception() {
|
|
1118
|
+
try {
|
|
1119
|
+
console.log('🪄 [Dynamic] Configurando interceptação dinâmica de construtores...');
|
|
1120
|
+
// 🎯 Interceptar Function.prototype.constructor para capturar 'new'
|
|
1121
|
+
const originalConstructor = Function.prototype.constructor;
|
|
1122
|
+
// 🎁 Interceptar criação de instâncias via Proxy no globalThis
|
|
1123
|
+
const originalGlobalDescriptor = Object.getOwnPropertyDescriptor(globalThis, 'Function');
|
|
1124
|
+
if (originalGlobalDescriptor) {
|
|
1125
|
+
Object.defineProperty(globalThis, 'Function', {
|
|
1126
|
+
value: new Proxy(Function, {
|
|
1127
|
+
construct(target, args, newTarget) {
|
|
1128
|
+
const instance = Reflect.construct(target, args, newTarget);
|
|
1129
|
+
// Se a função criada é uma classe Statement, interceptar
|
|
1130
|
+
if (typeof instance === 'function' &&
|
|
1131
|
+
instance.name &&
|
|
1132
|
+
(instance.name.includes('Statement') ||
|
|
1133
|
+
instance.name.includes('Statements'))) {
|
|
1134
|
+
console.log(`🪄 [Dynamic] Interceptando construtor Statement: ${instance.name}`);
|
|
1135
|
+
return StatementTracker.interceptStatement(instance);
|
|
1136
|
+
}
|
|
1137
|
+
return instance;
|
|
1138
|
+
},
|
|
1139
|
+
}),
|
|
1140
|
+
configurable: true,
|
|
1141
|
+
writable: true,
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
console.log('✅ [Dynamic] Interceptação dinâmica ativada');
|
|
1145
|
+
}
|
|
1146
|
+
catch (error) {
|
|
1147
|
+
console.warn('⚠️ [Dynamic] Erro na interceptação dinâmica:', error);
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
/**
|
|
1151
|
+
* ✨ NOVO: Interceptar classes Statement já importadas nos módulos
|
|
1152
|
+
*/
|
|
1153
|
+
static interceptImportedStatements() {
|
|
1154
|
+
try {
|
|
1155
|
+
console.log('🔍 [ImportedStatements] Procurando classes Statement importadas...');
|
|
1156
|
+
// 1️⃣ ESTRATÉGIA: Interceptar via require.cache (Node.js modules)
|
|
1157
|
+
if (typeof require !== 'undefined' && require.cache) {
|
|
1158
|
+
const moduleIds = Object.keys(require.cache);
|
|
1159
|
+
console.log(`🔍 [ImportedStatements] Analisando ${moduleIds.length} módulos carregados...`);
|
|
1160
|
+
for (const moduleId of moduleIds) {
|
|
1161
|
+
try {
|
|
1162
|
+
// Verificar se o módulo contém "Statement" no caminho
|
|
1163
|
+
if (moduleId.includes('Statement') ||
|
|
1164
|
+
moduleId.includes('statements')) {
|
|
1165
|
+
console.log(`🎯 [ImportedStatements] Módulo Statement encontrado: ${moduleId}`);
|
|
1166
|
+
const moduleExports = require.cache[moduleId]?.exports;
|
|
1167
|
+
if (moduleExports && typeof moduleExports === 'object') {
|
|
1168
|
+
// Procurar por classes exportadas
|
|
1169
|
+
for (const [exportName, exportValue] of Object.entries(moduleExports)) {
|
|
1170
|
+
if (typeof exportValue === 'function' &&
|
|
1171
|
+
exportName.includes('Statement') &&
|
|
1172
|
+
exportValue.prototype) {
|
|
1173
|
+
console.log(`🎯 [ImportedStatements] Classe Statement importada: ${exportName}`);
|
|
1174
|
+
// Interceptar a classe diretamente
|
|
1175
|
+
try {
|
|
1176
|
+
const InterceptedClass = StatementTracker.interceptStatement(exportValue);
|
|
1177
|
+
moduleExports[exportName] = InterceptedClass;
|
|
1178
|
+
console.log(`✅ [ImportedStatements] ${exportName} interceptado com sucesso!`);
|
|
1179
|
+
}
|
|
1180
|
+
catch (error) {
|
|
1181
|
+
console.warn(`⚠️ [ImportedStatements] Erro ao interceptar ${exportName}:`, error);
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
catch (error) {
|
|
1189
|
+
// Ignorar erros individuais de módulos
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
// 2️⃣ ESTRATÉGIA: Interceptar via globalThis (ES modules)
|
|
1194
|
+
const globalObj = globalThis;
|
|
1195
|
+
for (const key in globalObj) {
|
|
1196
|
+
try {
|
|
1197
|
+
const value = globalObj[key];
|
|
1198
|
+
if (typeof value === 'function' &&
|
|
1199
|
+
value.prototype &&
|
|
1200
|
+
key.includes('Statement')) {
|
|
1201
|
+
console.log(`🎯 [ImportedStatements] Statement global: ${key}`);
|
|
1202
|
+
try {
|
|
1203
|
+
const InterceptedClass = StatementTracker.interceptStatement(value);
|
|
1204
|
+
globalObj[key] = InterceptedClass;
|
|
1205
|
+
console.log(`✅ [ImportedStatements] ${key} interceptado globalmente!`);
|
|
1206
|
+
}
|
|
1207
|
+
catch (error) {
|
|
1208
|
+
console.warn(`⚠️ [ImportedStatements] Erro ao interceptar global ${key}:`, error);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
catch (error) {
|
|
1213
|
+
// Ignorar erros
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
console.log('✅ [ImportedStatements] Interceptação de módulos importados concluída');
|
|
1217
|
+
}
|
|
1218
|
+
catch (error) {
|
|
1219
|
+
console.warn('⚠️ [ImportedStatements] Erro na interceptação de módulos:', error);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* 🔄 Detecta classes Statement do contexto atual do teste (fallback ESM)
|
|
1224
|
+
*/
|
|
1225
|
+
static detectStatementClassesFromTestContext() {
|
|
1226
|
+
const detectedClasses = new Set();
|
|
1227
|
+
try {
|
|
1228
|
+
// Analisar stack trace para encontrar classes Statement
|
|
1229
|
+
const stack = new Error().stack || '';
|
|
1230
|
+
// 🎯 FILTROS RIGOROSOS: Evitar métodos do sistema próprio
|
|
1231
|
+
const systemMethods = [
|
|
1232
|
+
'detectStatementClassesFromTestContext',
|
|
1233
|
+
'detectStatementClassesFromSpecFile',
|
|
1234
|
+
'detectStatementClassesFromCallStack',
|
|
1235
|
+
'setupStatementInterception',
|
|
1236
|
+
'setupDynamicInterception',
|
|
1237
|
+
'loadStatementClassFromPath',
|
|
1238
|
+
'makeStatementClassGloballyAvailable',
|
|
1239
|
+
];
|
|
1240
|
+
// Procurar padrões de Statement no stack
|
|
1241
|
+
const statementRegex = /(\w*Statement\w*)/g;
|
|
1242
|
+
let match;
|
|
1243
|
+
while ((match = statementRegex.exec(stack)) !== null) {
|
|
1244
|
+
const className = match[1];
|
|
1245
|
+
// 🚫 FILTRAR métodos do sistema
|
|
1246
|
+
if (className &&
|
|
1247
|
+
className !== 'Statement' &&
|
|
1248
|
+
className.length > 3 &&
|
|
1249
|
+
!systemMethods.includes(className) &&
|
|
1250
|
+
!className.includes('detect') &&
|
|
1251
|
+
!className.includes('setup') &&
|
|
1252
|
+
!className.includes('load') &&
|
|
1253
|
+
!className.includes('make') &&
|
|
1254
|
+
!className.includes('get')) {
|
|
1255
|
+
detectedClasses.add(className);
|
|
1256
|
+
console.log(`🎯 Classe Statement real encontrada no stack: ${className}`);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
catch (error) {
|
|
1261
|
+
console.warn(`⚠️ Erro na análise de contexto: ${error}`);
|
|
1262
|
+
}
|
|
1263
|
+
return Array.from(detectedClasses);
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* 🛠️ Obtém módulo fs compatível com ESM
|
|
1267
|
+
*/
|
|
1268
|
+
static getFileSystemModule() {
|
|
1269
|
+
try {
|
|
1270
|
+
// Tentar diferentes formas de acessar fs
|
|
1271
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
1272
|
+
return eval('require')('fs');
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
catch { }
|
|
1276
|
+
return null;
|
|
1277
|
+
}
|
|
1278
|
+
/**
|
|
1279
|
+
* 🛠️ Obtém módulo path compatível com ESM
|
|
1280
|
+
*/
|
|
1281
|
+
static getPathModule() {
|
|
1282
|
+
try {
|
|
1283
|
+
if (typeof process !== 'undefined' && process.versions?.node) {
|
|
1284
|
+
return eval('require')('path');
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
catch { }
|
|
1288
|
+
// Fallback manual para path
|
|
1289
|
+
return {
|
|
1290
|
+
basename: (p) => p.split(/[\\/]/).pop() || '',
|
|
1291
|
+
dirname: (p) => p.substring(0, p.lastIndexOf('/')) ||
|
|
1292
|
+
p.substring(0, p.lastIndexOf('\\')),
|
|
1293
|
+
join: (...args) => args.join('/'),
|
|
1294
|
+
resolve: (...args) => args.join('/'),
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
/**
|
|
1298
|
+
* 🌐 Detecta classes Statement do contexto global - APENAS análise real
|
|
1299
|
+
*/
|
|
1300
|
+
static detectStatementClassesFromGlobal(detectedClasses) {
|
|
1301
|
+
try {
|
|
1302
|
+
// 🔍 ANÁLISE RIGOROSA: Só aceitar classes que realmente parecem Statement
|
|
1303
|
+
const contexts = [
|
|
1304
|
+
globalThis,
|
|
1305
|
+
globalThis.global,
|
|
1306
|
+
globalThis.window,
|
|
1307
|
+
].filter(Boolean);
|
|
1308
|
+
for (const context of contexts) {
|
|
1309
|
+
try {
|
|
1310
|
+
const propertyNames = Object.getOwnPropertyNames(context);
|
|
1311
|
+
for (const key of propertyNames) {
|
|
1312
|
+
try {
|
|
1313
|
+
const value = context[key];
|
|
1314
|
+
// 🎯 FILTROS MAIS AGRESSIVOS para identificar classes Statement reais
|
|
1315
|
+
if (typeof value === 'function' &&
|
|
1316
|
+
key.length > 4 && // Reduzido para detectar mais classes
|
|
1317
|
+
(key.includes('Statement') ||
|
|
1318
|
+
key.startsWith('Statements') ||
|
|
1319
|
+
key.endsWith('Statement') ||
|
|
1320
|
+
key.endsWith('Statements') ||
|
|
1321
|
+
/^[A-Z][a-zA-Z]*Statement[s]?/.test(key)) && // Padrão mais amplo
|
|
1322
|
+
!key.includes('Test') &&
|
|
1323
|
+
!key.includes('Mock') &&
|
|
1324
|
+
!key.includes('Fake') &&
|
|
1325
|
+
!key.includes('Stub') &&
|
|
1326
|
+
!key.includes('Proxy') &&
|
|
1327
|
+
!key.includes('Wrapper') &&
|
|
1328
|
+
!key.includes('Helper') &&
|
|
1329
|
+
!key.includes('Util') &&
|
|
1330
|
+
!key.includes('Base') &&
|
|
1331
|
+
!key.includes('Abstract')) {
|
|
1332
|
+
// 🔍 VALIDAÇÃO MAIS FLEXÍVEL: Verificar se tem métodos típicos de Statement ou é uma classe válida
|
|
1333
|
+
const prototype = value.prototype;
|
|
1334
|
+
if (prototype) {
|
|
1335
|
+
const methods = Object.getOwnPropertyNames(prototype);
|
|
1336
|
+
const hasStatementMethods = methods.some((method) => method.includes('get') ||
|
|
1337
|
+
method.includes('post') ||
|
|
1338
|
+
method.includes('put') ||
|
|
1339
|
+
method.includes('delete') ||
|
|
1340
|
+
method.includes('consulta') ||
|
|
1341
|
+
method.includes('validar') ||
|
|
1342
|
+
method.includes('criar') ||
|
|
1343
|
+
method.includes('request') ||
|
|
1344
|
+
method.includes('execute') ||
|
|
1345
|
+
method.includes('buscar') ||
|
|
1346
|
+
method.includes('obter') ||
|
|
1347
|
+
method.includes('enviar') ||
|
|
1348
|
+
method.includes('processar') ||
|
|
1349
|
+
method.includes('realizar') ||
|
|
1350
|
+
method.includes('executar'));
|
|
1351
|
+
// 🚀 ACEITAR se tem métodos Statement OU se é uma classe que parece Statement
|
|
1352
|
+
const hasConstructor = methods.includes('constructor');
|
|
1353
|
+
const isStatementLike = hasConstructor &&
|
|
1354
|
+
(hasStatementMethods || methods.length > 2);
|
|
1355
|
+
if (isStatementLike) {
|
|
1356
|
+
detectedClasses.add(key);
|
|
1357
|
+
console.log(`\n🎯 Classe Statement detectada: ${key}`);
|
|
1358
|
+
if (hasStatementMethods) {
|
|
1359
|
+
console.log(` 📋 Métodos relevantes encontrados: ${methods
|
|
1360
|
+
.filter((m) => m.includes('get') ||
|
|
1361
|
+
m.includes('post') ||
|
|
1362
|
+
m.includes('consulta') ||
|
|
1363
|
+
m.includes('validar') ||
|
|
1364
|
+
m.includes('criar') ||
|
|
1365
|
+
m.includes('request'))
|
|
1366
|
+
.slice(0, 3)
|
|
1367
|
+
.join(', ')}`);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
catch {
|
|
1374
|
+
// Ignorar propriedades que causam erro ao acessar
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
catch {
|
|
1379
|
+
// Ignorar contextos problemáticos
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
catch (error) {
|
|
1384
|
+
console.warn(`⚠️ Erro ao analisar contexto global: ${error}`);
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
/**
|
|
1388
|
+
* 🌐 Configuração universal que intercepta qualquer instanciação de classe com padrão Statement
|
|
1389
|
+
*/
|
|
1390
|
+
static setupUniversalStatementInterception() {
|
|
1391
|
+
try {
|
|
1392
|
+
// 🚀 INTERCEPTAÇÃO CRÍTICA: Interceptar Function constructor
|
|
1393
|
+
const originalFunction = globalThis.Function;
|
|
1394
|
+
// 🎯 INTERCEPTAÇÃO DE CONSTRUCTOR GLOBAL
|
|
1395
|
+
const originalConstruct = Reflect.construct;
|
|
1396
|
+
if (typeof Reflect !== 'undefined' && originalConstruct) {
|
|
1397
|
+
;
|
|
1398
|
+
Reflect.construct = function (target, argumentsList, newTarget) {
|
|
1399
|
+
const instance = originalConstruct.call(this, target, argumentsList, newTarget);
|
|
1400
|
+
if (typeof target === 'function') {
|
|
1401
|
+
const className = target.name;
|
|
1402
|
+
if (className &&
|
|
1403
|
+
(className.includes('Statement') ||
|
|
1404
|
+
className.includes('statement'))) {
|
|
1405
|
+
console.log(`🎯 Interceptando via Reflect.construct: ${className}`);
|
|
1406
|
+
return StatementTracker.interceptStatement(instance.constructor);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
return instance;
|
|
1410
|
+
};
|
|
1411
|
+
}
|
|
1412
|
+
// 🚀 NOVA ABORDAGEM: Interceptar eval para capturar imports dinâmicos
|
|
1413
|
+
const originalEval = globalThis.eval;
|
|
1414
|
+
globalThis.eval = function (code) {
|
|
1415
|
+
const result = originalEval.call(this, code);
|
|
1416
|
+
// Se o código contém Statement classes
|
|
1417
|
+
if (code.includes('Statement') && typeof result === 'function') {
|
|
1418
|
+
const className = result.name;
|
|
1419
|
+
if (className && className.includes('Statement')) {
|
|
1420
|
+
console.log(`🔍 Classe Statement detectada via eval: ${className}`);
|
|
1421
|
+
TestAnnotations.interceptClassDirectly(result, className);
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return result;
|
|
1425
|
+
};
|
|
1426
|
+
// 🌐 INTERCEPTAÇÃO UNIVERSAL DE NEW - SIMPLIFICADA
|
|
1427
|
+
try {
|
|
1428
|
+
// Usar abordagem mais simples via monkey patching
|
|
1429
|
+
}
|
|
1430
|
+
catch (constructorError) {
|
|
1431
|
+
console.warn(`⚠️ Erro na interceptação de constructor: ${constructorError}`);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
catch (error) {
|
|
1435
|
+
console.warn(`⚠️ Erro na interceptação universal: ${error}`);
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
/**
|
|
1439
|
+
* 🎯 Intercepta uma classe diretamente no momento da descoberta
|
|
1440
|
+
*/
|
|
1441
|
+
static interceptClassDirectly(ClassToIntercept, className) {
|
|
1442
|
+
try {
|
|
1443
|
+
if (typeof ClassToIntercept !== 'function') {
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
console.log(`🔧 Interceptando classe diretamente: ${className}`);
|
|
1447
|
+
// Substituir todos os métodos da classe
|
|
1448
|
+
const prototype = ClassToIntercept.prototype;
|
|
1449
|
+
const methodNames = Object.getOwnPropertyNames(prototype);
|
|
1450
|
+
for (const methodName of methodNames) {
|
|
1451
|
+
if (methodName !== 'constructor' &&
|
|
1452
|
+
typeof prototype[methodName] === 'function') {
|
|
1453
|
+
const originalMethod = prototype[methodName];
|
|
1454
|
+
prototype[methodName] = function (...args) {
|
|
1455
|
+
const statementName = `${className}.${methodName}`;
|
|
1456
|
+
console.log(`🎯 Executando Statement: ${statementName}`);
|
|
1457
|
+
// ✅ CORREÇÃO: Garantir que existe um CT ativo antes de executar statement
|
|
1458
|
+
StatementTracker.ensureCTCreated();
|
|
1459
|
+
// ✅ CORREÇÃO: Registrar statement dentro do CT, não criar novo CT
|
|
1460
|
+
StatementTracker.startStatement(className, methodName);
|
|
1461
|
+
try {
|
|
1462
|
+
const result = originalMethod.apply(this, args);
|
|
1463
|
+
// Se é Promise
|
|
1464
|
+
if (result && typeof result.then === 'function') {
|
|
1465
|
+
return result
|
|
1466
|
+
.then((res) => {
|
|
1467
|
+
StatementTracker.finishStatement(Date.now(), true);
|
|
1468
|
+
return res;
|
|
1469
|
+
})
|
|
1470
|
+
.catch((err) => {
|
|
1471
|
+
StatementTracker.finishStatement(Date.now(), false, err?.message || 'Erro desconhecido');
|
|
1472
|
+
throw err;
|
|
1473
|
+
});
|
|
1474
|
+
}
|
|
1475
|
+
// Método síncrono
|
|
1476
|
+
StatementTracker.finishStatement(Date.now(), true);
|
|
1477
|
+
return result;
|
|
1478
|
+
}
|
|
1479
|
+
catch (error) {
|
|
1480
|
+
StatementTracker.finishStatement(Date.now(), false, error?.message || 'Erro desconhecido');
|
|
1481
|
+
throw error;
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
console.log(`✅ Classe ${className} interceptada diretamente com sucesso`);
|
|
1487
|
+
}
|
|
1488
|
+
catch (error) {
|
|
1489
|
+
console.warn(`⚠️ Erro ao interceptar classe ${className}: ${error}`);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* 🏗️ Criar instância interceptada
|
|
1494
|
+
*/
|
|
1495
|
+
static createInterceptedInstance(instance, className) {
|
|
1496
|
+
try {
|
|
1497
|
+
// Interceptar métodos da instância
|
|
1498
|
+
const methodNames = Object.getOwnPropertyNames(Object.getPrototypeOf(instance));
|
|
1499
|
+
for (const methodName of methodNames) {
|
|
1500
|
+
if (methodName !== 'constructor' &&
|
|
1501
|
+
typeof instance[methodName] === 'function') {
|
|
1502
|
+
const originalMethod = instance[methodName];
|
|
1503
|
+
instance[methodName] = function (...args) {
|
|
1504
|
+
const statementName = `${className}.${methodName}`;
|
|
1505
|
+
console.log(`🎯 Executando Statement: ${statementName}`);
|
|
1506
|
+
// ✅ CORREÇÃO: Garantir que existe um CT ativo antes de executar statement
|
|
1507
|
+
StatementTracker.ensureCTCreated();
|
|
1508
|
+
// ✅ CORREÇÃO: Registrar statement dentro do CT, não criar novo CT
|
|
1509
|
+
StatementTracker.startStatement(className, methodName);
|
|
1510
|
+
try {
|
|
1511
|
+
const result = originalMethod.apply(this, args);
|
|
1512
|
+
// Se é Promise
|
|
1513
|
+
if (result && typeof result.then === 'function') {
|
|
1514
|
+
return result
|
|
1515
|
+
.then((res) => {
|
|
1516
|
+
StatementTracker.finishStatement(Date.now(), true);
|
|
1517
|
+
return res;
|
|
1518
|
+
})
|
|
1519
|
+
.catch((err) => {
|
|
1520
|
+
StatementTracker.finishStatement(Date.now(), false, err?.message || 'Erro desconhecido');
|
|
1521
|
+
throw err;
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
// Método síncrono
|
|
1525
|
+
StatementTracker.finishStatement(Date.now(), true);
|
|
1526
|
+
return result;
|
|
1527
|
+
}
|
|
1528
|
+
catch (error) {
|
|
1529
|
+
StatementTracker.finishStatement(Date.now(), false, error?.message || 'Erro desconhecido');
|
|
1530
|
+
throw error;
|
|
1531
|
+
}
|
|
1532
|
+
};
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
return instance;
|
|
1536
|
+
}
|
|
1537
|
+
catch (error) {
|
|
1538
|
+
console.warn(`⚠️ Erro ao criar instância interceptada: ${error}`);
|
|
1539
|
+
return instance;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* 🌐 Configuração universal de interceptação de constructor
|
|
1544
|
+
*/
|
|
1545
|
+
static setupUniversalConstructorInterception() {
|
|
1546
|
+
try {
|
|
1547
|
+
// 🎯 INTERCEPTAÇÃO GLOBAL DO NEW
|
|
1548
|
+
const originalReflectConstruct = Reflect.construct;
|
|
1549
|
+
if (typeof Reflect !== 'undefined' && originalReflectConstruct) {
|
|
1550
|
+
Reflect.construct = function (target, argumentsList, newTarget) {
|
|
1551
|
+
const instance = originalReflectConstruct.call(this, target, argumentsList, newTarget);
|
|
1552
|
+
// Se é uma classe Statement
|
|
1553
|
+
if (typeof target === 'function' &&
|
|
1554
|
+
target.name &&
|
|
1555
|
+
(target.name.includes('Statement') ||
|
|
1556
|
+
target.name.includes('statement'))) {
|
|
1557
|
+
console.log(`🚀 NEW ${target.name}() interceptado universalmente`);
|
|
1558
|
+
return TestAnnotations.createInterceptedInstance(instance, target.name);
|
|
1559
|
+
}
|
|
1560
|
+
return instance;
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
console.log('🌐 Interceptação universal de constructor configurada');
|
|
1564
|
+
}
|
|
1565
|
+
catch (error) {
|
|
1566
|
+
console.warn(`⚠️ Erro na interceptação universal de constructor: ${error}`);
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
/**
|
|
1570
|
+
* 🚀 Monitoramento contínuo de instanciações de Statement durante o teste
|
|
1571
|
+
*/
|
|
1572
|
+
static setupContinuousStatementMonitoring(testName) {
|
|
1573
|
+
try {
|
|
1574
|
+
// Configurar um observador de mudanças no contexto global
|
|
1575
|
+
const monitoringInterval = setInterval(() => {
|
|
1576
|
+
try {
|
|
1577
|
+
// Verificar se há novas classes Statement no contexto global
|
|
1578
|
+
const contexts = [
|
|
1579
|
+
globalThis,
|
|
1580
|
+
globalThis.global,
|
|
1581
|
+
globalThis.window,
|
|
1582
|
+
].filter(Boolean);
|
|
1583
|
+
for (const context of contexts) {
|
|
1584
|
+
Object.getOwnPropertyNames(context).forEach((propName) => {
|
|
1585
|
+
try {
|
|
1586
|
+
const prop = context[propName];
|
|
1587
|
+
if (typeof prop === 'function' &&
|
|
1588
|
+
(propName.includes('Statement') ||
|
|
1589
|
+
propName.includes('statement')) &&
|
|
1590
|
+
!prop._alreadyIntercepted) {
|
|
1591
|
+
// Marcar como interceptada para evitar loop
|
|
1592
|
+
;
|
|
1593
|
+
prop._alreadyIntercepted = true;
|
|
1594
|
+
// Interceptar usando Proxy no contexto
|
|
1595
|
+
context[propName] = new Proxy(prop, {
|
|
1596
|
+
construct(target, args, newTarget) {
|
|
1597
|
+
console.log(`🚀 NEW ${propName}() interceptado durante execução`);
|
|
1598
|
+
// Criar instância original
|
|
1599
|
+
const instance = Reflect.construct(target, args, newTarget);
|
|
1600
|
+
// Retornar versão interceptada
|
|
1601
|
+
return StatementTracker.interceptStatement(instance.constructor);
|
|
1602
|
+
},
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
catch {
|
|
1607
|
+
// Ignorar propriedades problemáticas
|
|
1608
|
+
}
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
catch (error) {
|
|
1613
|
+
console.warn(`⚠️ Erro no monitoramento contínuo: ${error}`);
|
|
1614
|
+
}
|
|
1615
|
+
}, 500); // Verificar a cada 500ms
|
|
1616
|
+
// Limpar o monitoramento após 30 segundos
|
|
1617
|
+
setTimeout(() => {
|
|
1618
|
+
clearInterval(monitoringInterval);
|
|
1619
|
+
}, 30_000);
|
|
1620
|
+
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
console.error(`❌ Erro ao configurar monitoramento contínuo: ${error}`);
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* 🆕 Configuração dinâmica para interceptar instanciações em tempo real
|
|
1627
|
+
*/
|
|
1628
|
+
static setupDynamicInterception(statementClasses) {
|
|
1629
|
+
try {
|
|
1630
|
+
// 🎯 NOVA ABORDAGEM: Interceptar diretamente no contexto global usando substituição de classe
|
|
1631
|
+
for (const className of statementClasses) {
|
|
1632
|
+
try {
|
|
1633
|
+
const contexts = [
|
|
1634
|
+
globalThis,
|
|
1635
|
+
globalThis.global,
|
|
1636
|
+
globalThis.window,
|
|
1637
|
+
].filter(Boolean);
|
|
1638
|
+
for (const context of contexts) {
|
|
1639
|
+
if (context[className] &&
|
|
1640
|
+
typeof context[className] === 'function') {
|
|
1641
|
+
const OriginalClass = context[className];
|
|
1642
|
+
console.log(`🔧 Configurando interceptação real para: ${className}`);
|
|
1643
|
+
// Criar uma nova classe que substitui a original completamente
|
|
1644
|
+
const InterceptedClass = (...args) => {
|
|
1645
|
+
console.log(`🚀 NEW ${className}() interceptado em tempo real!`);
|
|
1646
|
+
// Criar instância original
|
|
1647
|
+
const instance = new OriginalClass(...args);
|
|
1648
|
+
// Interceptar TODOS os métodos da instância
|
|
1649
|
+
const methodNames = Object.getOwnPropertyNames(Object.getPrototypeOf(instance));
|
|
1650
|
+
for (const methodName of methodNames) {
|
|
1651
|
+
if (methodName !== 'constructor' &&
|
|
1652
|
+
typeof instance[methodName] === 'function') {
|
|
1653
|
+
const originalMethod = instance[methodName];
|
|
1654
|
+
instance[methodName] = function (...methodArgs) {
|
|
1655
|
+
// ❌ REMOVIDO: startCT/endCT - Statements não são CTs!
|
|
1656
|
+
// Um Statement é um método DENTRO de um CT (test())
|
|
1657
|
+
// O StatementTracker.interceptStatement() já gerencia isso corretamente
|
|
1658
|
+
// ❌ REMOVIDO: Log duplicado - o StatementTracker já faz o log
|
|
1659
|
+
// Apenas executar o método original sem criar CT falso
|
|
1660
|
+
return originalMethod.apply(this, methodArgs);
|
|
1661
|
+
};
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return instance;
|
|
1665
|
+
};
|
|
1666
|
+
// Preservar prototype e propriedades estáticas
|
|
1667
|
+
InterceptedClass.prototype = OriginalClass.prototype;
|
|
1668
|
+
Object.setPrototypeOf(InterceptedClass, OriginalClass);
|
|
1669
|
+
// Substituir no contexto
|
|
1670
|
+
context[className] = InterceptedClass;
|
|
1671
|
+
console.log(`✅ Interceptação direta configurada para: ${className}`);
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
catch (error) {
|
|
1676
|
+
console.warn(`⚠️ Erro ao configurar interceptação direta para ${className}: ${error}`);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
catch (error) {
|
|
1681
|
+
console.warn(`⚠️ Erro na interceptação dinâmica: ${error}`);
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
/**
|
|
1685
|
+
* Configura interceptador seguro para registrar logs no UnifiedReportManager.
|
|
1686
|
+
* @param testName Nome do teste.
|
|
1687
|
+
* @param className Nome da classe/contexto.
|
|
1688
|
+
* @param originalMethod Método original de log.
|
|
1689
|
+
* @param targetClass Classe alvo.
|
|
1690
|
+
* @param methodName Nome do método.
|
|
1691
|
+
*/
|
|
1692
|
+
static setupSafeInterceptor(testName, className, originalMethod, targetClass, methodName) {
|
|
1693
|
+
const interceptorKey = `${className}-${testName}`;
|
|
1694
|
+
if (interceptorsAtivos.has(interceptorKey)) {
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
interceptorsAtivos.add(interceptorKey);
|
|
1698
|
+
targetClass[methodName] = (message) => {
|
|
1699
|
+
try {
|
|
1700
|
+
originalMethod.call(targetClass, message);
|
|
1701
|
+
}
|
|
1702
|
+
catch {
|
|
1703
|
+
// Ignorar erros do método original
|
|
1704
|
+
}
|
|
1705
|
+
try {
|
|
1706
|
+
const prefixedMessage = className !== 'Mixed' ? `[${className}] ${message}` : message;
|
|
1707
|
+
UnifiedReportManager.adicionarLog(testName, prefixedMessage);
|
|
1708
|
+
}
|
|
1709
|
+
catch {
|
|
1710
|
+
// Ignorar erros do UnifiedReportManager
|
|
1711
|
+
}
|
|
1712
|
+
};
|
|
1713
|
+
setTimeout(() => {
|
|
1714
|
+
interceptorsAtivos.delete(interceptorKey);
|
|
1715
|
+
}, 30_000);
|
|
1716
|
+
}
|
|
1717
|
+
// ============================================
|
|
1718
|
+
// 🌐 INTEGRAÇÃO TESTHUB - SELEÇÃO DE SISTEMA
|
|
1719
|
+
// ============================================
|
|
1720
|
+
/**
|
|
1721
|
+
* 🌐 Seleciona o sistema do TestHub para o teste.
|
|
1722
|
+
* O sistema deve ser selecionado ANTES de usar qualquer ação (API, Frontend, SSH, Banco).
|
|
1723
|
+
* As configurações (URLs, credenciais, hosts) serão injetadas automaticamente.
|
|
1724
|
+
*
|
|
1725
|
+
* @param systemName Nome do sistema (use o enum Systems para IntelliSense)
|
|
1726
|
+
* @param environmentName Nome do ambiente (opcional, usa process.env.ENV se não fornecido)
|
|
1727
|
+
*
|
|
1728
|
+
* @example
|
|
1729
|
+
* // IMPORTANTE: Na primeira execução, use string diretamente:
|
|
1730
|
+
* await TestAnnotations.setSystem("AUTENTICA_IN_HML")
|
|
1731
|
+
*
|
|
1732
|
+
* // Após primeira execução, o enum será gerado e você pode usar:
|
|
1733
|
+
* import { Systems } from "../.rbqa/SystemsEnum"
|
|
1734
|
+
* await TestAnnotations.setSystem(Systems.AUTENTICA_IN_HML) // 👈 IntelliSense disponível
|
|
1735
|
+
*
|
|
1736
|
+
* // Exemplo completo:
|
|
1737
|
+
* test("CT001", async ({}, testInfo) => {
|
|
1738
|
+
* TestAnnotations.Api.testInfo = testInfo
|
|
1739
|
+
* await TestAnnotations.setSystem(Systems.AUTENTICA_IN_HML)
|
|
1740
|
+
*
|
|
1741
|
+
* // Agora as URLs e credenciais estão configuradas automaticamente
|
|
1742
|
+
* await new StatementsToken().getToken()
|
|
1743
|
+
* })
|
|
1744
|
+
*/
|
|
1745
|
+
static async setSystem(systemName, environmentName) {
|
|
1746
|
+
// Aguarda criação do browser se foi solicitada (Frontend/Mixed)
|
|
1747
|
+
await TestContext.ensurePage();
|
|
1748
|
+
try {
|
|
1749
|
+
// ✅ VALIDAÇÃO: Verificar se systemName foi fornecido
|
|
1750
|
+
if (!systemName ||
|
|
1751
|
+
systemName === 'undefined' ||
|
|
1752
|
+
typeof systemName !== 'string') {
|
|
1753
|
+
throw new Error(`
|
|
1754
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1755
|
+
❌ NOME DO SISTEMA INVÁLIDO
|
|
1756
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1757
|
+
|
|
1758
|
+
O parâmetro "systemName" é obrigatório e deve ser uma string válida.
|
|
1759
|
+
|
|
1760
|
+
Valor recebido: ${JSON.stringify(systemName)} (tipo: ${typeof systemName})
|
|
1761
|
+
|
|
1762
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1763
|
+
💡 POSSÍVEIS CAUSAS:
|
|
1764
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1765
|
+
|
|
1766
|
+
1. ❌ Tentou importar Systems do framework:
|
|
1767
|
+
import { Systems } from "@silasfmartins/testhub" // ← ERRADO!
|
|
1768
|
+
|
|
1769
|
+
2. ❌ Tentou usar Systems antes de gerar o enum:
|
|
1770
|
+
import { Systems } from "../.rbqa/SystemsEnum" // ← Arquivo não existe!
|
|
1771
|
+
await TestAnnotations.setSystem(Systems.AUTENTICA_IN_HML)
|
|
1772
|
+
|
|
1773
|
+
3. ❌ Esqueceu de passar o parâmetro:
|
|
1774
|
+
await TestAnnotations.setSystem() // ← systemName obrigatório!
|
|
1775
|
+
|
|
1776
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1777
|
+
✅ SOLUÇÃO - PRIMEIRA EXECUÇÃO:
|
|
1778
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1779
|
+
|
|
1780
|
+
Use STRING diretamente (sem import do enum):
|
|
1781
|
+
|
|
1782
|
+
import { TestAnnotations } from "@silasfmartins/testhub"
|
|
1783
|
+
|
|
1784
|
+
test("Meu teste", async ({}, testInfo) => {
|
|
1785
|
+
TestAnnotations.Api.testInfo = testInfo
|
|
1786
|
+
await TestAnnotations.setSystem("AUTENTICA_IN_HML") // ← String direta
|
|
1787
|
+
})
|
|
1788
|
+
|
|
1789
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1790
|
+
✅ SOLUÇÃO - PRÓXIMAS EXECUÇÕES (com IntelliSense):
|
|
1791
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1792
|
+
|
|
1793
|
+
Após a primeira execução, o arquivo .rbqa/SystemsEnum.ts será gerado.
|
|
1794
|
+
Então você pode importar o enum DO SEU PROJETO (NÃO do framework):
|
|
1795
|
+
|
|
1796
|
+
import { TestAnnotations } from "@silasfmartins/testhub"
|
|
1797
|
+
import { Systems } from "../.rbqa/SystemsEnum" // ← Do projeto!
|
|
1798
|
+
|
|
1799
|
+
test("Meu teste", async ({}, testInfo) => {
|
|
1800
|
+
TestAnnotations.Api.testInfo = testInfo
|
|
1801
|
+
await TestAnnotations.setSystem(Systems.AUTENTICA_IN_HML) // ← Com IntelliSense!
|
|
1802
|
+
})
|
|
1803
|
+
|
|
1804
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
1805
|
+
`);
|
|
1806
|
+
}
|
|
1807
|
+
// 2. Inicializar SystemsManager (carrega lista de sistemas do backend)
|
|
1808
|
+
const systemsManager = SystemsManager.getInstance();
|
|
1809
|
+
await systemsManager.initialize();
|
|
1810
|
+
// 3. Obter tipo de projeto do ambiente
|
|
1811
|
+
// ⚠️ IMPORTANTE: Para projetos Scenarios, não filtrar por tipo de ambiente
|
|
1812
|
+
// Scenarios podem usar sistemas de qualquer tipo (API, Mobile, etc.)
|
|
1813
|
+
const detectedProjectType = process.env.PROJECT_TYPE || 'API';
|
|
1814
|
+
const isScenarios = detectedProjectType === 'Scenarios';
|
|
1815
|
+
// Se for Scenarios, não passar projectType para permitir qualquer tipo de ambiente
|
|
1816
|
+
const projectType = isScenarios
|
|
1817
|
+
? undefined
|
|
1818
|
+
: detectedProjectType;
|
|
1819
|
+
console.log(`🔍 Tipo de projeto detectado: ${detectedProjectType}${isScenarios ? ' (permitindo qualquer tipo de ambiente)' : ''}`);
|
|
1820
|
+
// 4. Selecionar sistema (valida e carrega configurações)
|
|
1821
|
+
const selectedSystem = await systemsManager.selectSystem(systemName, environmentName, projectType);
|
|
1822
|
+
// 5. Injetar configurações automaticamente
|
|
1823
|
+
const configManager = DynamicConfigManager.getInstance();
|
|
1824
|
+
// Injetar em process.env para compatibilidade com código legado
|
|
1825
|
+
configManager.injectIntoEnvironment();
|
|
1826
|
+
// Registrar informações do CI (Azure/GitHub) — útil para identificar quem iniciou a execução
|
|
1827
|
+
try {
|
|
1828
|
+
UnifiedReportManager.registrarCIInfo();
|
|
1829
|
+
}
|
|
1830
|
+
catch (error) {
|
|
1831
|
+
console.warn('⚠️ Falha ao registrar CI info:', error);
|
|
1832
|
+
}
|
|
1833
|
+
// Injetar baseURL no Playwright se for teste WEB
|
|
1834
|
+
if (detectedProjectType === 'WEB' || detectedProjectType === 'FRONTEND') {
|
|
1835
|
+
configManager.injectPlaywrightBaseUrl();
|
|
1836
|
+
}
|
|
1837
|
+
// 🆕 Auto-conectar SSH se for projeto SSH
|
|
1838
|
+
// IMPORTANTE: Usa detectedProjectType (sempre definido), não projectType (pode ser undefined)
|
|
1839
|
+
// Auto-conectar quando:
|
|
1840
|
+
// - o projeto explicitamente é do tipo SSH, OU
|
|
1841
|
+
// - o sistema selecionado contém informações típicas de SSH (host, username, password, privateKeyPath)
|
|
1842
|
+
// Isso evita que projetos Frontend/Mixed que executam cenários SSH precisem definir PROJECT_TYPE=SSH.
|
|
1843
|
+
try {
|
|
1844
|
+
// Auto-conexão SSH foi desativada: os usuários devem chamar
|
|
1845
|
+
// manualmente `SSHActions.connectWithKey(...)` ou usar `SSHActions` diretamente.
|
|
1846
|
+
// Isto evita conexões automáticas indesejadas.
|
|
1847
|
+
// Exemplo recomendado no teste:
|
|
1848
|
+
// await SSHActions.connectWithKey('secrets/UserSistAutBRM')
|
|
1849
|
+
// Ou configure o SSH_KEY_PATH e chame manualmente:
|
|
1850
|
+
// await SSHActions.connectWithKey(process.env.SSH_KEY_PATH)
|
|
1851
|
+
}
|
|
1852
|
+
catch (sshError) {
|
|
1853
|
+
console.warn(`⚠️ [SSH] Não foi possível auto-conectar: ${sshError}`);
|
|
1854
|
+
console.warn('💡 Use SSHActions.connectWithKey() manualmente (ex: await SSHActions.connectWithKey("secrets/UserSistAutBRM"))');
|
|
1855
|
+
}
|
|
1856
|
+
// 6. Registrar no relatório
|
|
1857
|
+
try {
|
|
1858
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
1859
|
+
if (testInfo?.title) {
|
|
1860
|
+
const cnName = testInfo.titlePath?.[1] || testInfo.title;
|
|
1861
|
+
UnifiedReportManager.adicionarLog(cnName, `🌐 Sistema configurado: ${selectedSystem.name} (${selectedSystem.environmentName})`);
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
catch {
|
|
1865
|
+
// Ignorar se TestContext não estiver disponível ainda
|
|
1866
|
+
}
|
|
1867
|
+
// 7. 🆕 Registrar sistema no StatementTracker para captura nos CTs
|
|
1868
|
+
try {
|
|
1869
|
+
StatementTracker.setCurrentSystem(selectedSystem.name);
|
|
1870
|
+
}
|
|
1871
|
+
catch (error) {
|
|
1872
|
+
console.warn('⚠️ Não foi possível registrar sistema no StatementTracker:', error);
|
|
1873
|
+
}
|
|
1874
|
+
// 8. 🔧 CRÍTICO: Limpar cache de contextos para garantir novo baseURL
|
|
1875
|
+
try {
|
|
1876
|
+
await ApiActions.clearContextCache();
|
|
1877
|
+
}
|
|
1878
|
+
catch (error) {
|
|
1879
|
+
console.warn('⚠️ Não foi possível limpar cache de contextos:', error);
|
|
1880
|
+
}
|
|
1881
|
+
console.log(`✅ Sistema ${systemName} configurado com sucesso!`);
|
|
1882
|
+
}
|
|
1883
|
+
catch (error) {
|
|
1884
|
+
console.error(`❌ Erro ao configurar sistema: ${error}`);
|
|
1885
|
+
throw new Error(`Falha ao configurar sistema "${systemName}": ${error instanceof Error ? error.message : String(error)}`);
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
/**
|
|
1889
|
+
* 🌐 Obtém a URL do sistema atualmente configurado.
|
|
1890
|
+
* Deve ser chamado APÓS setSystem().
|
|
1891
|
+
*
|
|
1892
|
+
* @returns URL do sistema ou undefined se nenhum sistema foi configurado
|
|
1893
|
+
*
|
|
1894
|
+
* @example
|
|
1895
|
+
* await TestAnnotations.setSystem("AUTENTICA_IN_HML")
|
|
1896
|
+
* const url = TestAnnotations.getSystemUrl()
|
|
1897
|
+
* console.log(url) // "https://autenticainthml.redecorp.br"
|
|
1898
|
+
*/
|
|
1899
|
+
static getSystemUrl() {
|
|
1900
|
+
try {
|
|
1901
|
+
const systemsManager = SystemsManager.getInstance();
|
|
1902
|
+
const config = systemsManager.getSystemConfig();
|
|
1903
|
+
return config?.baseUrl;
|
|
1904
|
+
}
|
|
1905
|
+
catch (error) {
|
|
1906
|
+
console.warn(`⚠️ Erro ao obter URL do sistema: ${error}`);
|
|
1907
|
+
return undefined;
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
/**
|
|
1911
|
+
* 🌐 Obtém as configurações completas do sistema atualmente configurado.
|
|
1912
|
+
* Deve ser chamado APÓS setSystem().
|
|
1913
|
+
*
|
|
1914
|
+
* @returns Configurações do sistema ou null se nenhum sistema foi configurado
|
|
1915
|
+
*
|
|
1916
|
+
* @example
|
|
1917
|
+
* await TestAnnotations.setSystem("AUTENTICA_IN_HML")
|
|
1918
|
+
* const config = TestAnnotations.getSystemConfig()
|
|
1919
|
+
* console.log(config?.baseUrl) // "https://autenticainthml.redecorp.br"
|
|
1920
|
+
* console.log(config?.username) // "usuario"
|
|
1921
|
+
*/
|
|
1922
|
+
static getSystemConfig() {
|
|
1923
|
+
try {
|
|
1924
|
+
const systemsManager = SystemsManager.getInstance();
|
|
1925
|
+
return systemsManager.getSystemConfig();
|
|
1926
|
+
}
|
|
1927
|
+
catch (error) {
|
|
1928
|
+
console.warn(`⚠️ Erro ao obter configuração do sistema: ${error}`);
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
/**
|
|
1933
|
+
* Configura um teste como API, com interceptador e logs.
|
|
1934
|
+
*/
|
|
1935
|
+
static Api = {
|
|
1936
|
+
set testInfo(testInfo) {
|
|
1937
|
+
TestContext.setTestType('API');
|
|
1938
|
+
TestContext.setTestInfo(testInfo);
|
|
1939
|
+
// ✅ CRÍTICO: Detectar CN e CT baseado na estrutura do titlePath
|
|
1940
|
+
let cnName;
|
|
1941
|
+
let ctName;
|
|
1942
|
+
if (testInfo.titlePath && testInfo.titlePath.length >= 3) {
|
|
1943
|
+
cnName = testInfo.titlePath[1];
|
|
1944
|
+
ctName = testInfo.titlePath[2];
|
|
1945
|
+
}
|
|
1946
|
+
else if (testInfo.titlePath && testInfo.titlePath.length === 2) {
|
|
1947
|
+
cnName = testInfo.titlePath[0];
|
|
1948
|
+
ctName = testInfo.titlePath[1];
|
|
1949
|
+
}
|
|
1950
|
+
else {
|
|
1951
|
+
cnName = testInfo.title;
|
|
1952
|
+
ctName = testInfo.title;
|
|
1953
|
+
}
|
|
1954
|
+
StatementTracker.setTestName(cnName);
|
|
1955
|
+
// 🆕 CRÍTICO: Capturar e registrar CT automaticamente
|
|
1956
|
+
TestAnnotations.capturarCTDoPlaywright(cnName, ctName, testInfo);
|
|
1957
|
+
testStartTimes.set(cnName, Date.now());
|
|
1958
|
+
UnifiedReportManager.setTestContext(cnName, 'API');
|
|
1959
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
1960
|
+
ensureTerminalLogCaptureActive(cnName, ctName);
|
|
1961
|
+
// 🔍 NOVO: Ativar logs automáticos de Statements
|
|
1962
|
+
ativarStatementLogs();
|
|
1963
|
+
ativarInterceptacaoMagica();
|
|
1964
|
+
// 🆕 INTERCEPTAÇÃO AUTOMÁTICA DE STATEMENTS
|
|
1965
|
+
TestAnnotations.setupStatementInterception(cnName);
|
|
1966
|
+
// 🚀 MONITORAMENTO CONTÍNUO - interceptar novas instanciações
|
|
1967
|
+
TestAnnotations.setupContinuousStatementMonitoring(cnName);
|
|
1968
|
+
TestAnnotations.setupSafeInterceptor(cnName, 'API', ApiActions.logEvent.bind(ApiActions), ApiActions, 'logEvent');
|
|
1969
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
1970
|
+
},
|
|
1971
|
+
};
|
|
1972
|
+
/**
|
|
1973
|
+
* Configura um teste como Frontend, com logs e cobertura automática.
|
|
1974
|
+
*/
|
|
1975
|
+
static Frontend = {
|
|
1976
|
+
/**
|
|
1977
|
+
* Configura contexto Frontend com TestInfo e cobertura automática
|
|
1978
|
+
* @param testInfo TestInfo do Playwright
|
|
1979
|
+
* @param enableCoverage Se deve habilitar cobertura automática (padrão: true)
|
|
1980
|
+
*/
|
|
1981
|
+
configure(testInfo, enableCoverage = true) {
|
|
1982
|
+
TestContext.setTestType('Frontend');
|
|
1983
|
+
TestContext.setTestInfo(testInfo);
|
|
1984
|
+
// ✅ CRÍTICO: Detectar CN e CT baseado na estrutura do titlePath
|
|
1985
|
+
let cnName;
|
|
1986
|
+
let ctName;
|
|
1987
|
+
if (testInfo.titlePath && testInfo.titlePath.length >= 3) {
|
|
1988
|
+
cnName = testInfo.titlePath[1];
|
|
1989
|
+
ctName = testInfo.titlePath[2];
|
|
1990
|
+
}
|
|
1991
|
+
else if (testInfo.titlePath && testInfo.titlePath.length === 2) {
|
|
1992
|
+
cnName = testInfo.titlePath[0];
|
|
1993
|
+
ctName = testInfo.titlePath[1];
|
|
1994
|
+
}
|
|
1995
|
+
else {
|
|
1996
|
+
cnName = testInfo.title;
|
|
1997
|
+
ctName = testInfo.title;
|
|
1998
|
+
}
|
|
1999
|
+
StatementTracker.setTestName(cnName);
|
|
2000
|
+
console.log(`🏷️ [FRONTEND] StatementTracker inicializado para CN: "${cnName}"`);
|
|
2001
|
+
testStartTimes.set(cnName, Date.now());
|
|
2002
|
+
UnifiedReportManager.setTestContext(cnName, 'Frontend');
|
|
2003
|
+
// 🆕 CRÍTICO: Capturar e registrar CT automaticamente
|
|
2004
|
+
TestAnnotations.capturarCTDoPlaywright(cnName, ctName, testInfo);
|
|
2005
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
2006
|
+
ensureTerminalLogCaptureActive(cnName, ctName);
|
|
2007
|
+
UnifiedReportManager.adicionarLog(cnName, `🌐 Inicializando contexto Frontend (cobertura: ${enableCoverage ? 'ON' : 'OFF'})`);
|
|
2008
|
+
// ✅ NOVO: Configurar cobertura automática se habilitada
|
|
2009
|
+
if (enableCoverage) {
|
|
2010
|
+
TestAnnotations.setupFrontendCoverage(testInfo);
|
|
2011
|
+
}
|
|
2012
|
+
// 🔍 NOVO: Ativar logs automáticos de Statements
|
|
2013
|
+
ativarStatementLogs();
|
|
2014
|
+
ativarInterceptacaoMagica();
|
|
2015
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
2016
|
+
},
|
|
2017
|
+
/**
|
|
2018
|
+
* 🆕 Inicializa contexto Frontend com page e testInfo em uma única chamada
|
|
2019
|
+
* @param options Objeto com page e testInfo do Playwright
|
|
2020
|
+
* @example
|
|
2021
|
+
* test('CT001', async ({ page }, testInfo) => {
|
|
2022
|
+
* TestAnnotations.Frontend.init({ page, testInfo })
|
|
2023
|
+
* await TestAnnotations.setSystem('MEU_SISTEMA')
|
|
2024
|
+
* await new MinhaPage().minhaAcao()
|
|
2025
|
+
* })
|
|
2026
|
+
*/
|
|
2027
|
+
init(options) {
|
|
2028
|
+
// ✅ Setar page no TestContext PRIMEIRO
|
|
2029
|
+
TestContext.setPage(options.page);
|
|
2030
|
+
// Depois configurar o resto
|
|
2031
|
+
this.configure(options.testInfo, options.enableCoverage ?? true);
|
|
2032
|
+
},
|
|
2033
|
+
// Propriedade legacy para compatibilidade (requer TestContext.setPage manual)
|
|
2034
|
+
set testInfo(testInfo) {
|
|
2035
|
+
// Dispara criação lazy do browser (só quando fixture está ativo)
|
|
2036
|
+
TestContext.requestBrowserCreation();
|
|
2037
|
+
this.configure(testInfo, true);
|
|
2038
|
+
},
|
|
2039
|
+
};
|
|
2040
|
+
// Removed `Front` alias: use `Frontend` instead
|
|
2041
|
+
/**
|
|
2042
|
+
* Configura um teste como Mobile, com limpeza automática de sessão.
|
|
2043
|
+
*/
|
|
2044
|
+
static Mobile = {
|
|
2045
|
+
set testInfo(testInfo) {
|
|
2046
|
+
TestContext.setTestType('Mobile');
|
|
2047
|
+
TestContext.setTestInfo(testInfo);
|
|
2048
|
+
// ✅ CRÍTICO: Usar CN (test.describe = titlePath[1]) como identificador do teste
|
|
2049
|
+
// titlePath[0] = filename, titlePath[1] = CN (test.describe), titlePath[2] = CT (test)
|
|
2050
|
+
const cnName = testInfo.titlePath?.[1] || testInfo.title;
|
|
2051
|
+
StatementTracker.setTestName(cnName);
|
|
2052
|
+
console.log(`🏷️ [MOBILE] StatementTracker inicializado para CN: "${cnName}"`);
|
|
2053
|
+
testStartTimes.set(cnName, Date.now());
|
|
2054
|
+
UnifiedReportManager.setTestContext(cnName, 'Mobile');
|
|
2055
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
2056
|
+
ensureTerminalLogCaptureActive(cnName);
|
|
2057
|
+
UnifiedReportManager.adicionarLog(cnName, '📱 Inicializando contexto Mobile');
|
|
2058
|
+
// 🔍 NOVO: Ativar logs automáticos de Statements
|
|
2059
|
+
ativarStatementLogs();
|
|
2060
|
+
ativarInterceptacaoMagica();
|
|
2061
|
+
TestAnnotations.setupMobileAutoCleanup(testInfo);
|
|
2062
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
2063
|
+
},
|
|
2064
|
+
};
|
|
2065
|
+
/**
|
|
2066
|
+
* Configura limpeza automática para testes mobile.
|
|
2067
|
+
* @param testInfo TestInfo do Playwright.
|
|
2068
|
+
*/
|
|
2069
|
+
static setupMobileAutoCleanup(testInfo) {
|
|
2070
|
+
const originalAttach = testInfo.attach;
|
|
2071
|
+
let sessionClosed = false;
|
|
2072
|
+
testInfo.attach = async function (name, options) {
|
|
2073
|
+
if (!sessionClosed &&
|
|
2074
|
+
(name.includes('stdout') ||
|
|
2075
|
+
name.includes('stderr') ||
|
|
2076
|
+
name.includes('trace'))) {
|
|
2077
|
+
setTimeout(async () => {
|
|
2078
|
+
if (!sessionClosed) {
|
|
2079
|
+
sessionClosed = true;
|
|
2080
|
+
try {
|
|
2081
|
+
await MobileConnection.closeSession();
|
|
2082
|
+
UnifiedReportManager.adicionarLog(testInfo.title, '✅ Sessão mobile encerrada automaticamente');
|
|
2083
|
+
}
|
|
2084
|
+
catch (error) {
|
|
2085
|
+
UnifiedReportManager.adicionarLog(testInfo.title, `⚠️ Erro ao encerrar sessão mobile: ${String(error)}`);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
}, 100);
|
|
2089
|
+
}
|
|
2090
|
+
return originalAttach.call(this, name, options);
|
|
2091
|
+
};
|
|
2092
|
+
setTimeout(async () => {
|
|
2093
|
+
if (!sessionClosed) {
|
|
2094
|
+
sessionClosed = true;
|
|
2095
|
+
try {
|
|
2096
|
+
const hasActiveSession = MobileConnection.isConnected();
|
|
2097
|
+
if (hasActiveSession) {
|
|
2098
|
+
await MobileConnection.closeSession();
|
|
2099
|
+
UnifiedReportManager.adicionarLog(testInfo.title, '✅ Sessão mobile encerrada automaticamente (timeout)');
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
catch {
|
|
2103
|
+
// Silenciosamente limpar em caso de erro
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
}, 35_000);
|
|
2107
|
+
}
|
|
2108
|
+
/**
|
|
2109
|
+
* Método utilitário para configurar mobile capabilities.
|
|
2110
|
+
* @param capabilities Objeto de capabilities do dispositivo.
|
|
2111
|
+
*/
|
|
2112
|
+
static async setupMobileCapabilities(capabilities) {
|
|
2113
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
2114
|
+
if (testInfo?.title) {
|
|
2115
|
+
UnifiedReportManager.adicionarLog(testInfo.title, `🔧 Configurando capabilities para ${capabilities.platformName}`);
|
|
2116
|
+
try {
|
|
2117
|
+
const fullCapabilities = {
|
|
2118
|
+
...capabilities,
|
|
2119
|
+
deviceName: capabilities.deviceId, // deviceName é obrigatório
|
|
2120
|
+
};
|
|
2121
|
+
await MobileConnection.createSession(fullCapabilities);
|
|
2122
|
+
UnifiedReportManager.adicionarLog(testInfo.title, `✅ Sessão mobile criada - Device: ${capabilities.deviceId}`);
|
|
2123
|
+
}
|
|
2124
|
+
catch (error) {
|
|
2125
|
+
UnifiedReportManager.adicionarLog(testInfo.title, `❌ Erro ao criar sessão mobile: ${String(error)}`);
|
|
2126
|
+
throw error;
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
/**
|
|
2131
|
+
* Configura um teste como SSH, com interceptador e logs.
|
|
2132
|
+
*/
|
|
2133
|
+
static SSH = {
|
|
2134
|
+
set testInfo(testInfo) {
|
|
2135
|
+
TestContext.setTestType('SSH');
|
|
2136
|
+
TestContext.setTestInfo(testInfo);
|
|
2137
|
+
// ✅ CRÍTICO: Usar CN (test.describe = titlePath[1]) como identificador do teste
|
|
2138
|
+
// titlePath[0] = filename, titlePath[1] = CN (test.describe), titlePath[2] = CT (test)
|
|
2139
|
+
const cnName = testInfo.titlePath?.[1] || testInfo.title;
|
|
2140
|
+
StatementTracker.setTestName(cnName);
|
|
2141
|
+
console.log(`🏷️ [SSH] StatementTracker inicializado para CN: "${cnName}"`);
|
|
2142
|
+
testStartTimes.set(cnName, Date.now());
|
|
2143
|
+
UnifiedReportManager.setTestContext(cnName, 'SSH');
|
|
2144
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
2145
|
+
ensureTerminalLogCaptureActive(cnName);
|
|
2146
|
+
TestAnnotations.setupSafeInterceptor(cnName, 'SSH', SSHActions.logEvent.bind(SSHActions), SSHActions, 'logEvent');
|
|
2147
|
+
UnifiedReportManager.adicionarLog(cnName, '🔧 Inicializando contexto SSH');
|
|
2148
|
+
// 🔍 CRITICAL: Ativar interceptação ANTES de qualquer coisa
|
|
2149
|
+
ativarStatementLogs();
|
|
2150
|
+
ativarInterceptacaoMagica();
|
|
2151
|
+
// ✅ SSH: Interceptar classes Statement globais E importadas
|
|
2152
|
+
TestAnnotations.interceptGlobalStatements();
|
|
2153
|
+
TestAnnotations.interceptImportedStatements();
|
|
2154
|
+
console.log('🌟 Interceptação mágica ativada - Statements serão logados automaticamente');
|
|
2155
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
2156
|
+
},
|
|
2157
|
+
};
|
|
2158
|
+
/**
|
|
2159
|
+
* Configura um teste como Banco, com interceptador e logs.
|
|
2160
|
+
*/
|
|
2161
|
+
static Banco = {
|
|
2162
|
+
set testInfo(testInfo) {
|
|
2163
|
+
TestContext.setTestType('Banco');
|
|
2164
|
+
TestContext.setTestInfo(testInfo);
|
|
2165
|
+
// ✅ CRÍTICO: Usar CN (test.describe = titlePath[1]) como identificador do teste
|
|
2166
|
+
// titlePath[0] = filename, titlePath[1] = CN (test.describe), titlePath[2] = CT (test)
|
|
2167
|
+
const cnName = testInfo.titlePath?.[1] || testInfo.title;
|
|
2168
|
+
StatementTracker.setTestName(cnName);
|
|
2169
|
+
console.log(`🏷️ [BANCO] StatementTracker inicializado para CN: "${cnName}"`);
|
|
2170
|
+
testStartTimes.set(cnName, Date.now());
|
|
2171
|
+
UnifiedReportManager.setTestContext(cnName, 'Banco');
|
|
2172
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
2173
|
+
ensureTerminalLogCaptureActive(cnName);
|
|
2174
|
+
TestAnnotations.setupSafeInterceptor(cnName, 'BANCO', BancoActions.logEvent.bind(BancoActions), BancoActions, 'logEvent');
|
|
2175
|
+
UnifiedReportManager.adicionarLog(cnName, '🗄️ Inicializando contexto Banco de Dados');
|
|
2176
|
+
// 🔍 NOVO: Ativar logs automáticos de Statements
|
|
2177
|
+
ativarStatementLogs();
|
|
2178
|
+
ativarInterceptacaoMagica();
|
|
2179
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
2180
|
+
},
|
|
2181
|
+
};
|
|
2182
|
+
/**
|
|
2183
|
+
* Configura um teste como Mixed, com interceptadores e limpeza automática.
|
|
2184
|
+
*/
|
|
2185
|
+
static Mixed = {
|
|
2186
|
+
set testInfo(testInfo) {
|
|
2187
|
+
TestContext.setTestType('Mixed');
|
|
2188
|
+
// Dispara criação lazy do browser (só quando fixture está ativo)
|
|
2189
|
+
TestContext.requestBrowserCreation();
|
|
2190
|
+
TestContext.setTestInfo(testInfo);
|
|
2191
|
+
// ✅ CRÍTICO: Usar CN (test.describe = titlePath[1]) como identificador do teste
|
|
2192
|
+
// titlePath[0] = filename, titlePath[1] = CN (test.describe), titlePath[2] = CT (test)
|
|
2193
|
+
const cnName = testInfo.titlePath?.[1] || testInfo.title;
|
|
2194
|
+
StatementTracker.setTestName(cnName);
|
|
2195
|
+
console.log(`🏷️ [MIXED] StatementTracker inicializado para CN: "${cnName}"`);
|
|
2196
|
+
testStartTimes.set(cnName, Date.now());
|
|
2197
|
+
UnifiedReportManager.setTestContext(cnName, 'Mixed');
|
|
2198
|
+
// 🆕 CRÍTICO: Garantir TerminalLogCapture ativo no worker (multi-worker support)
|
|
2199
|
+
ensureTerminalLogCaptureActive(cnName);
|
|
2200
|
+
TestAnnotations.setupAllInterceptors(cnName);
|
|
2201
|
+
UnifiedReportManager.adicionarLog(cnName, '🔀 Inicializando contexto Mixed - Múltiplos tipos habilitados');
|
|
2202
|
+
// 🔍 NOVO: Ativar logs automáticos de Statements
|
|
2203
|
+
ativarStatementLogs();
|
|
2204
|
+
ativarInterceptacaoMagica();
|
|
2205
|
+
TestAnnotations.setupMobileAutoCleanup(testInfo);
|
|
2206
|
+
TestAnnotations.setupTestFinalization(testInfo);
|
|
2207
|
+
},
|
|
2208
|
+
};
|
|
2209
|
+
/**
|
|
2210
|
+
* Adiciona um log ao UnifiedReportManager para o teste.
|
|
2211
|
+
* @param testName Nome do teste.
|
|
2212
|
+
* @param message Mensagem de log.
|
|
2213
|
+
* @param tipo Tipo do log ('info', 'success', 'error', 'warning').
|
|
2214
|
+
*/
|
|
2215
|
+
static addLog(testName, message, tipo = 'info') {
|
|
2216
|
+
const emojis = { info: 'ℹ️', success: '✅', error: '❌', warning: '⚠️' };
|
|
2217
|
+
UnifiedReportManager.adicionarLog(testName, `${emojis[tipo]} ${message}`);
|
|
2218
|
+
}
|
|
2219
|
+
/**
|
|
2220
|
+
* Define metadados para o teste no UnifiedReportManager.
|
|
2221
|
+
* @param testName Nome do teste.
|
|
2222
|
+
* @param metadata Objeto de metadados.
|
|
2223
|
+
*/
|
|
2224
|
+
static setMetadata(testName, metadata) {
|
|
2225
|
+
UnifiedReportManager.setTestMetadata(testName, metadata);
|
|
2226
|
+
}
|
|
2227
|
+
/**
|
|
2228
|
+
* Define a duração do teste no UnifiedReportManager.
|
|
2229
|
+
* @param testName Nome do teste.
|
|
2230
|
+
* @param duracao Duração em segundos.
|
|
2231
|
+
*/
|
|
2232
|
+
static setDuration(testName, duracao) {
|
|
2233
|
+
UnifiedReportManager.setTestDuration(testName, duracao);
|
|
2234
|
+
}
|
|
2235
|
+
/**
|
|
2236
|
+
* Obtém o contexto atual do teste.
|
|
2237
|
+
* @returns O tipo de contexto ou null.
|
|
2238
|
+
*/
|
|
2239
|
+
static getCurrentTestContext() {
|
|
2240
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
2241
|
+
if (testInfo?.title) {
|
|
2242
|
+
const metrics = UnifiedReportManager.getMetrics();
|
|
2243
|
+
const teste = metrics.testes.find((t) => t.nome === testInfo.title);
|
|
2244
|
+
return teste?.tipo || null;
|
|
2245
|
+
}
|
|
2246
|
+
return null;
|
|
2247
|
+
}
|
|
2248
|
+
/**
|
|
2249
|
+
* Marca o teste como falha no UnifiedReportManager.
|
|
2250
|
+
* @param testName Nome do teste.
|
|
2251
|
+
* @param reason Motivo da falha (opcional).
|
|
2252
|
+
*/
|
|
2253
|
+
static markTestAsFailed(testName, reason) {
|
|
2254
|
+
const errorMessage = reason
|
|
2255
|
+
? `❌ Teste falhou: ${reason}`
|
|
2256
|
+
: '❌ Teste marcado como falha';
|
|
2257
|
+
UnifiedReportManager.adicionarLog(testName, errorMessage);
|
|
2258
|
+
}
|
|
2259
|
+
/**
|
|
2260
|
+
* Marca o teste como sucesso no UnifiedReportManager.
|
|
2261
|
+
* @param testName Nome do teste.
|
|
2262
|
+
* @param message Mensagem de sucesso (opcional).
|
|
2263
|
+
*/
|
|
2264
|
+
static markTestAsSuccess(testName, message) {
|
|
2265
|
+
const successMessage = message
|
|
2266
|
+
? `✅ ${message}`
|
|
2267
|
+
: '✅ Teste concluído com sucesso';
|
|
2268
|
+
UnifiedReportManager.adicionarLog(testName, successMessage);
|
|
2269
|
+
}
|
|
2270
|
+
/**
|
|
2271
|
+
* Adiciona um screenshot ao relatório do teste.
|
|
2272
|
+
* @param testName Nome do teste.
|
|
2273
|
+
* @param screenshotName Nome do screenshot.
|
|
2274
|
+
* @param buffer Buffer da imagem.
|
|
2275
|
+
*/
|
|
2276
|
+
static async addScreenshot(testName, screenshotName, buffer) {
|
|
2277
|
+
try {
|
|
2278
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
2279
|
+
if (testInfo) {
|
|
2280
|
+
await testInfo.attach(`📸 ${screenshotName}`, {
|
|
2281
|
+
body: buffer,
|
|
2282
|
+
contentType: 'image/png',
|
|
2283
|
+
});
|
|
2284
|
+
UnifiedReportManager.adicionarLog(testName, `📸 Screenshot capturado: ${screenshotName}`);
|
|
2285
|
+
}
|
|
2286
|
+
}
|
|
2287
|
+
catch (error) {
|
|
2288
|
+
UnifiedReportManager.adicionarLog(testName, `❌ Erro ao anexar screenshot: ${error}`);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
/**
|
|
2292
|
+
* Obtém estatísticas globais dos testes.
|
|
2293
|
+
* @returns Objeto com total, concluídos, ativos e por tipo.
|
|
2294
|
+
*/
|
|
2295
|
+
static getGlobalStats() {
|
|
2296
|
+
const metrics = UnifiedReportManager.getMetrics();
|
|
2297
|
+
return {
|
|
2298
|
+
totalTests: metrics.totalTestes,
|
|
2299
|
+
completedTests: metrics.testes.length,
|
|
2300
|
+
activeTests: testStartTimes.size,
|
|
2301
|
+
testsByType: metrics.testesPorTipo,
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
/**
|
|
2305
|
+
* Limpa interceptadores ativos (debug).
|
|
2306
|
+
*/
|
|
2307
|
+
static clearInterceptors() {
|
|
2308
|
+
interceptorsAtivos.clear();
|
|
2309
|
+
console.log('🧹 Interceptadores limpos');
|
|
2310
|
+
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Obtém interceptadores ativos (debug).
|
|
2313
|
+
* @returns Array de chaves de interceptadores ativos.
|
|
2314
|
+
*/
|
|
2315
|
+
static getActiveInterceptors() {
|
|
2316
|
+
return Array.from(interceptorsAtivos);
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* 🔥 FORÇA interceptação direta das classes Statement do projeto real
|
|
2320
|
+
*/
|
|
2321
|
+
static forceInterceptRealProjectStatements(statementClasses) {
|
|
2322
|
+
try {
|
|
2323
|
+
console.log('🔥 === INTERCEPTAÇÃO FORÇADA PARA PROJETO REAL ===');
|
|
2324
|
+
// 🎯 Estratégia 1: Interceptar require/import para capturar no momento do carregamento
|
|
2325
|
+
const originalRequire = globalThis.require;
|
|
2326
|
+
if (originalRequire) {
|
|
2327
|
+
;
|
|
2328
|
+
globalThis.require = function (modulePath) {
|
|
2329
|
+
const result = originalRequire.apply(this, arguments);
|
|
2330
|
+
// Se o módulo carregado contém classes Statement
|
|
2331
|
+
if (result && typeof result === 'object') {
|
|
2332
|
+
for (const exportName of Object.keys(result)) {
|
|
2333
|
+
if (statementClasses.includes(exportName) &&
|
|
2334
|
+
typeof result[exportName] === 'function') {
|
|
2335
|
+
console.log(`🎯 [REQUIRE] Interceptando classe Statement: ${exportName}`);
|
|
2336
|
+
result[exportName] =
|
|
2337
|
+
TestAnnotations.createForceInterceptedClass(result[exportName], exportName);
|
|
2338
|
+
console.log(`✅ [REQUIRE] ${exportName} interceptada com sucesso`);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
return result;
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
// 🎯 Estratégia 2: Monitorar globals com throttling para evitar spam
|
|
2346
|
+
let lastMonitorTime = 0;
|
|
2347
|
+
const monitorThrottleTime = 1000; // 1 segundo entre verificações
|
|
2348
|
+
const monitoringInterval = setInterval(() => {
|
|
2349
|
+
const now = Date.now();
|
|
2350
|
+
if (now - lastMonitorTime < monitorThrottleTime) {
|
|
2351
|
+
return; // Skip se muito recente
|
|
2352
|
+
}
|
|
2353
|
+
lastMonitorTime = now;
|
|
2354
|
+
statementClasses.forEach((className) => {
|
|
2355
|
+
const contexts = [
|
|
2356
|
+
globalThis,
|
|
2357
|
+
globalThis.global,
|
|
2358
|
+
globalThis.window,
|
|
2359
|
+
].filter(Boolean);
|
|
2360
|
+
for (const context of contexts) {
|
|
2361
|
+
if (context[className] &&
|
|
2362
|
+
typeof context[className] === 'function' &&
|
|
2363
|
+
!context[className]._alreadyForceIntercepted) {
|
|
2364
|
+
console.log(`🔥 [MONITOR] Encontrada classe Statement não interceptada: ${className}`);
|
|
2365
|
+
const OriginalClass = context[className];
|
|
2366
|
+
const InterceptedClass = TestAnnotations.createForceInterceptedClass(OriginalClass, className);
|
|
2367
|
+
InterceptedClass._alreadyForceIntercepted = true;
|
|
2368
|
+
// Substituir no contexto
|
|
2369
|
+
context[className] = InterceptedClass;
|
|
2370
|
+
console.log(`✅ [MONITOR] ${className} interceptada e substituída`);
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
});
|
|
2374
|
+
}, 500); // Verificar a cada 500ms (reduzido de 100ms)
|
|
2375
|
+
// Parar o monitoramento após 10 segundos
|
|
2376
|
+
setTimeout(() => {
|
|
2377
|
+
clearInterval(monitoringInterval);
|
|
2378
|
+
console.log('🕐 [MONITOR] Monitoramento de interceptação finalizado');
|
|
2379
|
+
}, 10_000);
|
|
2380
|
+
console.log(`🚀 [FORÇA] Interceptação forçada configurada para ${statementClasses.length} classes`);
|
|
2381
|
+
}
|
|
2382
|
+
catch (error) {
|
|
2383
|
+
console.warn(`⚠️ [FORÇA] Erro na interceptação forçada: ${error}`);
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
/**
|
|
2387
|
+
* 🎯 Cria uma versão completamente interceptada de uma classe Statement
|
|
2388
|
+
*/
|
|
2389
|
+
static createForceInterceptedClass(OriginalClass, className) {
|
|
2390
|
+
try {
|
|
2391
|
+
console.log(`🔧 [FORÇA] Criando interceptação para: ${className}`);
|
|
2392
|
+
// Criar nova classe que substitui completamente a original
|
|
2393
|
+
const ForceInterceptedClass = (...args) => {
|
|
2394
|
+
console.log(`🚀 [FORÇA] NEW ${className}() INTERCEPTADO DEFINITIVAMENTE!`);
|
|
2395
|
+
console.log('📋 === CT EM EXECUÇÃO === [INTERCEPTAÇÃO FORÇADA]');
|
|
2396
|
+
console.log(` 🎯 Classe: ${className}`);
|
|
2397
|
+
console.log(` ⏰ [${new Date().toISOString()}] Instância criada`);
|
|
2398
|
+
console.log(' 🚀 FUNCIONANDO NO PROJETO REAL!');
|
|
2399
|
+
// Criar instância original
|
|
2400
|
+
const instance = new OriginalClass(...args);
|
|
2401
|
+
// Interceptar TODOS os métodos da instância
|
|
2402
|
+
const proto = Object.getPrototypeOf(instance);
|
|
2403
|
+
const methodNames = Object.getOwnPropertyNames(proto);
|
|
2404
|
+
for (const methodName of methodNames) {
|
|
2405
|
+
if (methodName !== 'constructor' &&
|
|
2406
|
+
typeof instance[methodName] === 'function') {
|
|
2407
|
+
const originalMethod = instance[methodName];
|
|
2408
|
+
instance[methodName] = function (...methodArgs) {
|
|
2409
|
+
// ✅ CORRIGIDO: NÃO chamar startCT/endCT aqui!
|
|
2410
|
+
// O StatementTracker.interceptStatement() já faz isso automaticamente
|
|
2411
|
+
console.log(`📋 === STATEMENT EM EXECUÇÃO === ${className}.${methodName}()`);
|
|
2412
|
+
console.log(` 🎯 [FORÇA] Método interceptado: ${methodName}()`);
|
|
2413
|
+
console.log(` ⏰ [${new Date().toISOString()}] Iniciando execução...`);
|
|
2414
|
+
console.log(' 🚀 CAPTURADO NO PROJETO REAL!');
|
|
2415
|
+
const startTime = Date.now();
|
|
2416
|
+
try {
|
|
2417
|
+
const result = originalMethod.apply(this, methodArgs);
|
|
2418
|
+
// Se é Promise
|
|
2419
|
+
if (result && typeof result.then === 'function') {
|
|
2420
|
+
return result
|
|
2421
|
+
.then((res) => {
|
|
2422
|
+
const duration = Date.now() - startTime;
|
|
2423
|
+
console.log(` ✅ [${new Date().toISOString()}] Finalizada: ${className}.${methodName}() - ${duration}ms`);
|
|
2424
|
+
console.log(` 📊 Resultado: ${typeof res === 'boolean' ? (res ? 'true (PASS)' : 'false (FAIL)') : res ? 'success' : 'null/undefined'}`);
|
|
2425
|
+
console.log('📋 === STATEMENT FINALIZADA ===');
|
|
2426
|
+
return res;
|
|
2427
|
+
})
|
|
2428
|
+
.catch((err) => {
|
|
2429
|
+
const duration = Date.now() - startTime;
|
|
2430
|
+
console.log(` ❌ [${new Date().toISOString()}] Erro: ${className}.${methodName}() - ${duration}ms`);
|
|
2431
|
+
console.log('📋 === STATEMENT COM ERRO ===');
|
|
2432
|
+
throw err;
|
|
2433
|
+
});
|
|
2434
|
+
}
|
|
2435
|
+
// Método síncrono
|
|
2436
|
+
const duration = Date.now() - startTime;
|
|
2437
|
+
console.log(` ✅ [${new Date().toISOString()}] Finalizada: ${className}.${methodName}() - ${duration}ms`);
|
|
2438
|
+
console.log(` 📊 Resultado: ${typeof result === 'boolean' ? (result ? 'true (PASS)' : 'false (FAIL)') : result ? 'success' : 'null/undefined'}`);
|
|
2439
|
+
console.log('📋 === STATEMENT FINALIZADA ===');
|
|
2440
|
+
return result;
|
|
2441
|
+
}
|
|
2442
|
+
catch (error) {
|
|
2443
|
+
const duration = Date.now() - startTime;
|
|
2444
|
+
console.log(` ❌ [${new Date().toISOString()}] Erro: ${className}.${methodName}() - ${duration}ms`);
|
|
2445
|
+
console.log(` 🔍 Erro: ${error?.message || 'Erro desconhecido'}`);
|
|
2446
|
+
console.log('📋 === STATEMENT COM ERRO ===');
|
|
2447
|
+
throw error;
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
return instance;
|
|
2453
|
+
};
|
|
2454
|
+
// Preservar prototype e propriedades estáticas
|
|
2455
|
+
ForceInterceptedClass.prototype = OriginalClass.prototype;
|
|
2456
|
+
Object.setPrototypeOf(ForceInterceptedClass, OriginalClass);
|
|
2457
|
+
// Copiar propriedades estáticas
|
|
2458
|
+
Object.getOwnPropertyNames(OriginalClass).forEach((prop) => {
|
|
2459
|
+
if (prop !== 'length' && prop !== 'name' && prop !== 'prototype') {
|
|
2460
|
+
try {
|
|
2461
|
+
;
|
|
2462
|
+
ForceInterceptedClass[prop] = OriginalClass[prop];
|
|
2463
|
+
}
|
|
2464
|
+
catch {
|
|
2465
|
+
// Ignorar propriedades que não podem ser copiadas
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
});
|
|
2469
|
+
console.log(`✅ [FORÇA] Classe ${className} completamente interceptada`);
|
|
2470
|
+
return ForceInterceptedClass;
|
|
2471
|
+
}
|
|
2472
|
+
catch (error) {
|
|
2473
|
+
console.warn(`⚠️ [FORÇA] Erro ao criar interceptação para ${className}: ${error}`);
|
|
2474
|
+
return OriginalClass;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
/**
|
|
2478
|
+
* 🚀 Gerencia execução de métodos CT detectados via log
|
|
2479
|
+
*/
|
|
2480
|
+
static handleMethodExecution(methodName) {
|
|
2481
|
+
try {
|
|
2482
|
+
// ❌ REMOVIDO: Logs de finalização de CT antigos aqui
|
|
2483
|
+
// O log estava aparecendo DEPOIS do "NOVO CT INICIADO" porque é executado
|
|
2484
|
+
// no início da função. A finalização correta é feita pelo StatementTracker.finalizarCT()
|
|
2485
|
+
// que é chamado pelo CustomReporter quando o teste termina.
|
|
2486
|
+
// ✅ CORREÇÃO: Não criar CT aqui - deixar o StatementTracker.ensureCTCreated() gerenciar
|
|
2487
|
+
// O CT será criado automaticamente na primeira statement pelo interceptStatement()
|
|
2488
|
+
// Aqui apenas registramos o Statement ativo para compatibilidade
|
|
2489
|
+
currentActiveCT = {
|
|
2490
|
+
name: methodName,
|
|
2491
|
+
startTime: Date.now(),
|
|
2492
|
+
statement: methodName,
|
|
2493
|
+
};
|
|
2494
|
+
// ❌ REMOVIDO: Logs duplicados de "NOVO CT INICIADO"
|
|
2495
|
+
// O StatementTracker.startCT já faz os logs necessários
|
|
2496
|
+
// ❌ REMOVIDO: Não criar CT aqui - StatementTracker.ensureCTCreated() faz isso
|
|
2497
|
+
// StatementTracker.startCT(formattedCtName) ← ESTAVA CRIANDO CT PARA CADA STATEMENT!
|
|
2498
|
+
// ✅ CORRIGIDO: Usar nome do método original, não ID aleatório
|
|
2499
|
+
currentActiveCT.name = methodName;
|
|
2500
|
+
}
|
|
2501
|
+
catch (error) {
|
|
2502
|
+
console.warn(`⚠️ Erro ao gerenciar execução de método: ${error}`);
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
/**
|
|
2506
|
+
* 🚨 NOVO: Gerencia erros de validação detectados via log
|
|
2507
|
+
*/
|
|
2508
|
+
static handleValidationError(errorMessage) {
|
|
2509
|
+
// 🔧 DESABILITADO TEMPORARIAMENTE PARA EVITAR LOOP INFINITO
|
|
2510
|
+
// Este método estava causando spam de "❌ ERRO DE VALIDAÇÃO DETECTADO (sem CT ativo)"
|
|
2511
|
+
// Vamos simplificar e não processar esses erros automaticamente
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
/**
|
|
2515
|
+
* 🏁 Finaliza o CT ativo quando o teste termina
|
|
2516
|
+
*/
|
|
2517
|
+
static finalizarCTAtivo() {
|
|
2518
|
+
try {
|
|
2519
|
+
// 1. Finalizar CT ativo do TestAnnotations (sistema legado)
|
|
2520
|
+
if (currentActiveCT) {
|
|
2521
|
+
const duration = ((Date.now() - currentActiveCT.startTime) /
|
|
2522
|
+
1000).toFixed(2);
|
|
2523
|
+
const currentTime = new Date().toLocaleTimeString('pt-BR');
|
|
2524
|
+
console.log(`\n✅ CT FINALIZADO (FIM DO TESTE): ${currentActiveCT.statement}`);
|
|
2525
|
+
console.log(` ⏱️ Duração: ${duration}s`);
|
|
2526
|
+
console.log(` 🕐 Finalizado: ${currentTime}\n`);
|
|
2527
|
+
// ❌ REMOVIDO: StatementTracker.endCT() - O teardown já finaliza todos os CTs automaticamente
|
|
2528
|
+
// O método finalizarCTAtivo() é do sistema legado e não deve interferir com StatementTracker
|
|
2529
|
+
// Limpar CT ativo
|
|
2530
|
+
currentActiveCT = null;
|
|
2531
|
+
}
|
|
2532
|
+
// ❌ REMOVIDO: Finalização manual de CTs
|
|
2533
|
+
// O UnifiedTeardown.run() já faz isso automaticamente chamando finalizeRunningCTsAsFailed()
|
|
2534
|
+
// Não precisamos fazer aqui para evitar duplicação e erros
|
|
2535
|
+
// 🆕 3. Adicionar logs capturados ao relatório final
|
|
2536
|
+
try {
|
|
2537
|
+
const testInfo = TestContext.getSafeTestInfo();
|
|
2538
|
+
if (testInfo?.title && logsCapturados.length > 0) {
|
|
2539
|
+
// Adicionar header para seção de logs de ações
|
|
2540
|
+
UnifiedReportManager.adicionarLog(testInfo.title, '\n📋 === LOGS DE AÇÕES DE CTs ===');
|
|
2541
|
+
// Adicionar todos os logs capturados
|
|
2542
|
+
for (const log of logsCapturados) {
|
|
2543
|
+
UnifiedReportManager.adicionarLog(testInfo.title, log);
|
|
2544
|
+
}
|
|
2545
|
+
console.log(`📊 Adicionados ${logsCapturados.length} logs de ações ao relatório`);
|
|
2546
|
+
}
|
|
2547
|
+
}
|
|
2548
|
+
catch (error) {
|
|
2549
|
+
console.warn(`⚠️ Erro ao adicionar logs capturados: ${error}`);
|
|
2550
|
+
}
|
|
2551
|
+
// 🆕 4. Restaurar console original e limpar logs
|
|
2552
|
+
TestAnnotations.restaurarConsoleOriginal();
|
|
2553
|
+
}
|
|
2554
|
+
catch (error) {
|
|
2555
|
+
console.warn(`⚠️ Erro na finalização de CTs: ${error}`);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2558
|
+
/**
|
|
2559
|
+
* 🆕 Restaurar console original e limpar dados
|
|
2560
|
+
*/
|
|
2561
|
+
static restaurarConsoleOriginal() {
|
|
2562
|
+
try {
|
|
2563
|
+
if (interceptacaoLogsAtiva) {
|
|
2564
|
+
console.log = originalConsoleLog;
|
|
2565
|
+
console.info = originalConsoleInfo;
|
|
2566
|
+
console.warn = originalConsoleWarn;
|
|
2567
|
+
console.error = originalConsoleError;
|
|
2568
|
+
interceptacaoLogsAtiva = false;
|
|
2569
|
+
logsCapturados = [];
|
|
2570
|
+
atualizarReferenciaGlobalDosLogs();
|
|
2571
|
+
console.log('🔧 Console original restaurado e logs limpos');
|
|
2572
|
+
}
|
|
2573
|
+
}
|
|
2574
|
+
catch (error) {
|
|
2575
|
+
console.warn(`⚠️ Erro ao restaurar console: ${error}`);
|
|
2576
|
+
}
|
|
2577
|
+
}
|
|
2578
|
+
/**
|
|
2579
|
+
* 🆕 Obter logs capturados (para debugging)
|
|
2580
|
+
*/
|
|
2581
|
+
static getLogsCapturados() {
|
|
2582
|
+
return [...logsCapturados];
|
|
2583
|
+
}
|
|
2584
|
+
// ============================================
|
|
2585
|
+
// 🔐 MÉTODOS DE AUTENTICAÇÃO E STORAGE STATE
|
|
2586
|
+
// ============================================
|
|
2587
|
+
/**
|
|
2588
|
+
* 💾 Salva o estado de autenticação atual (cookies + localStorage + sessionStorage)
|
|
2589
|
+
* @param filePath Caminho opcional para salvar (padrão: ./test-results/auth/auth-state.json)
|
|
2590
|
+
* @example
|
|
2591
|
+
* // Após fazer login
|
|
2592
|
+
* await TestAnnotations.saveAuthState('./auth/admin-user.json')
|
|
2593
|
+
*/
|
|
2594
|
+
static async saveAuthState(filePath) {
|
|
2595
|
+
return await TestContext.saveAuthState(filePath);
|
|
2596
|
+
}
|
|
2597
|
+
/**
|
|
2598
|
+
* 🔄 Restaura o estado de autenticação salvo
|
|
2599
|
+
* @param filePath Caminho do arquivo de estado
|
|
2600
|
+
* @example
|
|
2601
|
+
* // Antes de rodar testes
|
|
2602
|
+
* await TestAnnotations.restoreAuthState('./auth/admin-user.json')
|
|
2603
|
+
*/
|
|
2604
|
+
static async restoreAuthState(filePath) {
|
|
2605
|
+
await TestContext.restoreAuthState(filePath);
|
|
2606
|
+
}
|
|
2607
|
+
/**
|
|
2608
|
+
* 🧹 Limpa todo o estado de autenticação
|
|
2609
|
+
* @example
|
|
2610
|
+
* // Logout completo
|
|
2611
|
+
* await TestAnnotations.clearAuthState()
|
|
2612
|
+
*/
|
|
2613
|
+
static async clearAuthState() {
|
|
2614
|
+
await TestContext.clearAuthState();
|
|
2615
|
+
}
|
|
2616
|
+
/**
|
|
2617
|
+
* 📄 Verifica se existe um arquivo de estado salvo
|
|
2618
|
+
* @param filePath Caminho do arquivo
|
|
2619
|
+
*/
|
|
2620
|
+
static authStateExists(filePath) {
|
|
2621
|
+
return TestContext.authStateExists(filePath);
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* 🔍 Lê o estado salvo sem aplicar
|
|
2625
|
+
* @param filePath Caminho do arquivo
|
|
2626
|
+
*/
|
|
2627
|
+
static readAuthState(filePath) {
|
|
2628
|
+
return TestContext.readAuthState(filePath);
|
|
2629
|
+
}
|
|
2630
|
+
/**
|
|
2631
|
+
* 🗑️ Remove um arquivo de estado
|
|
2632
|
+
* @param filePath Caminho do arquivo
|
|
2633
|
+
*/
|
|
2634
|
+
static deleteAuthState(filePath) {
|
|
2635
|
+
return TestContext.deleteAuthState(filePath);
|
|
2636
|
+
}
|
|
2637
|
+
/**
|
|
2638
|
+
* 📋 Lista todos os estados salvos
|
|
2639
|
+
*/
|
|
2640
|
+
static listAuthStates() {
|
|
2641
|
+
return TestContext.listAuthStates();
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
// Removed uppercase aliases: keep only the typed properties (Api, Frontend, Mobile, Banco)
|
|
2645
|
+
// Exportar também as classes individuais para uso direto
|
|
2646
|
+
export const { Api, Frontend, Mobile, SSH, Banco, Mixed, Auto, } = TestAnnotations;
|
|
2647
|
+
// NOTE: Uppercase aliases removed. Use `Api`, `Frontend`, `Mobile`, `Banco` (camelCase) imports instead.
|