UNPKG

@vizzly-testing/cli

Version:

Visual review platform for UI developers and designers

164 lines (147 loc) 6.05 kB
import { loadConfig } from '../utils/config-loader.js'; import { ConsoleUI } from '../utils/console-ui.js'; import { createServiceContainer } from '../container/index.js'; import { getApiUrl } from '../utils/environment-config.js'; /** * Status command implementation * @param {string} buildId - Build ID to check status for * @param {Object} options - Command options * @param {Object} globalOptions - Global CLI options */ export async function statusCommand(buildId, options = {}, globalOptions = {}) { // Create UI handler const ui = new ConsoleUI({ json: globalOptions.json, verbose: globalOptions.verbose, color: !globalOptions.noColor }); // Note: ConsoleUI handles cleanup via global process listeners try { ui.info(`Checking status for build: ${buildId}`); // Load configuration with CLI overrides const allOptions = { ...globalOptions, ...options }; const config = await loadConfig(globalOptions.config, allOptions); // Validate API token if (!config.apiKey) { ui.error('API token required. Use --token or set VIZZLY_TOKEN environment variable'); return; } // Get API service ui.startSpinner('Fetching build status...'); const container = await createServiceContainer(config, 'status'); const apiService = await container.get('apiService'); // Get build details via unified ApiService const buildStatus = await apiService.getBuild(buildId); ui.stopSpinner(); // Extract build data from API response const build = buildStatus.build || buildStatus; // Display build summary ui.success(`Build: ${build.name || build.id}`); ui.info(`Status: ${build.status.toUpperCase()}`); ui.info(`Environment: ${build.environment}`); if (build.branch) { ui.info(`Branch: ${build.branch}`); } if (build.commit_sha) { ui.info(`Commit: ${build.commit_sha.substring(0, 8)} - ${build.commit_message || 'No message'}`); } // Show screenshot and comparison stats ui.info(`Screenshots: ${build.screenshot_count || 0} total`); ui.info(`Comparisons: ${build.total_comparisons || 0} total (${build.new_comparisons || 0} new, ${build.changed_comparisons || 0} changed, ${build.identical_comparisons || 0} identical)`); if (build.approval_status) { ui.info(`Approval Status: ${build.approval_status}`); } // Show timing information if (build.created_at) { ui.info(`Created: ${new Date(build.created_at).toLocaleString()}`); } if (build.completed_at) { ui.info(`Completed: ${new Date(build.completed_at).toLocaleString()}`); } else if (build.status !== 'completed' && build.status !== 'failed') { ui.info(`Started: ${new Date(build.started_at || build.created_at).toLocaleString()}`); } if (build.execution_time_ms) { ui.info(`Execution Time: ${Math.round(build.execution_time_ms / 1000)}s`); } // Show build URL if we can construct it const baseUrl = config.baseUrl || getApiUrl(); if (baseUrl && build.project_id) { const buildUrl = baseUrl.replace('/api', '') + `/projects/${build.project_id}/builds/${build.id}`; ui.info(`View Build: ${buildUrl}`); } // Output JSON data for --json mode if (globalOptions.json) { const statusData = { buildId: build.id, status: build.status, name: build.name, createdAt: build.created_at, updatedAt: build.updated_at, completedAt: build.completed_at, environment: build.environment, branch: build.branch, commit: build.commit_sha, commitMessage: build.commit_message, screenshotsTotal: build.screenshot_count || 0, comparisonsTotal: build.total_comparisons || 0, newComparisons: build.new_comparisons || 0, changedComparisons: build.changed_comparisons || 0, identicalComparisons: build.identical_comparisons || 0, approvalStatus: build.approval_status, executionTime: build.execution_time_ms, isBaseline: build.is_baseline, userAgent: build.user_agent }; ui.data(statusData); } // Show additional info in verbose mode if (globalOptions.verbose) { ui.info('\n--- Additional Details ---'); if (build.approved_screenshots > 0 || build.rejected_screenshots > 0 || build.pending_screenshots > 0) { ui.info(`Screenshot Approvals: ${build.approved_screenshots || 0} approved, ${build.rejected_screenshots || 0} rejected, ${build.pending_screenshots || 0} pending`); } if (build.avg_diff_percentage !== null) { ui.info(`Average Diff: ${(build.avg_diff_percentage * 100).toFixed(2)}%`); } if (build.github_pull_request_number) { ui.info(`GitHub PR: #${build.github_pull_request_number}`); } if (build.is_baseline) { ui.info('This build is marked as a baseline'); } ui.info(`User Agent: ${build.user_agent || 'Unknown'}`); ui.info(`Build ID: ${build.id}`); ui.info(`Project ID: ${build.project_id}`); } // Show progress if build is still processing if (build.status === 'processing' || build.status === 'pending') { const totalJobs = build.completed_jobs + build.failed_jobs + build.processing_screenshots; if (totalJobs > 0) { const progress = (build.completed_jobs + build.failed_jobs) / totalJobs; ui.info(`Progress: ${Math.round(progress * 100)}% complete`); } } ui.cleanup(); // Exit with appropriate code based on build status if (build.status === 'failed' || build.failed_jobs > 0) { process.exit(1); } } catch (error) { ui.error('Failed to get build status', error); } } /** * Validate status options * @param {string} buildId - Build ID to check * @param {Object} options - Command options */ export function validateStatusOptions(buildId) { const errors = []; if (!buildId || buildId.trim() === '') { errors.push('Build ID is required'); } return errors; }