@vizzly-testing/cli 0.3.2 → 0.5.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 (60) hide show
  1. package/README.md +50 -28
  2. package/dist/cli.js +34 -30
  3. package/dist/client/index.js +1 -1
  4. package/dist/commands/run.js +38 -11
  5. package/dist/commands/tdd.js +30 -18
  6. package/dist/commands/upload.js +56 -5
  7. package/dist/server/handlers/api-handler.js +83 -0
  8. package/dist/server/handlers/tdd-handler.js +138 -0
  9. package/dist/server/http-server.js +132 -0
  10. package/dist/services/api-service.js +22 -2
  11. package/dist/services/server-manager.js +45 -29
  12. package/dist/services/test-runner.js +66 -69
  13. package/dist/services/uploader.js +11 -4
  14. package/dist/types/commands/run.d.ts +4 -1
  15. package/dist/types/commands/tdd.d.ts +5 -1
  16. package/dist/types/sdk/index.d.ts +6 -6
  17. package/dist/types/server/handlers/api-handler.d.ts +49 -0
  18. package/dist/types/server/handlers/tdd-handler.d.ts +85 -0
  19. package/dist/types/server/http-server.d.ts +5 -0
  20. package/dist/types/services/api-service.d.ts +1 -0
  21. package/dist/types/services/server-manager.d.ts +148 -3
  22. package/dist/types/services/test-runner.d.ts +1 -0
  23. package/dist/types/services/uploader.d.ts +2 -1
  24. package/dist/types/utils/ci-env.d.ts +55 -0
  25. package/dist/types/utils/config-helpers.d.ts +1 -1
  26. package/dist/types/utils/console-ui.d.ts +1 -1
  27. package/dist/types/utils/git.d.ts +12 -0
  28. package/dist/utils/ci-env.js +293 -0
  29. package/dist/utils/console-ui.js +4 -14
  30. package/dist/utils/git.js +38 -0
  31. package/docs/api-reference.md +17 -5
  32. package/docs/getting-started.md +1 -1
  33. package/docs/tdd-mode.md +9 -9
  34. package/docs/test-integration.md +9 -17
  35. package/docs/upload-command.md +7 -0
  36. package/package.json +4 -5
  37. package/dist/screenshot-wrapper.js +0 -68
  38. package/dist/server/index.js +0 -522
  39. package/dist/services/service-utils.js +0 -171
  40. package/dist/types/index.js +0 -153
  41. package/dist/types/screenshot-wrapper.d.ts +0 -27
  42. package/dist/types/server/index.d.ts +0 -38
  43. package/dist/types/services/service-utils.d.ts +0 -45
  44. package/dist/types/types/index.d.ts +0 -373
  45. package/dist/types/utils/diagnostics.d.ts +0 -69
  46. package/dist/types/utils/error-messages.d.ts +0 -42
  47. package/dist/types/utils/framework-detector.d.ts +0 -5
  48. package/dist/types/utils/help.d.ts +0 -11
  49. package/dist/types/utils/image-comparison.d.ts +0 -42
  50. package/dist/types/utils/package.d.ts +0 -1
  51. package/dist/types/utils/project-detection.d.ts +0 -19
  52. package/dist/types/utils/ui-helpers.d.ts +0 -23
  53. package/dist/utils/diagnostics.js +0 -184
  54. package/dist/utils/error-messages.js +0 -34
  55. package/dist/utils/framework-detector.js +0 -40
  56. package/dist/utils/help.js +0 -66
  57. package/dist/utils/image-comparison.js +0 -172
  58. package/dist/utils/package.js +0 -9
  59. package/dist/utils/project-detection.js +0 -145
  60. package/dist/utils/ui-helpers.js +0 -86
@@ -1,184 +0,0 @@
1
- /**
2
- * Diagnostics utilities for the doctor command
3
- */
4
- import { spawn } from 'child_process';
5
- import { access, constants } from 'fs/promises';
6
- import { getApiToken } from './environment-config.js';
7
-
8
- /**
9
- * Check if required dependencies are available
10
- */
11
- export async function checkDependencies() {
12
- const dependencies = {
13
- node: await checkNodeVersion(),
14
- npm: await checkCommandAvailable('npm'),
15
- git: await checkCommandAvailable('git'),
16
- odiff: await checkOdiffBinary()
17
- };
18
- const allOk = Object.values(dependencies).every(dep => dep.available);
19
- return {
20
- all_ok: allOk,
21
- details: dependencies
22
- };
23
- }
24
-
25
- /**
26
- * Check Node.js version
27
- */
28
- async function checkNodeVersion() {
29
- try {
30
- const version = process.version;
31
- const majorVersion = parseInt(version.slice(1).split('.')[0]);
32
- const supported = majorVersion >= 20;
33
- return {
34
- available: true,
35
- version,
36
- supported,
37
- message: supported ? 'OK' : 'Node.js 20+ required'
38
- };
39
- } catch (error) {
40
- return {
41
- available: false,
42
- error: error.message
43
- };
44
- }
45
- }
46
-
47
- /**
48
- * Check if a command is available
49
- */
50
- async function checkCommandAvailable(command) {
51
- return new Promise(resolve => {
52
- const child = spawn(command, ['--version'], {
53
- stdio: 'pipe'
54
- });
55
- let output = '';
56
- child.stdout.on('data', data => {
57
- output += data.toString();
58
- });
59
- child.on('close', code => {
60
- resolve({
61
- available: code === 0,
62
- version: output.trim().split('\n')[0] || 'Unknown',
63
- message: code === 0 ? 'OK' : `Command '${command}' not found`
64
- });
65
- });
66
- child.on('error', () => {
67
- resolve({
68
- available: false,
69
- message: `Command '${command}' not found`
70
- });
71
- });
72
- });
73
- }
74
-
75
- /**
76
- * Check if odiff binary is available
77
- */
78
- async function checkOdiffBinary() {
79
- try {
80
- // Check if odiff-bin package is installed and binary is accessible
81
- const {
82
- findOdiffBin
83
- } = await import('odiff-bin');
84
- const odiffPath = findOdiffBin();
85
- await access(odiffPath, constants.F_OK | constants.X_OK);
86
- return {
87
- available: true,
88
- path: odiffPath,
89
- message: 'OK'
90
- };
91
- } catch (error) {
92
- return {
93
- available: false,
94
- error: error.message,
95
- message: 'odiff binary not found or not executable'
96
- };
97
- }
98
- }
99
-
100
- /**
101
- * Check API connectivity
102
- */
103
-
104
- export async function checkApiConnectivity(config) {
105
- try {
106
- const apiUrl = config?.apiUrl || 'https://vizzly.dev';
107
- const apiKey = config?.apiKey || getApiToken();
108
-
109
- // Basic URL validation
110
- try {
111
- new globalThis.URL(apiUrl);
112
- } catch {
113
- throw new Error(`Invalid API URL: ${apiUrl}`);
114
- }
115
- const result = {
116
- url: apiUrl,
117
- has_token: !!apiKey,
118
- reachable: false,
119
- authenticated: false
120
- };
121
-
122
- // Try to reach the API
123
- try {
124
- const response = await fetch(`${apiUrl}/health`, {
125
- method: 'GET',
126
- timeout: 5000
127
- });
128
- result.reachable = response.ok;
129
- result.status_code = response.status;
130
-
131
- // Test authentication if we have a token
132
- if (apiKey && result.reachable) {
133
- const authResponse = await fetch(`${apiUrl}/api/user`, {
134
- method: 'GET',
135
- headers: {
136
- Authorization: `Bearer ${apiKey}`,
137
- 'Content-Type': 'application/json'
138
- },
139
- timeout: 5000
140
- });
141
- result.authenticated = authResponse.ok;
142
- result.auth_status_code = authResponse.status;
143
- }
144
- } catch (fetchError) {
145
- result.reachable = false;
146
- result.error = fetchError.message;
147
- }
148
- return result;
149
- } catch (error) {
150
- return {
151
- error: error.message,
152
- reachable: false,
153
- authenticated: false
154
- };
155
- }
156
- }
157
-
158
- /**
159
- * Check terminal capabilities
160
- */
161
- export function checkTerminalCapabilities() {
162
- return {
163
- stdout_is_tty: Boolean(process.stdout.isTTY),
164
- stdin_is_tty: Boolean(process.stdin.isTTY),
165
- supports_color: Boolean(process.stdout.isTTY && process.env.TERM !== 'dumb'),
166
- terminal_type: process.env.TERM || 'unknown',
167
- columns: process.stdout.columns || 0,
168
- rows: process.stdout.rows || 0
169
- };
170
- }
171
-
172
- /**
173
- * Get system information
174
- */
175
- export function getSystemInfo() {
176
- return {
177
- platform: process.platform,
178
- arch: process.arch,
179
- node_version: process.version,
180
- memory_usage: process.memoryUsage(),
181
- uptime: process.uptime(),
182
- working_directory: process.cwd()
183
- };
184
- }
@@ -1,34 +0,0 @@
1
- export const ERROR_MESSAGES = {
2
- NO_API_TOKEN: {
3
- message: 'No API token provided',
4
- hint: 'Set the VIZZLY_TOKEN environment variable or pass --token flag',
5
- docs: 'https://github.com/vizzly-testing/cli#set-up-your-api-token'
6
- },
7
- BUILD_CREATION_FAILED: {
8
- message: 'Failed to create build',
9
- hint: 'Check your API token permissions and network connection',
10
- docs: 'https://github.com/vizzly-testing/cli/blob/main/docs/upload-command.md#troubleshooting'
11
- },
12
- NO_SCREENSHOTS_FOUND: {
13
- message: 'No screenshots found',
14
- hint: 'Make sure your test code calls vizzlyScreenshot() or check the screenshots directory',
15
- docs: 'https://github.com/vizzly-testing/cli/blob/main/docs/getting-started.md'
16
- },
17
- PORT_IN_USE: {
18
- message: 'Port is already in use',
19
- hint: 'Try a different port with --port flag or stop the process using this port',
20
- docs: 'https://github.com/vizzly-testing/cli/blob/main/docs/test-integration.md#troubleshooting'
21
- }
22
- };
23
- export function formatError(errorCode, context = {}) {
24
- const errorInfo = ERROR_MESSAGES[errorCode];
25
- if (!errorInfo) return {
26
- message: errorCode
27
- };
28
- return {
29
- message: errorInfo.message,
30
- hint: errorInfo.hint,
31
- docs: errorInfo.docs,
32
- context
33
- };
34
- }
@@ -1,40 +0,0 @@
1
- import fs from 'fs/promises';
2
- import path from 'path';
3
-
4
- /**
5
- * Detect testing framework from project dependencies
6
- * @returns {Promise<string|null>} Detected framework or null
7
- */
8
- export async function detectFramework() {
9
- try {
10
- const packageJsonPath = path.join(process.cwd(), 'package.json');
11
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, 'utf8'));
12
- const dependencies = {
13
- ...(packageJson.dependencies || {}),
14
- ...(packageJson.devDependencies || {})
15
- };
16
-
17
- // Check for common testing frameworks
18
- if (dependencies['@playwright/test'] || dependencies.playwright) {
19
- return 'playwright';
20
- }
21
- if (dependencies.cypress) {
22
- return 'cypress';
23
- }
24
- if (dependencies.puppeteer) {
25
- return 'puppeteer';
26
- }
27
-
28
- // Check for config files
29
- const files = await fs.readdir(process.cwd());
30
- if (files.some(f => f.includes('playwright.config'))) {
31
- return 'playwright';
32
- }
33
- if (files.some(f => f.includes('cypress.config'))) {
34
- return 'cypress';
35
- }
36
- return null;
37
- } catch {
38
- return null;
39
- }
40
- }
@@ -1,66 +0,0 @@
1
- import { createColors } from './colors.js';
2
-
3
- /**
4
- * Display help information for the CLI
5
- * @param {Object} globalOptions - Global CLI options
6
- */
7
- export function showHelp(globalOptions = {}) {
8
- const colors = createColors({
9
- useColor: !globalOptions.noColor
10
- });
11
- if (globalOptions.json) {
12
- console.log(JSON.stringify({
13
- status: 'info',
14
- message: 'Vizzly CLI Help',
15
- commands: [{
16
- name: 'run',
17
- description: 'Run tests with Vizzly visual testing'
18
- }, {
19
- name: 'upload',
20
- description: 'Upload screenshots to Vizzly'
21
- }, {
22
- name: 'init',
23
- description: 'Initialize Vizzly configuration'
24
- }],
25
- timestamp: new Date().toISOString()
26
- }));
27
- return;
28
- }
29
- console.log('');
30
- console.log(colors.cyan(colors.bold('🔍 Vizzly CLI')));
31
- console.log(colors.dim('Visual testing tool for UI developers'));
32
- console.log('');
33
- console.log(colors.yellow(colors.bold('Available Commands:')));
34
- console.log('');
35
- console.log(` ${colors.green(colors.bold('run'))} Run tests with Vizzly visual testing`);
36
- console.log(` ${colors.green(colors.bold('upload'))} Upload screenshots to Vizzly`);
37
- console.log(` ${colors.green(colors.bold('init'))} Initialize Vizzly configuration`);
38
- console.log('');
39
- console.log(colors.dim('Use ') + colors.cyan('vizzly <command> --help') + colors.dim(' for command-specific options'));
40
- console.log('');
41
- }
42
-
43
- /**
44
- * Show error for unknown command
45
- * @param {string} command - Unknown command name
46
- * @param {Object} globalOptions - Global CLI options
47
- */
48
- export function showUnknownCommand(command, globalOptions = {}) {
49
- const colors = createColors({
50
- useColor: !globalOptions.noColor
51
- });
52
- if (globalOptions.json) {
53
- console.error(JSON.stringify({
54
- status: 'error',
55
- message: `Unknown command: ${command}`,
56
- availableCommands: ['run', 'upload', 'init'],
57
- timestamp: new Date().toISOString()
58
- }));
59
- process.exit(1);
60
- }
61
- console.error(colors.red(`✖ Unknown command: ${command}`));
62
- console.error('');
63
- console.error(colors.dim('Available commands: run, upload, init'));
64
- console.error('');
65
- process.exit(1);
66
- }
@@ -1,172 +0,0 @@
1
- import { compare } from 'odiff-bin';
2
- import fs from 'fs/promises';
3
- import path from 'path';
4
- import os from 'os';
5
- import crypto from 'crypto';
6
- import { VizzlyError } from '../errors/vizzly-error.js';
7
-
8
- /**
9
- * Compare two images and return the difference
10
- * @param {Buffer} imageBuffer1 - First image buffer
11
- * @param {Buffer} imageBuffer2 - Second image buffer
12
- * @param {Object} options - Comparison options
13
- * @param {number} options.threshold - Matching threshold (0-1)
14
- * @param {boolean} options.ignoreAntialiasing - Ignore antialiasing
15
- * @param {boolean} options.ignoreColors - Ignore colors (not supported by odiff)
16
- * @param {Array} options.ignoreRegions - Regions to ignore in comparison
17
- * @returns {Promise<Object>} Comparison result
18
- */
19
- export async function compareImages(imageBuffer1, imageBuffer2, options = {}) {
20
- // Create temporary files for odiff
21
- const tempDir = os.tmpdir();
22
- const tempId = crypto.randomBytes(8).toString('hex');
23
- const basePath = path.join(tempDir, `vizzly-base-${tempId}.png`);
24
- const comparePath = path.join(tempDir, `vizzly-compare-${tempId}.png`);
25
- const diffPath = path.join(tempDir, `vizzly-diff-${tempId}.png`);
26
- try {
27
- // Write buffers to temporary files
28
- await fs.writeFile(basePath, imageBuffer1);
29
- await fs.writeFile(comparePath, imageBuffer2);
30
-
31
- // Configure odiff options
32
- const odiffOptions = {
33
- threshold: options.threshold || 0.1,
34
- antialiasing: options.ignoreAntialiasing !== false,
35
- outputDiffMask: true,
36
- failOnLayoutDiff: false,
37
- noFailOnFsErrors: false,
38
- diffColor: '#ff0000',
39
- // Red for differences
40
- captureDiffLines: true,
41
- reduceRamUsage: false,
42
- ignoreRegions: options.ignoreRegions || []
43
- };
44
-
45
- // Run odiff comparison
46
- const result = await compare(basePath, comparePath, diffPath, odiffOptions);
47
-
48
- // Process results
49
- if (result.match) {
50
- return {
51
- misMatchPercentage: 0,
52
- diffPixels: 0,
53
- totalPixels: 0,
54
- dimensionDifference: {
55
- width: 0,
56
- height: 0
57
- },
58
- diffBuffer: null
59
- };
60
- }
61
-
62
- // Handle different failure reasons
63
- switch (result.reason) {
64
- case 'layout-diff':
65
- {
66
- return {
67
- misMatchPercentage: 100,
68
- dimensionDifference: {
69
- width: 'unknown',
70
- height: 'unknown'
71
- },
72
- error: 'Image dimensions do not match',
73
- diffBuffer: null
74
- };
75
- }
76
- case 'pixel-diff':
77
- {
78
- // Read the diff image
79
- const diffBuffer = await fs.readFile(diffPath);
80
- return {
81
- misMatchPercentage: result.diffPercentage,
82
- diffPixels: result.diffCount,
83
- totalPixels: Math.round(result.diffCount / (result.diffPercentage / 100)),
84
- dimensionDifference: {
85
- width: 0,
86
- height: 0
87
- },
88
- diffBuffer,
89
- diffLines: result.diffLines
90
- };
91
- }
92
- case 'file-not-exists':
93
- throw new VizzlyError(`Image file not found: ${result.file}`, 'IMAGE_NOT_FOUND', {
94
- file: result.file
95
- });
96
- default:
97
- throw new VizzlyError('Unknown comparison result', 'COMPARISON_UNKNOWN', {
98
- result
99
- });
100
- }
101
- } catch (error) {
102
- // Re-throw VizzlyErrors
103
- if (error instanceof VizzlyError) {
104
- throw error;
105
- }
106
- throw new VizzlyError('Failed to compare images', 'IMAGE_COMPARISON_FAILED', {
107
- error: error.message
108
- });
109
- } finally {
110
- // Clean up temporary files
111
- await Promise.all([fs.unlink(basePath).catch(() => {}), fs.unlink(comparePath).catch(() => {}), fs.unlink(diffPath).catch(() => {})]);
112
- }
113
- }
114
-
115
- /**
116
- * Check if buffer is a valid PNG image
117
- * @param {Buffer} buffer - Image buffer
118
- * @returns {boolean} True if valid PNG
119
- */
120
- export function isValidPNG(buffer) {
121
- // Check PNG signature
122
- if (!buffer || buffer.length < 8) {
123
- return false;
124
- }
125
-
126
- // PNG signature: 137 80 78 71 13 10 26 10
127
- const pngSignature = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
128
- return buffer.subarray(0, 8).equals(pngSignature);
129
- }
130
-
131
- /**
132
- * Get image dimensions from PNG buffer
133
- * @param {Buffer} buffer - Image buffer
134
- * @returns {Object|null} Dimensions or null if invalid
135
- */
136
- export function getImageDimensions(buffer) {
137
- if (!isValidPNG(buffer)) {
138
- return null;
139
- }
140
- try {
141
- // PNG dimensions are stored in the IHDR chunk
142
- // Skip PNG signature (8 bytes) + chunk length (4 bytes) + chunk type (4 bytes)
143
- const width = buffer.readUInt32BE(16);
144
- const height = buffer.readUInt32BE(20);
145
- return {
146
- width,
147
- height
148
- };
149
- } catch {
150
- return null;
151
- }
152
- }
153
-
154
- /**
155
- * Compare images with ignore regions
156
- * @param {Buffer} imageBuffer1 - First image buffer
157
- * @param {Buffer} imageBuffer2 - Second image buffer
158
- * @param {Array<{x: number, y: number, width: number, height: number}>} ignoreRegions - Regions to ignore
159
- * @returns {Promise<Object>} Comparison result
160
- */
161
- export async function compareImagesWithIgnoreRegions(imageBuffer1, imageBuffer2, ignoreRegions = []) {
162
- // Convert ignore regions to odiff format
163
- const odiffIgnoreRegions = ignoreRegions.map(region => ({
164
- x1: region.x,
165
- y1: region.y,
166
- x2: region.x + region.width,
167
- y2: region.y + region.height
168
- }));
169
- return compareImages(imageBuffer1, imageBuffer2, {
170
- ignoreRegions: odiffIgnoreRegions
171
- });
172
- }
@@ -1,9 +0,0 @@
1
- import { readFile } from 'fs/promises';
2
- import { resolve, dirname } from 'path';
3
- import { fileURLToPath } from 'url';
4
- const __dirname = dirname(fileURLToPath(import.meta.url));
5
- export async function getPackageInfo() {
6
- const packagePath = resolve(__dirname, '../../package.json');
7
- const content = await readFile(packagePath, 'utf-8');
8
- return JSON.parse(content);
9
- }
@@ -1,145 +0,0 @@
1
- /**
2
- * Project Detection Utilities
3
- * Automatically detect project type and framework
4
- */
5
-
6
- import { readFile, access } from 'fs/promises';
7
- import { join } from 'path';
8
-
9
- /**
10
- * Detect project type and framework
11
- * @param {string} directory - Directory to analyze
12
- * @returns {Promise<Object>} Project information
13
- */
14
- export async function detectProjectType(directory = process.cwd()) {
15
- const packageJsonPath = join(directory, 'package.json');
16
- let packageJsonData = null;
17
- try {
18
- const content = await readFile(packageJsonPath, 'utf8');
19
- packageJsonData = JSON.parse(content);
20
- } catch {
21
- // No package.json found
22
- }
23
-
24
- // Detect framework based on dependencies and files
25
- const framework = await detectFramework(directory, packageJsonData);
26
- const type = detectProjectTypeFromFramework(framework);
27
- return {
28
- type,
29
- framework,
30
- hasPackageJson: !!packageJsonData,
31
- projectName: packageJsonData?.name || 'unknown',
32
- dependencies: packageJsonData?.dependencies || {},
33
- devDependencies: packageJsonData?.devDependencies || {}
34
- };
35
- }
36
-
37
- /**
38
- * Detect testing framework
39
- * @param {string} directory - Directory to analyze
40
- * @param {Object} packageJson - Package.json content
41
- * @returns {Promise<string>} Framework name
42
- */
43
- async function detectFramework(directory, packageJson) {
44
- const dependencies = {
45
- ...packageJson?.dependencies,
46
- ...packageJson?.devDependencies
47
- };
48
-
49
- // Check for specific framework dependencies
50
- if (dependencies.cypress) {
51
- return 'cypress';
52
- }
53
- if (dependencies.playwright || dependencies['@playwright/test']) {
54
- return 'playwright';
55
- }
56
- if (dependencies.webdriverio || dependencies['@wdio/cli']) {
57
- return 'webdriver';
58
- }
59
- if (dependencies.jest || dependencies['@jest/core']) {
60
- return 'jest';
61
- }
62
- if (dependencies.vitest) {
63
- return 'vitest';
64
- }
65
- if (dependencies.mocha) {
66
- return 'mocha';
67
- }
68
-
69
- // Check for config files
70
- const configFiles = ['cypress.config.js', 'playwright.config.js', 'wdio.conf.js', 'jest.config.js', 'vitest.config.js'];
71
- for (const configFile of configFiles) {
72
- try {
73
- await access(join(directory, configFile));
74
- return configFile.split('.')[0];
75
- } catch {
76
- // File doesn't exist
77
- }
78
- }
79
- return 'generic';
80
- }
81
-
82
- /**
83
- * Determine project type from framework
84
- * @param {string} framework - Framework name
85
- * @returns {string} Project type
86
- */
87
- function detectProjectTypeFromFramework(framework) {
88
- const e2eFrameworks = ['cypress', 'playwright', 'webdriver'];
89
- if (e2eFrameworks.includes(framework)) {
90
- return 'e2e';
91
- }
92
- return 'web';
93
- }
94
-
95
- /**
96
- * Get suggested test command for framework
97
- * @param {string} framework - Framework name
98
- * @param {Object} packageJson - Package.json content
99
- * @returns {string} Suggested test command
100
- */
101
- export function getSuggestedTestCommand(framework, packageJson) {
102
- const scripts = packageJson?.scripts || {};
103
-
104
- // Check for common script names
105
- const testScripts = ['test:e2e', 'test:integration', 'e2e', 'cypress:run', 'playwright:test', 'test'];
106
- for (const script of testScripts) {
107
- if (scripts[script]) {
108
- return `npm run ${script}`;
109
- }
110
- }
111
-
112
- // Framework-specific defaults
113
- switch (framework) {
114
- case 'cypress':
115
- return 'npx cypress run';
116
- case 'playwright':
117
- return 'npx playwright test';
118
- case 'webdriver':
119
- return 'npx wdio run';
120
- case 'jest':
121
- return 'npx jest';
122
- case 'vitest':
123
- return 'npx vitest run';
124
- default:
125
- return 'npm test';
126
- }
127
- }
128
-
129
- /**
130
- * Get suggested screenshots directory for framework
131
- * @param {string} framework - Framework name
132
- * @returns {string} Screenshots directory
133
- */
134
- export function getSuggestedScreenshotsDir(framework) {
135
- switch (framework) {
136
- case 'cypress':
137
- return './cypress/screenshots';
138
- case 'playwright':
139
- return './test-results';
140
- case 'webdriver':
141
- return './screenshots';
142
- default:
143
- return './screenshots';
144
- }
145
- }