@browserstack/mcp-server 1.2.2-beta → 1.2.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/inmemory-store.d.ts +1 -0
- package/dist/lib/inmemory-store.js +1 -0
- package/dist/lib/instrumentation.js +2 -0
- package/dist/server-factory.js +2 -0
- package/dist/tools/accessibility.js +238 -78
- package/dist/tools/accessiblity-utils/auth-config.d.ts +39 -0
- package/dist/tools/accessiblity-utils/auth-config.js +125 -0
- package/dist/tools/accessiblity-utils/scanner.d.ts +1 -1
- package/dist/tools/accessiblity-utils/scanner.js +2 -1
- package/dist/tools/add-percy-snapshots.d.ts +5 -0
- package/dist/tools/add-percy-snapshots.js +17 -0
- package/dist/tools/bstack-sdk.d.ts +2 -15
- package/dist/tools/bstack-sdk.js +7 -124
- package/dist/tools/list-test-files.d.ts +2 -0
- package/dist/tools/list-test-files.js +33 -0
- package/dist/tools/percy-sdk.d.ts +4 -0
- package/dist/tools/percy-sdk.js +88 -0
- package/dist/tools/percy-snapshot-utils/constants.d.ts +16 -0
- package/dist/tools/percy-snapshot-utils/constants.js +500 -0
- package/dist/tools/percy-snapshot-utils/detect-test-files.d.ts +10 -0
- package/dist/tools/percy-snapshot-utils/detect-test-files.js +194 -0
- package/dist/tools/percy-snapshot-utils/types.d.ts +15 -0
- package/dist/tools/percy-snapshot-utils/utils.d.ts +4 -0
- package/dist/tools/percy-snapshot-utils/utils.js +30 -0
- package/dist/tools/sdk-utils/{commands.d.ts → bstack/commands.d.ts} +1 -1
- package/dist/tools/sdk-utils/bstack/commands.js +88 -0
- package/dist/tools/sdk-utils/bstack/configUtils.d.ts +4 -0
- package/dist/tools/sdk-utils/bstack/configUtils.js +66 -0
- package/dist/tools/sdk-utils/bstack/constants.d.ts +58 -0
- package/dist/tools/sdk-utils/{constants.js → bstack/constants.js} +117 -78
- package/dist/tools/sdk-utils/{constants.d.ts → bstack/frameworks.d.ts} +1 -1
- package/dist/tools/sdk-utils/bstack/frameworks.js +57 -0
- package/dist/tools/sdk-utils/bstack/index.d.ts +4 -0
- package/dist/tools/sdk-utils/bstack/index.js +5 -0
- package/dist/tools/sdk-utils/bstack/sdkHandler.d.ts +4 -0
- package/dist/tools/sdk-utils/bstack/sdkHandler.js +74 -0
- package/dist/tools/sdk-utils/common/constants.d.ts +10 -0
- package/dist/tools/sdk-utils/common/constants.js +86 -0
- package/dist/tools/sdk-utils/common/formatUtils.d.ts +5 -0
- package/dist/tools/sdk-utils/common/formatUtils.js +27 -0
- package/dist/tools/sdk-utils/common/index.d.ts +3 -0
- package/dist/tools/sdk-utils/common/index.js +4 -0
- package/dist/tools/sdk-utils/common/instructionUtils.d.ts +8 -0
- package/dist/tools/sdk-utils/common/instructionUtils.js +20 -0
- package/dist/tools/sdk-utils/common/schema.d.ts +61 -0
- package/dist/tools/sdk-utils/common/schema.js +28 -0
- package/dist/tools/sdk-utils/common/types.d.ts +66 -0
- package/dist/tools/sdk-utils/common/types.js +50 -0
- package/dist/tools/sdk-utils/common/utils.d.ts +25 -0
- package/dist/tools/sdk-utils/common/utils.js +84 -0
- package/dist/tools/sdk-utils/handler.d.ts +5 -0
- package/dist/tools/sdk-utils/handler.js +144 -0
- package/dist/tools/sdk-utils/percy-automate/constants.d.ts +11 -0
- package/dist/tools/sdk-utils/percy-automate/constants.js +365 -0
- package/dist/tools/sdk-utils/percy-automate/frameworks.d.ts +8 -0
- package/dist/tools/sdk-utils/percy-automate/frameworks.js +50 -0
- package/dist/tools/sdk-utils/percy-automate/handler.d.ts +3 -0
- package/dist/tools/sdk-utils/percy-automate/handler.js +30 -0
- package/dist/tools/sdk-utils/percy-automate/index.d.ts +1 -0
- package/dist/tools/sdk-utils/percy-automate/index.js +2 -0
- package/dist/tools/sdk-utils/percy-automate/types.d.ts +13 -0
- package/dist/tools/sdk-utils/percy-automate/types.js +1 -0
- package/dist/tools/sdk-utils/percy-bstack/constants.d.ts +4 -0
- package/dist/tools/sdk-utils/{percy → percy-bstack}/constants.js +13 -39
- package/dist/tools/sdk-utils/percy-bstack/frameworks.d.ts +2 -0
- package/dist/tools/sdk-utils/percy-bstack/frameworks.js +27 -0
- package/dist/tools/sdk-utils/percy-bstack/handler.d.ts +4 -0
- package/dist/tools/sdk-utils/percy-bstack/handler.js +99 -0
- package/dist/tools/sdk-utils/percy-bstack/index.d.ts +4 -0
- package/dist/tools/sdk-utils/percy-bstack/index.js +4 -0
- package/dist/tools/sdk-utils/percy-bstack/instructions.d.ts +7 -0
- package/dist/tools/sdk-utils/{percy → percy-bstack}/instructions.js +5 -9
- package/dist/tools/sdk-utils/percy-bstack/types.d.ts +13 -0
- package/dist/tools/sdk-utils/percy-bstack/types.js +5 -0
- package/dist/tools/sdk-utils/percy-web/constants.d.ts +41 -0
- package/dist/tools/sdk-utils/percy-web/constants.js +941 -0
- package/dist/tools/sdk-utils/percy-web/fetchPercyToken.d.ts +4 -0
- package/dist/tools/sdk-utils/percy-web/fetchPercyToken.js +28 -0
- package/dist/tools/sdk-utils/percy-web/frameworks.d.ts +7 -0
- package/dist/tools/sdk-utils/percy-web/frameworks.js +103 -0
- package/dist/tools/sdk-utils/percy-web/handler.d.ts +4 -0
- package/dist/tools/sdk-utils/percy-web/handler.js +27 -0
- package/dist/tools/sdk-utils/percy-web/index.d.ts +4 -0
- package/dist/tools/sdk-utils/percy-web/index.js +4 -0
- package/dist/tools/sdk-utils/percy-web/types.d.ts +12 -0
- package/dist/tools/sdk-utils/percy-web/types.js +1 -0
- package/package.json +1 -1
- package/dist/tools/sdk-utils/commands.js +0 -65
- package/dist/tools/sdk-utils/instructions.d.ts +0 -6
- package/dist/tools/sdk-utils/instructions.js +0 -99
- package/dist/tools/sdk-utils/percy/constants.d.ts +0 -3
- package/dist/tools/sdk-utils/percy/instructions.d.ts +0 -10
- package/dist/tools/sdk-utils/percy/types.d.ts +0 -5
- /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,194 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import logger from "../../logger.js";
|
|
4
|
+
import { EXCLUDED_DIRS, TEST_FILE_DETECTION, backendIndicators, strongUIIndicators, } from "../percy-snapshot-utils/constants.js";
|
|
5
|
+
async function walkDir(dir, extensions) {
|
|
6
|
+
const result = [];
|
|
7
|
+
try {
|
|
8
|
+
const entries = await fs.promises.readdir(dir, { withFileTypes: true });
|
|
9
|
+
for (const entry of entries) {
|
|
10
|
+
const fullPath = path.join(dir, entry.name);
|
|
11
|
+
if (entry.isDirectory()) {
|
|
12
|
+
if (!EXCLUDED_DIRS.has(entry.name) && !entry.name.startsWith(".")) {
|
|
13
|
+
result.push(...(await walkDir(fullPath, extensions)));
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
else if (extensions.some((ext) => entry.name.endsWith(ext))) {
|
|
17
|
+
result.push(fullPath);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
logger.error(`Failed to read directory: ${dir}`);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
async function fileContainsRegex(filePath, regexes) {
|
|
27
|
+
if (!regexes.length)
|
|
28
|
+
return false;
|
|
29
|
+
try {
|
|
30
|
+
const content = await fs.promises.readFile(filePath, "utf8");
|
|
31
|
+
return regexes.some((re) => re.test(content));
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
logger.warn(`Failed to read file: ${filePath}`);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
async function batchRegexCheck(filePath, regexGroups) {
|
|
39
|
+
try {
|
|
40
|
+
const content = await fs.promises.readFile(filePath, "utf8");
|
|
41
|
+
return regexGroups.map((regexes) => regexes.length > 0 ? regexes.some((re) => re.test(content)) : false);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
logger.warn(`Failed to read file: ${filePath}`);
|
|
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
|
+
logger.error(`Unsupported language: ${language}`);
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
// Step 1: Collect all files with matching extensions
|
|
77
|
+
let files = [];
|
|
78
|
+
try {
|
|
79
|
+
files = await walkDir(baseDir, config.extensions);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
if (files.length === 0) {
|
|
85
|
+
throw new Error("No files found with the specified extensions");
|
|
86
|
+
}
|
|
87
|
+
const candidateFiles = new Map();
|
|
88
|
+
// Step 2: Fast name-based identification with scoring
|
|
89
|
+
for (const file of files) {
|
|
90
|
+
const fileName = path.basename(file);
|
|
91
|
+
const score = getFileScore(fileName, config);
|
|
92
|
+
if (config.namePatterns.some((pattern) => pattern.test(fileName))) {
|
|
93
|
+
candidateFiles.set(file, score);
|
|
94
|
+
logger.debug(`File matched by name pattern: ${file} (score: ${score})`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Step 3: Content-based test detection for remaining files
|
|
98
|
+
const remainingFiles = files.filter((file) => !candidateFiles.has(file));
|
|
99
|
+
const contentCheckPromises = remainingFiles.map(async (file) => {
|
|
100
|
+
const hasTestContent = await fileContainsRegex(file, config.contentRegex);
|
|
101
|
+
if (hasTestContent) {
|
|
102
|
+
const fileName = path.basename(file);
|
|
103
|
+
const score = getFileScore(fileName, config);
|
|
104
|
+
candidateFiles.set(file, score);
|
|
105
|
+
logger.debug(`File matched by content regex: ${file} (score: ${score})`);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
await Promise.all(contentCheckPromises);
|
|
109
|
+
// Step 4: Handle SpecFlow .feature files for C# + SpecFlow
|
|
110
|
+
if (language === "csharp" && framework === "specflow") {
|
|
111
|
+
try {
|
|
112
|
+
const featureFiles = await walkDir(baseDir, [".feature"]);
|
|
113
|
+
featureFiles.forEach((file) => candidateFiles.set(file, 2));
|
|
114
|
+
logger.info(`Added ${featureFiles.length} SpecFlow .feature files`);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
logger.warn(`Failed to collect SpecFlow .feature files from baseDir: ${baseDir}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (candidateFiles.size === 0) {
|
|
121
|
+
logger.info("No test files found matching patterns");
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
// Step 6: UI Detection with fallback patterns
|
|
125
|
+
const uiFiles = [];
|
|
126
|
+
const filesToCheck = Array.from(candidateFiles.keys());
|
|
127
|
+
// Batch process UI detection for better performance
|
|
128
|
+
const batchSize = 10;
|
|
129
|
+
for (let i = 0; i < filesToCheck.length; i += batchSize) {
|
|
130
|
+
const batch = filesToCheck.slice(i, i + batchSize);
|
|
131
|
+
const batchPromises = batch.map(async (file) => {
|
|
132
|
+
// First, use the new precise UI detection
|
|
133
|
+
const isUITest = await isLikelyUITest(file);
|
|
134
|
+
if (isUITest) {
|
|
135
|
+
logger.debug(`File included - strong UI indicators: ${file}`);
|
|
136
|
+
return file;
|
|
137
|
+
}
|
|
138
|
+
// If not clearly UI, run the traditional checks
|
|
139
|
+
const [hasExplicitUI, hasUIIndicators, hasBackend, shouldExclude] = await batchRegexCheck(file, [
|
|
140
|
+
config.uiDriverRegex,
|
|
141
|
+
config.uiIndicatorRegex,
|
|
142
|
+
config.backendRegex,
|
|
143
|
+
config.excludeRegex || [],
|
|
144
|
+
]);
|
|
145
|
+
// Skip if explicitly excluded (mocks, unit tests, etc.)
|
|
146
|
+
if (shouldExclude) {
|
|
147
|
+
logger.debug(`File excluded by exclude regex: ${file}`);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
// Skip backend tests in any mode
|
|
151
|
+
if (hasBackend) {
|
|
152
|
+
logger.debug(`File excluded as backend test: ${file}`);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
// Include if has explicit UI drivers
|
|
156
|
+
if (hasExplicitUI) {
|
|
157
|
+
logger.debug(`File included - explicit UI drivers: ${file}`);
|
|
158
|
+
return file;
|
|
159
|
+
}
|
|
160
|
+
// Include if has UI indicators (for cases where drivers aren't explicitly imported)
|
|
161
|
+
if (hasUIIndicators) {
|
|
162
|
+
logger.debug(`File included - UI indicators: ${file}`);
|
|
163
|
+
return file;
|
|
164
|
+
}
|
|
165
|
+
// In non-strict mode, include high-scoring test files even without explicit UI patterns
|
|
166
|
+
if (!strictMode) {
|
|
167
|
+
const score = candidateFiles.get(file) || 0;
|
|
168
|
+
if (score >= 3) {
|
|
169
|
+
// High confidence UI test based on naming
|
|
170
|
+
logger.debug(`File included - high confidence score: ${file} (score: ${score})`);
|
|
171
|
+
return file;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
logger.debug(`File excluded - no UI patterns detected: ${file}`);
|
|
175
|
+
return null;
|
|
176
|
+
});
|
|
177
|
+
const batchResults = await Promise.all(batchPromises);
|
|
178
|
+
uiFiles.push(...batchResults.filter((file) => file !== null));
|
|
179
|
+
}
|
|
180
|
+
// Step 7: Sort by score (higher confidence files first)
|
|
181
|
+
uiFiles.sort((a, b) => {
|
|
182
|
+
const scoreA = candidateFiles.get(a) || 0;
|
|
183
|
+
const scoreB = candidateFiles.get(b) || 0;
|
|
184
|
+
return scoreB - scoreA;
|
|
185
|
+
});
|
|
186
|
+
logger.info(`Returning ${uiFiles.length} UI test files from ${candidateFiles.size} total test files`);
|
|
187
|
+
return uiFiles;
|
|
188
|
+
}
|
|
189
|
+
export async function listUITestFilesStrict(options) {
|
|
190
|
+
return listTestFiles({ ...options, strictMode: true });
|
|
191
|
+
}
|
|
192
|
+
export async function listUITestFilesRelaxed(options) {
|
|
193
|
+
return listTestFiles({ ...options, strictMode: false });
|
|
194
|
+
}
|