@browserstack/mcp-server 1.2.3 → 1.2.4

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 (157) hide show
  1. package/README.md +88 -2
  2. package/dist/lib/device-cache.js +20 -17
  3. package/dist/lib/inmemory-store.d.ts +1 -0
  4. package/dist/lib/inmemory-store.js +1 -0
  5. package/dist/lib/utils.d.ts +5 -0
  6. package/dist/lib/utils.js +27 -0
  7. package/dist/server-factory.js +6 -0
  8. package/dist/tools/add-percy-snapshots.d.ts +5 -0
  9. package/dist/tools/add-percy-snapshots.js +17 -0
  10. package/dist/tools/appautomate-utils/appium-sdk/config-generator.d.ts +1 -0
  11. package/dist/tools/appautomate-utils/appium-sdk/config-generator.js +50 -0
  12. package/dist/tools/appautomate-utils/appium-sdk/constants.d.ts +23 -0
  13. package/dist/tools/appautomate-utils/appium-sdk/constants.js +43 -0
  14. package/dist/tools/appautomate-utils/appium-sdk/formatter.d.ts +8 -0
  15. package/dist/tools/appautomate-utils/appium-sdk/formatter.js +59 -0
  16. package/dist/tools/appautomate-utils/appium-sdk/handler.d.ts +3 -0
  17. package/dist/tools/appautomate-utils/appium-sdk/handler.js +52 -0
  18. package/dist/tools/appautomate-utils/appium-sdk/index.d.ts +7 -0
  19. package/dist/tools/appautomate-utils/appium-sdk/index.js +8 -0
  20. package/dist/tools/appautomate-utils/appium-sdk/instructions.d.ts +3 -0
  21. package/dist/tools/appautomate-utils/appium-sdk/instructions.js +47 -0
  22. package/dist/tools/appautomate-utils/appium-sdk/languages/csharp.d.ts +2 -0
  23. package/dist/tools/appautomate-utils/appium-sdk/languages/csharp.js +78 -0
  24. package/dist/tools/appautomate-utils/appium-sdk/languages/java.d.ts +8 -0
  25. package/dist/tools/appautomate-utils/appium-sdk/languages/java.js +87 -0
  26. package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.d.ts +3 -0
  27. package/dist/tools/appautomate-utils/appium-sdk/languages/nodejs.js +194 -0
  28. package/dist/tools/appautomate-utils/appium-sdk/languages/python.d.ts +3 -0
  29. package/dist/tools/appautomate-utils/appium-sdk/languages/python.js +76 -0
  30. package/dist/tools/appautomate-utils/appium-sdk/languages/ruby.d.ts +2 -0
  31. package/dist/tools/appautomate-utils/appium-sdk/languages/ruby.js +85 -0
  32. package/dist/tools/appautomate-utils/appium-sdk/types.d.ts +57 -0
  33. package/dist/tools/appautomate-utils/appium-sdk/types.js +54 -0
  34. package/dist/tools/appautomate-utils/appium-sdk/utils.d.ts +17 -0
  35. package/dist/tools/appautomate-utils/appium-sdk/utils.js +64 -0
  36. package/dist/tools/appautomate-utils/{appautomate.d.ts → native-execution/appautomate.d.ts} +1 -1
  37. package/dist/tools/appautomate-utils/{appautomate.js → native-execution/appautomate.js} +2 -2
  38. package/dist/tools/appautomate-utils/native-execution/constants.d.ts +10 -0
  39. package/dist/tools/appautomate-utils/native-execution/constants.js +36 -0
  40. package/dist/tools/appautomate-utils/native-execution/types.d.ts +19 -0
  41. package/dist/tools/appautomate-utils/{types.js → native-execution/types.js} +5 -1
  42. package/dist/tools/appautomate.js +25 -40
  43. package/dist/tools/bstack-sdk.d.ts +2 -15
  44. package/dist/tools/bstack-sdk.js +10 -119
  45. package/dist/tools/build-insights.d.ts +7 -0
  46. package/dist/tools/build-insights.js +67 -0
  47. package/dist/tools/list-test-files.d.ts +2 -0
  48. package/dist/tools/list-test-files.js +36 -0
  49. package/dist/tools/percy-sdk.d.ts +4 -0
  50. package/dist/tools/percy-sdk.js +71 -0
  51. package/dist/tools/percy-snapshot-utils/constants.d.ts +16 -0
  52. package/dist/tools/percy-snapshot-utils/constants.js +500 -0
  53. package/dist/tools/percy-snapshot-utils/detect-test-files.d.ts +10 -0
  54. package/dist/tools/percy-snapshot-utils/detect-test-files.js +175 -0
  55. package/dist/tools/percy-snapshot-utils/types.d.ts +15 -0
  56. package/dist/tools/percy-snapshot-utils/utils.d.ts +4 -0
  57. package/dist/tools/percy-snapshot-utils/utils.js +30 -0
  58. package/dist/tools/rca-agent-utils/constants.d.ts +13 -0
  59. package/dist/tools/rca-agent-utils/constants.js +24 -0
  60. package/dist/tools/rca-agent-utils/format-rca.d.ts +1 -0
  61. package/dist/tools/rca-agent-utils/format-rca.js +37 -0
  62. package/dist/tools/rca-agent-utils/get-build-id.d.ts +1 -0
  63. package/dist/tools/rca-agent-utils/get-build-id.js +18 -0
  64. package/dist/tools/rca-agent-utils/get-failed-test-id.d.ts +2 -0
  65. package/dist/tools/rca-agent-utils/get-failed-test-id.js +69 -0
  66. package/dist/tools/rca-agent-utils/rca-data.d.ts +9 -0
  67. package/dist/tools/rca-agent-utils/rca-data.js +196 -0
  68. package/dist/tools/rca-agent-utils/types.d.ts +48 -0
  69. package/dist/tools/rca-agent-utils/types.js +20 -0
  70. package/dist/tools/rca-agent.d.ts +14 -0
  71. package/dist/tools/rca-agent.js +119 -0
  72. package/dist/tools/review-agent-utils/build-counts.d.ts +7 -0
  73. package/dist/tools/review-agent-utils/build-counts.js +44 -0
  74. package/dist/tools/review-agent-utils/percy-approve-reject.d.ts +6 -0
  75. package/dist/tools/review-agent-utils/percy-approve-reject.js +39 -0
  76. package/dist/tools/review-agent-utils/percy-diffs.d.ts +9 -0
  77. package/dist/tools/review-agent-utils/percy-diffs.js +35 -0
  78. package/dist/tools/review-agent-utils/percy-snapshots.d.ts +11 -0
  79. package/dist/tools/review-agent-utils/percy-snapshots.js +58 -0
  80. package/dist/tools/review-agent.d.ts +5 -0
  81. package/dist/tools/review-agent.js +56 -0
  82. package/dist/tools/run-percy-scan.d.ts +8 -0
  83. package/dist/tools/run-percy-scan.js +37 -0
  84. package/dist/tools/sdk-utils/{commands.d.ts → bstack/commands.d.ts} +1 -1
  85. package/dist/tools/sdk-utils/bstack/commands.js +88 -0
  86. package/dist/tools/sdk-utils/bstack/configUtils.d.ts +4 -0
  87. package/dist/tools/sdk-utils/bstack/configUtils.js +66 -0
  88. package/dist/tools/sdk-utils/bstack/constants.d.ts +58 -0
  89. package/dist/tools/sdk-utils/{constants.js → bstack/constants.js} +117 -78
  90. package/dist/tools/sdk-utils/{constants.d.ts → bstack/frameworks.d.ts} +1 -1
  91. package/dist/tools/sdk-utils/bstack/frameworks.js +57 -0
  92. package/dist/tools/sdk-utils/bstack/index.d.ts +4 -0
  93. package/dist/tools/sdk-utils/bstack/index.js +5 -0
  94. package/dist/tools/sdk-utils/bstack/sdkHandler.d.ts +4 -0
  95. package/dist/tools/sdk-utils/bstack/sdkHandler.js +74 -0
  96. package/dist/tools/sdk-utils/common/constants.d.ts +10 -0
  97. package/dist/tools/sdk-utils/common/constants.js +86 -0
  98. package/dist/tools/sdk-utils/common/formatUtils.d.ts +5 -0
  99. package/dist/tools/sdk-utils/common/formatUtils.js +27 -0
  100. package/dist/tools/sdk-utils/common/index.d.ts +3 -0
  101. package/dist/tools/sdk-utils/common/index.js +4 -0
  102. package/dist/tools/sdk-utils/common/instructionUtils.d.ts +8 -0
  103. package/dist/tools/sdk-utils/common/instructionUtils.js +20 -0
  104. package/dist/tools/sdk-utils/common/schema.d.ts +73 -0
  105. package/dist/tools/sdk-utils/common/schema.js +51 -0
  106. package/dist/tools/sdk-utils/common/types.d.ts +66 -0
  107. package/dist/tools/sdk-utils/{types.js → common/types.js} +15 -2
  108. package/dist/tools/sdk-utils/common/utils.d.ts +25 -0
  109. package/dist/tools/sdk-utils/common/utils.js +90 -0
  110. package/dist/tools/sdk-utils/handler.d.ts +4 -0
  111. package/dist/tools/sdk-utils/handler.js +119 -0
  112. package/dist/tools/sdk-utils/percy-automate/constants.d.ts +11 -0
  113. package/dist/tools/sdk-utils/percy-automate/constants.js +338 -0
  114. package/dist/tools/sdk-utils/percy-automate/frameworks.d.ts +8 -0
  115. package/dist/tools/sdk-utils/percy-automate/frameworks.js +50 -0
  116. package/dist/tools/sdk-utils/percy-automate/handler.d.ts +3 -0
  117. package/dist/tools/sdk-utils/percy-automate/handler.js +30 -0
  118. package/dist/tools/sdk-utils/percy-automate/index.d.ts +1 -0
  119. package/dist/tools/sdk-utils/percy-automate/index.js +2 -0
  120. package/dist/tools/sdk-utils/percy-automate/types.d.ts +13 -0
  121. package/dist/tools/sdk-utils/percy-automate/types.js +1 -0
  122. package/dist/tools/sdk-utils/percy-bstack/constants.d.ts +4 -0
  123. package/dist/tools/sdk-utils/{percy → percy-bstack}/constants.js +13 -39
  124. package/dist/tools/sdk-utils/percy-bstack/frameworks.d.ts +2 -0
  125. package/dist/tools/sdk-utils/percy-bstack/frameworks.js +27 -0
  126. package/dist/tools/sdk-utils/percy-bstack/handler.d.ts +4 -0
  127. package/dist/tools/sdk-utils/percy-bstack/handler.js +99 -0
  128. package/dist/tools/sdk-utils/percy-bstack/index.d.ts +4 -0
  129. package/dist/tools/sdk-utils/percy-bstack/index.js +4 -0
  130. package/dist/tools/sdk-utils/percy-bstack/instructions.d.ts +7 -0
  131. package/dist/tools/sdk-utils/{percy → percy-bstack}/instructions.js +5 -9
  132. package/dist/tools/sdk-utils/percy-bstack/types.d.ts +13 -0
  133. package/dist/tools/sdk-utils/percy-bstack/types.js +5 -0
  134. package/dist/tools/sdk-utils/percy-web/constants.d.ts +41 -0
  135. package/dist/tools/sdk-utils/percy-web/constants.js +883 -0
  136. package/dist/tools/sdk-utils/percy-web/fetchPercyToken.d.ts +4 -0
  137. package/dist/tools/sdk-utils/percy-web/fetchPercyToken.js +32 -0
  138. package/dist/tools/sdk-utils/percy-web/frameworks.d.ts +7 -0
  139. package/dist/tools/sdk-utils/percy-web/frameworks.js +103 -0
  140. package/dist/tools/sdk-utils/percy-web/handler.d.ts +4 -0
  141. package/dist/tools/sdk-utils/percy-web/handler.js +27 -0
  142. package/dist/tools/sdk-utils/percy-web/index.d.ts +4 -0
  143. package/dist/tools/sdk-utils/percy-web/index.js +4 -0
  144. package/dist/tools/sdk-utils/percy-web/types.d.ts +12 -0
  145. package/dist/tools/sdk-utils/percy-web/types.js +1 -0
  146. package/dist/tools/testmanagement-utils/create-testrun.d.ts +4 -4
  147. package/dist/tools/testmanagement-utils/update-testrun.d.ts +4 -4
  148. package/package.json +2 -1
  149. package/dist/tools/appautomate-utils/types.d.ts +0 -5
  150. package/dist/tools/sdk-utils/commands.js +0 -65
  151. package/dist/tools/sdk-utils/instructions.d.ts +0 -6
  152. package/dist/tools/sdk-utils/instructions.js +0 -99
  153. package/dist/tools/sdk-utils/percy/constants.d.ts +0 -3
  154. package/dist/tools/sdk-utils/percy/instructions.d.ts +0 -10
  155. package/dist/tools/sdk-utils/percy/types.d.ts +0 -5
  156. package/dist/tools/sdk-utils/types.d.ts +0 -40
  157. /package/dist/tools/{sdk-utils/percy → percy-snapshot-utils}/types.js +0 -0
@@ -0,0 +1,500 @@
1
+ import { z } from "zod";
2
+ import { SDKSupportedLanguages, SDKSupportedTestingFrameworks, } from "../sdk-utils/common/types.js";
3
+ export const UpdateTestFileWithInstructionsParams = {
4
+ uuid: z
5
+ .string()
6
+ .describe("UUID referencing the in-memory array of test file paths"),
7
+ index: z.number().describe("Index of the test file to update"),
8
+ };
9
+ export const ListTestFilesParamsShape = {
10
+ dirs: z
11
+ .array(z.string())
12
+ .describe("Array of directory paths to search for test files"),
13
+ language: z
14
+ .enum(SDKSupportedLanguages)
15
+ .describe("Programming language"),
16
+ framework: z
17
+ .enum(SDKSupportedTestingFrameworks)
18
+ .describe("Testing framework (optional)"),
19
+ };
20
+ export const TEST_FILE_DETECTION = {
21
+ java: {
22
+ extensions: [".java"],
23
+ namePatterns: [
24
+ /Test\.java$/,
25
+ /Tests\.java$/,
26
+ /Steps\.java$/,
27
+ /.*UI.*Test\.java$/,
28
+ /.*Web.*Test\.java$/,
29
+ /.*E2E.*Test\.java$/,
30
+ /.*Integration.*Test\.java$/,
31
+ /.*Functional.*Test\.java$/,
32
+ ],
33
+ contentRegex: [
34
+ /@Test\b/,
35
+ /@RunWith\b/,
36
+ /@CucumberOptions\b/,
37
+ /import\s+org\.junit/,
38
+ /import\s+org\.testng/,
39
+ /import\s+io\.cucumber/,
40
+ /import\s+org\.jbehave/,
41
+ ],
42
+ uiDriverRegex: [
43
+ /import\s+org\.openqa\.selenium/,
44
+ /import\s+org\.seleniumhq\.selenium/,
45
+ /import\s+io\.appium\.java_client/,
46
+ /import\s+com\.microsoft\.playwright/,
47
+ /import\s+com\.codeborne\.selenide/,
48
+ /import\s+net\.serenitybdd/,
49
+ /import\s+cucumber\.api\.java\.en/,
50
+ /new\s+\w*Driver\s*\(/,
51
+ /\.findElement\s*\(/,
52
+ /\.get\s*\(['"]https?:/,
53
+ /\.click\s*\(/,
54
+ /\.navigate\(\)/,
55
+ /WebDriver/,
56
+ /RemoteWebDriver/,
57
+ /ChromeDriver/,
58
+ /FirefoxDriver/,
59
+ ],
60
+ uiIndicatorRegex: [
61
+ // UI interactions without explicit driver imports
62
+ /\.sendKeys\s*\(/,
63
+ /\.getText\s*\(/,
64
+ /\.isDisplayed\s*\(/,
65
+ /By\.id\s*\(/,
66
+ /By\.className\s*\(/,
67
+ /By\.xpath\s*\(/,
68
+ /waitForElement/,
69
+ /waitForVisible/,
70
+ /assertTitle/,
71
+ /screenshot/,
72
+ /captureScreenshot/,
73
+ // Page Object patterns
74
+ /PageObject/,
75
+ /BasePage/,
76
+ /WebPage/,
77
+ // UI test annotations and patterns
78
+ /@UITest/,
79
+ /@WebTest/,
80
+ /@E2ETest/,
81
+ // Common UI assertions
82
+ /assertUrl/,
83
+ /verifyText/,
84
+ /checkElement/,
85
+ // Browser/window operations
86
+ /maximizeWindow/,
87
+ /setWindowSize/,
88
+ /switchTo/,
89
+ // Cucumber UI steps
90
+ /Given.*I\s+(open|visit|navigate)/,
91
+ /When.*I\s+(click|type|select)/,
92
+ /Then.*I\s+(see|verify|check)/,
93
+ /And.*I\s+(wait|scroll)/,
94
+ ],
95
+ backendRegex: [
96
+ /import\s+org\.springframework\.test/,
97
+ /import\s+javax\.persistence/,
98
+ /@DataJpaTest/,
99
+ /@WebMvcTest/,
100
+ /@MockBean/,
101
+ /EntityManager/,
102
+ /JdbcTemplate/,
103
+ /TestRestTemplate/,
104
+ /@Repository/,
105
+ /@Service/,
106
+ /@Entity/,
107
+ ],
108
+ excludeRegex: [
109
+ /UnitTest/,
110
+ /MockTest/,
111
+ /StubTest/,
112
+ /DatabaseTest/,
113
+ /import\s+org\.mockito/,
114
+ /@Mock\b/,
115
+ /@Spy\b/,
116
+ ],
117
+ },
118
+ csharp: {
119
+ extensions: [".cs"],
120
+ namePatterns: [
121
+ /Test\.cs$/,
122
+ /Tests\.cs$/,
123
+ /Steps\.cs$/,
124
+ /.*UI.*Test\.cs$/,
125
+ /.*Web.*Test\.cs$/,
126
+ /.*E2E.*Test\.cs$/,
127
+ ],
128
+ contentRegex: [
129
+ /\[Test\]/,
130
+ /\[TestCase\]/,
131
+ /\[Fact\]/,
132
+ /\[Theory\]/,
133
+ /\[Binding\]/,
134
+ /using\s+NUnit\.Framework/,
135
+ /using\s+Xunit/,
136
+ /using\s+TechTalk\.SpecFlow/,
137
+ ],
138
+ uiDriverRegex: [
139
+ /using\s+OpenQA\.Selenium/,
140
+ /using\s+Appium/,
141
+ /using\s+Microsoft\.Playwright/,
142
+ /using\s+Selenide/,
143
+ /using\s+Atata/,
144
+ /new\s+\w*Driver\s*\(/,
145
+ /\.FindElement\s*\(/,
146
+ /\.Navigate\(\)/,
147
+ /IWebDriver/,
148
+ /WebDriver/,
149
+ ],
150
+ uiIndicatorRegex: [
151
+ /\.SendKeys\s*\(/,
152
+ /\.Click\s*\(/,
153
+ /\.Text/,
154
+ /\.Displayed/,
155
+ /By\.Id/,
156
+ /By\.ClassName/,
157
+ /By\.XPath/,
158
+ /WaitForElement/,
159
+ /TakeScreenshot/,
160
+ /PageObject/,
161
+ /\[UITest\]/,
162
+ /\[WebTest\]/,
163
+ /\[E2ETest\]/,
164
+ /NavigateTo/,
165
+ /VerifyText/,
166
+ /AssertUrl/,
167
+ ],
168
+ backendRegex: [
169
+ /using\s+Microsoft\.EntityFrameworkCore/,
170
+ /using\s+System\.Data/,
171
+ /DbContext/,
172
+ /Repository/,
173
+ /Controller/,
174
+ /\[ApiTest\]/,
175
+ /\[DatabaseTest\]/,
176
+ ],
177
+ excludeRegex: [/\[UnitTest\]/, /Mock/, /Stub/, /using\s+Moq/],
178
+ },
179
+ nodejs: {
180
+ extensions: [".js", ".ts"],
181
+ namePatterns: [
182
+ /.test.js$/,
183
+ /.spec.js$/,
184
+ /.test.ts$/,
185
+ /.spec.ts$/,
186
+ /.*ui.*.test.(js|ts)$/,
187
+ /.*web.*.test.(js|ts)$/,
188
+ /.*e2e.*.(js|ts)$/,
189
+ /.*integration.*.test.(js|ts)$/,
190
+ ],
191
+ contentRegex: [
192
+ /\bdescribe\s*\(/,
193
+ /\bit\s*\(/,
194
+ /\btest\s*\(/,
195
+ /require\(['"]mocha['"]\)/,
196
+ /require\(['"]jest['"]\)/,
197
+ /import.*from\s+['"]jest['"]/,
198
+ /from\s+['"]@jest/,
199
+ ],
200
+ uiDriverRegex: [
201
+ /require\(['"]selenium-webdriver['"]\)/,
202
+ /require\(['"]webdriverio['"]\)/,
203
+ /require\(['"]puppeteer['"]\)/,
204
+ /require\(['"]playwright['"]\)/,
205
+ /require\(['"]cypress['"]\)/,
206
+ /require\(['"]@wdio\/sync['"]\)/,
207
+ /import.*from\s+['"]selenium-webdriver['"]/,
208
+ /import.*from\s+['"]webdriverio['"]/,
209
+ /import.*from\s+['"]puppeteer['"]/,
210
+ /import.*from\s+['"]playwright['"]/,
211
+ /import.*from\s+['"]cypress['"]/,
212
+ /import.*from\s+['"]@wdio/,
213
+ /\.launch\(/,
214
+ /\.goto\(/,
215
+ /driver\./,
216
+ /browser\./,
217
+ ],
218
+ uiIndicatorRegex: [
219
+ // Browser automation - SPECIFIC CONTEXT
220
+ /driver\.click\(/,
221
+ /driver\.type\(/,
222
+ /driver\.fill\(/,
223
+ /browser\.click\(/,
224
+ /driver\.waitForSelector\(/,
225
+ /browser\.waitForElement\(/,
226
+ /driver\.screenshot\(/,
227
+ /browser\.screenshot\(/,
228
+ /driver\.evaluate\(/,
229
+ /driver\.focus\(/,
230
+ /driver\.hover\(/,
231
+ // Page object patterns - UI specific
232
+ /page\.goto/,
233
+ /page\.click/,
234
+ /page\.fill/,
235
+ /page\.screenshot/,
236
+ /page\.waitForSelector/,
237
+ /page\.locator/,
238
+ /page\.getByRole/,
239
+ // Cypress specific patterns
240
+ /cy\.visit/,
241
+ /cy\.get/,
242
+ /cy\.click/,
243
+ /cy\.type/,
244
+ /cy\.should/,
245
+ /cy\.wait/,
246
+ /cy\.screenshot/,
247
+ /cy\.viewport/,
248
+ // WebDriverIO specific patterns
249
+ /browser\.url/,
250
+ /browser\.click/,
251
+ /browser\.setValue/,
252
+ /\$\(['"][#.]/,
253
+ /\$\$\(['"][#.]/, // CSS/XPath selectors
254
+ // Playwright specific
255
+ /expect.*toBeVisible/,
256
+ /expect.*toHaveText/,
257
+ /expect.*toBeEnabled/,
258
+ /locator\(/,
259
+ /getByText\(/,
260
+ /getByRole\(/,
261
+ /getByTestId\(/,
262
+ // DOM queries in test context
263
+ /findElement/,
264
+ /querySelector.*\)\.click/,
265
+ /getElementById.*\)\.click/,
266
+ // Test descriptions clearly indicating UI
267
+ /describe.*['"`].*UI/,
268
+ /describe.*['"`].*Web/,
269
+ /describe.*['"`].*E2E/,
270
+ /describe.*['"`].*Browser/,
271
+ /describe.*['"`].*Selenium/,
272
+ /it.*['"`].*(click|type|navigate|visit|see).*element/,
273
+ /it.*['"`].*(open|load).*page/,
274
+ /it.*['"`].*browser/,
275
+ ],
276
+ backendRegex: [
277
+ /require\(['"]express['"]\)/,
278
+ /require\(['"]fastify['"]\)/,
279
+ /require\(['"]supertest['"]\)/,
280
+ /request\(app\)/,
281
+ /mongoose/,
282
+ /sequelize/,
283
+ /prisma/,
284
+ /knex/,
285
+ /app\.get\(/,
286
+ /app\.post\(/,
287
+ /server\./,
288
+ /\.connect\(/,
289
+ /\.query\(/,
290
+ ],
291
+ excludeRegex: [
292
+ /\.unit\./,
293
+ /\.mock\./,
294
+ /jest\.mock/,
295
+ /sinon/,
296
+ /describe.*['"`]Unit/,
297
+ /describe.*['"`]Mock/,
298
+ ],
299
+ },
300
+ python: {
301
+ extensions: [".py"],
302
+ namePatterns: [
303
+ /^test_.*\.py$/,
304
+ /_test\.py$/,
305
+ /test.*ui.*\.py$/,
306
+ /test.*web.*\.py$/,
307
+ /test.*e2e.*\.py$/,
308
+ /test.*integration.*\.py$/,
309
+ ],
310
+ contentRegex: [
311
+ /import\s+pytest/,
312
+ /@pytest\.mark/,
313
+ /def\s+test_/,
314
+ /\bpytest\./,
315
+ /import\s+unittest/,
316
+ /class.*TestCase/,
317
+ ],
318
+ uiDriverRegex: [
319
+ /import\s+selenium/,
320
+ /from\s+selenium/,
321
+ /import\s+playwright/,
322
+ /from\s+playwright/,
323
+ /import\s+appium/,
324
+ /from\s+appium/,
325
+ /import\s+splinter/,
326
+ /from\s+splinter/,
327
+ /driver\s*=\s*webdriver\./,
328
+ /webdriver\.Chrome/,
329
+ /webdriver\.Firefox/,
330
+ ],
331
+ uiIndicatorRegex: [
332
+ // Selenium patterns without imports
333
+ /\.find_element/,
334
+ /\.click\(/,
335
+ /\.send_keys/,
336
+ /\.get\(/,
337
+ /\.screenshot/,
338
+ /\.execute_script/,
339
+ /\.switch_to/,
340
+ /By\.ID/,
341
+ /By\.CLASS_NAME/,
342
+ /By\.XPATH/,
343
+ // Playwright patterns
344
+ /page\.goto/,
345
+ /page\.click/,
346
+ /page\.fill/,
347
+ /page\.screenshot/,
348
+ /expect.*to_be_visible/,
349
+ /expect.*to_have_text/,
350
+ // Generic UI patterns
351
+ /WebDriverWait/,
352
+ /expected_conditions/,
353
+ /ActionChains/,
354
+ /@pytest\.mark\.ui/,
355
+ /@pytest\.mark\.web/,
356
+ /@pytest\.mark\.e2e/,
357
+ // Page object patterns
358
+ /BasePage/,
359
+ /PageObject/,
360
+ /WebPage/,
361
+ // BDD step patterns
362
+ /def\s+.*_(open|visit|navigate|click|type|see|verify)/,
363
+ ],
364
+ backendRegex: [
365
+ /import\s+flask/,
366
+ /from\s+flask/,
367
+ /import\s+fastapi/,
368
+ /from\s+fastapi/,
369
+ /import\s+django/,
370
+ /from\s+django/,
371
+ /sqlalchemy/,
372
+ /requests\.get/,
373
+ /requests\.post/,
374
+ /TestClient/,
375
+ /@pytest\.mark\.django_db/,
376
+ /django\.test/,
377
+ ],
378
+ excludeRegex: [
379
+ /unittest\.mock/,
380
+ /from\s+unittest\.mock/,
381
+ /mock\.patch/,
382
+ /@pytest\.mark\.unit/,
383
+ /@mock\./,
384
+ ],
385
+ },
386
+ ruby: {
387
+ extensions: [".rb"],
388
+ namePatterns: [
389
+ /_spec\.rb$/,
390
+ /_test\.rb$/,
391
+ /.*ui.*_spec\.rb$/,
392
+ /.*web.*_spec\.rb$/,
393
+ /.*e2e.*_spec\.rb$/,
394
+ ],
395
+ contentRegex: [
396
+ /\bdescribe\s/,
397
+ /\bit\s/,
398
+ /require\s+['"]rspec/,
399
+ /require\s+['"]minitest/,
400
+ /RSpec\.describe/,
401
+ ],
402
+ uiDriverRegex: [
403
+ /require\s+['"]selenium-webdriver['"]/,
404
+ /require\s+['"]capybara['"]/,
405
+ /require\s+['"]appium_lib['"]/,
406
+ /require\s+['"]watir['"]/,
407
+ /Selenium::WebDriver/,
408
+ /Capybara\./,
409
+ ],
410
+ uiIndicatorRegex: [
411
+ // Capybara without explicit require
412
+ /visit\s/,
413
+ /click_button/,
414
+ /click_link/,
415
+ /fill_in/,
416
+ /find\(['"]/,
417
+ /has_content/,
418
+ /page\./,
419
+ /current_path/,
420
+ // Selenium patterns
421
+ /\.find_element/,
422
+ /\.click/,
423
+ /\.send_keys/,
424
+ // Generic UI patterns
425
+ /screenshot/,
426
+ /driver\./,
427
+ /browser\./,
428
+ /feature\s+['"]/,
429
+ /scenario\s+['"]/,
430
+ /expect.*to\s+have_content/,
431
+ /expect.*to\s+have_selector/,
432
+ ],
433
+ backendRegex: [
434
+ /require\s+['"]sinatra['"]/,
435
+ /require\s+['"]rails['"]/,
436
+ /ActiveRecord/,
437
+ /DatabaseCleaner/,
438
+ /FactoryBot/,
439
+ ],
440
+ excludeRegex: [
441
+ /double\(/,
442
+ /instance_double/,
443
+ /class_double/,
444
+ /allow\(.*\)\.to\s+receive/,
445
+ /mock/i,
446
+ ],
447
+ },
448
+ };
449
+ export const EXCLUDED_DIRS = new Set([
450
+ "node_modules",
451
+ ".venv",
452
+ "venv",
453
+ "__pycache__",
454
+ "site-packages",
455
+ "dist",
456
+ "build",
457
+ ".git",
458
+ ".mypy_cache",
459
+ ".pytest_cache",
460
+ ".tox",
461
+ ".idea",
462
+ ".vscode",
463
+ "coverage",
464
+ ".nyc_output",
465
+ "target",
466
+ "bin",
467
+ "obj",
468
+ "packages",
469
+ ".nuget",
470
+ ]);
471
+ export const backendIndicators = [
472
+ /import\s+requests/,
473
+ /requests\.(get|post|put|delete|patch)/,
474
+ /@pytest\.mark\.(api|backend|integration)/,
475
+ /BASE_URL\s*=/,
476
+ /\.status_code/,
477
+ /\.json\(\)/,
478
+ /TestClient/,
479
+ /Bearer\s+/,
480
+ /Authorization.*Bearer/,
481
+ ];
482
+ export const strongUIIndicators = [
483
+ // Browser automation with specific context
484
+ /(driver|browser|page)\.(click|type|fill|screenshot|wait)/,
485
+ /webdriver\.(Chrome|Firefox|Safari|Edge)/,
486
+ /(selenium|playwright|puppeteer|cypress).*import/,
487
+ // CSS/XPath selectors
488
+ /By\.(ID|CLASS_NAME|XPATH|CSS_SELECTOR)/,
489
+ /\$\(['"#[.][^'"]*['"]\)/, // $(".class") or $("#id")
490
+ // Page Object Model
491
+ /class.*Page.*:/,
492
+ /class.*PageObject/,
493
+ // UI test markers
494
+ /@(ui|web|e2e|browser)_?test/,
495
+ /@pytest\.mark\.(ui|web|e2e|browser)/,
496
+ // Browser navigation
497
+ /\.goto\s*\(['"]https?:/,
498
+ /\.visit\s*\(['"]https?:/,
499
+ /\.navigate\(\)\.to\(/,
500
+ ];
@@ -0,0 +1,10 @@
1
+ import { SDKSupportedLanguage, SDKSupportedTestingFrameworkEnum } from "../sdk-utils/common/types.js";
2
+ export interface ListTestFilesOptions {
3
+ language: SDKSupportedLanguage;
4
+ framework?: SDKSupportedTestingFrameworkEnum;
5
+ baseDir: string;
6
+ strictMode?: boolean;
7
+ }
8
+ export declare function listTestFiles(options: ListTestFilesOptions): Promise<string[]>;
9
+ export declare function listUITestFilesStrict(options: Omit<ListTestFilesOptions, "strictMode">): Promise<string[]>;
10
+ export declare function listUITestFilesRelaxed(options: Omit<ListTestFilesOptions, "strictMode">): Promise<string[]>;
@@ -0,0 +1,175 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { EXCLUDED_DIRS, TEST_FILE_DETECTION, backendIndicators, strongUIIndicators, } from "../percy-snapshot-utils/constants.js";
4
+ import logger from "../../logger.js";
5
+ async function walkDir(dir, extensions, depth = 6) {
6
+ const result = [];
7
+ if (depth < 0)
8
+ return result;
9
+ try {
10
+ const entries = await fs.promises.readdir(dir, { withFileTypes: true });
11
+ for (const entry of entries) {
12
+ const fullPath = path.join(dir, entry.name);
13
+ if (entry.isDirectory()) {
14
+ if (!EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
15
+ result.push(...(await walkDir(fullPath, extensions, depth - 1)));
16
+ }
17
+ }
18
+ else if (extensions.some((ext) => entry.name.endsWith(ext))) {
19
+ result.push(fullPath);
20
+ }
21
+ }
22
+ }
23
+ catch {
24
+ logger.info("Failed to read user directory");
25
+ }
26
+ return result;
27
+ }
28
+ async function fileContainsRegex(filePath, regexes) {
29
+ if (!regexes.length)
30
+ return false;
31
+ try {
32
+ const content = await fs.promises.readFile(filePath, "utf8");
33
+ return regexes.some((re) => re.test(content));
34
+ }
35
+ catch {
36
+ return false;
37
+ }
38
+ }
39
+ async function batchRegexCheck(filePath, regexGroups) {
40
+ try {
41
+ const content = await fs.promises.readFile(filePath, "utf8");
42
+ return regexGroups.map((regexes) => regexes.length > 0 ? regexes.some((re) => re.test(content)) : false);
43
+ }
44
+ catch {
45
+ return regexGroups.map(() => false);
46
+ }
47
+ }
48
+ async function isLikelyUITest(filePath) {
49
+ try {
50
+ const content = await fs.promises.readFile(filePath, "utf8");
51
+ if (backendIndicators.some((pattern) => pattern.test(content))) {
52
+ return false;
53
+ }
54
+ return strongUIIndicators.some((pattern) => pattern.test(content));
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ function getFileScore(fileName, config) {
61
+ let score = 0;
62
+ // Higher score for explicit UI test naming
63
+ if (/ui|web|e2e|integration|functional/i.test(fileName))
64
+ score += 3;
65
+ if (config.namePatterns.some((pattern) => pattern.test(fileName)))
66
+ score += 2;
67
+ return score;
68
+ }
69
+ export async function listTestFiles(options) {
70
+ const { language, framework, baseDir, strictMode = false } = options;
71
+ const config = TEST_FILE_DETECTION[language];
72
+ if (!config) {
73
+ return [];
74
+ }
75
+ // Step 1: Collect all files with matching extensions
76
+ let files = [];
77
+ try {
78
+ files = await walkDir(baseDir, config.extensions, 6);
79
+ }
80
+ catch {
81
+ return [];
82
+ }
83
+ if (files.length === 0) {
84
+ throw new Error("No files found with the specified extensions");
85
+ }
86
+ const candidateFiles = new Map();
87
+ // Step 2: Fast name-based identification with scoring
88
+ for (const file of files) {
89
+ const fileName = path.basename(file);
90
+ const score = getFileScore(fileName, config);
91
+ if (config.namePatterns.some((pattern) => pattern.test(fileName))) {
92
+ candidateFiles.set(file, score);
93
+ }
94
+ }
95
+ // Step 3: Content-based test detection for remaining files
96
+ const remainingFiles = files.filter((file) => !candidateFiles.has(file));
97
+ const contentCheckPromises = remainingFiles.map(async (file) => {
98
+ const hasTestContent = await fileContainsRegex(file, config.contentRegex);
99
+ if (hasTestContent) {
100
+ const fileName = path.basename(file);
101
+ const score = getFileScore(fileName, config);
102
+ candidateFiles.set(file, score);
103
+ }
104
+ });
105
+ await Promise.all(contentCheckPromises);
106
+ // Step 4: Handle SpecFlow .feature files for C# + SpecFlow
107
+ if (language === "csharp" && framework === "specflow") {
108
+ try {
109
+ const featureFiles = await walkDir(baseDir, [".feature"], 6);
110
+ featureFiles.forEach((file) => candidateFiles.set(file, 2));
111
+ }
112
+ catch {
113
+ // ignore
114
+ }
115
+ }
116
+ if (candidateFiles.size === 0) {
117
+ return [];
118
+ }
119
+ // Step 6: UI Detection with fallback patterns
120
+ const uiFiles = [];
121
+ const filesToCheck = Array.from(candidateFiles.keys());
122
+ // Batch process UI detection for better performance
123
+ const batchSize = 10;
124
+ for (let i = 0; i < filesToCheck.length; i += batchSize) {
125
+ const batch = filesToCheck.slice(i, i + batchSize);
126
+ const batchPromises = batch.map(async (file) => {
127
+ // First, use the new precise UI detection
128
+ const isUITest = await isLikelyUITest(file);
129
+ if (isUITest) {
130
+ return file;
131
+ }
132
+ // If not clearly UI, run the traditional checks
133
+ const [hasExplicitUI, hasUIIndicators, hasBackend, shouldExclude] = await batchRegexCheck(file, [
134
+ config.uiDriverRegex,
135
+ config.uiIndicatorRegex,
136
+ config.backendRegex,
137
+ config.excludeRegex || [],
138
+ ]);
139
+ if (shouldExclude) {
140
+ return null;
141
+ }
142
+ if (hasBackend) {
143
+ return null;
144
+ }
145
+ if (hasExplicitUI) {
146
+ return file;
147
+ }
148
+ if (hasUIIndicators) {
149
+ return file;
150
+ }
151
+ if (!strictMode) {
152
+ const score = candidateFiles.get(file) || 0;
153
+ if (score >= 3) {
154
+ return file;
155
+ }
156
+ }
157
+ return null;
158
+ });
159
+ const batchResults = await Promise.all(batchPromises);
160
+ uiFiles.push(...batchResults.filter((file) => file !== null));
161
+ }
162
+ // Step 7: Sort by score (higher confidence files first)
163
+ uiFiles.sort((a, b) => {
164
+ const scoreA = candidateFiles.get(a) || 0;
165
+ const scoreB = candidateFiles.get(b) || 0;
166
+ return scoreB - scoreA;
167
+ });
168
+ return uiFiles;
169
+ }
170
+ export async function listUITestFilesStrict(options) {
171
+ return listTestFiles({ ...options, strictMode: true });
172
+ }
173
+ export async function listUITestFilesRelaxed(options) {
174
+ return listTestFiles({ ...options, strictMode: false });
175
+ }
@@ -0,0 +1,15 @@
1
+ import { SDKSupportedTestingFrameworkEnum, SDKSupportedLanguageEnum } from "../sdk-utils/common/types.js";
2
+ export type ListTestFilesParams = {
3
+ dirs: string[];
4
+ language: SDKSupportedLanguageEnum;
5
+ framework?: SDKSupportedTestingFrameworkEnum;
6
+ };
7
+ export interface DetectionConfig {
8
+ extensions: string[];
9
+ namePatterns: RegExp[];
10
+ contentRegex: RegExp[];
11
+ uiDriverRegex: RegExp[];
12
+ uiIndicatorRegex: RegExp[];
13
+ backendRegex: RegExp[];
14
+ excludeRegex?: RegExp[];
15
+ }