@appliqation/automation-sdk 2.1.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 (36) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +441 -0
  3. package/package.json +107 -0
  4. package/src/AppliqationClient.js +562 -0
  5. package/src/constants.js +245 -0
  6. package/src/core/AuthManager.js +353 -0
  7. package/src/core/HttpClient.js +475 -0
  8. package/src/index.d.ts +333 -0
  9. package/src/index.js +26 -0
  10. package/src/playwright/JwtBrowserAuth.js +240 -0
  11. package/src/playwright/fixture.js +92 -0
  12. package/src/playwright/global-setup.js +243 -0
  13. package/src/playwright/helpers/jwt-browser-auth.js +227 -0
  14. package/src/playwright/index.js +16 -0
  15. package/src/reporters/cypress/CypressReporter.js +387 -0
  16. package/src/reporters/cypress/UuidExtractor.js +139 -0
  17. package/src/reporters/cypress/index.js +30 -0
  18. package/src/reporters/jest/JestReporter.js +361 -0
  19. package/src/reporters/jest/UuidExtractor.js +174 -0
  20. package/src/reporters/jest/index.js +28 -0
  21. package/src/reporters/playwright/AppliqationReporter.js +654 -0
  22. package/src/reporters/playwright/helpers/DeviceOsDetector.js +435 -0
  23. package/src/reporters/playwright/helpers/UuidExtractor.js +290 -0
  24. package/src/reporters/playwright/index.d.ts +96 -0
  25. package/src/reporters/playwright/index.js +14 -0
  26. package/src/services/OrphanTestService.js +74 -0
  27. package/src/services/ResultService.js +252 -0
  28. package/src/services/RunMatrixService.js +309 -0
  29. package/src/utils/PayloadBuilder.js +280 -0
  30. package/src/utils/RunDataNormalizer.js +335 -0
  31. package/src/utils/UuidValidator.js +102 -0
  32. package/src/utils/errors.js +217 -0
  33. package/src/utils/index.js +17 -0
  34. package/src/utils/logger.js +124 -0
  35. package/src/utils/mapAppqUuid.js +83 -0
  36. package/src/utils/validator.js +157 -0
@@ -0,0 +1,435 @@
1
+ const os = require('os');
2
+ const { normalizeBrowser, normalizeOS, normalizeDevice } = require('../../../utils/RunDataNormalizer');
3
+
4
+ /**
5
+ * Device and OS Detector for Playwright Tests
6
+ * Detects device type and operating system from Playwright project configuration
7
+ */
8
+ class DeviceOsDetector {
9
+ /**
10
+ * Detect device type from Playwright project
11
+ * @param {Object} project - Playwright project object
12
+ * @returns {string} Device type (Desktop, Mobile, Tablet)
13
+ */
14
+ static detectDevice(project) {
15
+ if (!project) return this.getDefaultDevice();
16
+
17
+ const name = (project.name || '').toLowerCase();
18
+ const userAgent = (project.use?.userAgent || '').toLowerCase();
19
+ const viewport = project.use?.viewport;
20
+ const isMobile = project.use?.isMobile;
21
+ const hasTouch = project.use?.hasTouch;
22
+
23
+ // Explicit mobile flag
24
+ if (isMobile === true) {
25
+ return normalizeDevice(this.detectMobileType(name, viewport));
26
+ }
27
+
28
+ // Check project name for device indicators
29
+ if (this.isMobileDevice(name)) {
30
+ return normalizeDevice(this.detectMobileType(name, viewport));
31
+ }
32
+
33
+ if (this.isTabletDevice(name)) {
34
+ return normalizeDevice('Tablet');
35
+ }
36
+
37
+ // Check user agent
38
+ if (userAgent) {
39
+ if (this.isMobileUserAgent(userAgent)) {
40
+ return normalizeDevice(this.detectMobileType(userAgent, viewport));
41
+ }
42
+ if (this.isTabletUserAgent(userAgent)) {
43
+ return normalizeDevice('Tablet');
44
+ }
45
+ }
46
+
47
+ // Check viewport size
48
+ if (viewport) {
49
+ if (this.isTabletViewport(viewport)) {
50
+ return normalizeDevice('Tablet');
51
+ }
52
+ if (this.isMobileViewport(viewport)) {
53
+ return normalizeDevice('Mobile');
54
+ }
55
+ }
56
+
57
+ // Check touch capability
58
+ if (hasTouch === true && viewport && this.isMobileViewport(viewport)) {
59
+ return normalizeDevice('Mobile');
60
+ }
61
+
62
+ return normalizeDevice('Desktop');
63
+ }
64
+
65
+ /**
66
+ * Detect mobile type (Mobile vs Tablet)
67
+ * @param {string} identifier - Name or user agent
68
+ * @param {Object} viewport - Viewport dimensions
69
+ * @returns {string} 'Mobile' or 'Tablet'
70
+ */
71
+ static detectMobileType(identifier, viewport) {
72
+ const lower = (identifier || '').toLowerCase();
73
+
74
+ // Explicit tablet indicators
75
+ if (lower.includes('tablet') || lower.includes('ipad')) {
76
+ return 'Tablet';
77
+ }
78
+
79
+ // Check viewport if available
80
+ if (viewport) {
81
+ if (this.isTabletViewport(viewport)) {
82
+ return 'Tablet';
83
+ }
84
+ }
85
+
86
+ return 'Mobile';
87
+ }
88
+
89
+ /**
90
+ * Check if name indicates mobile device
91
+ * @param {string} name - Project name
92
+ * @returns {boolean} True if mobile
93
+ */
94
+ static isMobileDevice(name) {
95
+ const mobileKeywords = [
96
+ 'mobile', 'iphone', 'android', 'pixel', 'galaxy',
97
+ 'xiaomi', 'huawei', 'moto', 'nexus', 'oneplus'
98
+ ];
99
+
100
+ return mobileKeywords.some(keyword => name.includes(keyword));
101
+ }
102
+
103
+ /**
104
+ * Check if name indicates tablet device
105
+ * @param {string} name - Project name
106
+ * @returns {boolean} True if tablet
107
+ */
108
+ static isTabletDevice(name) {
109
+ const tabletKeywords = [
110
+ 'tablet', 'ipad', 'galaxy tab', 'kindle', 'surface'
111
+ ];
112
+
113
+ return tabletKeywords.some(keyword => name.includes(keyword));
114
+ }
115
+
116
+ /**
117
+ * Check if user agent indicates mobile
118
+ * @param {string} userAgent - User agent string
119
+ * @returns {boolean} True if mobile
120
+ */
121
+ static isMobileUserAgent(userAgent) {
122
+ const mobilePatterns = [
123
+ /mobile/i,
124
+ /android/i,
125
+ /iphone/i,
126
+ /ipod/i,
127
+ /blackberry/i,
128
+ /windows phone/i
129
+ ];
130
+
131
+ return mobilePatterns.some(pattern => pattern.test(userAgent));
132
+ }
133
+
134
+ /**
135
+ * Check if user agent indicates tablet
136
+ * @param {string} userAgent - User agent string
137
+ * @returns {boolean} True if tablet
138
+ */
139
+ static isTabletUserAgent(userAgent) {
140
+ const tabletPatterns = [
141
+ /ipad/i,
142
+ /tablet/i,
143
+ /kindle/i,
144
+ /playbook/i
145
+ ];
146
+
147
+ return tabletPatterns.some(pattern => pattern.test(userAgent));
148
+ }
149
+
150
+ /**
151
+ * Check if viewport is mobile size
152
+ * @param {Object} viewport - Viewport object with width/height
153
+ * @returns {boolean} True if mobile size
154
+ */
155
+ static isMobileViewport(viewport) {
156
+ if (!viewport || !viewport.width) return false;
157
+
158
+ // Typical mobile viewport width: 320-480px
159
+ return viewport.width >= 320 && viewport.width <= 480;
160
+ }
161
+
162
+ /**
163
+ * Check if viewport is tablet size
164
+ * @param {Object} viewport - Viewport object with width/height
165
+ * @returns {boolean} True if tablet size
166
+ */
167
+ static isTabletViewport(viewport) {
168
+ if (!viewport || !viewport.width) return false;
169
+
170
+ // Typical tablet viewport width: 768-1024px
171
+ return viewport.width >= 600 && viewport.width <= 1024;
172
+ }
173
+
174
+ /**
175
+ * Detect OS from Playwright project
176
+ * @param {Object} project - Playwright project object
177
+ * @returns {string} Operating system name
178
+ */
179
+ static detectOS(project) {
180
+ if (!project) return normalizeOS(this.getSystemOS());
181
+
182
+ // Priority 1: Check metadata.os for explicit OS specification
183
+ const explicitOS = project.use?.metadata?.os;
184
+ if (explicitOS && typeof explicitOS === 'string') {
185
+ return normalizeOS(explicitOS);
186
+ }
187
+
188
+ const name = (project.name || '').toLowerCase();
189
+ const userAgent = (project.use?.userAgent || '').toLowerCase();
190
+
191
+ // Priority 2: Check project name for OS indicators
192
+ const osFromName = this.extractOSFromName(name);
193
+ if (osFromName) return normalizeOS(osFromName);
194
+
195
+ // Priority 3: Check user agent for OS indicators
196
+ const osFromUA = this.extractOSFromUserAgent(userAgent);
197
+ if (osFromUA) return normalizeOS(osFromUA);
198
+
199
+ // Fallback to system OS
200
+ return normalizeOS(this.getSystemOS());
201
+ }
202
+
203
+ /**
204
+ * Extract OS from project name
205
+ * @param {string} name - Project name
206
+ * @returns {string|null} OS name or null
207
+ */
208
+ static extractOSFromName(name) {
209
+ const osMap = {
210
+ 'windows7': 'Windows',
211
+ 'win7': 'Windows',
212
+ 'windows11': 'Windows',
213
+ 'win11': 'Windows',
214
+ 'windows10': 'Windows',
215
+ 'win10': 'Windows',
216
+ 'windows': 'Windows',
217
+ 'macos': 'macOS',
218
+ 'mac': 'macOS',
219
+ 'darwin': 'macOS',
220
+ 'linux': 'Linux',
221
+ 'ubuntu': 'Ubuntu',
222
+ 'android': 'Android',
223
+ 'ios': 'macOS',
224
+ 'iphone': 'macOS',
225
+ 'ipad': 'macOS'
226
+ };
227
+
228
+ for (const [keyword, os] of Object.entries(osMap)) {
229
+ if (name.includes(keyword)) {
230
+ return os;
231
+ }
232
+ }
233
+
234
+ return null;
235
+ }
236
+
237
+ /**
238
+ * Extract OS from user agent
239
+ * @param {string} userAgent - User agent string
240
+ * @returns {string|null} OS name or null
241
+ */
242
+ static extractOSFromUserAgent(userAgent) {
243
+ if (!userAgent) return null;
244
+
245
+ if (/windows/i.test(userAgent)) return 'Windows';
246
+ if (/mac os x/i.test(userAgent)) return 'macOS';
247
+ if (/macintosh/i.test(userAgent)) return 'macOS';
248
+ if (/android/i.test(userAgent)) return 'Android';
249
+ if (/ipad|iphone|ipod/i.test(userAgent)) return 'macOS';
250
+ if (/linux/i.test(userAgent)) return 'Linux';
251
+ if (/ubuntu/i.test(userAgent)) return 'Ubuntu';
252
+
253
+ return null;
254
+ }
255
+
256
+ /**
257
+ * Get system OS
258
+ * @returns {string} Current system OS
259
+ */
260
+ static getSystemOS() {
261
+ const platform = os.platform();
262
+ const release = os.release();
263
+
264
+ const osMap = {
265
+ 'win32': this.getWindowsVersion(release),
266
+ 'darwin': 'macOS',
267
+ 'linux': 'Linux'
268
+ };
269
+
270
+ return osMap[platform] || 'Unknown';
271
+ }
272
+
273
+ /**
274
+ * Get Windows version from release
275
+ * @param {string} release - OS release string
276
+ * @returns {string} Windows version (generic, will be normalized)
277
+ */
278
+ static getWindowsVersion(release) {
279
+ // Return generic Windows - let normalizer handle version details
280
+ return 'Windows';
281
+ }
282
+
283
+ /**
284
+ * Get default device type
285
+ * @returns {string} Default device type
286
+ */
287
+ static getDefaultDevice() {
288
+ return normalizeDevice('Desktop');
289
+ }
290
+
291
+ /**
292
+ * Detect browser from Playwright project
293
+ * @param {Object} project - Playwright project object
294
+ * @param {Object} browserInstance - Optional Playwright browser instance for version detection
295
+ * @returns {string} Browser name (normalized)
296
+ */
297
+ static detectBrowser(project, browserInstance = null) {
298
+ if (!project) return normalizeBrowser('Chromium');
299
+
300
+ // Check browserName property FIRST (takes precedence over project name)
301
+ // This allows explicit browser version strings like "chrome142" or "safari 18.7.2"
302
+ const browserName = project.use?.browserName;
303
+ let browserVersion = project.use?.metadata?.browserVersion;
304
+
305
+ // Auto-detect browser version if browser instance provided and no manual version
306
+ if (!browserVersion && browserInstance) {
307
+ browserVersion = this.extractBrowserVersion(browserInstance);
308
+ }
309
+
310
+ if (browserName) {
311
+ const browserMap = {
312
+ 'chromium': 'Chrome',
313
+ 'firefox': 'Firefox',
314
+ 'webkit': 'Safari'
315
+ };
316
+ // Map standard Playwright names to browser names
317
+ const mapped = browserMap[browserName.toLowerCase()] || browserName;
318
+
319
+ // If metadata.browserVersion is provided OR auto-detected, append it before normalization
320
+ // This allows: { browserName: 'chromium', metadata: { browserVersion: '142' } }
321
+ // or auto-detected from browser.version()
322
+ // to become "Chrome 142"
323
+ const browserWithVersion = browserVersion ? `${mapped} ${browserVersion}` : mapped;
324
+
325
+ return normalizeBrowser(browserWithVersion);
326
+ }
327
+
328
+ // Fallback: Check project name for browser
329
+ const name = (project.name || '').toLowerCase();
330
+ if (name.includes('chrome') || name.includes('chromium')) {
331
+ return normalizeBrowser('Chrome');
332
+ }
333
+ if (name.includes('firefox') || name.includes('ff')) {
334
+ return normalizeBrowser('Firefox');
335
+ }
336
+ if (name.includes('webkit') || name.includes('safari')) {
337
+ return normalizeBrowser('Safari');
338
+ }
339
+ if (name.includes('edge')) {
340
+ return normalizeBrowser('Edge');
341
+ }
342
+
343
+ // Default
344
+ return normalizeBrowser('Chrome');
345
+ }
346
+
347
+ /**
348
+ * Extract browser version from Playwright browser instance
349
+ * @param {Object} browserInstance - Playwright browser instance
350
+ * @returns {string|null} Major version number or null
351
+ */
352
+ static extractBrowserVersion(browserInstance) {
353
+ try {
354
+ // Check if browser instance has version() method
355
+ if (!browserInstance || typeof browserInstance.version !== 'function') {
356
+ return null;
357
+ }
358
+
359
+ // Note: browser.version() is synchronous in Playwright
360
+ const fullVersion = browserInstance.version();
361
+
362
+ if (!fullVersion || typeof fullVersion !== 'string') {
363
+ return null;
364
+ }
365
+
366
+ // Extract major version (e.g., "140.0.6778.44" -> "140")
367
+ const versionMatch = fullVersion.match(/^(\d+)/);
368
+ if (versionMatch && versionMatch[1]) {
369
+ return versionMatch[1];
370
+ }
371
+
372
+ return null;
373
+ } catch (error) {
374
+ // Silently fail - version detection is optional
375
+ return null;
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Get complete device info from Playwright project
381
+ * @param {Object} project - Playwright project object
382
+ * @param {Object} browserInstance - Optional Playwright browser instance for version detection
383
+ * @returns {Object} { device, os, browser }
384
+ */
385
+ static getDeviceInfo(project, browserInstance = null) {
386
+ return {
387
+ device: this.detectDevice(project),
388
+ os: this.detectOS(project),
389
+ browser: this.detectBrowser(project, browserInstance)
390
+ };
391
+ }
392
+
393
+ /**
394
+ * Get device/OS matrix configurations from Playwright config
395
+ * Splits multi-project configs into separate run matrices
396
+ * @param {Array} projects - Array of Playwright project objects
397
+ * @returns {Array} Array of unique device/OS combinations
398
+ */
399
+ static getMatrixConfigurations(projects) {
400
+ if (!Array.isArray(projects) || projects.length === 0) {
401
+ return [{
402
+ device: this.getDefaultDevice(),
403
+ os: this.getSystemOS(),
404
+ browsers: ['Chrome']
405
+ }];
406
+ }
407
+
408
+ const configurations = new Map();
409
+
410
+ for (const project of projects) {
411
+ const device = this.detectDevice(project);
412
+ const os = this.detectOS(project);
413
+ const browser = this.detectBrowser(project);
414
+
415
+ const key = `${device}-${os}`;
416
+
417
+ if (!configurations.has(key)) {
418
+ configurations.set(key, {
419
+ device,
420
+ os,
421
+ browsers: []
422
+ });
423
+ }
424
+
425
+ const config = configurations.get(key);
426
+ if (!config.browsers.includes(browser)) {
427
+ config.browsers.push(browser);
428
+ }
429
+ }
430
+
431
+ return Array.from(configurations.values());
432
+ }
433
+ }
434
+
435
+ module.exports = DeviceOsDetector;