@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.
Files changed (296) hide show
  1. package/.github/copilot-instructions.md +520 -0
  2. package/biome.json +37 -0
  3. package/dist/index.d.ts +45 -0
  4. package/dist/index.js +169 -0
  5. package/dist/scripts/consumer-postinstall.d.ts +15 -0
  6. package/dist/scripts/consumer-postinstall.js +785 -0
  7. package/dist/scripts/generate-docs.d.ts +16 -0
  8. package/dist/scripts/generate-docs.js +1363 -0
  9. package/dist/scripts/generate-index.d.ts +2 -0
  10. package/dist/scripts/generate-index.js +314 -0
  11. package/dist/scripts/init-api.d.ts +2 -0
  12. package/dist/scripts/init-api.js +525 -0
  13. package/dist/scripts/init-banco.d.ts +2 -0
  14. package/dist/scripts/init-banco.js +347 -0
  15. package/dist/scripts/init-frontend.d.ts +2 -0
  16. package/dist/scripts/init-frontend.js +627 -0
  17. package/dist/scripts/init-mobile.d.ts +2 -0
  18. package/dist/scripts/init-mobile.js +481 -0
  19. package/dist/scripts/init-scenarios.d.ts +2 -0
  20. package/dist/scripts/init-scenarios.js +846 -0
  21. package/dist/scripts/init-ssh.d.ts +2 -0
  22. package/dist/scripts/init-ssh.js +639 -0
  23. package/dist/scripts/package-versions.d.ts +57 -0
  24. package/dist/scripts/package-versions.js +768 -0
  25. package/dist/scripts/postinstall.d.ts +1 -0
  26. package/dist/scripts/postinstall.js +527 -0
  27. package/dist/scripts/robust-build.d.ts +7 -0
  28. package/dist/scripts/robust-build.js +88 -0
  29. package/dist/scripts/setup-local-packages.d.ts +31 -0
  30. package/dist/scripts/setup-local-packages.js +237 -0
  31. package/dist/scripts/smart-override.d.ts +2 -0
  32. package/dist/scripts/smart-override.js +1360 -0
  33. package/dist/scripts/sync-configs.d.ts +27 -0
  34. package/dist/scripts/sync-configs.js +248 -0
  35. package/dist/scripts/test-biome-parse.d.ts +5 -0
  36. package/dist/scripts/test-biome-parse.js +84 -0
  37. package/dist/scripts/ultracite-setup.d.ts +4 -0
  38. package/dist/scripts/ultracite-setup.js +310 -0
  39. package/dist/scripts/update-all-init-scripts.d.ts +2 -0
  40. package/dist/scripts/update-all-init-scripts.js +52 -0
  41. package/dist/scripts/update-biome-schema.d.ts +15 -0
  42. package/dist/scripts/update-biome-schema.js +124 -0
  43. package/dist/src/AutoCoreFacade.d.ts +145 -0
  44. package/dist/src/AutoCoreFacade.js +217 -0
  45. package/dist/src/api/ApiActions.d.ts +297 -0
  46. package/dist/src/api/ApiActions.js +1905 -0
  47. package/dist/src/api/Certificate.d.ts +60 -0
  48. package/dist/src/api/Certificate.js +79 -0
  49. package/dist/src/api/JsonResponse.d.ts +116 -0
  50. package/dist/src/api/JsonResponse.js +206 -0
  51. package/dist/src/appium/DeviceFarmViewer.d.ts +79 -0
  52. package/dist/src/appium/DeviceFarmViewer.js +1083 -0
  53. package/dist/src/appium/MobileActions.d.ts +347 -0
  54. package/dist/src/appium/MobileActions.js +1632 -0
  55. package/dist/src/appium/MobileConnection.d.ts +160 -0
  56. package/dist/src/appium/MobileConnection.js +772 -0
  57. package/dist/src/config/envLoader.d.ts +123 -0
  58. package/dist/src/config/envLoader.js +361 -0
  59. package/dist/src/config/jest-safe-setup.d.ts +19 -0
  60. package/dist/src/config/jest-safe-setup.js +369 -0
  61. package/dist/src/config/timeouts.d.ts +32 -0
  62. package/dist/src/config/timeouts.js +38 -0
  63. package/dist/src/desktop/DesktopActions.d.ts +46 -0
  64. package/dist/src/desktop/DesktopActions.js +398 -0
  65. package/dist/src/desktop/DesktopConnection.d.ts +32 -0
  66. package/dist/src/desktop/DesktopConnection.js +84 -0
  67. package/dist/src/domain/entities/TestExecution.d.ts +117 -0
  68. package/dist/src/domain/entities/TestExecution.js +150 -0
  69. package/dist/src/domain/entities/TestReport.d.ts +114 -0
  70. package/dist/src/domain/entities/TestReport.js +179 -0
  71. package/dist/src/domain/repositories/ITestRepository.d.ts +196 -0
  72. package/dist/src/domain/repositories/ITestRepository.js +14 -0
  73. package/dist/src/domain/schemas/ValidationSchemas.d.ts +159 -0
  74. package/dist/src/domain/schemas/ValidationSchemas.js +181 -0
  75. package/dist/src/functions/errors/BaseError.d.ts +78 -0
  76. package/dist/src/functions/errors/BaseError.js +245 -0
  77. package/dist/src/functions/errors/ConfigurationError.d.ts +16 -0
  78. package/dist/src/functions/errors/ConfigurationError.js +48 -0
  79. package/dist/src/functions/errors/ErrorCatalog.d.ts +148 -0
  80. package/dist/src/functions/errors/ErrorCatalog.js +157 -0
  81. package/dist/src/functions/errors/GlobalErrorHandler.d.ts +101 -0
  82. package/dist/src/functions/errors/GlobalErrorHandler.js +281 -0
  83. package/dist/src/functions/errors/IntegrationError.d.ts +17 -0
  84. package/dist/src/functions/errors/IntegrationError.js +51 -0
  85. package/dist/src/functions/errors/SecurityError.d.ts +14 -0
  86. package/dist/src/functions/errors/SecurityError.js +42 -0
  87. package/dist/src/functions/errors/SystemError.d.ts +12 -0
  88. package/dist/src/functions/errors/SystemError.js +36 -0
  89. package/dist/src/functions/errors/ValidationError.d.ts +14 -0
  90. package/dist/src/functions/errors/ValidationError.js +61 -0
  91. package/dist/src/functions/errors/index.d.ts +12 -0
  92. package/dist/src/functions/errors/index.js +13 -0
  93. package/dist/src/global-setup.d.ts +1 -0
  94. package/dist/src/global-setup.js +1037 -0
  95. package/dist/src/helpers/BancoActions.d.ts +188 -0
  96. package/dist/src/helpers/BancoActions.js +581 -0
  97. package/dist/src/helpers/EnviromentHelper.d.ts +17 -0
  98. package/dist/src/helpers/EnviromentHelper.js +66 -0
  99. package/dist/src/helpers/ParallelExecutionHelper.d.ts +183 -0
  100. package/dist/src/helpers/ParallelExecutionHelper.js +375 -0
  101. package/dist/src/helpers/SyncSignal.d.ts +15 -0
  102. package/dist/src/helpers/SyncSignal.js +44 -0
  103. package/dist/src/hubdocs/CategoryDetector.d.ts +83 -0
  104. package/dist/src/hubdocs/CategoryDetector.js +401 -0
  105. package/dist/src/hubdocs/DirectStatementInterceptor.d.ts +54 -0
  106. package/dist/src/hubdocs/DirectStatementInterceptor.js +243 -0
  107. package/dist/src/hubdocs/ExecutionTracker.d.ts +107 -0
  108. package/dist/src/hubdocs/ExecutionTracker.js +702 -0
  109. package/dist/src/hubdocs/HubDocs.d.ts +395 -0
  110. package/dist/src/hubdocs/HubDocs.js +3586 -0
  111. package/dist/src/hubdocs/StatementMethodFilter.d.ts +71 -0
  112. package/dist/src/hubdocs/StatementMethodFilter.js +618 -0
  113. package/dist/src/hubdocs/StatementTracker.d.ts +417 -0
  114. package/dist/src/hubdocs/StatementTracker.js +2419 -0
  115. package/dist/src/hubdocs/SwaggerGenerator.d.ts +59 -0
  116. package/dist/src/hubdocs/SwaggerGenerator.js +405 -0
  117. package/dist/src/hubdocs/index.d.ts +9 -0
  118. package/dist/src/hubdocs/index.js +9 -0
  119. package/dist/src/hubdocs/types.d.ts +114 -0
  120. package/dist/src/hubdocs/types.js +5 -0
  121. package/dist/src/infrastructure/DependencyContainer.d.ts +142 -0
  122. package/dist/src/infrastructure/DependencyContainer.js +250 -0
  123. package/dist/src/infrastructure/adapters/AppiumAdapter.d.ts +168 -0
  124. package/dist/src/infrastructure/adapters/AppiumAdapter.js +468 -0
  125. package/dist/src/infrastructure/adapters/OracleAdapter.d.ts +150 -0
  126. package/dist/src/infrastructure/adapters/OracleAdapter.js +388 -0
  127. package/dist/src/infrastructure/adapters/PlaywrightAdapter.d.ts +192 -0
  128. package/dist/src/infrastructure/adapters/PlaywrightAdapter.js +382 -0
  129. package/dist/src/infrastructure/adapters/SSHAdapter.d.ts +141 -0
  130. package/dist/src/infrastructure/adapters/SSHAdapter.js +428 -0
  131. package/dist/src/interfaces.d.ts +501 -0
  132. package/dist/src/interfaces.js +25 -0
  133. package/dist/src/internal/fakes/__fake-actions__.d.ts +17 -0
  134. package/dist/src/internal/fakes/__fake-actions__.js +21 -0
  135. package/dist/src/internal/fakes/__forbidden__.d.ts +10 -0
  136. package/dist/src/internal/fakes/__forbidden__.js +18 -0
  137. package/dist/src/internal/fakes/__honeypot__.d.ts +15 -0
  138. package/dist/src/internal/fakes/__honeypot__.js +24 -0
  139. package/dist/src/octane/OctaneReporter.d.ts +13 -0
  140. package/dist/src/octane/OctaneReporter.js +61 -0
  141. package/dist/src/playwright/CryptoActions.d.ts +20 -0
  142. package/dist/src/playwright/CryptoActions.js +75 -0
  143. package/dist/src/playwright/EnhancedWebActions.d.ts +7 -0
  144. package/dist/src/playwright/EnhancedWebActions.js +65 -0
  145. package/dist/src/playwright/WebActions.d.ts +1599 -0
  146. package/dist/src/playwright/WebActions.js +11788 -0
  147. package/dist/src/playwright/actions/ActionTimeline.d.ts +36 -0
  148. package/dist/src/playwright/actions/ActionTimeline.js +101 -0
  149. package/dist/src/playwright/actions/RecoveryQueue.d.ts +82 -0
  150. package/dist/src/playwright/actions/RecoveryQueue.js +130 -0
  151. package/dist/src/playwright/actions/SelectorCache.d.ts +53 -0
  152. package/dist/src/playwright/actions/SelectorCache.js +96 -0
  153. package/dist/src/playwright/actions/index.d.ts +13 -0
  154. package/dist/src/playwright/actions/index.js +14 -0
  155. package/dist/src/playwright/actions/types.d.ts +147 -0
  156. package/dist/src/playwright/actions/types.js +5 -0
  157. package/dist/src/playwright/fixtures.d.ts +112 -0
  158. package/dist/src/playwright/fixtures.js +718 -0
  159. package/dist/src/playwright/network-logs-reporter.d.ts +7 -0
  160. package/dist/src/playwright/network-logs-reporter.js +66 -0
  161. package/dist/src/playwright/registerRecoveryWrappers.d.ts +1 -0
  162. package/dist/src/playwright/registerRecoveryWrappers.js +54 -0
  163. package/dist/src/security/BuildSecurity.d.ts +12 -0
  164. package/dist/src/security/BuildSecurity.js +138 -0
  165. package/dist/src/security/EulaProtection.d.ts +70 -0
  166. package/dist/src/security/EulaProtection.js +155 -0
  167. package/dist/src/security/HoneypotManager.d.ts +46 -0
  168. package/dist/src/security/HoneypotManager.js +234 -0
  169. package/dist/src/security/KeysManager.d.ts +36 -0
  170. package/dist/src/security/KeysManager.js +158 -0
  171. package/dist/src/security/ProofOfWorkIntegration.d.ts +64 -0
  172. package/dist/src/security/ProofOfWorkIntegration.js +206 -0
  173. package/dist/src/security/SecurityValidation.d.ts +21 -0
  174. package/dist/src/security/SecurityValidation.js +163 -0
  175. package/dist/src/security/SourceMapProtection.d.ts +55 -0
  176. package/dist/src/security/SourceMapProtection.js +220 -0
  177. package/dist/src/security/protector.d.ts +1 -0
  178. package/dist/src/security/protector.js +97 -0
  179. package/dist/src/ssh/SSHActions.d.ts +262 -0
  180. package/dist/src/ssh/SSHActions.js +790 -0
  181. package/dist/src/ssh/SSHClient.d.ts +99 -0
  182. package/dist/src/ssh/SSHClient.js +409 -0
  183. package/dist/src/statements/BaseStatement.d.ts +38 -0
  184. package/dist/src/statements/BaseStatement.js +78 -0
  185. package/dist/src/testContext/AuthStateManager.d.ts +93 -0
  186. package/dist/src/testContext/AuthStateManager.js +256 -0
  187. package/dist/src/testContext/CoverageManager.d.ts +198 -0
  188. package/dist/src/testContext/CoverageManager.js +917 -0
  189. package/dist/src/testContext/TestAnnotations.d.ts +476 -0
  190. package/dist/src/testContext/TestAnnotations.js +2647 -0
  191. package/dist/src/testContext/TestContext.d.ts +138 -0
  192. package/dist/src/testContext/TestContext.js +369 -0
  193. package/dist/src/testContext/UnifiedHtmlGenerator.d.ts +7 -0
  194. package/dist/src/testContext/UnifiedHtmlGenerator.js +264 -0
  195. package/dist/src/testContext/UnifiedReportManager.d.ts +211 -0
  196. package/dist/src/testContext/UnifiedReportManager.js +1206 -0
  197. package/dist/src/testhub/DynamicConfigManager.d.ts +121 -0
  198. package/dist/src/testhub/DynamicConfigManager.js +320 -0
  199. package/dist/src/testhub/SystemsManager.d.ts +119 -0
  200. package/dist/src/testhub/SystemsManager.js +365 -0
  201. package/dist/src/testhub/TestHubClient.d.ts +335 -0
  202. package/dist/src/testhub/TestHubClient.js +1215 -0
  203. package/dist/src/testhub/TestHubReporter.d.ts +62 -0
  204. package/dist/src/testhub/TestHubReporter.js +576 -0
  205. package/dist/src/testhub/TestHubVars.d.ts +116 -0
  206. package/dist/src/testhub/TestHubVars.js +273 -0
  207. package/dist/src/utils/ActionInterceptor.d.ts +59 -0
  208. package/dist/src/utils/ActionInterceptor.js +741 -0
  209. package/dist/src/utils/ArtifactsCompressor.d.ts +43 -0
  210. package/dist/src/utils/ArtifactsCompressor.js +181 -0
  211. package/dist/src/utils/AutoLogsFinal.d.ts +47 -0
  212. package/dist/src/utils/AutoLogsFinal.js +148 -0
  213. package/dist/src/utils/CodeGenSession.d.ts +114 -0
  214. package/dist/src/utils/CodeGenSession.js +264 -0
  215. package/dist/src/utils/ConfigLogger.d.ts +133 -0
  216. package/dist/src/utils/ConfigLogger.js +611 -0
  217. package/dist/src/utils/CustomReporter.d.ts +22 -0
  218. package/dist/src/utils/CustomReporter.js +352 -0
  219. package/dist/src/utils/DataStore.d.ts +171 -0
  220. package/dist/src/utils/DataStore.js +484 -0
  221. package/dist/src/utils/DatabaseInterceptor.d.ts +19 -0
  222. package/dist/src/utils/DatabaseInterceptor.js +295 -0
  223. package/dist/src/utils/DateHelper.d.ts +16 -0
  224. package/dist/src/utils/DateHelper.js +120 -0
  225. package/dist/src/utils/DateValidator.d.ts +4 -0
  226. package/dist/src/utils/DateValidator.js +51 -0
  227. package/dist/src/utils/DocumentGenerator.d.ts +35 -0
  228. package/dist/src/utils/DocumentGenerator.js +129 -0
  229. package/dist/src/utils/EvidenceCapture.d.ts +90 -0
  230. package/dist/src/utils/EvidenceCapture.js +600 -0
  231. package/dist/src/utils/EvidenceReportGenerator.d.ts +70 -0
  232. package/dist/src/utils/EvidenceReportGenerator.js +799 -0
  233. package/dist/src/utils/FrameManagementUtil.d.ts +42 -0
  234. package/dist/src/utils/FrameManagementUtil.js +75 -0
  235. package/dist/src/utils/GlobalStatementsInterceptor.d.ts +1 -0
  236. package/dist/src/utils/GlobalStatementsInterceptor.js +1 -0
  237. package/dist/src/utils/HTMLTemplate.d.ts +1 -0
  238. package/dist/src/utils/HTMLTemplate.js +1034 -0
  239. package/dist/src/utils/InterceptacaoMagica.d.ts +23 -0
  240. package/dist/src/utils/InterceptacaoMagica.js +365 -0
  241. package/dist/src/utils/LogSanitizer.d.ts +35 -0
  242. package/dist/src/utils/LogSanitizer.js +110 -0
  243. package/dist/src/utils/Logger.d.ts +65 -0
  244. package/dist/src/utils/Logger.js +284 -0
  245. package/dist/src/utils/McpLocalClient.d.ts +141 -0
  246. package/dist/src/utils/McpLocalClient.js +871 -0
  247. package/dist/src/utils/PDFEvidenceGenerator.d.ts +20 -0
  248. package/dist/src/utils/PDFEvidenceGenerator.js +156 -0
  249. package/dist/src/utils/SpecFileAnalyzer.d.ts +35 -0
  250. package/dist/src/utils/SpecFileAnalyzer.js +209 -0
  251. package/dist/src/utils/StatementInterceptor.d.ts +18 -0
  252. package/dist/src/utils/StatementInterceptor.js +87 -0
  253. package/dist/src/utils/StatementLogger.d.ts +33 -0
  254. package/dist/src/utils/StatementLogger.js +113 -0
  255. package/dist/src/utils/StatementsInterceptor.d.ts +1 -0
  256. package/dist/src/utils/StatementsInterceptor.js +1 -0
  257. package/dist/src/utils/TeamsFlushHook.d.ts +17 -0
  258. package/dist/src/utils/TeamsFlushHook.js +168 -0
  259. package/dist/src/utils/TerminalLogCapture.d.ts +158 -0
  260. package/dist/src/utils/TerminalLogCapture.js +531 -0
  261. package/dist/src/utils/TestMethodLogger.d.ts +70 -0
  262. package/dist/src/utils/TestMethodLogger.js +95 -0
  263. package/dist/src/utils/UnifiedTeardown.d.ts +4 -0
  264. package/dist/src/utils/UnifiedTeardown.js +400 -0
  265. package/dist/src/utils/XPathCatalog.d.ts +152 -0
  266. package/dist/src/utils/XPathCatalog.js +350 -0
  267. package/dist/src/utils/generators.d.ts +90 -0
  268. package/dist/src/utils/generators.js +167 -0
  269. package/dist/src/utils/testRecovery/ResilientPlaywright.d.ts +152 -0
  270. package/dist/src/utils/testRecovery/ResilientPlaywright.js +715 -0
  271. package/dist/src/utils/testRecovery/TestRecoveryClient.d.ts +801 -0
  272. package/dist/src/utils/testRecovery/TestRecoveryClient.js +1415 -0
  273. package/dist/src/utils/testRecovery/autoFixCode.d.ts +65 -0
  274. package/dist/src/utils/testRecovery/autoFixCode.js +32 -0
  275. package/dist/vitest.config.d.ts +2 -0
  276. package/dist/vitest.config.js +59 -0
  277. package/dist/wdio.conf.d.ts +1 -0
  278. package/dist/wdio.conf.js +420 -0
  279. package/package.json +137 -0
  280. package/protect-loader.mjs +643 -0
  281. package/scripts/consumer-postinstall.ts +975 -0
  282. package/scripts/generate-index.ts +343 -0
  283. package/scripts/init-api.ts +613 -0
  284. package/scripts/init-banco.ts +437 -0
  285. package/scripts/init-frontend.ts +727 -0
  286. package/scripts/init-mobile.ts +558 -0
  287. package/scripts/init-scenarios.ts +925 -0
  288. package/scripts/init-ssh.ts +734 -0
  289. package/scripts/package-versions.ts +978 -0
  290. package/scripts/postinstall.ts +605 -0
  291. package/scripts/smart-override.ts +1675 -0
  292. package/scripts/sync-configs.ts +302 -0
  293. package/scripts/ultracite-setup.ts +370 -0
  294. package/src/types/globals.d.ts +48 -0
  295. package/tsconfig.json +29 -0
  296. package/types/autocore-sync-signal.d.ts +10 -0
@@ -0,0 +1,799 @@
1
+ /**
2
+ * 📸 Organizador de Evidências do AutoCore
3
+ * @description Coleta e organiza screenshots já gerados pelos projetos consumidores (Web/Mobile)
4
+ * @author TestHUB Team
5
+ * @version 1.0.0
6
+ *
7
+ * ⚠️ IMPORTANTE: Este projeto AutoCore NÃO gera evidências
8
+ * Ele apenas coleta e organiza evidências dos projetos consumidores:
9
+ * - Web: C:\Users\{user}\PlayWright\test-simplifique-e2e\test-results\web-screenshots\
10
+ * - Mobile: C:\Users\{user}\PlayWright\mobile\test-results\mobile-screenshots\
11
+ */
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+ export class EvidenceReportGenerator {
15
+ /**
16
+ * 🎯 Detectar o tipo do projeto atual baseado nas pastas de evidências
17
+ *
18
+ * ℹ️ AutoCore trabalha no projeto atual e verifica se há:
19
+ * - Web: test-results/web-screenshots/
20
+ * - Mobile: test-results/mobile-screenshots/
21
+ */
22
+ static detectCurrentProjectType() {
23
+ const currentDir = process.cwd();
24
+ const testResultsPath = path.join(currentDir, 'test-results');
25
+ if (!fs.existsSync(testResultsPath)) {
26
+ console.log('⚠️ Pasta test-results não encontrada no projeto atual');
27
+ return null;
28
+ }
29
+ // Verificar se é projeto Mobile
30
+ const mobileScreenshotsPath = path.join(testResultsPath, 'mobile-screenshots');
31
+ if (fs.existsSync(mobileScreenshotsPath)) {
32
+ console.log(`🎯 Projeto Mobile detectado: ${currentDir}`);
33
+ return {
34
+ path: currentDir,
35
+ type: 'Mobile',
36
+ screenshotFolder: 'mobile-screenshots',
37
+ };
38
+ }
39
+ // Verificar se é projeto Web
40
+ const webScreenshotsPath = path.join(testResultsPath, 'web-screenshots');
41
+ if (fs.existsSync(webScreenshotsPath)) {
42
+ console.log(`🎯 Projeto Web detectado: ${currentDir}`);
43
+ return {
44
+ path: currentDir,
45
+ type: 'Web',
46
+ screenshotFolder: 'web-screenshots',
47
+ };
48
+ }
49
+ console.log('⚠️ Nenhuma pasta de screenshots específica encontrada (web-screenshots ou mobile-screenshots)');
50
+ return null;
51
+ }
52
+ /**
53
+ * 🔍 Coleta screenshots em uma pasta específica do projeto consumidor
54
+ */
55
+ static async findScreenshotsInDirectory(directoryPath, projectType) {
56
+ const screenshots = [];
57
+ try {
58
+ if (!fs.existsSync(directoryPath)) {
59
+ return screenshots;
60
+ }
61
+ const files = fs.readdirSync(directoryPath, { withFileTypes: true });
62
+ for (const file of files) {
63
+ const fullPath = path.join(directoryPath, file.name);
64
+ if (file.isDirectory()) {
65
+ // Busca recursiva em subpastas
66
+ const subScreenshots = await EvidenceReportGenerator.findScreenshotsInDirectory(fullPath, projectType);
67
+ screenshots.push(...subScreenshots);
68
+ }
69
+ else if (file.isFile() && /\.(png|jpg|jpeg)$/i.test(file.name)) {
70
+ const stats = fs.statSync(fullPath);
71
+ const testName = EvidenceReportGenerator.extractTestName(fullPath, projectType);
72
+ // 🔧 CORRIGIDO: Caminho relativo à pasta playwright-report
73
+ const playwrightReportDir = path.join(process.cwd(), 'playwright-report');
74
+ const relativePath = path
75
+ .relative(playwrightReportDir, fullPath)
76
+ .replace(/\\/g, '/');
77
+ const base64 = EvidenceReportGenerator.convertImageToBase64(fullPath);
78
+ screenshots.push({
79
+ fileName: file.name,
80
+ fullPath,
81
+ relativePath,
82
+ testName,
83
+ timestamp: stats.mtime,
84
+ size: stats.size,
85
+ base64,
86
+ });
87
+ }
88
+ }
89
+ }
90
+ catch (error) {
91
+ console.warn(`⚠️ Erro ao escanear pasta ${directoryPath}:`, error);
92
+ }
93
+ return screenshots;
94
+ }
95
+ /**
96
+ * ✅ Gera relatório de evidências procurando screenshots nas pastas padrão
97
+ */
98
+ static async generateEvidenceReport() {
99
+ try {
100
+ // 🎯 DETECTAR TIPO DO PROJETO ATUAL
101
+ const currentProject = EvidenceReportGenerator.detectCurrentProjectType();
102
+ if (!currentProject) {
103
+ await EvidenceReportGenerator.generateEmptyReport();
104
+ return;
105
+ }
106
+ // 🔍 PROCURAR SCREENSHOTS NO PROJETO ATUAL
107
+ let allScreenshots = [];
108
+ console.log(`🔍 Coletando evidências de ${currentProject.type}: ${currentProject.path}`);
109
+ const testResultsPath = path.join(currentProject.path, 'test-results');
110
+ const screenshotPath = path.join(testResultsPath, currentProject.screenshotFolder);
111
+ console.log(`📂 Verificando: ${screenshotPath}`);
112
+ if (fs.existsSync(screenshotPath)) {
113
+ const projectScreenshots = await EvidenceReportGenerator.findScreenshotsInDirectory(screenshotPath, currentProject.type);
114
+ allScreenshots = allScreenshots.concat(projectScreenshots);
115
+ console.log(`📸 Coletados ${projectScreenshots.length} screenshot(s) de ${currentProject.type}`);
116
+ }
117
+ else {
118
+ console.log(`⚠️ Pasta de evidências não encontrada: ${screenshotPath}`);
119
+ }
120
+ if (allScreenshots.length === 0) {
121
+ await EvidenceReportGenerator.generateEmptyReport();
122
+ return;
123
+ }
124
+ // Gerar relatório HTML
125
+ const html = EvidenceReportGenerator.generateHTML(allScreenshots);
126
+ // ✅ CORRIGIDO: Criar pasta playwright-report se não existir
127
+ const playwrightReportDir = path.join(process.cwd(), 'playwright-report');
128
+ if (!fs.existsSync(playwrightReportDir)) {
129
+ fs.mkdirSync(playwrightReportDir, { recursive: true });
130
+ }
131
+ // ✅ CORRIGIDO: Salvar relatório APENAS em playwright-report
132
+ const reportPath = path.join(playwrightReportDir, 'relatorio-evidencias.html');
133
+ fs.writeFileSync(reportPath, html, 'utf-8');
134
+ }
135
+ catch (error) {
136
+ console.warn('⚠️ [EvidenceReportGenerator] Erro ao gerar relatório:', error);
137
+ }
138
+ }
139
+ /**
140
+ * ✅ Procura screenshots em diretórios específicos
141
+ */
142
+ static async findScreenshots(directories) {
143
+ const screenshots = [];
144
+ for (const dir of directories) {
145
+ if (!fs.existsSync(dir))
146
+ continue;
147
+ try {
148
+ const files = EvidenceReportGenerator.findFilesRecursively(dir, /\.(png|jpg|jpeg)$/i);
149
+ for (const filePath of files) {
150
+ try {
151
+ const stats = fs.statSync(filePath);
152
+ const fileName = path.basename(filePath);
153
+ // 🔧 CORRIGIDO: Caminho relativo à pasta playwright-report
154
+ const playwrightReportDir = path.join(process.cwd(), 'playwright-report');
155
+ const relativePath = path
156
+ .relative(playwrightReportDir, filePath)
157
+ .replace(/\\/g, '/');
158
+ // Extrair nome do teste do caminho ou nome do arquivo
159
+ const testName = EvidenceReportGenerator.extractTestName(filePath, 'Legacy');
160
+ // 🆕 Converter imagem para base64
161
+ const base64 = EvidenceReportGenerator.convertImageToBase64(filePath);
162
+ screenshots.push({
163
+ fileName,
164
+ fullPath: filePath,
165
+ relativePath,
166
+ testName,
167
+ timestamp: stats.mtime,
168
+ size: stats.size,
169
+ base64, // 🆕 Incluir base64 no objeto
170
+ });
171
+ }
172
+ catch (fileError) {
173
+ // Ignorar erros de arquivos individuais
174
+ }
175
+ }
176
+ }
177
+ catch (dirError) {
178
+ // Ignorar erros de diretórios
179
+ }
180
+ }
181
+ // Ordenar por timestamp (mais recentes primeiro)
182
+ screenshots.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
183
+ return screenshots;
184
+ }
185
+ /**
186
+ * 🆕 Converte imagem para base64
187
+ */
188
+ static convertImageToBase64(imagePath) {
189
+ try {
190
+ const imageBuffer = fs.readFileSync(imagePath);
191
+ const ext = path.extname(imagePath).toLowerCase();
192
+ let mimeType = 'image/png';
193
+ if (ext === '.jpg' || ext === '.jpeg') {
194
+ mimeType = 'image/jpeg';
195
+ }
196
+ else if (ext === '.gif') {
197
+ mimeType = 'image/gif';
198
+ }
199
+ else if (ext === '.webp') {
200
+ mimeType = 'image/webp';
201
+ }
202
+ const base64 = imageBuffer.toString('base64');
203
+ return `data:${mimeType};base64,${base64}`;
204
+ }
205
+ catch (error) {
206
+ console.warn(`⚠️ Erro ao converter imagem para base64: ${imagePath}`, error);
207
+ return ''; // Retornar string vazia em caso de erro
208
+ }
209
+ }
210
+ /**
211
+ * ✅ Encontra arquivos recursivamente
212
+ */
213
+ static findFilesRecursively(dir, pattern) {
214
+ const files = [];
215
+ try {
216
+ const items = fs.readdirSync(dir);
217
+ for (const item of items) {
218
+ const itemPath = path.join(dir, item);
219
+ const stats = fs.statSync(itemPath);
220
+ if (stats.isDirectory()) {
221
+ files.push(...EvidenceReportGenerator.findFilesRecursively(itemPath, pattern));
222
+ }
223
+ else if (pattern.test(item)) {
224
+ files.push(itemPath);
225
+ }
226
+ }
227
+ }
228
+ catch (error) {
229
+ // Ignorar erros de acesso
230
+ }
231
+ return files;
232
+ }
233
+ /**
234
+ * ✅ Extrai nome do teste do caminho ou nome do arquivo
235
+ */
236
+ static extractTestName(fullPath, projectType) {
237
+ // Tentar extrair do caminho
238
+ const pathParts = fullPath.split(/[/\\]/);
239
+ const fileName = path.basename(fullPath);
240
+ // 🔍 Para projetos Web/Mobile, procurar padrões específicos
241
+ if (projectType === 'Web' || projectType === 'Mobile') {
242
+ // Procurar por partes que parecem nomes de teste no caminho
243
+ for (let i = pathParts.length - 1; i >= 0; i--) {
244
+ const part = pathParts[i];
245
+ if (part.includes('test') ||
246
+ part.includes('spec') ||
247
+ part.includes('CT') ||
248
+ part.includes('-chromium') ||
249
+ part.includes('-webkit')) {
250
+ return part
251
+ .replace(/-chromium|-webkit|-firefox/gi, '')
252
+ .replace(/^\d+/, '')
253
+ .trim();
254
+ }
255
+ }
256
+ }
257
+ // Fallback: usar nome do arquivo sem extensão
258
+ return (fileName
259
+ .replace(/\.(png|jpg|jpeg)$/i, '')
260
+ .replace(/-\d+$/, '')
261
+ .trim() || 'Teste não identificado');
262
+ }
263
+ /**
264
+ * ✅ Gera HTML do relatório com recursos avançados (igual HTMLTemplate)
265
+ */
266
+ static generateHTML(screenshots) {
267
+ const groupedByTest = EvidenceReportGenerator.groupScreenshotsByTest(screenshots);
268
+ const totalTests = Object.keys(groupedByTest).length;
269
+ const totalScreenshots = screenshots.length;
270
+ const totalSize = screenshots.reduce((sum, s) => sum + s.size, 0);
271
+ // Estatísticas por tipo de projeto
272
+ const projectStats = {
273
+ Web: screenshots.filter((s) => s.fullPath.includes('web-screenshots'))
274
+ .length,
275
+ Mobile: screenshots.filter((s) => s.fullPath.includes('mobile-screenshots')).length,
276
+ Other: screenshots.filter((s) => !(s.fullPath.includes('web-screenshots') ||
277
+ s.fullPath.includes('mobile-screenshots'))).length,
278
+ };
279
+ return `
280
+ <!DOCTYPE html>
281
+ <html lang="pt-BR">
282
+ <head>
283
+ <meta charset="UTF-8" />
284
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
285
+ <title>📸 Relatório de Evidências - TestHUB</title>
286
+ <script src="https://cdn.tailwindcss.com"></script>
287
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
288
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
289
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
290
+ <script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
291
+ <style>
292
+ body { font-family: 'Inter', sans-serif; background-color: #0f172a; color: #f1f5f9; min-height: 100vh; }
293
+ .light-mode { background-color: #f9fafb !important; color: #222 !important; }
294
+ .card { background: #1e293b; border-radius: 1rem; padding: 1.5rem; box-shadow: 0 2px 8px #0002; }
295
+ .light-mode .card { background: #fff; color: #222; }
296
+ .btn { background: #334155; color: #fff; border: none; border-radius: 8px; padding: 0.5rem 1.2rem; font-weight: 600; cursor: pointer; margin-bottom: 0.25rem; transition: background 0.2s; }
297
+ .btn:hover { background: #475569; }
298
+ .light-mode .btn { background: #e2e8f0; color: #222; }
299
+ .chart-card { background: #1e293b; border-radius: 1rem; padding: 1rem; margin-bottom: 1.5rem; }
300
+ .light-mode .chart-card { background: #fff; }
301
+ .header-bar { background: #1e293b; padding: 1rem 2rem; border-radius: 1rem 1rem 0 0; margin-bottom: 1.5rem; display: flex; align-items: center; justify-content: space-between; }
302
+ .header-bar .header-title { font-size: 2rem; font-weight: 700; color: #fbbf24; }
303
+ .header-bar .header-meta { font-size: 1rem; color: #f1f5f9; }
304
+ .light-mode .header-bar { background: #f1f5f9; color: #222; }
305
+ .light-mode .header-title { color: #b45309; }
306
+ .light-mode .header-meta { color: #222 !important; }
307
+ .footer-bar { background: #1e293b; color: #fbbf24; padding: 1rem 2rem; border-radius: 0 0 1rem 1rem; margin-top: 2rem; text-align: center; font-size: 1rem; }
308
+ .light-mode .footer-bar { background: #f1f5f9; color: #b45309; }
309
+ .footer-bar a { color: #fbbf24; text-decoration: underline; }
310
+ .light-mode .footer-bar a { color: #b45309; }
311
+ .evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; }
312
+ .evidence-item { background: #334155; border-radius: 8px; overflow: hidden; transition: transform 0.2s; }
313
+ .evidence-item:hover { transform: scale(1.02); }
314
+ .light-mode .evidence-item { background: #f1f5f9; }
315
+ .evidence-img { width: 100%; height: 200px; object-fit: contain; background: #475569; cursor: pointer; }
316
+ .light-mode .evidence-img { background: #e2e8f0; }
317
+ .evidence-info { padding: 1rem; }
318
+ .evidence-title { font-weight: 600; margin-bottom: 0.5rem; }
319
+ .evidence-meta { font-size: 0.875rem; color: #94a3b8; }
320
+ .light-mode .evidence-meta { color: #64748b; }
321
+ .test-section { margin-bottom: 2rem; }
322
+ .test-header { background: #334155; padding: 1rem; border-radius: 8px 8px 0 0; cursor: pointer; display: flex; justify-content: between; align-items: center; }
323
+ .light-mode .test-header { background: #e2e8f0; }
324
+ .test-content { background: #1e293b; padding: 1rem; border-radius: 0 0 8px 8px; }
325
+ .light-mode .test-content { background: #f8fafc; }
326
+ .modal { display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.9); }
327
+ .modal-content { margin: auto; display: block; width: 90%; max-width: 1200px; max-height: 90%; }
328
+ .modal img { width: 100%; height: auto; }
329
+ .close { position: absolute; top: 15px; right: 35px; color: #f1f1f1; font-size: 40px; font-weight: bold; cursor: pointer; }
330
+ .close:hover { color: #bbb; }
331
+ @media (max-width: 768px) {
332
+ .grid-cols-2, .grid-cols-3 { grid-template-columns: 1fr !important; }
333
+ .header-bar { flex-direction: column; text-align: center; }
334
+ .header-meta { margin-top: 0.5rem; }
335
+ }
336
+ </style>
337
+ </head>
338
+ <body id="body-root">
339
+ <div class="header-bar">
340
+ <div class="header-title">📸 Relatório de Evidências</div>
341
+ <div class="header-meta">
342
+ <span>Total: <strong>${totalScreenshots}</strong></span> |
343
+ <span>Testes: <strong>${totalTests}</strong></span> |
344
+ <span>Tamanho: <strong>${EvidenceReportGenerator.formatFileSize(totalSize)}</strong></span> |
345
+ <span>Data: <strong>${new Date().toLocaleString('pt-BR')}</strong></span>
346
+ </div>
347
+ </div>
348
+
349
+ <div class="flex gap-2 mb-6 justify-end mr-6">
350
+ <button id="toggle-theme" class="btn" type="button" title="Alternar tema">🌙 Tema</button>
351
+ <button id="export-pdf" class="btn" type="button" title="Exportar PDF">📄 PDF</button>
352
+ <button id="export-csv" class="btn" type="button" title="Exportar CSV">📑 CSV</button>
353
+ <button id="export-json" class="btn" type="button" title="Exportar JSON">🗒️ JSON</button>
354
+ <button id="export-xlsx" class="btn" type="button" title="Exportar XLSX">📊 XLSX</button>
355
+ </div>
356
+
357
+ <div id="report-root" class="max-w-7xl mx-auto p-6" style="background:inherit;">
358
+ <h1 class="text-4xl font-bold mb-2">📸 Evidências Capturadas</h1>
359
+ <p class="text-slate-400 mb-6">Screenshots organizados por teste executado</p>
360
+
361
+ <!-- Estatísticas -->
362
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
363
+ <div class="card flex flex-col items-center">
364
+ <span class="text-2xl font-bold text-blue-400">${totalTests}</span>
365
+ <span class="text-slate-400">Testes</span>
366
+ </div>
367
+ <div class="card flex flex-col items-center">
368
+ <span class="text-2xl font-bold text-green-400">${totalScreenshots}</span>
369
+ <span class="text-green-400">Screenshots</span>
370
+ </div>
371
+ <div class="card flex flex-col items-center">
372
+ <span class="text-2xl font-bold text-purple-400">${projectStats.Web}</span>
373
+ <span class="text-purple-400">Web</span>
374
+ </div>
375
+ <div class="card flex flex-col items-center">
376
+ <span class="text-2xl font-bold text-orange-400">${projectStats.Mobile}</span>
377
+ <span class="text-orange-400">Mobile</span>
378
+ </div>
379
+ </div>
380
+
381
+ <!-- Gráficos -->
382
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
383
+ <div class="chart-card">
384
+ <h3 class="text-lg font-bold mb-4 text-center">📊 Screenshots por Tipo</h3>
385
+ <canvas id="chart-tipos" height="200"></canvas>
386
+ </div>
387
+ <div class="chart-card">
388
+ <h3 class="text-lg font-bold mb-4 text-center">🗓️ Screenshots por Teste</h3>
389
+ <canvas id="chart-testes" height="200"></canvas>
390
+ </div>
391
+ </div>
392
+
393
+ <!-- Evidências por Teste -->
394
+ ${Object.entries(groupedByTest)
395
+ .map(([testName, testScreenshots], index) => `
396
+ <div class="test-section">
397
+ <div class="test-header" onclick="toggleTestSection('test-${index}')">
398
+ <div class="flex items-center gap-3">
399
+ <span class="text-2xl">🧪</span>
400
+ <div>
401
+ <h3 class="text-lg font-bold">${testName}</h3>
402
+ <p class="text-sm text-slate-400">${testScreenshots.length} screenshot(s) • Última captura: ${testScreenshots[0].timestamp.toLocaleString('pt-BR')}</p>
403
+ </div>
404
+ </div>
405
+ <span class="text-2xl transform transition-transform" id="arrow-test-${index}">▼</span>
406
+ </div>
407
+ <div class="test-content" id="test-${index}">
408
+ <div class="evidence-grid">
409
+ ${testScreenshots
410
+ .map((screenshot) => `
411
+ <div class="evidence-item">
412
+ <img src="${screenshot.base64 || screenshot.relativePath}" alt="${screenshot.fileName}" class="evidence-img" onclick="openModal('${screenshot.base64 || screenshot.relativePath}', '${screenshot.fileName}')" loading="lazy">
413
+ <div class="evidence-info">
414
+ <div class="evidence-title">${screenshot.fileName}</div>
415
+ <div class="evidence-meta">
416
+ ${EvidenceReportGenerator.formatFileSize(screenshot.size)} •
417
+ ${screenshot.timestamp.toLocaleString('pt-BR')}
418
+ </div>
419
+ </div>
420
+ </div>
421
+ `)
422
+ .join('')}
423
+ </div>
424
+ </div>
425
+ </div>
426
+ `)
427
+ .join('')}
428
+ </div>
429
+
430
+ <!-- Modal para visualizar imagens -->
431
+ <div id="imageModal" class="modal">
432
+ <span class="close" onclick="closeModal()">&times;</span>
433
+ <div class="modal-content">
434
+ <img id="modalImg" src="" alt="">
435
+ </div>
436
+ </div>
437
+
438
+ <div class="footer-bar">
439
+ Desenvolvido por <strong>TestHUB AutoCore</strong> &mdash; Sistema de Organização de Evidências.<br>
440
+ <span>Relatório gerado automaticamente • Screenshots coletados dos projetos consumidores</span>
441
+ </div>
442
+
443
+ <script>
444
+ // Toggle de tema
445
+ document.getElementById('toggle-theme').onclick = () => {
446
+ document.getElementById('body-root').classList.toggle('light-mode')
447
+ document.querySelector('.header-bar').classList.toggle('light-mode')
448
+ document.querySelector('.footer-bar').classList.toggle('light-mode')
449
+ document.querySelectorAll('.card').forEach(card => card.classList.toggle('light-mode'))
450
+ document.querySelectorAll('.chart-card').forEach(card => card.classList.toggle('light-mode'))
451
+ document.querySelectorAll('.evidence-item').forEach(item => item.classList.toggle('light-mode'))
452
+ document.querySelectorAll('.test-header').forEach(header => header.classList.toggle('light-mode'))
453
+ document.querySelectorAll('.test-content').forEach(content => content.classList.toggle('light-mode'))
454
+ document.querySelectorAll('.evidence-img').forEach(img => img.classList.toggle('light-mode'))
455
+ document.querySelectorAll('.evidence-meta').forEach(meta => meta.classList.toggle('light-mode'))
456
+
457
+ const meta = document.querySelector('.header-meta')
458
+ if (document.getElementById('body-root').classList.contains('light-mode')) {
459
+ meta && (meta.style.color = '#222')
460
+ } else {
461
+ meta && (meta.style.color = '#f1f5f9')
462
+ }
463
+ }
464
+
465
+ // Toggle de seções de teste
466
+ function toggleTestSection(testId) {
467
+ const testContent = document.getElementById(testId)
468
+ const arrow = document.getElementById('arrow-' + testId)
469
+
470
+ if (testContent.style.display === 'none') {
471
+ testContent.style.display = 'block'
472
+ arrow.style.transform = 'rotate(180deg)'
473
+ } else {
474
+ testContent.style.display = 'none'
475
+ arrow.style.transform = 'rotate(0deg)'
476
+ }
477
+ }
478
+
479
+ // Modal de imagens
480
+ function openModal(src, alt) {
481
+ document.getElementById('imageModal').style.display = 'block'
482
+ document.getElementById('modalImg').src = src
483
+ document.getElementById('modalImg').alt = alt
484
+ }
485
+
486
+ function closeModal() {
487
+ document.getElementById('imageModal').style.display = 'none'
488
+ }
489
+
490
+ // Fechar modal ao clicar fora da imagem
491
+ window.onclick = function(event) {
492
+ const modal = document.getElementById('imageModal')
493
+ if (event.target == modal) {
494
+ closeModal()
495
+ }
496
+ }
497
+
498
+ // Dados para exportação
499
+ function getEvidenceData() {
500
+ const data = []
501
+ ${Object.entries(groupedByTest)
502
+ .map(([testName, testScreenshots]) => `
503
+ ${testScreenshots
504
+ .map((screenshot) => `
505
+ data.push({
506
+ teste: '${testName}',
507
+ arquivo: '${screenshot.fileName}',
508
+ tamanho: '${EvidenceReportGenerator.formatFileSize(screenshot.size)}',
509
+ timestamp: '${screenshot.timestamp.toLocaleString('pt-BR')}',
510
+ caminho: '${screenshot.relativePath}'
511
+ })
512
+ `)
513
+ .join('')}
514
+ `)
515
+ .join('')}
516
+ return data
517
+ }
518
+
519
+ // Exportação PDF
520
+ document.getElementById('export-pdf').onclick = async () => {
521
+ console.log('🔵 [PDF] Botão clicado - Iniciando geração do PDF de evidências...')
522
+
523
+ const element = document.getElementById('report-root')
524
+ if (!element) {
525
+ console.error('❌ [PDF] Elemento report-root não encontrado!')
526
+ alert('❌ Erro: Elemento do relatório não encontrado!')
527
+ return
528
+ }
529
+
530
+ const hasImages = element.querySelectorAll('img').length > 0
531
+ if (hasImages) {
532
+ console.log('📸 [PDF] Imagens detectadas - usando base64 para garantir renderização offline')
533
+ }
534
+
535
+ // Verificar se jsPDF está carregado
536
+ if (typeof jspdf === 'undefined') {
537
+ console.error('❌ [PDF] jsPDF não está carregado!')
538
+ alert('❌ Erro: Biblioteca jsPDF não carregada. Recarregue a página.')
539
+ return
540
+ }
541
+ console.log('✅ [PDF] jsPDF carregado com sucesso')
542
+
543
+ // 🆕 EXPANDIR TODAS AS SEÇÕES antes de gerar PDF
544
+ const allSections = document.querySelectorAll('[id^="test-"]')
545
+ const allArrows = document.querySelectorAll('[id^="arrow-test-"]')
546
+ const originalStates = []
547
+
548
+ allSections.forEach((section, index) => {
549
+ originalStates.push(section.style.display)
550
+ section.style.display = 'block'
551
+ if (allArrows[index]) {
552
+ allArrows[index].style.transform = 'rotate(180deg)'
553
+ }
554
+ })
555
+
556
+ // ✅ Aguardar imagens carregarem (já estão em base64)
557
+ const images = element.querySelectorAll('img')
558
+ console.log('📸 Total de imagens encontradas:', images.length)
559
+
560
+ await Promise.all(Array.from(images).map(img => {
561
+ if (img.complete) return Promise.resolve()
562
+ return new Promise(resolve => {
563
+ img.onload = resolve
564
+ img.onerror = resolve
565
+ setTimeout(resolve, 2000)
566
+ })
567
+ }))
568
+
569
+ console.log('✅ Imagens carregadas (base64 embutido)')
570
+ await new Promise(resolve => setTimeout(resolve, 300))
571
+
572
+ // Preparar elemento para PDF
573
+ const originalBg = element.style.background
574
+ const originalColor = element.style.color
575
+ element.style.background = '#fff'
576
+ element.style.color = '#222'
577
+
578
+ // Reduzir gráficos para PDF
579
+ document.querySelectorAll('.chart-card canvas').forEach((canvas) => {
580
+ canvas.style.width = '300px'
581
+ canvas.style.height = '150px'
582
+ canvas.style.transform = 'scale(0.85)'
583
+ })
584
+
585
+ console.log('📄 [PDF] Iniciando geração do PDF...')
586
+
587
+ try {
588
+ // Usar html2canvas para capturar o conteúdo como imagem
589
+ const canvas = await html2canvas(element, {
590
+ scale: 2,
591
+ useCORS: false,
592
+ allowTaint: true,
593
+ backgroundColor: '#fff',
594
+ logging: false,
595
+ imageTimeout: 0
596
+ })
597
+
598
+ // Criar PDF com jsPDF
599
+ const { jsPDF } = jspdf
600
+ const pdf = new jsPDF({
601
+ orientation: 'portrait',
602
+ unit: 'mm',
603
+ format: 'a4'
604
+ })
605
+
606
+ const imgData = canvas.toDataURL('image/jpeg', 0.98)
607
+ const imgWidth = 210 // A4 width in mm
608
+ const pageHeight = 297 // A4 height in mm
609
+ const imgHeight = (canvas.height * imgWidth) / canvas.width
610
+ let heightLeft = imgHeight
611
+ let position = 0
612
+
613
+ // Adicionar primeira página
614
+ pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)
615
+ heightLeft -= pageHeight
616
+
617
+ // Adicionar páginas extras se necessário
618
+ while (heightLeft > 0) {
619
+ position = heightLeft - imgHeight
620
+ pdf.addPage()
621
+ pdf.addImage(imgData, 'JPEG', 0, position, imgWidth, imgHeight)
622
+ heightLeft -= pageHeight
623
+ }
624
+
625
+ // Salvar PDF
626
+ pdf.save('relatorio-evidencias.pdf')
627
+
628
+ console.log('✅ PDF gerado com sucesso!')
629
+ alert('✅ PDF gerado com sucesso! Verifique sua pasta de Downloads.')
630
+ } catch (error) {
631
+ console.error('❌ Erro ao gerar PDF:', error)
632
+ alert('❌ Erro ao gerar PDF: ' + (error.message || error))
633
+ } finally {
634
+ // Restaurar estilos e estados originais
635
+ element.style.background = originalBg
636
+ element.style.color = originalColor
637
+
638
+ document.querySelectorAll('.chart-card canvas').forEach((canvas) => {
639
+ canvas.style.width = ''
640
+ canvas.style.height = ''
641
+ canvas.style.transform = ''
642
+ })
643
+
644
+ // Restaurar estados das seções
645
+ allSections.forEach((section, index) => {
646
+ section.style.display = originalStates[index] || 'none'
647
+ if (allArrows[index] && originalStates[index] === 'none') {
648
+ allArrows[index].style.transform = 'rotate(0deg)'
649
+ }
650
+ })
651
+ }
652
+ }
653
+
654
+ // Exportação CSV
655
+ document.getElementById('export-csv').onclick = () => {
656
+ const data = getEvidenceData()
657
+ const csv = [
658
+ ['Teste', 'Arquivo', 'Tamanho', 'Timestamp', 'Caminho'],
659
+ ...data.map(d => [d.teste, d.arquivo, d.tamanho, d.timestamp, d.caminho])
660
+ ].map(e => e.join(';')).join('\\n')
661
+ const blob = new Blob([csv], { type: 'text/csv' })
662
+ const a = document.createElement('a')
663
+ a.href = URL.createObjectURL(blob)
664
+ a.download = 'evidencias.csv'
665
+ a.click()
666
+ }
667
+
668
+ // Exportação JSON
669
+ document.getElementById('export-json').onclick = () => {
670
+ const blob = new Blob([JSON.stringify(getEvidenceData(), null, 2)], { type: 'application/json' })
671
+ const a = document.createElement('a')
672
+ a.href = URL.createObjectURL(blob)
673
+ a.download = 'evidencias.json'
674
+ a.click()
675
+ }
676
+
677
+ // Exportação XLSX
678
+ document.getElementById('export-xlsx').onclick = () => {
679
+ const ws = XLSX.utils.json_to_sheet(getEvidenceData())
680
+ const wb = XLSX.utils.book_new()
681
+ XLSX.utils.book_append_sheet(wb, ws, 'Evidencias')
682
+ XLSX.writeFile(wb, 'evidencias.xlsx')
683
+ }
684
+
685
+ // Gráfico de tipos
686
+ new Chart(document.getElementById('chart-tipos'), {
687
+ type: 'doughnut',
688
+ data: {
689
+ labels: ['Web', 'Mobile', 'Outros'],
690
+ datasets: [{
691
+ data: [${projectStats.Web}, ${projectStats.Mobile}, ${projectStats.Other}],
692
+ backgroundColor: ['#3b82f6', '#f59e0b', '#10b981'],
693
+ borderWidth: 2,
694
+ borderColor: '#1e293b'
695
+ }]
696
+ },
697
+ options: {
698
+ responsive: true,
699
+ plugins: {
700
+ legend: { position: 'bottom' }
701
+ }
702
+ }
703
+ })
704
+
705
+ // Gráfico de testes
706
+ new Chart(document.getElementById('chart-testes'), {
707
+ type: 'bar',
708
+ data: {
709
+ labels: [${Object.keys(groupedByTest)
710
+ .map((name) => `'${name.substring(0, 20)}...'`)
711
+ .join(', ')}],
712
+ datasets: [{
713
+ label: 'Screenshots',
714
+ data: [${Object.values(groupedByTest)
715
+ .map((screenshots) => screenshots.length)
716
+ .join(', ')}],
717
+ backgroundColor: '#3b82f6',
718
+ borderColor: '#1e40af',
719
+ borderWidth: 1
720
+ }]
721
+ },
722
+ options: {
723
+ responsive: true,
724
+ scales: {
725
+ y: { beginAtZero: true }
726
+ },
727
+ plugins: {
728
+ legend: { display: false }
729
+ }
730
+ }
731
+ })
732
+ </script>
733
+ </body>
734
+ </html>`;
735
+ }
736
+ /**
737
+ * ✅ Agrupa screenshots por teste
738
+ */
739
+ static groupScreenshotsByTest(screenshots) {
740
+ const grouped = {};
741
+ for (const screenshot of screenshots) {
742
+ if (!grouped[screenshot.testName]) {
743
+ grouped[screenshot.testName] = [];
744
+ }
745
+ grouped[screenshot.testName].push(screenshot);
746
+ }
747
+ return grouped;
748
+ }
749
+ /**
750
+ * ✅ Formata tamanho de arquivo
751
+ */
752
+ static formatFileSize(bytes) {
753
+ const sizes = ['B', 'KB', 'MB', 'GB'];
754
+ if (bytes === 0)
755
+ return '0 B';
756
+ const i = Math.floor(Math.log(bytes) / Math.log(1024));
757
+ return `${(bytes / 1024 ** i).toFixed(1)} ${sizes[i]}`;
758
+ }
759
+ /**
760
+ * ✅ Gera relatório vazio quando não há evidências nos projetos consumidores
761
+ */
762
+ static async generateEmptyReport() {
763
+ const html = `
764
+ <!DOCTYPE html>
765
+ <html lang="pt-BR">
766
+ <head>
767
+ <meta charset="UTF-8">
768
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
769
+ <title>📸 Organizador de Evidências - AutoCore</title>
770
+ <style>
771
+ * { margin: 0; padding: 0; box-sizing: border-box; }
772
+ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #f5f7fa; color: #2d3748; }
773
+ .container { max-width: 800px; margin: 0 auto; padding: 20px; text-align: center; }
774
+ .empty-state { background: white; padding: 4rem 2rem; border-radius: 12px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
775
+ .empty-state h1 { font-size: 2rem; color: #667eea; margin-bottom: 1rem; }
776
+ .empty-state p { color: #718096; font-size: 1.1rem; margin-bottom: 0.5rem; }
777
+ </style>
778
+ </head>
779
+ <body>
780
+ <div class="container">
781
+ <div class="empty-state">
782
+ <h1>📸 Relatório de Evidências</h1>
783
+ <p>Nenhum screenshot foi encontrado nas pastas do projeto.</p>
784
+ <p>Screenshots são automaticamente capturados durante a execução dos testes.</p>
785
+ <p><strong>Pastas verificadas:</strong> playwright-report, test-results, reports, evidence, screenshots</p>
786
+ </div>
787
+ </div>
788
+ </body>
789
+ </html>`;
790
+ // ✅ CORRIGIDO: Criar pasta playwright-report se não existir
791
+ const playwrightReportDir = path.join(process.cwd(), 'playwright-report');
792
+ if (!fs.existsSync(playwrightReportDir)) {
793
+ fs.mkdirSync(playwrightReportDir, { recursive: true });
794
+ }
795
+ // ✅ CORRIGIDO: Salvar relatório em playwright-report
796
+ const reportPath = path.join(playwrightReportDir, 'relatorio-evidencias.html');
797
+ fs.writeFileSync(reportPath, html, 'utf-8');
798
+ }
799
+ }