UNPKG

tdpw

Version:

CLI tool for uploading Playwright test reports to TestDino platform with TestDino storage support

219 lines â€ĸ 9.52 kB
"use strict"; /** * Cache command - Store test execution metadata after Playwright runs */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CacheCommand = exports.CacheOptionsSchema = void 0; const progress_1 = require("../../utils/progress"); const shard_detection_1 = require("../../core/shard-detection"); const cache_extractor_1 = require("../../core/cache-extractor"); const build_detector_1 = require("../../core/build-detector"); const git_1 = require("../../collectors/git"); const ci_1 = require("../../collectors/ci"); const system_1 = require("../../collectors/system"); const cache_api_1 = require("../../services/cache-api"); /** * Zod schema for cache options validation */ const zod_1 = require("zod"); exports.CacheOptionsSchema = zod_1.z.object({ cacheId: zod_1.z.string().optional(), workingDir: zod_1.z.string().optional(), token: zod_1.z.string().optional(), verbose: zod_1.z.boolean().optional().default(false), }); /** * Cache command implementation */ class CacheCommand { config; name = 'cache'; description = 'Store test execution metadata after Playwright runs'; constructor(config) { this.config = config; } /** * Execute the cache command */ async execute(options) { const progress = (0, progress_1.createProgressTracker)(); try { progress.start('🔍 Caching test execution metadata...'); if (options.verbose) { console.log('📊 Cache configuration:', { workingDir: options.workingDir || process.cwd(), customCacheId: options.cacheId || 'auto-detect', }); } // Step 1: Detect working directory const workingDir = options.workingDir || process.cwd(); progress.update(`📁 Working directory: ${workingDir}`); // Step 2: Auto-detect Playwright configuration and shard info progress.update('🔍 Detecting Playwright configuration...'); const shardInfo = await this.detectShardInfo(workingDir); if (options.verbose && shardInfo) { console.log('📊 Detected shard configuration:', shardInfo); } // Step 3: Extract test failure data using existing discovery and parsing progress.update('📋 Discovering and extracting test data...'); const failureData = await this.extractFailureData(workingDir); if (!failureData.hasData) { progress.warn('No test reports found - tests may not have completed yet'); console.log('💡 Run this command after your Playwright tests complete'); return; // Exit successfully (CI-friendly) } if (options.verbose) { console.log(`📋 Found ${failureData.failures.length} failed tests from ${failureData.reportPaths.length} reports`); } // Step 4: Collect build and CI metadata progress.update('đŸ—ī¸ Collecting build metadata...'); const metadata = await this.collectBuildMetadata(options); // Step 5: Prepare cache payload const cachePayload = this.prepareCachePayload({ metadata, shardInfo, failureData, timestamp: new Date().toISOString(), }); if (options.verbose) { console.log('📤 Cache payload summary:', { cacheId: cachePayload.cacheId, pipelineId: cachePayload.pipelineId, commit: cachePayload.commit, isSharded: cachePayload.isSharded, shardIndex: cachePayload.shardIndex, shardTotal: cachePayload.shardTotal, failures: cachePayload.failures.length, totalTests: cachePayload.summary.total, failedTests: cachePayload.summary.failed, branch: cachePayload.branch, repository: cachePayload.repository, }); } // Step 7: Send to API progress.update('📤 Sending cache data to TestDino API...'); await this.sendCacheData(cachePayload); progress.succeed('✅ Test execution metadata cached successfully'); } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; // Special handling for cache conflicts (409) - this is not an error if (errorMessage.includes('already exists')) { progress.warn('Cache data already exists for this shard'); console.log('â„šī¸ This shard has already been cached - skipping'); console.log('💡 This warning will not affect your CI pipeline'); return; } // CI-friendly error handling - warn but don't fail for other errors progress.warn(`Failed to cache test metadata: ${errorMessage}`); if (options.verbose) { console.error('🔍 Full error details:', error); } console.log('💡 This warning will not affect your CI pipeline'); // Exit with success code (0) to avoid breaking CI } } /** * Auto-detect shard information from Playwright configuration */ async detectShardInfo(workingDir) { return await shard_detection_1.PlaywrightShardDetector.detectShardInfo(workingDir); } /** * Discover and extract test failure data */ async extractFailureData(workingDir) { const extractor = new cache_extractor_1.CacheExtractor(workingDir); const result = await extractor.extractFailureData(); // Add hasData property to indicate if any reports were found return { ...result, hasData: result.reportPaths.length > 0, }; } /** * Collect build and CI metadata using existing collectors */ async collectBuildMetadata(options) { // Collect cache ID information from environment const cacheIdInfo = await build_detector_1.CacheIdDetector.detectCacheId(options.cacheId); // Collect Git metadata const gitCollector = new git_1.GitCollector(options.workingDir || process.cwd()); const gitMetadata = await gitCollector.getMetadata(); // Collect CI metadata const ciMetadata = ci_1.CiCollector.collect(); // Collect system metadata const systemMetadata = system_1.SystemCollector.collect(); return { cacheIdInfo, gitMetadata, ciMetadata, systemMetadata, }; } /** * Prepare cache payload for TestDino API based on PRD specification */ prepareCachePayload(data) { const { cacheIdInfo, ciMetadata } = data.metadata; const effectiveShardInfo = data.shardInfo || shard_detection_1.PlaywrightShardDetector.createDefaultShardInfo(); return { // Cache identification (new format) cacheId: cacheIdInfo.cacheId, pipelineId: cacheIdInfo.pipelineId, commit: cacheIdInfo.commit, // Git metadata branch: cacheIdInfo.branch, // Use original branch from cacheIdInfo repository: cacheIdInfo.repository, // CI information ci: { provider: ciMetadata.provider || 'unknown', pipelineId: ciMetadata.pipeline?.id || 'unknown', buildNumber: ciMetadata.build?.number || 'unknown', }, // Shard information (cleaner structure) isSharded: effectiveShardInfo.shardTotal > 1, shardIndex: effectiveShardInfo.shardTotal > 1 ? effectiveShardInfo.shardIndex : null, shardTotal: effectiveShardInfo.shardTotal > 1 ? effectiveShardInfo.shardTotal : null, // Test failure data (simplified) failures: data.failureData.failures.map(f => ({ file: f.file, testTitle: f.testTitle, })), // Test summary for this shard summary: data.failureData.summary, // Timestamp timestamp: data.timestamp, }; } /** * Send cache data to TestDino API */ async sendCacheData(payload) { const apiClient = new cache_api_1.CacheApiClient(this.config); // Check if cache endpoint is available (since APIs aren't implemented yet) const isEndpointAvailable = await apiClient.testCacheEndpoint(); if (!isEndpointAvailable) { console.log('â„šī¸ Cache API endpoint not available yet - using mock submission'); await apiClient.mockCacheSubmission(payload); return; } // Submit cache data to real API const result = await apiClient.submitCacheData(payload); if (result.success) { console.log(`✅ Cache data submitted successfully for cache: ${result.cacheId}`); if (result.message) { console.log(` ${result.message}`); } } else { throw new Error(`Cache submission failed for cache: ${result.cacheId}`); } } } exports.CacheCommand = CacheCommand; //# sourceMappingURL=cache.js.map