@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,1632 @@
1
+ import fs from 'node:fs';
2
+ import { BaseError, IntegrationError, ValidationError, } from '../functions/errors/index.js';
3
+ import { ExecutionTracker } from '../hubdocs/ExecutionTracker.js';
4
+ import { TestContext } from '../testContext/TestContext.js';
5
+ import { UnifiedReportManager } from '../testContext/UnifiedReportManager.js';
6
+ import { Logger } from '../utils/Logger.js';
7
+ import { MobileConnection } from './MobileConnection.js';
8
+ // 🤖 IMPORTAÇÃO CRÍTICA: Força inicialização do StatementTracker para projetos Mobile
9
+ import '../hubdocs/StatementTracker.js';
10
+ import path from 'node:path';
11
+ import { StatementTracker } from '../hubdocs/StatementTracker.js';
12
+ import { EvidenceCapture } from '../utils/EvidenceCapture.js';
13
+ import { DeviceFarmViewer } from './DeviceFarmViewer.js';
14
+ // 🚫 REMOVIDO: import { installDatabaseInterceptor } from '../utils/DatabaseInterceptor.js'
15
+ /**
16
+ * Enum para direções de swipe
17
+ */
18
+ export var Direcao;
19
+ (function (Direcao) {
20
+ Direcao["CIMA"] = "CIMA";
21
+ Direcao["BAIXO"] = "BAIXO";
22
+ Direcao["ESQUERDA"] = "ESQUERDA";
23
+ Direcao["DIREITA"] = "DIREITA";
24
+ })(Direcao || (Direcao = {}));
25
+ /**
26
+ * Classe utilitária para interações com aplicativos mobile via Appium
27
+ * Fornece métodos para manipulação de elementos, gestos e validações
28
+ * Baseada nos padrões Actions do framework mobile
29
+ */
30
+ export class MobileActions {
31
+ /**
32
+ * 🧹 Limpa pasta de screenshots mobile no início da execução
33
+ */
34
+ static async cleanMobileScreenshots() {
35
+ try {
36
+ const screenshotDir = path.join(process.cwd(), 'test-results', 'mobile-screenshots');
37
+ if (fs.existsSync(screenshotDir)) {
38
+ // Remove todos os arquivos PNG da pasta
39
+ const files = fs.readdirSync(screenshotDir);
40
+ const pngFiles = files.filter((file) => file.endsWith('.png'));
41
+ for (const file of pngFiles) {
42
+ const filePath = path.join(screenshotDir, file);
43
+ fs.unlinkSync(filePath);
44
+ }
45
+ Logger.info(`🧹 Limpos ${pngFiles.length} screenshot(s) mobile da execução anterior`);
46
+ }
47
+ else {
48
+ // Criar diretório se não existir
49
+ fs.mkdirSync(screenshotDir, { recursive: true });
50
+ Logger.info(`📁 Diretório de screenshots mobile criado: ${screenshotDir}`);
51
+ }
52
+ }
53
+ catch (error) {
54
+ Logger.warning(`⚠️ Erro ao limpar screenshots mobile: ${String(error)}`);
55
+ }
56
+ }
57
+ /**
58
+ * 🚫 ULTRA-RADICAL SSL KILLER: Initialize before any mobile operation
59
+ */
60
+ static async initializeSSLKillerForMobile() {
61
+ try {
62
+ // Force global SSL killer from MobileConnection
63
+ await MobileConnection.initializeGlobalSSLKiller();
64
+ // Additional mobile-specific SSL killer
65
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
66
+ process.env.HTTPS_PROXY_REJECT_UNAUTHORIZED = '0';
67
+ process.env.SSL_VERIFY = '0';
68
+ process.env.DISABLE_SSL_VERIFICATION = '1';
69
+ }
70
+ catch (error) {
71
+ Logger.warning(`⚠️ SSL Killer initialization warning: ${String(error)}`);
72
+ }
73
+ }
74
+ /**
75
+ * ✅ NOVO: Detecta o tipo do seletor mobile
76
+ * @private
77
+ * @param {string} selector Seletor a analisar
78
+ * @returns {string} Tipo do seletor
79
+ */
80
+ static detectMobileSelectorType(selector) {
81
+ // XPath
82
+ if (selector.startsWith('//') ||
83
+ selector.startsWith('/') ||
84
+ selector.includes('[@')) {
85
+ return 'XPath';
86
+ }
87
+ // iOS Accessibility ID (XCUID)
88
+ if (selector.includes('accessibility-id') || selector.includes('~')) {
89
+ return 'iOS Accessibility ID';
90
+ }
91
+ // Android Resource ID
92
+ if (selector.includes(':id/') || selector.includes('android:id/')) {
93
+ return 'Android Resource ID';
94
+ }
95
+ // iOS Predicate
96
+ if (selector.includes('type=') && selector.includes('name=')) {
97
+ return 'iOS Predicate';
98
+ }
99
+ // Android UiSelector
100
+ if (selector.includes('UiSelector') ||
101
+ selector.includes('resourceId') ||
102
+ selector.includes('className')) {
103
+ return 'Android UiSelector';
104
+ }
105
+ // Text/Content Description
106
+ if (selector.startsWith('text=') || selector.startsWith('content-desc=')) {
107
+ return 'Text/Content-Desc';
108
+ }
109
+ // Class Name
110
+ if (selector.includes('android.widget.') ||
111
+ selector.includes('XCUIElementType')) {
112
+ return 'Class Name';
113
+ }
114
+ // ID simples
115
+ if (!(selector.includes(' ') ||
116
+ selector.includes('[') ||
117
+ selector.includes('='))) {
118
+ return 'ID';
119
+ }
120
+ return 'Mobile Selector';
121
+ }
122
+ /**
123
+ * ✅ NOVO: Função auxiliar para criar objeto de ação mobile para o AutoDocs
124
+ * @private
125
+ * @param {string} action Nome da ação
126
+ * @param {string} selector Seletor do elemento
127
+ * @param {string} description Descrição da ação
128
+ * @param {any} [value] Valor da ação (opcional)
129
+ * @param {boolean} [captureScreenshot] Se deve capturar screenshot
130
+ * @returns {object} Objeto de ação mobile
131
+ */
132
+ static async createMobileAction(action, selector, description, value, captureScreenshot = false) {
133
+ const deviceId = MobileConnection.getCurrentDeviceId();
134
+ const platform = MobileConnection.getCurrentPlatform();
135
+ const testName = TestContext.getSafeTestInfo()?.title || 'Mobile Action';
136
+ let screenshotPath;
137
+ // 📸 Capturar screenshot se solicitado
138
+ if (captureScreenshot) {
139
+ try {
140
+ const screenshot = await MobileConnection.captureScreenshotBuffer(`${action}-${description || 'unknown'}`);
141
+ if (screenshot) {
142
+ // Salvar screenshot e obter o caminho
143
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
144
+ const safeName = `${action}-${description || 'action'}`.replace(/[^a-zA-Z0-9\-_]/g, '_');
145
+ const fileName = `mobile-${timestamp}-${safeName}.png`;
146
+ const evidenceDir = './test-results/mobile-screenshots';
147
+ if (!fs.existsSync(evidenceDir)) {
148
+ fs.mkdirSync(evidenceDir, { recursive: true });
149
+ }
150
+ const filePath = path.join(evidenceDir, fileName);
151
+ fs.writeFileSync(filePath, screenshot);
152
+ screenshotPath = `../test-results/mobile-screenshots/${fileName}`;
153
+ Logger.info(`📸 Screenshot capturada para ação ${action}: ${screenshotPath}`);
154
+ }
155
+ }
156
+ catch (error) {
157
+ Logger.warning(`⚠️ Erro ao capturar screenshot para ação ${action}: ${String(error)}`);
158
+ }
159
+ }
160
+ return {
161
+ action,
162
+ element: selector || 'unknown',
163
+ device: deviceId || 'unknown',
164
+ platform,
165
+ timestamp: new Date().toISOString(),
166
+ testName,
167
+ value: value || description || '',
168
+ selectorType: selector
169
+ ? MobileActions.detectMobileSelectorType(selector)
170
+ : undefined,
171
+ coordinates: undefined,
172
+ screenshot: screenshotPath,
173
+ };
174
+ }
175
+ /**
176
+ * ✅ HELPER: Garante que o driver existe e não é null
177
+ * @private
178
+ */
179
+ static ensureDriverExists() {
180
+ const driver = MobileConnection.getDriver();
181
+ if (!driver) {
182
+ throw new Error('Driver mobile não inicializado. Execute MobileConnection.connect() primeiro.');
183
+ }
184
+ return driver;
185
+ }
186
+ /**
187
+ * Executa um método específico no contexto mobile com logs unificados.
188
+ * @param nomeMetodo Nome do método para identificação nos logs.
189
+ * @param executarAcao Função que executa a ação específica.
190
+ * @returns Resultado da execução da ação.
191
+ * @private
192
+ */
193
+ static async executarComLog(nomeMetodo, executarAcao) {
194
+ // 🚫 FORCE SSL KILLER before EVERY mobile operation
195
+ await MobileActions.initializeSSLKillerForMobile();
196
+ const startTime = Date.now();
197
+ Logger.info(`🤖 ${nomeMetodo} - Iniciando ação mobile`);
198
+ // ✅ NOVO: Capturar evidência antes da ação
199
+ let beforeScreenshot = null;
200
+ try {
201
+ beforeScreenshot = await MobileConnection.captureEvidence(`before_${nomeMetodo}`);
202
+ }
203
+ catch (evidenceError) {
204
+ Logger.warning(`⚠️ Erro ao capturar evidência antes: ${String(evidenceError)}`);
205
+ }
206
+ // ✅ NOVO: Delay configurável para visualização (antes da ação)
207
+ const preActionDelay = Number.parseInt(process.env.MOBILE_PRE_ACTION_DELAY || '500', 10);
208
+ if (preActionDelay > 0) {
209
+ Logger.info(`Aguardando ${preActionDelay}ms antes da ação (visualização)`);
210
+ await new Promise((resolve) => setTimeout(resolve, preActionDelay));
211
+ }
212
+ // Registrar no UnifiedReportManager se o teste estiver configurado
213
+ const testInfo = TestContext.getSafeTestInfo();
214
+ if (testInfo?.title) {
215
+ try {
216
+ UnifiedReportManager.adicionarLog(testInfo.title, `🔄 Executando: ${nomeMetodo}`);
217
+ }
218
+ catch {
219
+ // Silenciosamente ignorar se o contexto não estiver configurado
220
+ }
221
+ }
222
+ try {
223
+ const resultado = await executarAcao();
224
+ const duration = Date.now() - startTime;
225
+ // ✅ NOVO: Capturar evidência após a ação
226
+ let afterScreenshot = null;
227
+ try {
228
+ afterScreenshot = await MobileConnection.captureEvidence(`after_${nomeMetodo}`);
229
+ // 🆕 INTEGRAR COM EVIDENCECAPTURE
230
+ if (testInfo?.title && afterScreenshot) {
231
+ await EvidenceCapture.captureAction(testInfo.title, `Mobile: ${nomeMetodo}`, `✅ Ação concluída com sucesso (${duration}ms)`, { duration, method: nomeMetodo }, afterScreenshot);
232
+ }
233
+ }
234
+ catch (evidenceError) {
235
+ Logger.warning(`⚠️ Erro ao capturar evidência depois: ${String(evidenceError)}`);
236
+ }
237
+ // ✅ NOVO: Delay configurável para visualização (após a ação)
238
+ const postActionDelay = Number.parseInt(process.env.MOBILE_POST_ACTION_DELAY || '1000', 10);
239
+ if (postActionDelay > 0) {
240
+ Logger.info(`Aguardando ${postActionDelay}ms após ação (visualização)`);
241
+ await new Promise((resolve) => setTimeout(resolve, postActionDelay));
242
+ }
243
+ Logger.success(`${nomeMetodo} executado com sucesso (${duration}ms)`);
244
+ // Log de sucesso no UnifiedReportManager
245
+ if (testInfo?.title) {
246
+ try {
247
+ UnifiedReportManager.adicionarLog(testInfo.title, `✅ ${nomeMetodo} executado com sucesso (${duration}ms)`);
248
+ }
249
+ catch {
250
+ // Silenciosamente ignorar se o contexto não estiver configurado
251
+ }
252
+ }
253
+ // Configurar limpeza automática se ainda não foi configurada
254
+ MobileActions.ensureAutoCleanupConfigured();
255
+ return resultado;
256
+ }
257
+ catch (error) {
258
+ const duration = Date.now() - startTime;
259
+ const errorMessage = String(error);
260
+ // ✅ NOVO: Capturar evidência do erro
261
+ let errorScreenshot = null;
262
+ try {
263
+ errorScreenshot = await MobileConnection.captureEvidence(`error_${nomeMetodo}`);
264
+ // 🆕 INTEGRAR COM EVIDENCECAPTURE
265
+ if (testInfo?.title && errorScreenshot) {
266
+ await EvidenceCapture.captureAction(testInfo.title, `Mobile: ${nomeMetodo}`, `❌ ERRO: ${errorMessage} (${duration}ms)`, { duration, method: nomeMetodo, error: errorMessage }, errorScreenshot);
267
+ }
268
+ }
269
+ catch (evidenceError) {
270
+ Logger.warning(`⚠️ Erro ao capturar evidência de erro: ${String(evidenceError)}`);
271
+ }
272
+ Logger.error(`❌ Erro ao executar ${nomeMetodo} após ${duration}ms`, error);
273
+ // Log de erro no UnifiedReportManager
274
+ if (testInfo?.title) {
275
+ try {
276
+ UnifiedReportManager.adicionarLog(testInfo.title, `❌ Erro ao executar ${nomeMetodo} (${duration}ms): ${errorMessage}`);
277
+ }
278
+ catch {
279
+ // Silenciosamente ignorar se o contexto não estiver configurado
280
+ }
281
+ }
282
+ // Criar um IntegrationError se não for já um erro customizado
283
+ if (error instanceof BaseError) {
284
+ throw error;
285
+ }
286
+ throw new IntegrationError('MOBILE_ACTION_FAILED', `Mobile action '${nomeMetodo}' failed: ${String(error)}`, `Falha na ação mobile '${nomeMetodo}': ${String(error)}`, error instanceof Error ? error : new Error(String(error)), {
287
+ metadata: { method: nomeMetodo, timestamp: new Date().toISOString() },
288
+ });
289
+ }
290
+ }
291
+ /**
292
+ * Garante que a limpeza automática está configurada para o teste mobile.
293
+ * @private
294
+ */
295
+ static ensureAutoCleanupConfigured() {
296
+ const testInfo = TestContext.getSafeTestInfo();
297
+ if (!(testInfo && MobileConnection.isConnected()))
298
+ return;
299
+ // Verificar se já foi configurado para este teste
300
+ const testTitle = testInfo.title;
301
+ if (global[`mobileCleanup_${testTitle}`]) {
302
+ return; // Já configurado
303
+ }
304
+ // Marcar como configurado
305
+ ;
306
+ global[`mobileCleanup_${testTitle}`] = true;
307
+ // Configurar limpeza automática via Promise cleanup
308
+ let cleanupExecuted = false;
309
+ // Interceptar quando o teste realmente termina
310
+ const executeCleanup = async () => {
311
+ if (cleanupExecuted)
312
+ return;
313
+ cleanupExecuted = true;
314
+ try {
315
+ const hasActiveSession = MobileConnection.isConnected();
316
+ if (hasActiveSession) {
317
+ Logger.info('🔚 Finalizando sessão mobile automaticamente...');
318
+ await MobileConnection.closeSession();
319
+ Logger.success('✅ Sessão mobile finalizada automaticamente');
320
+ if (testInfo?.title) {
321
+ UnifiedReportManager.adicionarLog(testInfo.title, '✅ Sessão mobile finalizada automaticamente');
322
+ }
323
+ }
324
+ }
325
+ catch (error) {
326
+ Logger.warning(`⚠️ Erro na limpeza automática mobile: ${String(error)}`);
327
+ }
328
+ };
329
+ // Estratégia 1: Interceptar via Promise.allSettled no final
330
+ setTimeout(() => {
331
+ const originalAllSettled = Promise.allSettled;
332
+ Promise.allSettled = function (...args) {
333
+ const result = originalAllSettled.apply(this, args);
334
+ result.finally(() => {
335
+ setTimeout(executeCleanup, 100);
336
+ });
337
+ return result;
338
+ };
339
+ }, 100);
340
+ // Estratégia 2: Timeout como fallback
341
+ setTimeout(executeCleanup, 30_000); // 30 segundos
342
+ // Estratégia 3: Detectar quando não há mais atividade
343
+ let lastActivity = Date.now();
344
+ const activityChecker = setInterval(() => {
345
+ const now = Date.now();
346
+ if (now - lastActivity > 5000) {
347
+ clearInterval(activityChecker);
348
+ setTimeout(executeCleanup, 500);
349
+ }
350
+ }, 1000);
351
+ global[`updateActivity_${testTitle}`] = () => {
352
+ lastActivity = Date.now();
353
+ };
354
+ }
355
+ /**
356
+ * Realiza clique inteligente em elemento mobile, com fallback para coordenadas.
357
+ * @param seletor Seletor do elemento.
358
+ * @param descriptionToAction Descrição da ação.
359
+ * @param getSnapshot Se deve capturar screenshot.
360
+ * @param timeout Tempo máximo de espera em ms.
361
+ */
362
+ static async click(seletor, descriptionToAction, getSnapshot = true, // 🔧 MUDANÇA: Agora captura screenshot por padrão
363
+ timeout = TestContext.getProjectTimeout()) {
364
+ const startTime = Date.now();
365
+ // 🆕 Marcar uso automático do Mobile
366
+ UnifiedReportManager.registrarInicioExecucao();
367
+ // 🆕 Registrar statement no StatementTracker
368
+ let statementTimestamp = null;
369
+ try {
370
+ statementTimestamp = StatementTracker.startStatement('MobileActions', `click: ${descriptionToAction}`);
371
+ }
372
+ catch (error) {
373
+ Logger.warning(`Não foi possível registrar statement no StatementTracker: ${String(error)}`);
374
+ }
375
+ // 🆕 Criar ação mobile para AutoDocs com screenshot
376
+ const mobileAction = await MobileActions.createMobileAction('click', seletor, descriptionToAction, undefined, getSnapshot);
377
+ // 🆕 Registrar no ExecutionTracker
378
+ ExecutionTracker.trackMobile(mobileAction);
379
+ // Atualizar atividade
380
+ const testInfo = TestContext.getSafeTestInfo();
381
+ if (testInfo?.title &&
382
+ global[`updateActivity_${testInfo.title}`]) {
383
+ ;
384
+ global[`updateActivity_${testInfo.title}`]();
385
+ }
386
+ const driver = MobileActions.ensureDriverExists();
387
+ await MobileActions.executarComLog(descriptionToAction, async () => {
388
+ const elemento = await driver.$(seletor);
389
+ await elemento.waitForDisplayed({ timeout });
390
+ // Estratégia 1: Tentar clique direto
391
+ try {
392
+ Logger.info('Tentando clique direto no elemento');
393
+ const presente = await elemento.isDisplayed();
394
+ if (!presente) {
395
+ throw new ValidationError('MOBILE_ELEMENT_NOT_VISIBLE', 'Element is not visible for interaction', 'Elemento não visível para interação', undefined, { metadata: { selector: seletor } });
396
+ }
397
+ // Verificação nativa de habilitação
398
+ try {
399
+ const habilitado = await elemento.isEnabled();
400
+ if (!habilitado) {
401
+ Logger.warning('⚠️ Elemento pode estar desabilitado, tentando clique mesmo assim');
402
+ }
403
+ }
404
+ catch (error) {
405
+ Logger.info(`Verificação de habilitação ignorada: ${String(error)}`);
406
+ }
407
+ await driver.pause(300);
408
+ await elemento.click();
409
+ Logger.success(`Clique direto bem-sucedido: ${descriptionToAction}`);
410
+ }
411
+ catch (error) {
412
+ Logger.warning(`Clique direto falhou, tentando por coordenadas: ${String(error)}`);
413
+ // Estratégia 2: Fallback para clique por coordenadas
414
+ try {
415
+ const location = await elemento.getLocation();
416
+ const size = await elemento.getSize();
417
+ const centerX = location.x + size.width / 2;
418
+ const centerY = location.y + size.height / 2;
419
+ Logger.info(`📍 Clicando por coordenadas: (${centerX}, ${centerY})`);
420
+ await driver.touchAction({
421
+ action: 'tap',
422
+ x: Math.round(centerX),
423
+ y: Math.round(centerY),
424
+ });
425
+ Logger.success(`✅ Clique por coordenadas bem-sucedido: ${descriptionToAction}`);
426
+ }
427
+ catch (coordError) {
428
+ Logger.error('Ambos os métodos de clique falharam');
429
+ // ✅ MELHORAR: Identificar tipo de erro
430
+ const errorMessage = String(error);
431
+ const coordErrorMessage = String(coordError);
432
+ let errorCategory = 'ELEMENTO_NAO_ENCONTRADO';
433
+ let userFriendlyMessage = 'Clique falhou: elemento não encontrado ou não acessível';
434
+ // Detectar problemas de SSL/Certificado/Frame
435
+ if (errorMessage.includes('SELF_SIGNED_CERT_IN_CHAIN') ||
436
+ coordErrorMessage.includes('SELF_SIGNED_CERT_IN_CHAIN') ||
437
+ errorMessage.includes('certificate') ||
438
+ errorMessage.includes('SSL') ||
439
+ errorMessage.includes('TLS')) {
440
+ errorCategory = 'CONEXAO_SSL_FRAME';
441
+ userFriendlyMessage =
442
+ '❌ PROBLEMA DE CONEXÃO: Erro de certificado SSL/TLS com o DeviceFarm. O frame mobile não consegue se comunicar com o servidor devido a problemas de certificado.';
443
+ }
444
+ // Detectar problemas de timeout/rede
445
+ else if (errorMessage.includes('timeout') ||
446
+ errorMessage.includes('ETIMEDOUT') ||
447
+ errorMessage.includes('ECONNRESET')) {
448
+ errorCategory = 'TIMEOUT_FRAME';
449
+ userFriendlyMessage =
450
+ '❌ PROBLEMA DE CONEXÃO: Timeout na comunicação com o DeviceFarm. O frame mobile perdeu conexão.';
451
+ }
452
+ // Detectar problemas de elemento não encontrado
453
+ else if (errorMessage.includes('element not found') ||
454
+ errorMessage.includes('no such element') ||
455
+ errorMessage.includes('element not visible')) {
456
+ errorCategory = 'ELEMENTO_NAO_ENCONTRADO';
457
+ userFriendlyMessage = `❌ PROBLEMA DE ELEMENTO: O elemento '${seletor}' não foi encontrado ou não está visível na tela.`;
458
+ }
459
+ // Detectar problemas de elemento não clicável
460
+ else if (errorMessage.includes('not clickable') ||
461
+ errorMessage.includes('element not interactable')) {
462
+ errorCategory = 'ELEMENTO_NAO_CLICAVEL';
463
+ userFriendlyMessage = `❌ PROBLEMA DE ELEMENTO: O elemento '${seletor}' foi encontrado mas não está clicável (pode estar sobreposto ou desabilitado).`;
464
+ }
465
+ Logger.info(`🔍 Diagnóstico: ${errorCategory}`);
466
+ Logger.info(`💡 ${userFriendlyMessage}`);
467
+ // 📝 NOVO: Registrar ação automaticamente no Statement ativo (erro)
468
+ const duration = Date.now() - startTime;
469
+ try {
470
+ StatementTracker.recordAction('MOBILE', `Click FALHOU: ${descriptionToAction || seletor}`, false, duration, {
471
+ selector: seletor,
472
+ action: 'click',
473
+ });
474
+ }
475
+ catch (recordError) {
476
+ // Falha silenciosa - não afetar funcionalidade principal
477
+ }
478
+ throw new IntegrationError(errorCategory, `Mobile click failed: ${errorMessage} | Coordinates: ${coordErrorMessage}`, userFriendlyMessage, error instanceof Error ? error : new Error(String(error)), {
479
+ metadata: {
480
+ selector: seletor,
481
+ originalError: errorMessage,
482
+ coordError: coordErrorMessage,
483
+ errorCategory,
484
+ diagnosticMessage: userFriendlyMessage,
485
+ },
486
+ });
487
+ }
488
+ }
489
+ if (getSnapshot) {
490
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
491
+ await MobileConnection.captureEvidence(descriptionToAction);
492
+ }
493
+ });
494
+ // 📝 NOVO: Registrar ação automaticamente no Statement ativo (sucesso)
495
+ const duration = Date.now() - startTime;
496
+ try {
497
+ StatementTracker.recordAction('MOBILE', descriptionToAction || `Click: ${seletor}`, true, duration, {
498
+ selector: seletor,
499
+ action: 'click',
500
+ });
501
+ }
502
+ catch (recordError) {
503
+ // Falha silenciosa - não afetar funcionalidade principal
504
+ }
505
+ // 🆕 Finalizar statement com sucesso
506
+ if (statementTimestamp) {
507
+ try {
508
+ StatementTracker.finishStatement(statementTimestamp, true, `✅ ${descriptionToAction} (${duration}ms)`);
509
+ }
510
+ catch (error) {
511
+ Logger.warning(`Não foi possível finalizar statement no StatementTracker: ${String(error)}`);
512
+ }
513
+ }
514
+ }
515
+ /**
516
+ * Realiza clique robusto em elemento mobile, com múltiplas tentativas.
517
+ * @param seletor Seletor do elemento.
518
+ * @param descriptionToAction Descrição da ação.
519
+ * @param getSnapshot Se deve capturar screenshot.
520
+ * @param timeout Tempo máximo de espera em ms.
521
+ * @param retry Número de tentativas.
522
+ */
523
+ static async clickNative(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout(), retry = 3) {
524
+ const driver = MobileActions.ensureDriverExists();
525
+ await MobileActions.executarComLog(descriptionToAction, async () => {
526
+ let lastError = null;
527
+ for (let tentativa = 1; tentativa <= retry; tentativa++) {
528
+ try {
529
+ Logger.info(`🔄 Tentativa ${tentativa}/${retry} de clique nativo`);
530
+ const elemento = await driver.$(seletor);
531
+ await elemento.waitForDisplayed({ timeout: timeout / retry });
532
+ const presente = await elemento.isDisplayed();
533
+ if (!presente) {
534
+ throw new Error(`Elemento não visível na tentativa ${tentativa}`);
535
+ }
536
+ await driver.pause(300);
537
+ await elemento.click();
538
+ Logger.success(`✅ Clique nativo bem-sucedido na tentativa ${tentativa}: ${descriptionToAction}`);
539
+ if (getSnapshot) {
540
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
541
+ await MobileConnection.captureEvidence(descriptionToAction);
542
+ }
543
+ return;
544
+ }
545
+ catch (error) {
546
+ lastError = error;
547
+ Logger.warning(`⚠️ Tentativa ${tentativa} falhou: ${String(error)}`);
548
+ if (tentativa < retry) {
549
+ await driver.pause(1000);
550
+ }
551
+ }
552
+ }
553
+ throw new Error(`Clique falhou após ${retry} tentativas. Último erro: ${lastError?.message}`);
554
+ });
555
+ }
556
+ /**
557
+ * Realiza clique por coordenadas no centro do elemento.
558
+ * @param seletor Seletor do elemento.
559
+ * @param descriptionToAction Descrição da ação.
560
+ * @param getSnapshot Se deve capturar screenshot.
561
+ * @param timeout Tempo máximo de espera em ms.
562
+ */
563
+ static async clickByCoordinates(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
564
+ const driver = MobileActions.ensureDriverExists();
565
+ await MobileActions.executarComLog(descriptionToAction, async () => {
566
+ const elemento = await driver.$(seletor);
567
+ await elemento.waitForDisplayed({ timeout });
568
+ const location = await elemento.getLocation();
569
+ const size = await elemento.getSize();
570
+ const centerX = location.x + size.width / 2;
571
+ const centerY = location.y + size.height / 2;
572
+ Logger.info(`📍 Clicando nas coordenadas: (${centerX}, ${centerY})`);
573
+ await driver.touchAction({
574
+ action: 'tap',
575
+ x: Math.round(centerX),
576
+ y: Math.round(centerY),
577
+ });
578
+ Logger.success(`✅ Clique por coordenadas bem-sucedido: ${descriptionToAction}`);
579
+ if (getSnapshot) {
580
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
581
+ await MobileConnection.captureEvidence(descriptionToAction);
582
+ }
583
+ });
584
+ }
585
+ /**
586
+ * Realiza um clique em uma coordenada específica na tela.
587
+ * @param point Coordenada {x, y}.
588
+ * @param descriptionToAction Descrição da ação.
589
+ * @param getSnapshot Se deve capturar screenshot.
590
+ */
591
+ static async clickPoint(point, descriptionToAction, getSnapshot = false) {
592
+ const testInfo = TestContext.getSafeTestInfo();
593
+ if (testInfo?.title &&
594
+ global[`updateActivity_${testInfo.title}`]) {
595
+ ;
596
+ global[`updateActivity_${testInfo.title}`]();
597
+ }
598
+ const driver = MobileActions.ensureDriverExists();
599
+ await MobileActions.executarComLog(descriptionToAction, async () => {
600
+ await driver.touchAction({
601
+ action: 'tap',
602
+ x: point.x,
603
+ y: point.y,
604
+ });
605
+ Logger.success(`✅ Clique na coordenada (${point.x}, ${point.y}): ${descriptionToAction}`);
606
+ if (getSnapshot) {
607
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
608
+ await MobileConnection.captureEvidence(descriptionToAction);
609
+ }
610
+ });
611
+ }
612
+ /**
613
+ * Valida se um elemento está presente na tela.
614
+ * @param seletor Seletor do elemento.
615
+ * @param descriptionToAction Descrição da ação.
616
+ * @param getSnapshot Se deve capturar screenshot.
617
+ * @param timeout Tempo máximo de espera em ms.
618
+ * @returns True se o elemento está presente.
619
+ */
620
+ static async validateObject(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
621
+ const driver = MobileActions.ensureDriverExists();
622
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
623
+ try {
624
+ const elemento = await driver.$(seletor);
625
+ await elemento.waitForDisplayed({ timeout });
626
+ const presente = await elemento.isDisplayed();
627
+ if (presente) {
628
+ Logger.success(`✅ Validação passou: elemento presente - ${descriptionToAction}`);
629
+ if (getSnapshot) {
630
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
631
+ await MobileConnection.captureEvidence(descriptionToAction);
632
+ }
633
+ return true;
634
+ }
635
+ const errorMsg = `Validação falhou: elemento não está visível - ${descriptionToAction}`;
636
+ Logger.error(errorMsg);
637
+ throw new Error(errorMsg);
638
+ }
639
+ catch (error) {
640
+ const errorMsg = `Validação falhou: elemento não encontrado - ${descriptionToAction}`;
641
+ Logger.error(errorMsg);
642
+ throw new Error(errorMsg);
643
+ }
644
+ });
645
+ }
646
+ /**
647
+ * Valida que um elemento não está presente na tela.
648
+ * @param seletor Seletor do elemento.
649
+ * @param descriptionToAction Descrição da ação.
650
+ * @param getSnapshot Se deve capturar screenshot.
651
+ * @param timeout Tempo máximo de espera em ms.
652
+ * @returns True se o elemento não está presente.
653
+ */
654
+ static async validateObjectNotExist(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
655
+ const driver = MobileActions.ensureDriverExists();
656
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
657
+ try {
658
+ const elemento = await driver.$(seletor);
659
+ await elemento.waitForDisplayed({ timeout, reverse: true });
660
+ const presente = await elemento.isDisplayed();
661
+ if (presente) {
662
+ const errorMsg = `Validação falhou: elemento ainda está presente - ${descriptionToAction}`;
663
+ Logger.error(errorMsg);
664
+ throw new Error(errorMsg);
665
+ }
666
+ Logger.success(`✅ Validação passou: elemento não está presente - ${descriptionToAction}`);
667
+ if (getSnapshot) {
668
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
669
+ await MobileConnection.captureEvidence(descriptionToAction);
670
+ }
671
+ return true;
672
+ }
673
+ catch (error) {
674
+ Logger.success(`✅ Validação passou: elemento não existe - ${descriptionToAction}`);
675
+ if (getSnapshot) {
676
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
677
+ await MobileConnection.captureEvidence(descriptionToAction);
678
+ }
679
+ return true;
680
+ }
681
+ });
682
+ }
683
+ /**
684
+ * Define um texto em um campo de entrada.
685
+ * @param seletor Seletor do campo.
686
+ * @param text Texto a ser inserido.
687
+ * @param descriptionToAction Descrição da ação.
688
+ * @param getSnapshot Se deve capturar screenshot.
689
+ * @param timeout Tempo máximo de espera em ms.
690
+ */
691
+ static async setText(seletor, text, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
692
+ const driver = MobileActions.ensureDriverExists();
693
+ await MobileActions.executarComLog(descriptionToAction, async () => {
694
+ const elemento = await driver.$(seletor);
695
+ await elemento.waitForDisplayed({ timeout });
696
+ try {
697
+ const habilitado = await elemento.isEnabled();
698
+ if (!habilitado) {
699
+ Logger.warning('⚠️ Elemento pode estar desabilitado');
700
+ }
701
+ }
702
+ catch (error) {
703
+ Logger.info(`ℹ️ Verificação de habilitação ignorada: ${String(error)}`);
704
+ }
705
+ await elemento.clearValue();
706
+ await elemento.setValue(text);
707
+ Logger.success(`✅ Texto inserido com sucesso: "${text}" - ${descriptionToAction}`);
708
+ if (getSnapshot) {
709
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
710
+ await MobileConnection.captureEvidence(descriptionToAction);
711
+ }
712
+ });
713
+ }
714
+ /**
715
+ * Envia caracteres individualmente para um elemento.
716
+ * @param seletor Seletor do campo.
717
+ * @param text Texto a ser inserido.
718
+ * @param descriptionToAction Descrição da ação.
719
+ * @param getSnapshot Se deve capturar screenshot.
720
+ * @param timeout Tempo máximo de espera em ms.
721
+ * @param delayBetweenChars Delay entre caracteres em ms.
722
+ */
723
+ static async setChar(seletor, text, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout(), delayBetweenChars = 100) {
724
+ const driver = MobileActions.ensureDriverExists();
725
+ await MobileActions.executarComLog(descriptionToAction, async () => {
726
+ const elemento = await driver.$(seletor);
727
+ await elemento.waitForDisplayed({ timeout });
728
+ try {
729
+ const habilitado = await elemento.isEnabled();
730
+ if (!habilitado) {
731
+ Logger.warning('⚠️ Elemento pode estar desabilitado');
732
+ }
733
+ }
734
+ catch (error) {
735
+ Logger.info(`ℹ️ Verificação de habilitação ignorada: ${String(error)}`);
736
+ }
737
+ await elemento.clearValue();
738
+ for (const char of text) {
739
+ await elemento.addValue(char);
740
+ await driver.pause(delayBetweenChars);
741
+ }
742
+ Logger.success(`✅ Caracteres digitados com sucesso: "${text}" - ${descriptionToAction}`);
743
+ if (getSnapshot) {
744
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
745
+ await MobileConnection.captureEvidence(descriptionToAction);
746
+ }
747
+ });
748
+ }
749
+ /**
750
+ * Obtém o texto presente em um elemento.
751
+ * @param seletor Seletor do elemento.
752
+ * @param descriptionToAction Descrição da ação.
753
+ * @param getSnapshot Se deve capturar screenshot.
754
+ * @param timeout Tempo máximo de espera em ms.
755
+ * @returns Texto do elemento.
756
+ */
757
+ static async getText(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
758
+ const driver = MobileActions.ensureDriverExists();
759
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
760
+ const elemento = await driver.$(seletor);
761
+ await elemento.waitForDisplayed({ timeout });
762
+ const texto = await elemento.getText();
763
+ Logger.success(`Texto obtido: "${texto}" - ${descriptionToAction}`);
764
+ if (getSnapshot) {
765
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
766
+ await MobileConnection.captureEvidence(descriptionToAction);
767
+ }
768
+ return texto;
769
+ });
770
+ }
771
+ /**
772
+ * Alterna o contexto da aplicação (ex: Nativo/Webview).
773
+ * @param contextName Nome do contexto.
774
+ * @param descriptionToAction Descrição da ação.
775
+ * @param getSnapshot Se deve capturar screenshot.
776
+ */
777
+ static async switchToContext(contextName, descriptionToAction, getSnapshot = false) {
778
+ const driver = MobileActions.ensureDriverExists();
779
+ await MobileActions.executarComLog(descriptionToAction, async () => {
780
+ const contextos = await driver.getContexts();
781
+ Logger.info(`📱 Contextos disponíveis: ${contextos.join(', ')}`);
782
+ if (!contextos.includes(contextName)) {
783
+ throw new Error(`Contexto '${contextName}' não encontrado. Disponíveis: ${contextos.join(', ')}`);
784
+ }
785
+ await driver.switchContext(contextName);
786
+ const contextoAtual = await driver.getContext();
787
+ Logger.success(`✅ Contexto alterado para: ${contextoAtual} - ${descriptionToAction}`);
788
+ if (getSnapshot) {
789
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
790
+ await MobileConnection.captureEvidence(descriptionToAction);
791
+ }
792
+ });
793
+ }
794
+ /**
795
+ * Verifica se um elemento está presente na tela.
796
+ * @param seletor Seletor do elemento.
797
+ * @param timeout Tempo máximo de espera em ms.
798
+ * @returns True se o elemento está presente.
799
+ */
800
+ static async isPresent(seletor, timeout = TestContext.getProjectTimeout()) {
801
+ const driver = MobileActions.ensureDriverExists();
802
+ try {
803
+ const elemento = await driver.$(seletor);
804
+ await elemento.waitForDisplayed({ timeout });
805
+ const presente = await elemento.isDisplayed();
806
+ Logger.info(`ℹ️ Elemento ${presente ? 'presente' : 'não presente'}: ${seletor}`);
807
+ return presente;
808
+ }
809
+ catch {
810
+ Logger.info(`ℹ️ Elemento não presente: ${seletor}`);
811
+ return false;
812
+ }
813
+ }
814
+ /**
815
+ * Realiza a ação de voltar na navegação.
816
+ * @param descriptionToAction Descrição da ação.
817
+ * @param getSnapshot Se deve capturar screenshot.
818
+ */
819
+ static async back(descriptionToAction, getSnapshot = false) {
820
+ const driver = MobileActions.ensureDriverExists();
821
+ await MobileActions.executarComLog(descriptionToAction, async () => {
822
+ await driver.back();
823
+ Logger.success(`Ação de voltar executada - ${descriptionToAction}`);
824
+ if (getSnapshot) {
825
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
826
+ await MobileConnection.captureEvidence(descriptionToAction);
827
+ }
828
+ });
829
+ }
830
+ /**
831
+ * Realiza um gesto de swipe na tela.
832
+ * @param xInit Coordenada X inicial.
833
+ * @param yInit Coordenada Y inicial.
834
+ * @param xFinal Coordenada X final.
835
+ * @param yFinal Coordenada Y final.
836
+ * @param descriptionToAction Descrição da ação.
837
+ * @param getSnapshot Se deve capturar screenshot.
838
+ * @param duracao Duração do swipe em ms.
839
+ */
840
+ static async swipe(xInit, yInit, xFinal, yFinal, descriptionToAction, getSnapshot = false, duracao = 1000) {
841
+ const driver = MobileActions.ensureDriverExists();
842
+ await MobileActions.executarComLog(descriptionToAction, async () => {
843
+ await driver.touchAction([
844
+ { action: 'press', x: xInit, y: yInit },
845
+ { action: 'wait', ms: duracao },
846
+ { action: 'moveTo', x: xFinal, y: yFinal },
847
+ { action: 'release' },
848
+ ]);
849
+ Logger.success(`✅ Swipe executado: (${xInit}, ${yInit}) → (${xFinal}, ${yFinal}) - ${descriptionToAction}`);
850
+ if (getSnapshot) {
851
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
852
+ await MobileConnection.captureEvidence(descriptionToAction);
853
+ }
854
+ });
855
+ }
856
+ /**
857
+ * Executa swipes até encontrar elemento.
858
+ * @param xInit Coordenada X inicial.
859
+ * @param yInit Coordenada Y inicial.
860
+ * @param xFinal Coordenada X final.
861
+ * @param yFinal Coordenada Y final.
862
+ * @param seletorElementoEsperado Seletor do elemento esperado.
863
+ * @param descriptionToAction Descrição da ação.
864
+ * @param getSnapshot Se deve capturar screenshot.
865
+ * @param maxTentativas Número máximo de tentativas.
866
+ * @param duracao Duração do swipe em ms.
867
+ * @returns True se o elemento foi encontrado.
868
+ */
869
+ static async swipeUntilFind(xInit, yInit, xFinal, yFinal, seletorElementoEsperado, descriptionToAction, getSnapshot = false, maxTentativas = 10, duracao = 1000) {
870
+ const driver = MobileActions.ensureDriverExists();
871
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
872
+ for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
873
+ Logger.info(`🔄 Tentativa ${tentativa}/${maxTentativas} de swipe até encontrar elemento`);
874
+ const presente = await MobileActions.isPresent(seletorElementoEsperado, 2000);
875
+ if (presente) {
876
+ Logger.success(`✅ Elemento encontrado após ${tentativa} tentativa(s) - ${descriptionToAction}`);
877
+ if (getSnapshot) {
878
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
879
+ await MobileConnection.captureEvidence(descriptionToAction);
880
+ }
881
+ return true;
882
+ }
883
+ if (tentativa < maxTentativas) {
884
+ await MobileActions.swipe(xInit, yInit, xFinal, yFinal, `Swipe ${tentativa} - ${descriptionToAction}`, false, duracao);
885
+ await driver.pause(500);
886
+ }
887
+ }
888
+ const errorMsg = `Elemento não encontrado após ${maxTentativas} tentativas de swipe - ${descriptionToAction}`;
889
+ Logger.error(errorMsg);
890
+ throw new Error(errorMsg);
891
+ });
892
+ }
893
+ /**
894
+ * Obtém valor de atributo de um elemento.
895
+ * @param seletor Seletor do elemento.
896
+ * @param attributeName Nome do atributo.
897
+ * @param timeout Tempo máximo de espera em ms.
898
+ * @returns Valor do atributo.
899
+ */
900
+ static async getAttribute(seletor, attributeName, timeout = TestContext.getProjectTimeout()) {
901
+ const driver = MobileActions.ensureDriverExists();
902
+ return await MobileActions.executarComLog(`Obter atributo '${attributeName}' do elemento: ${seletor}`, async () => {
903
+ const elemento = await driver.$(seletor);
904
+ await elemento.waitForDisplayed({ timeout });
905
+ const atributo = await elemento.getAttribute(attributeName);
906
+ Logger.success(`Atributo '${attributeName}' obtido: "${atributo}"`);
907
+ return atributo || '';
908
+ });
909
+ }
910
+ /**
911
+ * Simula cliques no teclado numérico.
912
+ * @param number Número a ser digitado.
913
+ * @param getSnapshot Se deve capturar screenshot.
914
+ */
915
+ static async clickNumberPad(number, getSnapshot = false) {
916
+ const driver = MobileActions.ensureDriverExists();
917
+ await MobileActions.executarComLog(`Digitar número '${number}' no teclado numérico`, async () => {
918
+ for (const digit of number) {
919
+ const digitSelector = `//android.widget.TextView[@text="${digit}"] | //XCUIElementTypeStaticText[@name="${digit}"]`;
920
+ const elemento = await driver.$(digitSelector);
921
+ await elemento.waitForDisplayed({ timeout: 5000 });
922
+ await elemento.click();
923
+ await driver.pause(300);
924
+ }
925
+ Logger.success(`Número '${number}' digitado no teclado numérico`);
926
+ if (getSnapshot) {
927
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
928
+ await MobileConnection.captureEvidence(`Teclado numérico - ${number}`);
929
+ }
930
+ });
931
+ }
932
+ /**
933
+ * Executa script personalizado no driver.
934
+ * @param scriptType Tipo do script.
935
+ * @param jsonParams Parâmetros do script.
936
+ * @param descriptionToAction Descrição da ação.
937
+ * @param getSnapshot Se deve capturar screenshot.
938
+ * @returns Resultado do script.
939
+ */
940
+ static async executeScript(scriptType, jsonParams, descriptionToAction, getSnapshot = false) {
941
+ const driver = MobileActions.ensureDriverExists();
942
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
943
+ const result = await driver.executeScript(scriptType, [jsonParams]);
944
+ Logger.success(`✅ Script '${scriptType}' executado - ${descriptionToAction}`);
945
+ if (getSnapshot) {
946
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
947
+ await MobileConnection.captureEvidence(descriptionToAction);
948
+ }
949
+ return result;
950
+ });
951
+ }
952
+ /**
953
+ * Executa swipe genérico Android.
954
+ * @param direcao Direção do swipe.
955
+ * @param descriptionToAction Descrição da ação.
956
+ * @param getSnapshot Se deve capturar screenshot.
957
+ * @param distancia Distância do swipe (0 a 1).
958
+ */
959
+ static async genericSwipeAndroid(direcao, descriptionToAction, getSnapshot = false, distancia = 0.5) {
960
+ const driver = MobileActions.ensureDriverExists();
961
+ await MobileActions.executarComLog(descriptionToAction, async () => {
962
+ const { width, height } = await driver.getWindowSize();
963
+ const centerX = width / 2;
964
+ const centerY = height / 2;
965
+ let startX, startY, endX, endY;
966
+ switch (direcao) {
967
+ case Direcao.CIMA:
968
+ startX = centerX;
969
+ startY = centerY + (height * distancia) / 2;
970
+ endX = centerX;
971
+ endY = centerY - (height * distancia) / 2;
972
+ break;
973
+ case Direcao.BAIXO:
974
+ startX = centerX;
975
+ startY = centerY - (height * distancia) / 2;
976
+ endX = centerX;
977
+ endY = centerY + (height * distancia) / 2;
978
+ break;
979
+ case Direcao.ESQUERDA:
980
+ startX = centerX + (width * distancia) / 2;
981
+ startY = centerY;
982
+ endX = centerX - (width * distancia) / 2;
983
+ endY = centerY;
984
+ break;
985
+ case Direcao.DIREITA:
986
+ startX = centerX - (width * distancia) / 2;
987
+ startY = centerY;
988
+ endX = centerX + (width * distancia) / 2;
989
+ endY = centerY;
990
+ break;
991
+ default:
992
+ throw new Error(`Direção inválida: ${direcao}`);
993
+ }
994
+ await driver.touchAction([
995
+ { action: 'press', x: Math.round(startX), y: Math.round(startY) },
996
+ { action: 'wait', ms: 1000 },
997
+ { action: 'moveTo', x: Math.round(endX), y: Math.round(endY) },
998
+ { action: 'release' },
999
+ ]);
1000
+ Logger.success(`✅ Swipe genérico Android executado para ${direcao} - ${descriptionToAction}`);
1001
+ if (getSnapshot) {
1002
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
1003
+ await MobileConnection.captureEvidence(descriptionToAction);
1004
+ }
1005
+ });
1006
+ }
1007
+ /**
1008
+ * Executa swipe genérico iOS.
1009
+ * @param direcao Direção do swipe.
1010
+ * @param descriptionToAction Descrição da ação.
1011
+ * @param getSnapshot Se deve capturar screenshot.
1012
+ * @param distancia Distância do swipe (0 a 1).
1013
+ */
1014
+ static async genericSwipeIOS(direcao, descriptionToAction, getSnapshot = false, distancia = 0.5) {
1015
+ const driver = MobileActions.ensureDriverExists();
1016
+ await MobileActions.executarComLog(descriptionToAction, async () => {
1017
+ try {
1018
+ let swipeDirection;
1019
+ switch (direcao) {
1020
+ case Direcao.CIMA:
1021
+ swipeDirection = 'up';
1022
+ break;
1023
+ case Direcao.BAIXO:
1024
+ swipeDirection = 'down';
1025
+ break;
1026
+ case Direcao.ESQUERDA:
1027
+ swipeDirection = 'left';
1028
+ break;
1029
+ case Direcao.DIREITA:
1030
+ swipeDirection = 'right';
1031
+ break;
1032
+ default:
1033
+ throw new Error(`Direção inválida: ${direcao}`);
1034
+ }
1035
+ await driver.executeScript('mobile: swipe', [
1036
+ {
1037
+ direction: swipeDirection,
1038
+ velocity: 1000 * distancia,
1039
+ },
1040
+ ]);
1041
+ Logger.success(`Swipe genérico iOS executado para ${direcao} - ${descriptionToAction}`);
1042
+ }
1043
+ catch (error) {
1044
+ Logger.warning('⚠️ Comando nativo falhou, usando swipe manual');
1045
+ await MobileActions.genericSwipeAndroid(direcao, descriptionToAction, false, distancia);
1046
+ }
1047
+ if (getSnapshot) {
1048
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
1049
+ await MobileConnection.captureEvidence(descriptionToAction);
1050
+ }
1051
+ });
1052
+ }
1053
+ /**
1054
+ * Executa swipe genérico Android até encontrar elemento.
1055
+ * @param direcao Direção do swipe.
1056
+ * @param seletorElementoEsperado Seletor do elemento esperado.
1057
+ * @param descriptionToAction Descrição da ação.
1058
+ * @param getSnapshot Se deve capturar screenshot.
1059
+ * @param maxTentativas Número máximo de tentativas.
1060
+ * @param distancia Distância do swipe (0 a 1).
1061
+ * @returns True se o elemento foi encontrado.
1062
+ */
1063
+ static async genericSwipeUntilFindAndroid(direcao, seletorElementoEsperado, descriptionToAction, getSnapshot = false, maxTentativas = 10, distancia = 0.5) {
1064
+ const driver = MobileActions.ensureDriverExists();
1065
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1066
+ for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
1067
+ Logger.info(`🔄 Tentativa ${tentativa}/${maxTentativas} de swipe genérico Android até encontrar elemento`);
1068
+ const presente = await MobileActions.isPresent(seletorElementoEsperado, 2000);
1069
+ if (presente) {
1070
+ Logger.success(`✅ Elemento encontrado após ${tentativa} tentativa(s) - ${descriptionToAction}`);
1071
+ if (getSnapshot) {
1072
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
1073
+ await MobileConnection.captureEvidence(descriptionToAction);
1074
+ }
1075
+ return true;
1076
+ }
1077
+ if (tentativa < maxTentativas) {
1078
+ await MobileActions.genericSwipeAndroid(direcao, `Swipe ${tentativa} - ${descriptionToAction}`, false, distancia);
1079
+ await driver.pause(500);
1080
+ }
1081
+ }
1082
+ const errorMsg = `Elemento não encontrado após ${maxTentativas} tentativas de swipe genérico Android - ${descriptionToAction}`;
1083
+ Logger.error(errorMsg);
1084
+ throw new Error(errorMsg);
1085
+ });
1086
+ }
1087
+ /**
1088
+ * Executa swipe genérico iOS até encontrar elemento.
1089
+ * @param direcao Direção do swipe.
1090
+ * @param seletorElementoEsperado Seletor do elemento esperado.
1091
+ * @param descriptionToAction Descrição da ação.
1092
+ * @param getSnapshot Se deve capturar screenshot.
1093
+ * @param maxTentativas Número máximo de tentativas.
1094
+ * @param distancia Distância do swipe (0 a 1).
1095
+ * @returns True se o elemento foi encontrado.
1096
+ */
1097
+ static async genericSwipeUntilFindIOS(direcao, seletorElementoEsperado, descriptionToAction, getSnapshot = false, maxTentativas = 10, distancia = 0.5) {
1098
+ const driver = MobileActions.ensureDriverExists();
1099
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1100
+ for (let tentativa = 1; tentativa <= maxTentativas; tentativa++) {
1101
+ Logger.info(`🔄 Tentativa ${tentativa}/${maxTentativas} de swipe genérico iOS até encontrar elemento`);
1102
+ const presente = await MobileActions.isPresent(seletorElementoEsperado, 2000);
1103
+ if (presente) {
1104
+ Logger.success(`✅ Elemento encontrado após ${tentativa} tentativa(s) - ${descriptionToAction}`);
1105
+ if (getSnapshot) {
1106
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
1107
+ await MobileConnection.captureEvidence(descriptionToAction);
1108
+ }
1109
+ return true;
1110
+ }
1111
+ if (tentativa < maxTentativas) {
1112
+ await MobileActions.genericSwipeIOS(direcao, `Swipe ${tentativa} - ${descriptionToAction}`, false, distancia);
1113
+ await driver.pause(500);
1114
+ }
1115
+ }
1116
+ const errorMsg = `Elemento não encontrado após ${maxTentativas} tentativas de swipe genérico iOS - ${descriptionToAction}`;
1117
+ Logger.error(errorMsg);
1118
+ throw new Error(errorMsg);
1119
+ });
1120
+ }
1121
+ /**
1122
+ * Realiza clique em elemento com verificação de presença.
1123
+ * @param seletor Seletor do elemento.
1124
+ * @param descriptionToAction Descrição da ação.
1125
+ * @param getSnapshot Se deve capturar screenshot.
1126
+ * @param timeout Tempo máximo de espera em ms.
1127
+ */
1128
+ static async clickOnObjectPresence(seletor, descriptionToAction, getSnapshot = false, timeout = TestContext.getProjectTimeout()) {
1129
+ const driver = MobileActions.ensureDriverExists();
1130
+ await MobileActions.executarComLog(descriptionToAction, async () => {
1131
+ const elemento = await driver.$(seletor);
1132
+ await elemento.waitForDisplayed({ timeout });
1133
+ const presente = await elemento.isDisplayed();
1134
+ if (!presente) {
1135
+ throw new Error(`Elemento não está presente na tela: ${seletor}`);
1136
+ }
1137
+ try {
1138
+ const habilitado = await elemento.isEnabled();
1139
+ if (!habilitado) {
1140
+ Logger.warning(`⚠️ Elemento está presente mas pode estar desabilitado: ${seletor}`);
1141
+ }
1142
+ }
1143
+ catch (error) {
1144
+ Logger.info(`ℹ️ Verificação de habilitação ignorada: ${String(error)}`);
1145
+ }
1146
+ await elemento.click();
1147
+ Logger.success(`✅ Clique realizado no elemento presente - ${descriptionToAction}`);
1148
+ if (getSnapshot) {
1149
+ // ? USAR MobileConnection.captureEvidence para integra��o com Playwright Report
1150
+ await MobileConnection.captureEvidence(descriptionToAction);
1151
+ }
1152
+ });
1153
+ }
1154
+ /**
1155
+ * Copia texto para o clipboard (Android/iOS).
1156
+ * @param text Texto a ser copiado.
1157
+ * @param descriptionToAction Descrição da ação.
1158
+ */
1159
+ static async setClipboardText(text, descriptionToAction = 'Set clipboard text') {
1160
+ const driver = MobileActions.ensureDriverExists();
1161
+ await MobileActions.executarComLog(descriptionToAction, async () => {
1162
+ try {
1163
+ await driver.setClipboard('plaintext', Buffer.from(text).toString('base64'));
1164
+ Logger.success(`Texto copiado para clipboard: "${text}"`);
1165
+ }
1166
+ catch (error) {
1167
+ Logger.warning(`Falha ao copiar texto para clipboard: ${String(error)}`);
1168
+ throw error;
1169
+ }
1170
+ });
1171
+ }
1172
+ /**
1173
+ * Obtém texto do clipboard (Android/iOS).
1174
+ * @param descriptionToAction Descrição da ação.
1175
+ * @returns Texto do clipboard.
1176
+ */
1177
+ static async getClipboardText(descriptionToAction = 'Get clipboard text') {
1178
+ const driver = MobileActions.ensureDriverExists();
1179
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1180
+ try {
1181
+ const base64 = await driver.getClipboard('plaintext');
1182
+ const text = Buffer.from(base64, 'base64').toString('utf-8');
1183
+ Logger.success(`Texto obtido do clipboard: "${text}"`);
1184
+ return text;
1185
+ }
1186
+ catch (error) {
1187
+ Logger.warning(`Falha ao obter texto do clipboard: ${String(error)}`);
1188
+ throw error;
1189
+ }
1190
+ });
1191
+ }
1192
+ /**
1193
+ * Executa comando shell (Android).
1194
+ * @param command Comando shell.
1195
+ * @param args Argumentos do comando.
1196
+ * @param descriptionToAction Descrição da ação.
1197
+ * @returns Resultado do comando.
1198
+ */
1199
+ static async executeShellCommand(command, args = [], descriptionToAction = 'Executar comando shell') {
1200
+ const driver = MobileActions.ensureDriverExists();
1201
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1202
+ try {
1203
+ const result = await driver.execute('mobile: shell', { command, args });
1204
+ Logger.success(`Comando shell executado: ${command} ${args.join(' ')}`);
1205
+ return result;
1206
+ }
1207
+ catch (error) {
1208
+ Logger.warning(`Falha ao executar comando shell: ${String(error)}`);
1209
+ throw error;
1210
+ }
1211
+ });
1212
+ }
1213
+ /**
1214
+ * Fecha o app e relança (forceAppLaunch).
1215
+ * @param descriptionToAction Descrição da ação.
1216
+ */
1217
+ static async relaunchApp(descriptionToAction = 'Relaunch app') {
1218
+ const driver = MobileActions.ensureDriverExists();
1219
+ await MobileActions.executarComLog(descriptionToAction, async () => {
1220
+ try {
1221
+ await driver.closeApp();
1222
+ await driver.launchApp();
1223
+ Logger.success('App relançado com sucesso');
1224
+ }
1225
+ catch (error) {
1226
+ Logger.warning(`Falha ao relançar app: ${String(error)}`);
1227
+ throw error;
1228
+ }
1229
+ });
1230
+ }
1231
+ /**
1232
+ * ✅ MELHORADO: Captura screenshot robusto com múltiplas tentativas
1233
+ * @param nome Nome do screenshot
1234
+ * @returns Buffer da imagem ou null
1235
+ */
1236
+ static async capturarScreenshot(nome) {
1237
+ const MAX_RETRIES = 3;
1238
+ const RETRY_DELAY = 2000;
1239
+ const STABILIZATION_DELAY = 1500;
1240
+ const MIN_SCREENSHOT_SIZE = 1000;
1241
+ Logger.info(`📸 Iniciando captura de screenshot mobile: ${nome}`);
1242
+ try {
1243
+ // 1. Verificações preliminares
1244
+ if (!MobileConnection.isConnected()) {
1245
+ Logger.error('❌ Driver mobile não conectado para screenshot');
1246
+ return null;
1247
+ }
1248
+ const driver = MobileActions.ensureDriverExists();
1249
+ if (!driver) {
1250
+ Logger.error('❌ Driver não disponível para screenshot');
1251
+ return null;
1252
+ }
1253
+ // 2. Verificar conectividade visual DeviceFarm
1254
+ const isVisuallyActive = await MobileConnection.verifyDeviceFarmVisualConnection();
1255
+ if (!isVisuallyActive) {
1256
+ Logger.warning('⚠️ DeviceFarm não está visualmente ativo');
1257
+ }
1258
+ // 3. Múltiplas tentativas de captura
1259
+ let screenshot = null;
1260
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
1261
+ try {
1262
+ Logger.info(`📸 Tentativa ${attempt}/${MAX_RETRIES} de captura: ${nome}`);
1263
+ // Aguardar estabilização
1264
+ await driver.pause(STABILIZATION_DELAY);
1265
+ // Capturar screenshot
1266
+ screenshot = await MobileConnection.captureScreenshotBuffer();
1267
+ if (screenshot && screenshot.length > MIN_SCREENSHOT_SIZE) {
1268
+ Logger.success(`Screenshot capturado: ${nome} (${screenshot.length} bytes)`);
1269
+ break;
1270
+ }
1271
+ Logger.warning(`⚠️ Screenshot inválido na tentativa ${attempt}`);
1272
+ screenshot = null;
1273
+ }
1274
+ catch (attemptError) {
1275
+ Logger.warning(`⚠️ Erro na tentativa ${attempt}: ${String(attemptError)}`);
1276
+ screenshot = null;
1277
+ }
1278
+ // Aguardar antes da próxima tentativa
1279
+ if (attempt < MAX_RETRIES) {
1280
+ await new Promise((resolve) => setTimeout(resolve, RETRY_DELAY));
1281
+ }
1282
+ }
1283
+ if (!screenshot) {
1284
+ Logger.error(`❌ Falha em todas as tentativas de screenshot: ${nome}`);
1285
+ return null;
1286
+ }
1287
+ // 4. Salvar arquivo local para evidência
1288
+ await MobileActions.saveScreenshotFile(screenshot, nome);
1289
+ // 5. Anexar ao relatório Playwright
1290
+ await MobileActions.attachScreenshotToReport(screenshot, nome);
1291
+ // 6. Registrar no sistema unificado
1292
+ MobileActions.logScreenshotToUnified(nome, screenshot.length);
1293
+ return screenshot;
1294
+ }
1295
+ catch (error) {
1296
+ Logger.error(`ERRO ao capturar screenshot: ${nome} - ${String(error)}`);
1297
+ return null;
1298
+ }
1299
+ }
1300
+ /**
1301
+ * Salva screenshot como arquivo local e integra com DeviceFarmViewer
1302
+ */
1303
+ static async saveScreenshotFile(screenshot, nome) {
1304
+ try {
1305
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
1306
+ const safeName = nome.replace(/[^a-zA-Z0-9\-_]/g, '_');
1307
+ const fileName = `mobile-${timestamp}-${safeName}.png`;
1308
+ const evidenceDir = './test-results/mobile-screenshots';
1309
+ if (!fs.existsSync(evidenceDir)) {
1310
+ fs.mkdirSync(evidenceDir, { recursive: true });
1311
+ }
1312
+ const filePath = path.join(evidenceDir, fileName);
1313
+ fs.writeFileSync(filePath, screenshot);
1314
+ if (fs.existsSync(filePath)) {
1315
+ const fileSize = fs.statSync(filePath).size;
1316
+ Logger.success(`💾 Screenshot salvo: ${filePath} (${fileSize} bytes)`);
1317
+ // ✅ INTEGRAÇÃO: Adicionar ao DeviceFarmViewer para visualização em tempo real
1318
+ try {
1319
+ DeviceFarmViewer.addScreenshot(fileName, nome);
1320
+ Logger.info(`📺 Screenshot adicionado ao viewer: ${nome}`);
1321
+ }
1322
+ catch (viewerError) {
1323
+ // Não falhar se viewer não estiver disponível
1324
+ Logger.info(`📺 Viewer não disponível: ${String(viewerError)}`);
1325
+ }
1326
+ }
1327
+ }
1328
+ catch (saveError) {
1329
+ Logger.error(`❌ Erro ao salvar screenshot: ${String(saveError)}`);
1330
+ }
1331
+ }
1332
+ /**
1333
+ * Anexa screenshot ao relatório Playwright
1334
+ */
1335
+ static async attachScreenshotToReport(screenshot, nome) {
1336
+ try {
1337
+ const testInfo = TestContext.testInfo;
1338
+ if (testInfo) {
1339
+ await testInfo.attach(`screenshot-mobile-${nome}`, {
1340
+ body: screenshot,
1341
+ contentType: 'image/png',
1342
+ });
1343
+ Logger.info('📎 Screenshot anexado ao Playwright');
1344
+ }
1345
+ }
1346
+ catch (attachError) {
1347
+ Logger.warning(`⚠️ Erro ao anexar: ${String(attachError)}`);
1348
+ }
1349
+ }
1350
+ /**
1351
+ * Registra screenshot no sistema unificado
1352
+ */
1353
+ static logScreenshotToUnified(nome, size) {
1354
+ try {
1355
+ const testInfo = TestContext.getSafeTestInfo();
1356
+ if (testInfo?.title) {
1357
+ UnifiedReportManager.adicionarLog(testInfo.title, `📸 Screenshot mobile: ${nome} (${size} bytes)`);
1358
+ }
1359
+ }
1360
+ catch {
1361
+ // Silencioso
1362
+ }
1363
+ }
1364
+ /**
1365
+ * ✅ MELHORADO: Captura screenshot forçado com verificação DeviceFarm
1366
+ * @param nome Nome do screenshot
1367
+ * @returns Promise<boolean> True se conseguiu capturar
1368
+ */
1369
+ static async capturarScreenshotForcado(nome) {
1370
+ const EXTENDED_STABILIZATION = 2000;
1371
+ const MIN_VALID_SIZE = 1000;
1372
+ try {
1373
+ Logger.info(`🔍 Iniciando captura forçada: ${nome}`);
1374
+ // 1. Verificar conectividade DeviceFarm
1375
+ const isVisuallyActive = await MobileConnection.verifyDeviceFarmVisualConnection();
1376
+ if (!isVisuallyActive) {
1377
+ Logger.warning('⚠️ DeviceFarm não está visualmente ativo');
1378
+ }
1379
+ // 2. Aguardar estabilização extra
1380
+ const driver = MobileActions.ensureDriverExists();
1381
+ await driver.pause(EXTENDED_STABILIZATION);
1382
+ // 3. Tentar múltiplas vezes
1383
+ const maxAttempts = 3;
1384
+ for (let attempts = 1; attempts <= maxAttempts; attempts++) {
1385
+ Logger.info(`📸 Tentativa ${attempts}/${maxAttempts} de captura forçada`);
1386
+ // ✅ USAR MobileConnection.captureEvidence para integração com Playwright Report
1387
+ const screenshot = await MobileConnection.captureEvidence(`${nome}_attempt${attempts}`);
1388
+ if (screenshot) {
1389
+ Logger.success(`Screenshot forçado capturado na tentativa ${attempts}`);
1390
+ return true;
1391
+ }
1392
+ if (attempts < maxAttempts) {
1393
+ Logger.warning('⚠️ Tentativa falhou, aguardando para próxima...');
1394
+ await driver.pause(EXTENDED_STABILIZATION);
1395
+ }
1396
+ }
1397
+ Logger.error(`❌ Falha em todas as tentativas de screenshot forçado: ${nome}`);
1398
+ return false;
1399
+ }
1400
+ catch (error) {
1401
+ Logger.error(`❌ Erro na captura forçada: ${String(error)}`);
1402
+ return false;
1403
+ }
1404
+ }
1405
+ /**
1406
+ * Valida se dois valores são iguais.
1407
+ * @param atual Valor atual.
1408
+ * @param esperado Valor esperado.
1409
+ * @param descricao Descrição da validação.
1410
+ */
1411
+ static async validateEquals(atual, esperado, descricao) {
1412
+ await MobileActions.executarComLog(descricao, async () => {
1413
+ if (atual === esperado) {
1414
+ Logger.success(`✅ Validação passou: ${descricao} (${atual} === ${esperado})`);
1415
+ }
1416
+ else {
1417
+ const errorMsg = `❌ Validação falhou: ${descricao} - Esperado: "${esperado}", Atual: "${atual}"`;
1418
+ Logger.error(errorMsg);
1419
+ throw new Error(errorMsg);
1420
+ }
1421
+ });
1422
+ }
1423
+ // ========================================
1424
+ // 🍎 iOS DEVICEFARM HELPERS INTEGRADOS
1425
+ // ========================================
1426
+ /**
1427
+ * 🎯 SELETOR PARA O ELEMENTO Wi-Fi (iOS DeviceFarm)
1428
+ * Baseado nas propriedades: type=XCUIElementTypeCell, name=Wi‑Fi, value=VivoQA_5G
1429
+ */
1430
+ static getWiFiSettingSelector() {
1431
+ // Múltiplas estratégias de seletor para máxima compatibilidade
1432
+ return [
1433
+ // 1. Por accessibility id (name)
1434
+ '~Wi‑Fi',
1435
+ // 2. Por valor específico (VivoQA_5G)
1436
+ '**/XCUIElementTypeCell[`value == "VivoQA_5G"`]',
1437
+ // 3. Por label
1438
+ '**/XCUIElementTypeCell[`label == "Wi‑Fi"`]',
1439
+ // 4. Por XPath específico (posição)
1440
+ '//XCUIElementTypeTable/XCUIElementTypeCell[5]',
1441
+ // 5. Por combinação de propriedades
1442
+ '**/XCUIElementTypeCell[`name == "Wi‑Fi" AND value == "VivoQA_5G"`]',
1443
+ ][0]; // Usar o primeiro (mais confiável)
1444
+ }
1445
+ /**
1446
+ * 🎯 SELETORES ALTERNATIVOS para fallback (iOS)
1447
+ */
1448
+ static getWiFiFallbackSelectors() {
1449
+ return [
1450
+ '~Wi‑Fi', // accessibility id
1451
+ '**/XCUIElementTypeCell[`value == "VivoQA_5G"`]', // por valor
1452
+ '**/XCUIElementTypeCell[`label == "Wi‑Fi"`]', // por label
1453
+ '//XCUIElementTypeTable/XCUIElementTypeCell[5]', // xpath posição
1454
+ '**/XCUIElementTypeCell[`name CONTAINS "Wi"`]', // nome parcial
1455
+ '(//XCUIElementTypeCell)[5]', // índice simples
1456
+ '**/XCUIElementTypeCell[`visible == 1 AND enabled == 1`][@name="Wi‑Fi"]', // completo
1457
+ ];
1458
+ }
1459
+ /**
1460
+ * 🔍 CLIQUE INTELIGENTE no elemento Wi-Fi com múltiplas estratégias (iOS)
1461
+ */
1462
+ static async clickWiFiSetting(descriptionToAction = 'Clique no elemento Wi-Fi (iOS DeviceFarm)', getSnapshot = true) {
1463
+ const driver = MobileActions.ensureDriverExists();
1464
+ const selectors = MobileActions.getWiFiFallbackSelectors();
1465
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1466
+ for (let i = 0; i < selectors.length; i++) {
1467
+ const selector = selectors[i];
1468
+ try {
1469
+ Logger.info(`Tentativa ${i + 1}/${selectors.length}: ${selector}`);
1470
+ // Aguardar elemento aparecer
1471
+ const element = await driver.$(selector);
1472
+ await element.waitForExist({ timeout: 10_000 });
1473
+ // Verificar se está visível
1474
+ const isDisplayed = await element.isDisplayed();
1475
+ if (!isDisplayed) {
1476
+ Logger.info('Elemento encontrado mas não visível');
1477
+ continue;
1478
+ }
1479
+ // Verificar se está habilitado
1480
+ const isEnabled = await element.isEnabled();
1481
+ if (!isEnabled) {
1482
+ Logger.info('Elemento encontrado mas não habilitado');
1483
+ continue;
1484
+ }
1485
+ // Realizar clique
1486
+ await element.click();
1487
+ Logger.info(`Clique realizado com sucesso usando: ${selector}`);
1488
+ // Aguardar transição
1489
+ await driver.pause(2000);
1490
+ if (getSnapshot) {
1491
+ await MobileConnection.captureEvidence(descriptionToAction);
1492
+ }
1493
+ return true;
1494
+ }
1495
+ catch (error) {
1496
+ Logger.error(`Falha com seletor ${i + 1}: ${String(error).substring(0, 100)}...`);
1497
+ }
1498
+ }
1499
+ Logger.error('Todos os seletores falharam para o elemento Wi-Fi');
1500
+ return false;
1501
+ });
1502
+ }
1503
+ /**
1504
+ * 🔍 CLIQUE POR COORDENADAS do elemento Wi-Fi (iOS)
1505
+ * Baseado nas propriedades: x=20, y=526, width=390, height=45
1506
+ */
1507
+ static async clickWiFiByCoordinates(descriptionToAction = 'Clique Wi-Fi por coordenadas (iOS)', getSnapshot = true) {
1508
+ const driver = MobileActions.ensureDriverExists();
1509
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1510
+ try {
1511
+ // Coordenadas originais do elemento
1512
+ const x = 20;
1513
+ const y = 526;
1514
+ const width = 390;
1515
+ const height = 45;
1516
+ // Calcular centro do elemento
1517
+ const centerX = x + width / 2; // 20 + 195 = 215
1518
+ const centerY = y + height / 2; // 526 + 22.5 = 548.5
1519
+ Logger.info(`Clicando nas coordenadas: (${centerX}, ${centerY})`);
1520
+ // Realizar clique por coordenadas
1521
+ await driver.touchAction([
1522
+ { action: 'tap', x: Math.round(centerX), y: Math.round(centerY) },
1523
+ ]);
1524
+ Logger.success('✅ Clique por coordenadas realizado');
1525
+ await driver.pause(2000);
1526
+ if (getSnapshot) {
1527
+ await MobileConnection.captureEvidence(descriptionToAction);
1528
+ }
1529
+ return true;
1530
+ }
1531
+ catch (error) {
1532
+ Logger.error(`❌ Falha no clique por coordenadas: ${String(error)}`);
1533
+ return false;
1534
+ }
1535
+ });
1536
+ }
1537
+ /**
1538
+ * 🔍 ESTRATÉGIA COMBINADA: Tentar seletores primeiro, depois coordenadas (iOS)
1539
+ */
1540
+ static async clickWiFiCombined(descriptionToAction = 'Clique combinado Wi-Fi (iOS DeviceFarm)', getSnapshot = true) {
1541
+ return await MobileActions.executarComLog(descriptionToAction, async () => {
1542
+ Logger.info('🍎 Iniciando clique no elemento Wi-Fi (VivoQA_5G)');
1543
+ // 1. Tentar seletores inteligentes
1544
+ const selectorSuccess = await MobileActions.clickWiFiSetting('Clique Wi-Fi por seletores', false);
1545
+ if (selectorSuccess) {
1546
+ if (getSnapshot) {
1547
+ await MobileConnection.captureEvidence(descriptionToAction);
1548
+ }
1549
+ return true;
1550
+ }
1551
+ Logger.info('🎯 Fallback: Tentando clique por coordenadas...');
1552
+ // 2. Fallback para coordenadas
1553
+ const coordinatesSuccess = await MobileActions.clickWiFiByCoordinates('Fallback clique Wi-Fi por coordenadas', false);
1554
+ if (coordinatesSuccess) {
1555
+ if (getSnapshot) {
1556
+ await MobileConnection.captureEvidence(descriptionToAction);
1557
+ }
1558
+ return true;
1559
+ }
1560
+ Logger.error('❌ Todas as estratégias falharam para o elemento Wi-Fi');
1561
+ return false;
1562
+ });
1563
+ }
1564
+ /**
1565
+ * 🔍 VERIFICAR se elemento Wi-Fi está presente na tela (iOS)
1566
+ */
1567
+ static async isWiFiSettingVisible() {
1568
+ const driver = MobileActions.ensureDriverExists();
1569
+ const selectors = MobileActions.getWiFiFallbackSelectors();
1570
+ for (const selector of selectors) {
1571
+ try {
1572
+ const element = await driver.$(selector);
1573
+ const exists = await element.isExisting();
1574
+ const displayed = exists ? await element.isDisplayed() : false;
1575
+ if (exists && displayed) {
1576
+ Logger.success(`✅ Elemento Wi-Fi encontrado com: ${selector}`);
1577
+ return true;
1578
+ }
1579
+ }
1580
+ catch {
1581
+ // Continuar tentando
1582
+ }
1583
+ }
1584
+ Logger.error('❌ Elemento Wi-Fi não encontrado na tela');
1585
+ return false;
1586
+ }
1587
+ /**
1588
+ * 🔍 OBTER informações do elemento Wi-Fi (iOS)
1589
+ */
1590
+ static async getWiFiElementInfo() {
1591
+ const driver = MobileActions.ensureDriverExists();
1592
+ const selector = MobileActions.getWiFiSettingSelector();
1593
+ try {
1594
+ const element = await driver.$(selector);
1595
+ await element.waitForExist({ timeout: 5000 });
1596
+ const rect = await element.getLocation();
1597
+ const size = await element.getSize();
1598
+ const isDisplayed = await element.isDisplayed();
1599
+ const isEnabled = await element.isEnabled();
1600
+ const text = await element.getText().catch(() => 'N/A');
1601
+ const value = await element.getValue().catch(() => 'N/A');
1602
+ return {
1603
+ selector,
1604
+ rect: { x: rect.x, y: rect.y, width: size.width, height: size.height },
1605
+ isDisplayed,
1606
+ isEnabled,
1607
+ text,
1608
+ value,
1609
+ coordinates: {
1610
+ centerX: rect.x + size.width / 2,
1611
+ centerY: rect.y + size.height / 2,
1612
+ },
1613
+ };
1614
+ }
1615
+ catch (error) {
1616
+ Logger.error(`❌ Erro ao obter informações: ${String(error)}`);
1617
+ return null;
1618
+ }
1619
+ }
1620
+ }
1621
+ // 🚫 AUTO-ATIVAÇÃO REMOVIDA: MobileActions são métodos internos que não devem ser interceptados
1622
+ // MobileActions.tap(), .swipe(), etc. não são CTs válidos e devem ser filtrados
1623
+ // A interceptação foi removida para evitar falsos CTs nos relatórios
1624
+ // CTs reais vêm das classes Statement (StatementsMobile, etc.) via InterceptacaoMagica.ts
1625
+ // CÓDIGO REMOVIDO:
1626
+ // try {
1627
+ // installDatabaseInterceptor(MobileActions, 'MobileActions')
1628
+ // } catch (error) {
1629
+ // if (process.env.AUTOCORE_DEBUG_LOGS === 'true') {
1630
+ // process.stderr.write(`[DatabaseInterceptor] Falha na auto-ativação MobileActions: ${String(error)}\n`)
1631
+ // }
1632
+ // }