@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,718 @@
|
|
|
1
|
+
import { test as base, expect as baseExpect } from '@playwright/test';
|
|
2
|
+
import { execSync } from 'node:child_process';
|
|
3
|
+
import * as fs from 'node:fs';
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
import { TestContext } from '../testContext/TestContext.js';
|
|
7
|
+
/**
|
|
8
|
+
* Classe para capturar e gerenciar logs de Network
|
|
9
|
+
*/
|
|
10
|
+
class NetworkLogger {
|
|
11
|
+
logs = [];
|
|
12
|
+
requestTimings = new Map();
|
|
13
|
+
currentPageUrl = '';
|
|
14
|
+
currentPageTitle = '';
|
|
15
|
+
/**
|
|
16
|
+
* Registra o início de uma requisição
|
|
17
|
+
*/
|
|
18
|
+
logRequest(request) {
|
|
19
|
+
const timestamp = new Date().toISOString();
|
|
20
|
+
const requestId = `${request.method()}-${request.url()}-${timestamp}`;
|
|
21
|
+
this.requestTimings.set(request.url(), Date.now());
|
|
22
|
+
const entry = {
|
|
23
|
+
timestamp,
|
|
24
|
+
pageUrl: this.currentPageUrl,
|
|
25
|
+
pageTitle: this.currentPageTitle,
|
|
26
|
+
type: 'request',
|
|
27
|
+
method: request.method(),
|
|
28
|
+
url: request.url(),
|
|
29
|
+
resourceType: request.resourceType(),
|
|
30
|
+
headers: request.headers(),
|
|
31
|
+
postData: request.postData() || undefined,
|
|
32
|
+
};
|
|
33
|
+
this.logs.push(entry);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Registra a resposta de uma requisição
|
|
37
|
+
*/
|
|
38
|
+
async logResponse(response) {
|
|
39
|
+
const timestamp = new Date().toISOString();
|
|
40
|
+
const startTime = this.requestTimings.get(response.url());
|
|
41
|
+
const duration = startTime ? Date.now() - startTime : undefined;
|
|
42
|
+
let size;
|
|
43
|
+
try {
|
|
44
|
+
const body = await response.body().catch(() => null);
|
|
45
|
+
size = body?.length;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
size = undefined;
|
|
49
|
+
}
|
|
50
|
+
const entry = {
|
|
51
|
+
timestamp,
|
|
52
|
+
pageUrl: this.currentPageUrl,
|
|
53
|
+
pageTitle: this.currentPageTitle,
|
|
54
|
+
type: 'response',
|
|
55
|
+
method: response.request().method(),
|
|
56
|
+
url: response.url(),
|
|
57
|
+
status: response.status(),
|
|
58
|
+
statusText: response.statusText(),
|
|
59
|
+
resourceType: response.request().resourceType(),
|
|
60
|
+
duration,
|
|
61
|
+
size,
|
|
62
|
+
headers: response.headers(),
|
|
63
|
+
mimeType: response.headers()['content-type'],
|
|
64
|
+
fromServiceWorker: response.fromServiceWorker(),
|
|
65
|
+
};
|
|
66
|
+
this.logs.push(entry);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Registra requisição que falhou
|
|
70
|
+
*/
|
|
71
|
+
logRequestFailed(request) {
|
|
72
|
+
const timestamp = new Date().toISOString();
|
|
73
|
+
const startTime = this.requestTimings.get(request.url());
|
|
74
|
+
const duration = startTime ? Date.now() - startTime : undefined;
|
|
75
|
+
const entry = {
|
|
76
|
+
timestamp,
|
|
77
|
+
pageUrl: this.currentPageUrl,
|
|
78
|
+
pageTitle: this.currentPageTitle,
|
|
79
|
+
type: 'failed',
|
|
80
|
+
method: request.method(),
|
|
81
|
+
url: request.url(),
|
|
82
|
+
resourceType: request.resourceType(),
|
|
83
|
+
duration,
|
|
84
|
+
errorText: request.failure()?.errorText,
|
|
85
|
+
};
|
|
86
|
+
this.logs.push(entry);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Atualiza informações da página atual
|
|
90
|
+
*/
|
|
91
|
+
updatePageInfo(url, title) {
|
|
92
|
+
this.currentPageUrl = url;
|
|
93
|
+
this.currentPageTitle = title;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Retorna todos os logs
|
|
97
|
+
*/
|
|
98
|
+
getLogs() {
|
|
99
|
+
return this.logs;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Gera relatório formatado em texto
|
|
103
|
+
*/
|
|
104
|
+
generateTextReport() {
|
|
105
|
+
if (this.logs.length === 0) {
|
|
106
|
+
return '📡 [Network Logs] Nenhuma requisição capturada';
|
|
107
|
+
}
|
|
108
|
+
const lines = [
|
|
109
|
+
'═══════════════════════════════════════════════════════════════════════════════',
|
|
110
|
+
'📡 NETWORK LOGS - AutoCore',
|
|
111
|
+
`📊 Total de registros: ${this.logs.length}`,
|
|
112
|
+
`🕐 Capturado em: ${new Date().toISOString()}`,
|
|
113
|
+
'═══════════════════════════════════════════════════════════════════════════════',
|
|
114
|
+
'',
|
|
115
|
+
];
|
|
116
|
+
// Agrupa por página
|
|
117
|
+
const byPage = new Map();
|
|
118
|
+
for (const log of this.logs) {
|
|
119
|
+
const pageKey = `${log.pageTitle || 'Sem título'} (${log.pageUrl || 'URL desconhecida'})`;
|
|
120
|
+
if (!byPage.has(pageKey)) {
|
|
121
|
+
byPage.set(pageKey, []);
|
|
122
|
+
}
|
|
123
|
+
byPage.get(pageKey).push(log);
|
|
124
|
+
}
|
|
125
|
+
for (const [pageKey, pageLogs] of byPage) {
|
|
126
|
+
lines.push(`\n📄 PÁGINA: ${pageKey}`);
|
|
127
|
+
lines.push('─'.repeat(79));
|
|
128
|
+
// Estatísticas da página
|
|
129
|
+
const requests = pageLogs.filter(l => l.type === 'request').length;
|
|
130
|
+
const responses = pageLogs.filter(l => l.type === 'response').length;
|
|
131
|
+
const failed = pageLogs.filter(l => l.type === 'failed').length;
|
|
132
|
+
const errors = pageLogs.filter(l => l.type === 'response' && l.status && l.status >= 400).length;
|
|
133
|
+
lines.push(` 📈 Requisições: ${requests} | Respostas: ${responses} | Falhas: ${failed} | Erros HTTP: ${errors}`);
|
|
134
|
+
lines.push('');
|
|
135
|
+
for (const log of pageLogs) {
|
|
136
|
+
const icon = log.type === 'request' ? '➡️' : log.type === 'response' ? (log.status && log.status >= 400 ? '❌' : '✅') : '💥';
|
|
137
|
+
const status = log.status ? ` [${log.status} ${log.statusText || ''}]` : '';
|
|
138
|
+
const duration = log.duration ? ` (${log.duration}ms)` : '';
|
|
139
|
+
const size = log.size ? ` ${this.formatBytes(log.size)}` : '';
|
|
140
|
+
const sw = log.fromServiceWorker ? ' [SW]' : '';
|
|
141
|
+
lines.push(` ${icon} ${log.timestamp.split('T')[1].split('.')[0]} | ${log.method.padEnd(7)} | ${log.resourceType.padEnd(12)} |${status}${duration}${size}${sw}`);
|
|
142
|
+
lines.push(` 🔗 ${this.truncateUrl(log.url, 100)}`);
|
|
143
|
+
if (log.type === 'failed' && log.errorText) {
|
|
144
|
+
lines.push(` ⚠️ Erro: ${log.errorText}`);
|
|
145
|
+
}
|
|
146
|
+
if (log.postData) {
|
|
147
|
+
lines.push(` 📤 Body: ${this.truncateString(log.postData, 200)}`);
|
|
148
|
+
}
|
|
149
|
+
lines.push('');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Resumo final
|
|
153
|
+
lines.push('═══════════════════════════════════════════════════════════════════════════════');
|
|
154
|
+
lines.push('📊 RESUMO');
|
|
155
|
+
lines.push('─'.repeat(79));
|
|
156
|
+
const totalRequests = this.logs.filter(l => l.type === 'request').length;
|
|
157
|
+
const totalResponses = this.logs.filter(l => l.type === 'response').length;
|
|
158
|
+
const totalFailed = this.logs.filter(l => l.type === 'failed').length;
|
|
159
|
+
const totalErrors = this.logs.filter(l => l.type === 'response' && l.status && l.status >= 400).length;
|
|
160
|
+
const avgDuration = this.calculateAvgDuration();
|
|
161
|
+
const totalSize = this.calculateTotalSize();
|
|
162
|
+
lines.push(` 📤 Total Requisições: ${totalRequests}`);
|
|
163
|
+
lines.push(` 📥 Total Respostas: ${totalResponses}`);
|
|
164
|
+
lines.push(` 💥 Requisições Falhas: ${totalFailed}`);
|
|
165
|
+
lines.push(` ❌ Erros HTTP (4xx/5xx): ${totalErrors}`);
|
|
166
|
+
lines.push(` ⏱️ Tempo Médio: ${avgDuration}ms`);
|
|
167
|
+
lines.push(` 📦 Dados Transferidos: ${this.formatBytes(totalSize)}`);
|
|
168
|
+
lines.push('═══════════════════════════════════════════════════════════════════════════════');
|
|
169
|
+
return lines.join('\n');
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Gera relatório em JSON
|
|
173
|
+
*/
|
|
174
|
+
generateJsonReport() {
|
|
175
|
+
return JSON.stringify({
|
|
176
|
+
capturedAt: new Date().toISOString(),
|
|
177
|
+
totalLogs: this.logs.length,
|
|
178
|
+
summary: {
|
|
179
|
+
requests: this.logs.filter(l => l.type === 'request').length,
|
|
180
|
+
responses: this.logs.filter(l => l.type === 'response').length,
|
|
181
|
+
failed: this.logs.filter(l => l.type === 'failed').length,
|
|
182
|
+
httpErrors: this.logs.filter(l => l.type === 'response' && l.status && l.status >= 400).length,
|
|
183
|
+
avgDurationMs: this.calculateAvgDuration(),
|
|
184
|
+
totalBytes: this.calculateTotalSize(),
|
|
185
|
+
},
|
|
186
|
+
logs: this.logs,
|
|
187
|
+
}, null, 2);
|
|
188
|
+
}
|
|
189
|
+
truncateUrl(url, maxLength) {
|
|
190
|
+
if (url.length <= maxLength)
|
|
191
|
+
return url;
|
|
192
|
+
return url.substring(0, maxLength - 3) + '...';
|
|
193
|
+
}
|
|
194
|
+
truncateString(str, maxLength) {
|
|
195
|
+
if (str.length <= maxLength)
|
|
196
|
+
return str;
|
|
197
|
+
return str.substring(0, maxLength - 3) + '...';
|
|
198
|
+
}
|
|
199
|
+
formatBytes(bytes) {
|
|
200
|
+
if (bytes === 0)
|
|
201
|
+
return '0 B';
|
|
202
|
+
const k = 1024;
|
|
203
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
204
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
205
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
206
|
+
}
|
|
207
|
+
calculateAvgDuration() {
|
|
208
|
+
const durations = this.logs.filter(l => l.duration).map(l => l.duration);
|
|
209
|
+
if (durations.length === 0)
|
|
210
|
+
return 0;
|
|
211
|
+
return Math.round(durations.reduce((a, b) => a + b, 0) / durations.length);
|
|
212
|
+
}
|
|
213
|
+
calculateTotalSize() {
|
|
214
|
+
return this.logs.filter(l => l.size).reduce((acc, l) => acc + (l.size || 0), 0);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Limpa os logs
|
|
218
|
+
*/
|
|
219
|
+
clear() {
|
|
220
|
+
this.logs = [];
|
|
221
|
+
this.requestTimings.clear();
|
|
222
|
+
this.currentPageUrl = '';
|
|
223
|
+
this.currentPageTitle = '';
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/** Cache de resolução detectada (evita re-detectar a cada teste) */
|
|
227
|
+
let _cachedResolution = null;
|
|
228
|
+
/**
|
|
229
|
+
* Detecta resolução real da tela do SO (não do browser headless).
|
|
230
|
+
* Prioridade:
|
|
231
|
+
* 1) Env vars AUTOCORE_VIEWPORT_WIDTH / AUTOCORE_VIEWPORT_HEIGHT
|
|
232
|
+
* 2) Detecção via SO (Windows PowerShell, Linux xrandr/xdpyinfo)
|
|
233
|
+
* 3) Fallback 1920×1080
|
|
234
|
+
*
|
|
235
|
+
* Sempre clampeia entre 1024×768 (mínimo) e 3840×2160 (4K máximo)
|
|
236
|
+
* para evitar viewports absurdos.
|
|
237
|
+
*/
|
|
238
|
+
function detectScreenResolution() {
|
|
239
|
+
if (_cachedResolution)
|
|
240
|
+
return _cachedResolution;
|
|
241
|
+
const MIN_W = 1024, MIN_H = 768, MAX_W = 3840, MAX_H = 2160;
|
|
242
|
+
const FALLBACK = { width: 1920, height: 1080 };
|
|
243
|
+
const clamp = (w, h) => ({
|
|
244
|
+
width: Math.max(MIN_W, Math.min(MAX_W, w)),
|
|
245
|
+
height: Math.max(MIN_H, Math.min(MAX_H, h)),
|
|
246
|
+
});
|
|
247
|
+
// 1) Env vars (override manual)
|
|
248
|
+
const envW = Number(process.env.AUTOCORE_VIEWPORT_WIDTH);
|
|
249
|
+
const envH = Number(process.env.AUTOCORE_VIEWPORT_HEIGHT);
|
|
250
|
+
if (envW > 0 && envH > 0) {
|
|
251
|
+
_cachedResolution = clamp(envW, envH);
|
|
252
|
+
console.log(`📐 [AutoCore] Viewport via env: ${_cachedResolution.width}x${_cachedResolution.height}`);
|
|
253
|
+
return _cachedResolution;
|
|
254
|
+
}
|
|
255
|
+
// 2) Detecção por SO
|
|
256
|
+
try {
|
|
257
|
+
const platform = os.platform();
|
|
258
|
+
if (platform === 'win32') {
|
|
259
|
+
// PowerShell: lê resolução primária via WMI (sem GUI necessário)
|
|
260
|
+
const raw = execSync(`powershell -NoProfile -Command "Add-Type -AssemblyName System.Windows.Forms; $s=[System.Windows.Forms.Screen]::PrimaryScreen.Bounds; Write-Output ('{0}x{1}' -f $s.Width,$s.Height)"`, { timeout: 5000, encoding: 'utf-8', windowsHide: true }).trim();
|
|
261
|
+
const match = raw.match(/(\d+)x(\d+)/);
|
|
262
|
+
if (match) {
|
|
263
|
+
_cachedResolution = clamp(Number(match[1]), Number(match[2]));
|
|
264
|
+
console.log(`📐 [AutoCore] Viewport via Windows screen: ${_cachedResolution.width}x${_cachedResolution.height}`);
|
|
265
|
+
return _cachedResolution;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
else if (platform === 'linux') {
|
|
269
|
+
// xdpyinfo ou xrandr para Linux com X11
|
|
270
|
+
try {
|
|
271
|
+
const raw = execSync('xdpyinfo 2>/dev/null | grep dimensions', { timeout: 3000, encoding: 'utf-8' }).trim();
|
|
272
|
+
const match = raw.match(/(\d+)x(\d+)/);
|
|
273
|
+
if (match) {
|
|
274
|
+
_cachedResolution = clamp(Number(match[1]), Number(match[2]));
|
|
275
|
+
console.log(`📐 [AutoCore] Viewport via xdpyinfo: ${_cachedResolution.width}x${_cachedResolution.height}`);
|
|
276
|
+
return _cachedResolution;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
// xdpyinfo indisponível, tentar xrandr
|
|
281
|
+
const raw = execSync('xrandr 2>/dev/null | grep "\\*" | head -1', { timeout: 3000, encoding: 'utf-8' }).trim();
|
|
282
|
+
const match = raw.match(/(\d+)x(\d+)/);
|
|
283
|
+
if (match) {
|
|
284
|
+
_cachedResolution = clamp(Number(match[1]), Number(match[2]));
|
|
285
|
+
console.log(`📐 [AutoCore] Viewport via xrandr: ${_cachedResolution.width}x${_cachedResolution.height}`);
|
|
286
|
+
return _cachedResolution;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
else if (platform === 'darwin') {
|
|
291
|
+
const raw = execSync('system_profiler SPDisplaysDataType 2>/dev/null | grep Resolution', { timeout: 3000, encoding: 'utf-8' }).trim();
|
|
292
|
+
const match = raw.match(/(\d+)\s*x\s*(\d+)/);
|
|
293
|
+
if (match) {
|
|
294
|
+
_cachedResolution = clamp(Number(match[1]), Number(match[2]));
|
|
295
|
+
console.log(`📐 [AutoCore] Viewport via macOS: ${_cachedResolution.width}x${_cachedResolution.height}`);
|
|
296
|
+
return _cachedResolution;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
// Detecção falhou silenciosamente
|
|
302
|
+
}
|
|
303
|
+
// 3) Fallback
|
|
304
|
+
_cachedResolution = FALLBACK;
|
|
305
|
+
console.log(`📐 [AutoCore] Viewport fallback: ${FALLBACK.width}x${FALLBACK.height}`);
|
|
306
|
+
return _cachedResolution;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Configura viewport com resolução dinâmica da máquina.
|
|
310
|
+
* Usa detecção de SO com deviceScaleFactor=1 para evitar zoom.
|
|
311
|
+
* Injeta meta viewport tag para garantir zoom 100% no conteúdo.
|
|
312
|
+
*/
|
|
313
|
+
async function setupResponsiveViewport(page) {
|
|
314
|
+
try {
|
|
315
|
+
const { width, height } = detectScreenResolution();
|
|
316
|
+
await page.setViewportSize({ width, height });
|
|
317
|
+
// Garantir zoom 100% via meta tag
|
|
318
|
+
await page.addInitScript(() => {
|
|
319
|
+
const meta = document.querySelector('meta[name="viewport"]');
|
|
320
|
+
if (!meta) {
|
|
321
|
+
const m = document.createElement('meta');
|
|
322
|
+
m.name = 'viewport';
|
|
323
|
+
m.content = 'width=device-width, initial-scale=1.0, maximum-scale=1.0';
|
|
324
|
+
document.head?.appendChild(m);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
console.log(`📐 [AutoCore-fixtures] Viewport final: ${width}x${height} (zoom 100%)`);
|
|
328
|
+
}
|
|
329
|
+
catch (err) {
|
|
330
|
+
console.warn('⚠️ Não foi possível ajustar viewport:', err);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Configura captura de logs de Network
|
|
335
|
+
*/
|
|
336
|
+
function setupNetworkLogging(page, networkLogger) {
|
|
337
|
+
// Atualiza info da página em cada navegação
|
|
338
|
+
page.on('framenavigated', async (frame) => {
|
|
339
|
+
if (frame === page.mainFrame()) {
|
|
340
|
+
try {
|
|
341
|
+
const url = page.url();
|
|
342
|
+
const title = await page.title().catch(() => '');
|
|
343
|
+
networkLogger.updatePageInfo(url, title);
|
|
344
|
+
}
|
|
345
|
+
catch {
|
|
346
|
+
// Ignora erros de página fechada
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
// Captura requisições
|
|
351
|
+
page.on('request', (request) => {
|
|
352
|
+
try {
|
|
353
|
+
networkLogger.logRequest(request);
|
|
354
|
+
}
|
|
355
|
+
catch {
|
|
356
|
+
// Ignora erros
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
// Captura respostas
|
|
360
|
+
page.on('response', async (response) => {
|
|
361
|
+
try {
|
|
362
|
+
await networkLogger.logResponse(response);
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
// Ignora erros
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
// Captura requisições que falharam
|
|
369
|
+
page.on('requestfailed', (request) => {
|
|
370
|
+
try {
|
|
371
|
+
networkLogger.logRequestFailed(request);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
// Ignora erros
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
console.log('📡 [AutoCore] Network logging ativado');
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Salva arquivos de network logs na pasta test-results/network-logs
|
|
381
|
+
*
|
|
382
|
+
* O Playwright NÃO limpa test-results/ automaticamente, então os arquivos são preservados.
|
|
383
|
+
* Para ter os arquivos dentro de playwright-report/, adicione NetworkLogsReporter ao seu playwright.config.ts:
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* import { NetworkLogsReporter } from '@silasfmartins/testhub'
|
|
387
|
+
* export default defineConfig({
|
|
388
|
+
* reporter: [['html'], NetworkLogsReporter],
|
|
389
|
+
* })
|
|
390
|
+
*/
|
|
391
|
+
function saveNetworkLogsToReport(testInfo, networkLogger) {
|
|
392
|
+
const logs = networkLogger.getLogs();
|
|
393
|
+
if (logs.length === 0)
|
|
394
|
+
return null;
|
|
395
|
+
try {
|
|
396
|
+
// 🔧 Usa test-results/network-logs que é preservado pelo Playwright
|
|
397
|
+
// O outputDir já é dentro de test-results, então usamos a raiz do projeto + test-results/network-logs
|
|
398
|
+
let projectRoot;
|
|
399
|
+
// outputDir é algo como: C:\...\test-sanity-frontend\test-results\chromium\...
|
|
400
|
+
// Queremos: C:\...\test-sanity-frontend
|
|
401
|
+
if (testInfo.outputDir) {
|
|
402
|
+
// Remove tudo a partir de 'test-results' (incluindo)
|
|
403
|
+
const testResultsIndex = testInfo.outputDir.indexOf('test-results');
|
|
404
|
+
if (testResultsIndex > 0) {
|
|
405
|
+
projectRoot = testInfo.outputDir.substring(0, testResultsIndex).replace(/[\\/]$/, '');
|
|
406
|
+
}
|
|
407
|
+
else {
|
|
408
|
+
projectRoot = testInfo.config?.rootDir || process.cwd();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
projectRoot = testInfo.config?.rootDir || process.cwd();
|
|
413
|
+
}
|
|
414
|
+
// 🔧 FIX ADICIONAL: Se rootDir termina com 'tests', remove para ir ao projeto raiz
|
|
415
|
+
if (projectRoot.endsWith('tests') || projectRoot.endsWith('tests/') || projectRoot.endsWith('tests\\')) {
|
|
416
|
+
projectRoot = path.dirname(projectRoot);
|
|
417
|
+
}
|
|
418
|
+
// 🔧 Salva em test-results/network-logs (pasta preservada pelo Playwright)
|
|
419
|
+
const reportDir = path.join(projectRoot, 'test-results', 'network-logs');
|
|
420
|
+
console.log(`📂 [AutoCore] Network logs directory:`);
|
|
421
|
+
console.log(` 📂 projectRoot: ${projectRoot}`);
|
|
422
|
+
console.log(` 📂 reportDir: ${reportDir}`);
|
|
423
|
+
// Cria a pasta se não existir
|
|
424
|
+
if (!fs.existsSync(reportDir)) {
|
|
425
|
+
fs.mkdirSync(reportDir, { recursive: true });
|
|
426
|
+
console.log(`📁 [AutoCore] Criada pasta: ${reportDir}`);
|
|
427
|
+
}
|
|
428
|
+
// Gera nome do arquivo baseado no teste (sanitizado)
|
|
429
|
+
const testName = testInfo.title.replace(/[^a-zA-Z0-9]/g, '_').substring(0, 50);
|
|
430
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
431
|
+
const baseName = `${testName}_${timestamp}`;
|
|
432
|
+
// Salva arquivo TXT
|
|
433
|
+
const txtPath = path.join(reportDir, `${baseName}.txt`);
|
|
434
|
+
const textReport = networkLogger.generateTextReport();
|
|
435
|
+
fs.writeFileSync(txtPath, textReport, 'utf-8');
|
|
436
|
+
// Verifica se arquivo foi criado
|
|
437
|
+
if (fs.existsSync(txtPath)) {
|
|
438
|
+
console.log(`✅ [AutoCore] Arquivo TXT criado: ${txtPath}`);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
console.error(`❌ [AutoCore] FALHA ao criar arquivo TXT: ${txtPath}`);
|
|
442
|
+
}
|
|
443
|
+
// Salva arquivo JSON
|
|
444
|
+
const jsonPath = path.join(reportDir, `${baseName}.json`);
|
|
445
|
+
const jsonReport = networkLogger.generateJsonReport();
|
|
446
|
+
fs.writeFileSync(jsonPath, jsonReport, 'utf-8');
|
|
447
|
+
// Verifica se arquivo foi criado
|
|
448
|
+
if (fs.existsSync(jsonPath)) {
|
|
449
|
+
console.log(`✅ [AutoCore] Arquivo JSON criado: ${jsonPath}`);
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
console.error(`❌ [AutoCore] FALHA ao criar arquivo JSON: ${jsonPath}`);
|
|
453
|
+
}
|
|
454
|
+
console.log(`📡 [AutoCore] Network logs salvos em: ${reportDir}`);
|
|
455
|
+
return { txtPath, jsonPath };
|
|
456
|
+
}
|
|
457
|
+
catch (err) {
|
|
458
|
+
console.error('❌ [AutoCore] Erro ao salvar network logs:', err);
|
|
459
|
+
console.error(` 📂 outputDir: ${testInfo.outputDir || '(não definido)'}`);
|
|
460
|
+
console.error(` 📂 config.rootDir: ${testInfo.config?.rootDir || '(não definido)'}`);
|
|
461
|
+
console.error(` 📂 cwd: ${process.cwd()}`);
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* Anexa logs de Network ao relatório do teste como Annotations
|
|
467
|
+
*/
|
|
468
|
+
async function attachNetworkLogs(testInfo, networkLogger) {
|
|
469
|
+
const logs = networkLogger.getLogs();
|
|
470
|
+
if (logs.length === 0) {
|
|
471
|
+
console.log('📡 [AutoCore] Nenhum log de network para anexar');
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
try {
|
|
475
|
+
// 1. Salva arquivos na pasta playwright-report
|
|
476
|
+
const savedFiles = saveNetworkLogsToReport(testInfo, networkLogger);
|
|
477
|
+
// 2. Anexa relatório em texto (legível) ao teste
|
|
478
|
+
const textReport = networkLogger.generateTextReport();
|
|
479
|
+
await testInfo.attach('network-logs.txt', {
|
|
480
|
+
body: Buffer.from(textReport, 'utf-8'),
|
|
481
|
+
contentType: 'text/plain',
|
|
482
|
+
});
|
|
483
|
+
// 3. Anexa relatório em JSON (para processamento)
|
|
484
|
+
const jsonReport = networkLogger.generateJsonReport();
|
|
485
|
+
await testInfo.attach('network-logs.json', {
|
|
486
|
+
body: Buffer.from(jsonReport, 'utf-8'),
|
|
487
|
+
contentType: 'application/json',
|
|
488
|
+
});
|
|
489
|
+
// 4. Adiciona Annotations para aparecer no relatório HTML
|
|
490
|
+
const requests = logs.filter(l => l.type === 'request').length;
|
|
491
|
+
const responses = logs.filter(l => l.type === 'response').length;
|
|
492
|
+
const failed = logs.filter(l => l.type === 'failed').length;
|
|
493
|
+
const errors = logs.filter(l => l.type === 'response' && l.status && l.status >= 400).length;
|
|
494
|
+
const totalSize = logs.filter(l => l.size).reduce((acc, l) => acc + (l.size || 0), 0);
|
|
495
|
+
const avgDuration = logs.filter(l => l.duration).length > 0
|
|
496
|
+
? Math.round(logs.filter(l => l.duration).reduce((acc, l) => acc + (l.duration || 0), 0) / logs.filter(l => l.duration).length)
|
|
497
|
+
: 0;
|
|
498
|
+
// Annotation com resumo de Network
|
|
499
|
+
testInfo.annotations.push({
|
|
500
|
+
type: '📡 Network Summary',
|
|
501
|
+
description: `Requests: ${requests} | Responses: ${responses} | Failed: ${failed} | HTTP Errors: ${errors}`,
|
|
502
|
+
});
|
|
503
|
+
testInfo.annotations.push({
|
|
504
|
+
type: '📊 Network Stats',
|
|
505
|
+
description: `Total Size: ${formatBytesStatic(totalSize)} | Avg Duration: ${avgDuration}ms`,
|
|
506
|
+
});
|
|
507
|
+
// Lista as URLs únicas visitadas
|
|
508
|
+
const uniquePages = [...new Set(logs.filter(l => l.pageUrl).map(l => l.pageUrl))];
|
|
509
|
+
if (uniquePages.length > 0) {
|
|
510
|
+
testInfo.annotations.push({
|
|
511
|
+
type: '🌐 Pages Visited',
|
|
512
|
+
description: uniquePages.slice(0, 5).join(' → ') + (uniquePages.length > 5 ? ` (+${uniquePages.length - 5} more)` : ''),
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
// Lista erros HTTP se houver
|
|
516
|
+
const httpErrors = logs.filter(l => l.type === 'response' && l.status && l.status >= 400);
|
|
517
|
+
if (httpErrors.length > 0) {
|
|
518
|
+
const errorSummary = httpErrors.slice(0, 3).map(e => `${e.status} ${e.method} ${e.url.substring(0, 50)}...`).join(' | ');
|
|
519
|
+
testInfo.annotations.push({
|
|
520
|
+
type: '❌ HTTP Errors',
|
|
521
|
+
description: errorSummary + (httpErrors.length > 3 ? ` (+${httpErrors.length - 3} more)` : ''),
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
// Lista requisições que falharam se houver
|
|
525
|
+
const failedRequests = logs.filter(l => l.type === 'failed');
|
|
526
|
+
if (failedRequests.length > 0) {
|
|
527
|
+
const failedSummary = failedRequests.slice(0, 3).map(f => `${f.method} ${f.url.substring(0, 50)}... (${f.errorText || 'unknown'})`).join(' | ');
|
|
528
|
+
testInfo.annotations.push({
|
|
529
|
+
type: '💥 Failed Requests',
|
|
530
|
+
description: failedSummary + (failedRequests.length > 3 ? ` (+${failedRequests.length - 3} more)` : ''),
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
// Adiciona link para os arquivos salvos
|
|
534
|
+
if (savedFiles) {
|
|
535
|
+
testInfo.annotations.push({
|
|
536
|
+
type: '📁 Network Logs Files',
|
|
537
|
+
description: `TXT: ${path.basename(savedFiles.txtPath)} | JSON: ${path.basename(savedFiles.jsonPath)}`,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
// 5. Imprime resumo no terminal para visibilidade durante execução
|
|
541
|
+
console.log('');
|
|
542
|
+
console.log('═══════════════════════════════════════════════════════════════════');
|
|
543
|
+
console.log('📡 [AutoCore] NETWORK LOGS SUMMARY');
|
|
544
|
+
console.log('═══════════════════════════════════════════════════════════════════');
|
|
545
|
+
console.log(` 📊 Total: ${logs.length} logs capturados`);
|
|
546
|
+
console.log(` ➡️ Requests: ${requests}`);
|
|
547
|
+
console.log(` ✅ Responses: ${responses}`);
|
|
548
|
+
console.log(` 💥 Failed: ${failed}`);
|
|
549
|
+
console.log(` ❌ HTTP Errors (4xx/5xx): ${errors}`);
|
|
550
|
+
console.log(` 📦 Total Size: ${formatBytesStatic(totalSize)}`);
|
|
551
|
+
console.log(` ⏱️ Avg Duration: ${avgDuration}ms`);
|
|
552
|
+
if (uniquePages.length > 0) {
|
|
553
|
+
console.log(` 🌐 Pages Visited: ${uniquePages.length}`);
|
|
554
|
+
uniquePages.slice(0, 3).forEach((page, i) => {
|
|
555
|
+
console.log(` ${i + 1}. ${page}`);
|
|
556
|
+
});
|
|
557
|
+
if (uniquePages.length > 3) {
|
|
558
|
+
console.log(` ... +${uniquePages.length - 3} more`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
if (httpErrors.length > 0) {
|
|
562
|
+
console.log(` ❌ HTTP Errors Details:`);
|
|
563
|
+
httpErrors.slice(0, 5).forEach(e => {
|
|
564
|
+
console.log(` • ${e.status} ${e.method} ${e.url.substring(0, 80)}${e.url.length > 80 ? '...' : ''}`);
|
|
565
|
+
});
|
|
566
|
+
if (httpErrors.length > 5) {
|
|
567
|
+
console.log(` ... +${httpErrors.length - 5} more errors`);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
if (failedRequests.length > 0) {
|
|
571
|
+
console.log(` 💥 Failed Requests Details:`);
|
|
572
|
+
failedRequests.slice(0, 5).forEach(f => {
|
|
573
|
+
console.log(` • ${f.method} ${f.url.substring(0, 60)}${f.url.length > 60 ? '...' : ''} (${f.errorText || 'unknown'})`);
|
|
574
|
+
});
|
|
575
|
+
if (failedRequests.length > 5) {
|
|
576
|
+
console.log(` ... +${failedRequests.length - 5} more failures`);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
if (savedFiles) {
|
|
580
|
+
console.log(` 📁 Files saved to: evidence/network-logs/`);
|
|
581
|
+
console.log(` • ${path.basename(savedFiles.txtPath)}`);
|
|
582
|
+
console.log(` • ${path.basename(savedFiles.jsonPath)}`);
|
|
583
|
+
console.log(` ℹ️ Serão movidos para playwright-report/evidence/network-logs/`);
|
|
584
|
+
}
|
|
585
|
+
console.log('═══════════════════════════════════════════════════════════════════');
|
|
586
|
+
console.log('');
|
|
587
|
+
}
|
|
588
|
+
catch (err) {
|
|
589
|
+
console.warn('⚠️ [AutoCore] Erro ao anexar logs de network:', err);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Formata bytes em formato legível (helper estático)
|
|
594
|
+
*/
|
|
595
|
+
function formatBytesStatic(bytes) {
|
|
596
|
+
if (bytes === 0)
|
|
597
|
+
return '0 B';
|
|
598
|
+
const k = 1024;
|
|
599
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
600
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
601
|
+
return `${Number.parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* 🔧 TEST UNIVERSAL - FUNCIONA PARA WEB E PROJETOS SEM BROWSER
|
|
605
|
+
*
|
|
606
|
+
* O browser SÓ é lançado quando o teste usa:
|
|
607
|
+
* - TestAnnotations.Frontend.testInfo = testInfo
|
|
608
|
+
* - TestAnnotations.Mixed.testInfo = testInfo
|
|
609
|
+
*
|
|
610
|
+
* Para testes API, SSH, Banco, Mobile: NÃO lança browser.
|
|
611
|
+
*
|
|
612
|
+
* O mecanismo é lazy: o fixture armazena uma factory no TestContext.
|
|
613
|
+
* Quando o setter de Frontend/Mixed é chamado, dispara a criação assíncrona.
|
|
614
|
+
* O primeiro await subsequente (ex.: setSystem) aguarda a conclusão.
|
|
615
|
+
*
|
|
616
|
+
* 📡 NETWORK LOGS: Captura automaticamente todos os logs de rede (quando browser ativo).
|
|
617
|
+
*
|
|
618
|
+
* @example Web (com browser)
|
|
619
|
+
* ```typescript
|
|
620
|
+
* import { test, expect } from '@silasfmartins/testhub'
|
|
621
|
+
*
|
|
622
|
+
* test('Web test', async ({}, testInfo) => {
|
|
623
|
+
* TestAnnotations.Frontend.testInfo = testInfo
|
|
624
|
+
* await TestAnnotations.setSystem('MEU_SISTEMA')
|
|
625
|
+
* await new MinhaPage().minhaAcao()
|
|
626
|
+
* })
|
|
627
|
+
* ```
|
|
628
|
+
*
|
|
629
|
+
* @example SSH/API/Banco (sem browser) - mesmo import!
|
|
630
|
+
* ```typescript
|
|
631
|
+
* import { test, expect, SSHActions } from '@silasfmartins/testhub'
|
|
632
|
+
*
|
|
633
|
+
* test('SSH test', async ({}, testInfo) => {
|
|
634
|
+
* TestAnnotations.SSH.testInfo = testInfo
|
|
635
|
+
* await SSHActions.connect({...})
|
|
636
|
+
* })
|
|
637
|
+
* ```
|
|
638
|
+
*/
|
|
639
|
+
// biome-ignore lint/suspicious/noConfusingVoidType: Playwright fixture pattern
|
|
640
|
+
export const test = base.extend({
|
|
641
|
+
// 🔧 Network Logger - captura todos os logs de rede
|
|
642
|
+
networkLogs: [async ({}, use) => {
|
|
643
|
+
const logger = new NetworkLogger();
|
|
644
|
+
await use(logger);
|
|
645
|
+
}, { scope: 'test' }],
|
|
646
|
+
// 🔧 Auto-inject: armazena factory lazy ANTES do teste, limpa DEPOIS
|
|
647
|
+
autoInjectPage: [async ({ playwright, networkLogs }, use, testInfo) => {
|
|
648
|
+
let page;
|
|
649
|
+
let context;
|
|
650
|
+
let browser;
|
|
651
|
+
// Armazena factory lazy no TestContext — só executa quando Frontend/Mixed chamar requestBrowserCreation()
|
|
652
|
+
TestContext.setPageFactory(async () => {
|
|
653
|
+
if (page)
|
|
654
|
+
return page;
|
|
655
|
+
// ⚠️ PROTEÇÃO: Se o tipo de teste foi definido como não-browser, não criar
|
|
656
|
+
if (TestContext.isNonBrowserTest()) {
|
|
657
|
+
console.log(`🚫 [AutoCore] Teste ${TestContext.getTestType()} detectado - browser não será criado`);
|
|
658
|
+
return null;
|
|
659
|
+
}
|
|
660
|
+
const browserName = testInfo.project?.use?.browserName || 'chromium';
|
|
661
|
+
console.log(`🌐 [AutoCore] Lançando browser lazy: ${browserName}`);
|
|
662
|
+
const browserType = playwright[browserName];
|
|
663
|
+
const resolution = detectScreenResolution();
|
|
664
|
+
browser = await browserType.launch();
|
|
665
|
+
context = await browser.newContext({
|
|
666
|
+
viewport: { width: resolution.width, height: resolution.height },
|
|
667
|
+
deviceScaleFactor: 1,
|
|
668
|
+
});
|
|
669
|
+
page = await context.newPage();
|
|
670
|
+
// Injeta no TestContext
|
|
671
|
+
TestContext.setPage(page);
|
|
672
|
+
console.log('🎭 [AutoCore] Page injetada automaticamente no TestContext (lazy)');
|
|
673
|
+
// Configura
|
|
674
|
+
setupNetworkLogging(page, networkLogs);
|
|
675
|
+
await setupResponsiveViewport(page);
|
|
676
|
+
return page;
|
|
677
|
+
});
|
|
678
|
+
// ---------- TEST BODY EXECUTA AQUI ----------
|
|
679
|
+
await use();
|
|
680
|
+
// ---------- CLEANUP (pós-teste) ----------
|
|
681
|
+
TestContext.setPageFactory(null);
|
|
682
|
+
if (page) {
|
|
683
|
+
// Anexa network logs ao relatório se browser foi usado
|
|
684
|
+
await attachNetworkLogs(testInfo, networkLogs);
|
|
685
|
+
await page.close().catch(() => { });
|
|
686
|
+
await context?.close().catch(() => { });
|
|
687
|
+
await browser?.close().catch(() => { });
|
|
688
|
+
}
|
|
689
|
+
else {
|
|
690
|
+
const testType = TestContext.getTestType() || 'desconhecido';
|
|
691
|
+
console.log(`🔧 [AutoCore] Teste "${testInfo.title}" executou sem browser (tipo: ${testType})`);
|
|
692
|
+
// ✅ NOVO: Anexar logs de API ao relatório mesmo sem browser
|
|
693
|
+
if (testType === 'API') {
|
|
694
|
+
try {
|
|
695
|
+
const { ApiActions } = await import('../api/ApiActions.js');
|
|
696
|
+
if (ApiActions.hasLogsForTest(testInfo.title)) {
|
|
697
|
+
const logs = ApiActions.getLogsForTest(testInfo.title);
|
|
698
|
+
if (logs.length > 0) {
|
|
699
|
+
await testInfo.attach('api-logs.txt', {
|
|
700
|
+
body: Buffer.from(logs.join('\n'), 'utf-8'),
|
|
701
|
+
contentType: 'text/plain',
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
// Anexar relatório final de API
|
|
706
|
+
await ApiActions.anexarResumoDeLogs();
|
|
707
|
+
}
|
|
708
|
+
catch {
|
|
709
|
+
// Silenciosamente ignorar se ApiActions não estiver disponível
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
// Resetar tipo de teste para o próximo
|
|
714
|
+
TestContext.resetTestType();
|
|
715
|
+
}, { auto: true }],
|
|
716
|
+
});
|
|
717
|
+
export { NetworkLogger };
|
|
718
|
+
export const expect = baseExpect;
|