diff --git a/.github/scripts/build-wp-env.js b/.github/scripts/build-wp-env.js new file mode 100644 index 00000000..fcd3ed7d --- /dev/null +++ b/.github/scripts/build-wp-env.js @@ -0,0 +1,218 @@ +'use strict'; + +const fs = require( 'fs' ); + +const wpEnv = require( '../../.wp-env.json' ); +const { + PHP_VERSION, + WP_CORE_VERSION, + HELLO_THEME_VERSION, + HELLO_PLUS_VERSION, + ELEMENTOR_VERSION, +} = process.env; + +if ( ! PHP_VERSION ) { + // eslint-disable-next-line no-console + console.error( 'missing env var PHP_VERSION' ); + process.exit( 1 ); +} + +if ( ! WP_CORE_VERSION ) { + // eslint-disable-next-line no-console + console.error( 'missing env var WP_CORE_VERSION' ); + process.exit( 1 ); +} + +// Set WordPress core version +let wpCore = null; +if ( WP_CORE_VERSION !== 'latest' ) { + wpCore = `WordPress/WordPress#${ WP_CORE_VERSION }`; +} + +// Set PHP version +wpEnv.phpVersion = PHP_VERSION; +wpEnv.core = wpCore; + +// Configure themes - Require built Hello Theme to avoid false positives +if ( fs.existsSync( './tmp/hello-theme' ) ) { + wpEnv.themes = [ './tmp/hello-theme' ]; + // eslint-disable-next-line no-console + console.log( 'โœ… Using built Hello Theme from ./tmp/hello-theme' ); +} else { + // eslint-disable-next-line no-console + console.error( 'Built Hello Theme not found at ./tmp/hello-theme' ); + // eslint-disable-next-line no-console + console.error( 'This prevents false positives from using unbuild source theme' ); + // eslint-disable-next-line no-console + console.error( 'Current directory contents:' ); + // eslint-disable-next-line no-console + console.error( fs.readdirSync( '.' ) ); + if ( fs.existsSync( './tmp' ) ) { + // eslint-disable-next-line no-console + console.error( './tmp contents:' ); + // eslint-disable-next-line no-console + console.error( fs.readdirSync( './tmp' ) ); + } + process.exit( 1 ); +} + +// Add Hello Plus if available (for Plus matrix tests) +if ( HELLO_PLUS_VERSION && fs.existsSync( './tmp/hello-plus' ) ) { + wpEnv.themes.push( './tmp/hello-plus' ); +} + +// Configure plugins +wpEnv.plugins = []; + +// Add Elementor plugin +if ( ELEMENTOR_VERSION ) { + // eslint-disable-next-line no-console + console.log( '๐Ÿ” =============================================' ); + // eslint-disable-next-line no-console + console.log( '๐Ÿ” BUILD-WP-ENV.JS ELEMENTOR DEBUG' ); + // eslint-disable-next-line no-console + console.log( '๐Ÿ” =============================================' ); + // eslint-disable-next-line no-console + console.log( `๐ŸŽฏ ELEMENTOR_VERSION: "${ ELEMENTOR_VERSION }"` ); + + if ( 'latest-stable' === ELEMENTOR_VERSION ) { + // Use WordPress.org directly for latest-stable (most reliable) + wpEnv.plugins.push( 'https://downloads.wordpress.org/plugin/elementor.latest-stable.zip' ); + // eslint-disable-next-line no-console + console.log( 'โœ… Using WordPress.org Elementor latest-stable (direct)' ); + } else if ( ELEMENTOR_VERSION.match( /^v?[0-9]+\.[0-9]+\.[0-9]+$/ ) ) { + // Use WordPress.org directly for semantic versions (e.g., 3.30.4, v3.30.4) + const cleanVersion = ELEMENTOR_VERSION.replace( /^v/, '' ); + wpEnv.plugins.push( `https://downloads.wordpress.org/plugin/elementor.${ cleanVersion }.zip` ); + // eslint-disable-next-line no-console + console.log( `โœ… Using WordPress.org Elementor ${ cleanVersion } (direct)` ); + } else if ( fs.existsSync( './tmp/elementor' ) ) { + // GitHub branches (main, feature-branch) - expect built artifacts from workflow + // eslint-disable-next-line no-console + console.log( `๐Ÿ” Using GitHub built artifacts for Elementor ${ ELEMENTOR_VERSION }` ); + // Debug: Verify Elementor directory structure + // eslint-disable-next-line no-console + console.log( '๐Ÿ” DEBUG: Elementor directory found, verifying structure...' ); + try { + const elementorContents = fs.readdirSync( './tmp/elementor' ); + // eslint-disable-next-line no-console + console.log( `๐Ÿ“ Elementor directory contents (${ elementorContents.length } items):`, elementorContents.slice( 0, 10 ) ); + + // Check for main plugin file + if ( fs.existsSync( './tmp/elementor/elementor.php' ) ) { + // eslint-disable-next-line no-console + console.log( 'โœ… Main plugin file found: elementor.php' ); + + // Read plugin header for verification + try { + const pluginContent = fs.readFileSync( './tmp/elementor/elementor.php', 'utf8' ); + const headerMatch = pluginContent.match( /Plugin Name:\s*(.+)/i ); + const versionMatch = pluginContent.match( /Version:\s*(.+)/i ); + if ( headerMatch ) { + // eslint-disable-next-line no-console + console.log( `๐Ÿ“„ Plugin Name: ${ headerMatch[ 1 ].trim() }` ); + } + if ( versionMatch ) { + // eslint-disable-next-line no-console + console.log( `๐Ÿท๏ธ Plugin Version: ${ versionMatch[ 1 ].trim() }` ); + } + } catch ( error ) { + // eslint-disable-next-line no-console + console.log( 'โš ๏ธ Could not read plugin header:', error.message ); + } + } else { + // eslint-disable-next-line no-console + console.log( 'โŒ Main plugin file missing: elementor.php' ); + const phpFiles = elementorContents.filter( ( file ) => file.endsWith( '.php' ) ); + // eslint-disable-next-line no-console + console.log( `๐Ÿ” Available PHP files (${ phpFiles.length }):`, phpFiles.slice( 0, 5 ) ); + } + + // Check for essential directories + const essentialDirs = [ 'includes', 'assets' ]; + essentialDirs.forEach( ( dir ) => { + if ( fs.existsSync( `./tmp/elementor/${ dir }` ) ) { + // eslint-disable-next-line no-console + console.log( `โœ… Essential directory found: ${ dir }` ); + } else { + // eslint-disable-next-line no-console + console.log( `โš ๏ธ Essential directory missing: ${ dir }` ); + } + } ); + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( 'โŒ Error reading Elementor directory:', error.message ); + } + + // Check if local Elementor installation is valid + const isValidElementor = fs.existsSync( './tmp/elementor/elementor.php' ) && + fs.existsSync( './tmp/elementor/includes' ) && + fs.existsSync( './tmp/elementor/assets' ); + + if ( isValidElementor ) { + // Use the GitHub built artifacts for branches + wpEnv.plugins.push( './tmp/elementor' ); + // eslint-disable-next-line no-console + console.log( `โœ… Using GitHub built artifacts for Elementor ${ ELEMENTOR_VERSION }` ); + } else { + // GitHub artifacts should be valid - if not, something went wrong in workflow + // eslint-disable-next-line no-console + console.error( `โŒ Invalid GitHub artifacts for Elementor ${ ELEMENTOR_VERSION }` ); + // eslint-disable-next-line no-console + console.error( 'Expected workflow to provide valid built artifacts in ./tmp/elementor' ); + process.exit( 1 ); + } + } else { + // eslint-disable-next-line no-console + console.error( `โŒ Elementor directory not found at ./tmp/elementor for branch/commit: ${ ELEMENTOR_VERSION }` ); + // eslint-disable-next-line no-console + console.error( 'Note: Semantic versions (e.g., 3.30.4) and latest-stable are downloaded directly from WordPress.org' ); + process.exit( 1 ); + } + + // eslint-disable-next-line no-console + console.log( '๐Ÿ” =============================================' ); + // eslint-disable-next-line no-console + console.log( '๐Ÿ” END BUILD-WP-ENV.JS ELEMENTOR DEBUG' ); + // eslint-disable-next-line no-console + console.log( '๐Ÿ” =============================================' ); +} + +// Test configuration +wpEnv.config = { + ...wpEnv.config, + ELEMENTOR_SHOW_HIDDEN_EXPERIMENTS: true, + SCRIPT_DEBUG: false, + WP_DEBUG: true, + WP_DEBUG_LOG: true, + WP_DEBUG_DISPLAY: false, +}; + +// Add version info for debugging +if ( HELLO_THEME_VERSION ) { + wpEnv.config.HELLO_THEME_VERSION = HELLO_THEME_VERSION; +} +if ( HELLO_PLUS_VERSION ) { + wpEnv.config.HELLO_PLUS_VERSION = HELLO_PLUS_VERSION; +} + +// eslint-disable-next-line no-console +console.log( 'Building wp-env configuration:' ); +// eslint-disable-next-line no-console +console.log( `- PHP Version: ${ PHP_VERSION }` ); +// eslint-disable-next-line no-console +console.log( `- WP Core: ${ wpCore || 'latest' }` ); +// eslint-disable-next-line no-console +console.log( `- Hello Theme: ${ HELLO_THEME_VERSION || 'current' }` ); +// eslint-disable-next-line no-console +console.log( `- Hello Plus: ${ HELLO_PLUS_VERSION || 'not included' }` ); +// eslint-disable-next-line no-console +console.log( `- Elementor: ${ ELEMENTOR_VERSION || 'latest-stable' }` ); +// eslint-disable-next-line no-console +console.log( `- Themes: ${ wpEnv.themes.join( ', ' ) }` ); +// eslint-disable-next-line no-console +console.log( `- Plugins: ${ wpEnv.plugins.join( ', ' ) }` ); + +fs.writeFileSync( '.wp-env.json', JSON.stringify( wpEnv, null, 4 ) ); +// eslint-disable-next-line no-console +console.log( 'โœ… wp-env.json updated successfully' ); diff --git a/.github/scripts/daily-report.js b/.github/scripts/daily-report.js new file mode 100644 index 00000000..8dcf46c7 --- /dev/null +++ b/.github/scripts/daily-report.js @@ -0,0 +1,180 @@ +/** + * Utility functions for the daily Hello Theme test matrix workflow + */ + +const { + getStatusEmoji, + getStatusText, + validateWorkflowData, + pollWorkflows, + generateStatusSummary, + setJobStatus, + generateResultLink, +} = require( './workflow-reporting.js' ); + +/** + * Generates a daily trigger workflow report + * @param {Object} github - GitHub API client + * @param {Object} context - GitHub Actions context + * @param {Object} core - GitHub Actions core utilities + * @param {Array} workflowData - Array of workflow run data + * @param {Object} timing - Timing configuration object + */ +async function generateDailyTriggerReport( github, context, core, workflowData, timing ) { + // eslint-disable-next-line no-console + console.log( `Generating report for ${ workflowData.length } daily compatibility tests` ); + + const validation = validateWorkflowData( workflowData ); + + if ( ! validation.hasValidRuns ) { + // eslint-disable-next-line no-console + console.warn( 'No valid workflow run IDs found, generating preliminary report' ); + return; + } + + // Poll workflows until completion + const results = await pollWorkflows( github, context, validation.validRuns, timing, { + logPrefix: 'daily workflows', + } ); + + // Generate and write report + const summary = generateDailyMarkdownReport( results ); + await core.summary.addRaw( summary ).write(); + // eslint-disable-next-line no-console + console.log( 'Generated daily trigger workflow report' ); + + setJobStatus( core, results, 'Some daily workflows failed or encountered errors' ); +} + +/** + * Generates a markdown report for daily trigger testing + * @param {Array} results - Array of workflow results + * @return {string} - Markdown formatted report + */ +function generateDailyMarkdownReport( results ) { + const summary = generateStatusSummary( results ); + + // Sort results by type and name for consistent display + const sortedResults = sortDailyResults( results ); + + let report = `# ๐Ÿงช Hello Theme Daily Test Matrix Results\n\n`; + + // Overall summary + report += `## ๐Ÿ“Š Summary\n\n`; + report += `| Status | Count |\n`; + report += `|--------|-------|\n`; + report += `| โœ… Success | ${ summary.success } |\n`; + report += `| โŒ Failed | ${ summary.failed } |\n`; + report += `| โŒ Error | ${ summary.error } |\n`; + if ( summary.running > 0 ) { + report += `| ๐Ÿ”„ Running | ${ summary.running } |\n`; + } + if ( summary.cancelled > 0 ) { + report += `| ๐Ÿšซ Cancelled | ${ summary.cancelled } |\n`; + } + if ( summary.skipped > 0 ) { + report += `| โญ๏ธ Skipped | ${ summary.skipped } |\n`; + } + report += `| **Total** | **${ summary.total }** |\n\n`; + + // Detailed results + report += `## ๐Ÿ“‹ Detailed Results\n\n`; + + // Group by matrix type + const coreTests = sortedResults.filter( ( r ) => r.combination && r.combination.includes( 'el' ) ); + const plusTests = sortedResults.filter( ( r ) => r.combination && r.combination.includes( 'hp' ) ); + + if ( coreTests.length > 0 ) { + report += `### โšก Hello Theme ร— Elementor Tests\n\n`; + report += generateTestTable( coreTests ); + report += `\n`; + } + + if ( plusTests.length > 0 ) { + report += `### ๐Ÿ”Œ Hello Theme ร— Hello Plus Tests\n\n`; + report += generateTestTable( plusTests ); + report += `\n`; + } + + // Footer + report += `## ๐ŸŽฏ Matrix Strategy\n\n`; + report += `- **Core Matrix**: Hello Theme ร— Elementor (main, latest, previous minors)\n`; + report += `- **Plus Matrix**: Hello Theme ร— Hello Plus (latest, previous patch - WordPress.org only)\n`; + report += `- **Versions**: Tests both main development and GA release combinations\n\n`; + + report += `*Generated at ${ new Date().toISOString() }*`; + + return report; +} + +/** + * Generates a test results table + * @param {Array} tests - Array of test results + * @return {string} - Markdown table + */ +function generateTestTable( tests ) { + let table = `| Status | Test Name | Duration | Link |\n`; + table += `|--------|-----------|----------|------|\n`; + + tests.forEach( ( test ) => { + const emoji = getStatusEmoji( test.status, test.conclusion ); + const statusText = getStatusText( test.status, test.conclusion ); + const duration = test.duration ? `${ test.duration }m` : '-'; + const link = generateResultLink( test ); + + table += `| ${ emoji } ${ statusText } | ${ test.name || test.combination } | ${ duration } | ${ link } |\n`; + } ); + + return table; +} + +/** + * Sorts daily test results for consistent display order + * @param {Array} results - Array of workflow results + * @return {Array} - Sorted array of results + */ +function sortDailyResults( results ) { + return results.sort( ( a, b ) => { + // First sort by matrix type (core vs plus) + const aIsCore = a.combination && a.combination.includes( 'el' ); + const bIsCore = b.combination && b.combination.includes( 'el' ); + + if ( aIsCore && ! bIsCore ) { + return -1; + } + if ( ! aIsCore && bIsCore ) { + return 1; + } + + // Then sort by Hello Theme version (main first, then GA) + const aIsMain = 'main' === a.hello_theme_version; + const bIsMain = 'main' === b.hello_theme_version; + + if ( aIsMain && ! bIsMain ) { + return -1; + } + if ( ! aIsMain && bIsMain ) { + return 1; + } + + // Finally sort by dependency version (main first, then latest, then previous) + const aVersion = a.elementor_version || a.hello_plus_version || ''; + const bVersion = b.elementor_version || b.hello_plus_version || ''; + + if ( 'main' === aVersion && bVersion !== 'main' ) { + return -1; + } + if ( aVersion !== 'main' && 'main' === bVersion ) { + return 1; + } + + // Alphabetical fallback + return ( a.name || a.combination || '' ).localeCompare( b.name || b.combination || '' ); + } ); +} + +module.exports = { + generateDailyTriggerReport, + generateDailyMarkdownReport, + sortDailyResults, +}; diff --git a/.github/scripts/workflow-reporting.js b/.github/scripts/workflow-reporting.js new file mode 100644 index 00000000..cd1fa143 --- /dev/null +++ b/.github/scripts/workflow-reporting.js @@ -0,0 +1,307 @@ +/** + * Base utility functions for workflow reporting + * Shared functionality between different workflow report types + * Adapted for Hello Theme compatibility testing + */ + +/** + * Gets default configuration for workflow polling + * @return {Object} - Default configuration object with timing settings + */ +function getDefaultPollingConfig() { + return { + maxWaitTime: 50 * 60 * 1000, // 50 minutes + initialWait: 15 * 60 * 1000, // 15 minutes + pollInterval: 3 * 60 * 1000, // 3 minutes + }; +} + +/** + * Gets timing configuration for daily trigger workflows + * @return {Object} - Configuration object with timing settings including trigger delays + */ +function getDailyTriggerTimingConfig() { + return { + ...getDefaultPollingConfig(), + triggerDelays: { + 'ht-main-el-main': 0, // No delay for first trigger + 'ht-main-el-latest': 15, // 15 seconds delay + 'ht-main-el-prev': 30, // 30 seconds delay + 'ht-ga-el-main': 45, // 45 seconds delay + 'ht-main-hp-latest': 60, // 60 seconds delay + 'ht-main-hp-prev': 75, // 75 seconds delay + 'ht-ga-hp-latest': 90, // 90 seconds delay + 'ht-ga-hp-prev': 105, // 105 seconds delay + }, + }; +} + +/** + * Generates status emoji based on workflow status and conclusion + * @param {string} status - Workflow status + * @param {string} conclusion - Workflow conclusion + * @param {Object} options - Additional options for status handling + * @param {boolean} options.supportMissing - Whether to support 'missing' status + * @return {string} - Emoji representing the status + */ +function getStatusEmoji( status, conclusion, options = {} ) { + const { supportMissing = false } = options; + + if ( supportMissing && ( 'missing' === status || 'missing' === conclusion ) ) { + return 'โš ๏ธ'; + } + + if ( 'error' === status || 'error' === conclusion ) { + return 'โŒ'; + } + + if ( status !== 'completed' ) { + return '๐Ÿ”„'; + } + + switch ( conclusion ) { + case 'success': + return 'โœ…'; + case 'failure': + return 'โŒ'; + case 'cancelled': + return '๐Ÿšซ'; + case 'skipped': + return 'โญ๏ธ'; + default: + return 'โ“'; + } +} + +/** + * Generates status text based on workflow status and conclusion + * @param {string} status - Workflow status + * @param {string} conclusion - Workflow conclusion + * @param {Object} options - Additional options for status handling + * @param {boolean} options.supportMissing - Whether to support 'missing' status + * @return {string} - Human readable status text + */ +function getStatusText( status, conclusion, options = {} ) { + const { supportMissing = false } = options; + + if ( supportMissing && ( 'missing' === status || 'missing' === conclusion ) ) { + return 'Missing'; + } + + if ( 'error' === status || 'error' === conclusion ) { + return 'Error'; + } + + if ( status !== 'completed' ) { + return 'Running'; + } + + switch ( conclusion ) { + case 'success': + return 'Success'; + case 'failure': + return 'Failed'; + case 'cancelled': + return 'Cancelled'; + case 'skipped': + return 'Skipped'; + default: + return 'Unknown'; + } +} + +/** + * Validates and filters workflow data + * @param {Array} workflowData - Array of workflow run data + * @return {Object} - Validation results object + */ +function validateWorkflowData( workflowData ) { + const validRuns = workflowData.filter( ( run ) => run && run.id && ! isNaN( run.id ) ); + const uniqueIds = [ ...new Set( validRuns.map( ( run ) => run.id ) ) ]; + + return { + total: workflowData.length, + valid: validRuns.length, + invalid: workflowData.length - validRuns.length, + validRuns, + hasValidRuns: validRuns.length > 0, + hasDuplicates: uniqueIds.length !== validRuns.length, + }; +} + +/** + * Core workflow polling logic + * @param {Object} github - GitHub API client + * @param {Object} context - GitHub Actions context + * @param {Array} workflowData - Array of workflow run data + * @param {Object} timing - Timing configuration object + * @param {Object} options - Additional options for polling behavior + * @param {Function} options.preprocessWorkflows - Function to preprocess workflows before polling + * @param {Function} options.createResult - Function to create result object from workflow run + * @param {string} options.logPrefix - Prefix for log messages + * @return {Array} - Array of results with status information + */ +async function pollWorkflows( github, context, workflowData, timing, options = {} ) { + const { + preprocessWorkflows = ( workflows ) => ( { actualWorkflows: workflows, additionalResults: [] } ), + createResult = ( workflow, workflowRun ) => ( { + ...workflow, + status: workflowRun.status, + conclusion: workflowRun.conclusion, + duration: workflowRun.updated_at + ? Math.round( ( new Date( workflowRun.updated_at ) - new Date( workflowRun.created_at ) ) / 1000 / 60 ) : 0, + created_at: workflowRun.created_at, + updated_at: workflowRun.updated_at, + } ), + logPrefix = 'workflows', + } = options; + + const { maxWaitTime, initialWait, pollInterval } = timing; + const startTime = Date.now(); + + // Initial wait + // eslint-disable-next-line no-console + console.log( `Waiting ${ initialWait / 1000 / 60 } minutes before starting to poll workflow statuses...` ); + await new Promise( ( resolve ) => setTimeout( resolve, initialWait ) ); + // eslint-disable-next-line no-console + console.log( `Starting to poll ${ logPrefix } statuses...` ); + + let allCompleted = false; + const results = []; + + // Preprocess workflows (e.g., separate missing entries) + const { actualWorkflows, additionalResults } = preprocessWorkflows( workflowData ); + + // Add any additional results (e.g., missing entries) + results.push( ...additionalResults ); + + // eslint-disable-next-line no-console + console.log( `Polling ${ actualWorkflows.length } actual workflows, ${ additionalResults.length } additional entries` ); + + while ( ! allCompleted && ( Date.now() - startTime ) < maxWaitTime ) { + allCompleted = true; + + for ( const workflow of actualWorkflows ) { + try { + const response = await github.rest.actions.getWorkflowRun( { + owner: context.repo.owner, + repo: context.repo.repo, + run_id: workflow.id, + } ); + + const workflowRun = response.data; + const status = workflowRun.status; + + if ( status !== 'completed' ) { + allCompleted = false; + } + + // Update or add result + const existingIndex = results.findIndex( ( r ) => r.id === workflow.id ); + const result = createResult( workflow, workflowRun ); + + if ( existingIndex >= 0 ) { + results[ existingIndex ] = result; + } else { + results.push( result ); + } + } catch ( error ) { + // eslint-disable-next-line no-console + console.log( `Error checking run ${ workflow.id }: ${ error.message }` ); + if ( ! results.find( ( r ) => r.id === workflow.id ) ) { + results.push( { + ...workflow, + status: 'error', + conclusion: 'error', + duration: 0, + error: error.message, + } ); + } + } + } + + if ( ! allCompleted ) { + const elapsedMinutes = Math.round( ( Date.now() - startTime ) / 1000 / 60 ); + const completedCount = results.filter( ( r ) => 'completed' === r.status ).length; + // eslint-disable-next-line no-console + console.log( `Polling... ${ completedCount }/${ actualWorkflows.length } ${ logPrefix } completed (${ elapsedMinutes } minutes elapsed)` ); + await new Promise( ( resolve ) => setTimeout( resolve, pollInterval ) ); + } + } + + return results; +} + +/** + * Generates common status summary statistics + * @param {Array} results - Array of workflow results + * @param {Object} options - Options for summary generation + * @param {boolean} options.includeMissing - Whether to include missing count + * @return {Object} - Object containing status counts + */ +function generateStatusSummary( results, options = {} ) { + const { includeMissing = false } = options; + + const summary = { + total: results.length, + success: results.filter( ( r ) => 'success' === r.conclusion ).length, + failed: results.filter( ( r ) => 'failure' === r.conclusion ).length, + error: results.filter( ( r ) => 'error' === r.status ).length, + cancelled: results.filter( ( r ) => 'cancelled' === r.conclusion ).length, + skipped: results.filter( ( r ) => 'skipped' === r.conclusion ).length, + running: results.filter( ( r ) => r.status !== 'completed' && r.status !== 'error' ).length, + }; + + if ( includeMissing ) { + summary.missing = results.filter( ( r ) => 'missing' === r.status ).length; + } + + return summary; +} + +/** + * Sets job status based on results + * @param {Object} core - GitHub Actions core utilities + * @param {Array} results - Array of workflow results + * @param {string} contextMessage - Context message for the failure + */ +function setJobStatus( core, results, contextMessage = 'Some workflows failed or encountered errors' ) { + const errors = results.filter( ( r ) => 'error' === r.status ).length; + const failed = results.filter( ( r ) => 'failure' === r.conclusion ).length; + + if ( errors > 0 || failed > 0 ) { + core.setFailed( `${ contextMessage }. Failed: ${ failed }, Errors: ${ errors }` ); + } +} + +/** + * Generates a standard link for workflow result + * @param {Object} result - Test result object + * @param {Object} options - Options for link generation + * @param {boolean} options.supportMissing - Whether to support missing status + * @return {string} - Link or status text + */ +function generateResultLink( result, options = {} ) { + const { supportMissing = false } = options; + + if ( supportMissing && ( 'missing' === result.status || 'missing' === result.conclusion ) ) { + return 'Missing'; + } + + if ( result.url ) { + return `[View Run](${ result.url })`; + } + return 'No Link'; +} + +module.exports = { + getDefaultPollingConfig, + getDailyTriggerTimingConfig, + getStatusEmoji, + getStatusText, + validateWorkflowData, + pollWorkflows, + generateStatusSummary, + setJobStatus, + generateResultLink, +}; diff --git a/.github/scripts/workflow-trigger.js b/.github/scripts/workflow-trigger.js new file mode 100644 index 00000000..6a64db3e --- /dev/null +++ b/.github/scripts/workflow-trigger.js @@ -0,0 +1,253 @@ +/** + * Utility functions for GitHub Actions workflow management + * Adapted for Hello Theme compatibility testing + */ + +/** + * Triggers a workflow and waits for it to appear in the runs list + * @param {Object} github - GitHub API client + * @param {Object} context - GitHub Actions context + * @param {Object} core - GitHub Actions core utilities + * @param {Object} config - Configuration object + * @param {string} config.combination - Combination identifier + * @param {string} config.name - Human-readable name + * @param {string} config.workflowId - Workflow ID to trigger + * @param {string} config.ref - Git ref to use + * @param {Object} config.inputs - Workflow inputs + * @param {number} [config.maxAttempts=12] - Maximum polling attempts + * @param {number} [config.pollInterval=10000] - Polling interval in milliseconds + * @param {number} [config.bufferTime=5000] - Buffer time for run detection + * @return {Object} - Object containing runId and runUrl + */ +async function triggerWorkflowAndWait( github, context, core, config ) { + const { + combination, + name, + workflowId, + ref, + inputs, + maxAttempts = 12, + pollInterval = 10000, + bufferTime = 5000, + } = config; + + const triggerTime = Date.now(); + const uniqueId = `${ combination }-${ triggerTime }`; + + // eslint-disable-next-line no-console + console.log( `=== Triggering: ${ name } (${ uniqueId }) ===` ); + // eslint-disable-next-line no-console + console.log( `Workflow: ${ workflowId }` ); + // eslint-disable-next-line no-console + console.log( `Ref: ${ ref }` ); + // eslint-disable-next-line no-console + console.log( `Inputs:`, JSON.stringify( inputs, null, 2 ) ); + + // Trigger the workflow + try { + await github.rest.actions.createWorkflowDispatch( { + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: workflowId, + ref, + inputs, + } ); + + // eslint-disable-next-line no-console + console.log( 'โœ… Workflow dispatch sent successfully' ); + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( 'โŒ Failed to trigger workflow:', error.message ); + throw new Error( `Failed to trigger workflow ${ workflowId }: ${ error.message }` ); + } + + // eslint-disable-next-line no-console + console.log( 'โณ Waiting for workflow run to appear...' ); + + // Wait and find the specific run with better detection + let newRun = null; + let attempts = 0; + + while ( ! newRun && attempts < maxAttempts ) { + await new Promise( ( resolve ) => setTimeout( resolve, pollInterval ) ); + attempts++; + + try { + const runs = await github.rest.actions.listWorkflowRuns( { + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: workflowId, + per_page: 50, // Increased to catch more runs + } ); + + // More specific run detection + const candidateRuns = runs.data.workflow_runs.filter( ( run ) => + 'workflow_dispatch' === run.event && + new Date( run.created_at ).getTime() >= triggerTime - bufferTime && + new Date( run.created_at ).getTime() <= triggerTime + ( pollInterval * attempts ) + bufferTime, + ); + + // Sort by creation time (newest first) and take the most recent + candidateRuns.sort( ( a, b ) => new Date( b.created_at ) - new Date( a.created_at ) ); + + if ( candidateRuns.length > 0 ) { + newRun = candidateRuns[ 0 ]; + // eslint-disable-next-line no-console + console.log( `โœ… Found ${ candidateRuns.length } candidate runs, selected newest: ${ newRun.id }` ); + // eslint-disable-next-line no-console + console.log( ` Created: ${ newRun.created_at }` ); + // eslint-disable-next-line no-console + console.log( ` Status: ${ newRun.status }` ); + } + + // eslint-disable-next-line no-console + console.log( `Attempt ${ attempts }/${ maxAttempts }: ${ newRun ? 'Found' : 'Not found' } new run for ${ combination }` ); + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( `Error on attempt ${ attempts }:`, error.message ); + } + } + + if ( ! newRun ) { + throw new Error( `Could not find the triggered workflow run for ${ combination } after ${ maxAttempts * pollInterval / 1000 } seconds` ); + } + + const runUrl = `https://github.com/${ context.repo.owner }/${ context.repo.repo }/actions/runs/${ newRun.id }`; + + // eslint-disable-next-line no-console + console.log( `โœ… Successfully captured run ID for ${ combination }: ${ newRun.id }` ); + // eslint-disable-next-line no-console + console.log( `๐Ÿ“‹ Run URL: ${ runUrl }` ); + + return { + runId: newRun.id, + runUrl, + status: newRun.status, + createdAt: newRun.created_at, + }; +} + +/** + * Applies delay based on timing configuration + * @param {string} combination - Combination identifier (e.g., 'baseline', 'ht-3.4', 'hp-1.3') + * @param {Object} timing - Timing configuration object with triggerDelays + */ +async function applyTriggerDelay( combination, timing ) { + if ( ! timing.triggerDelays || ! timing.triggerDelays[ combination ] ) { + return; // No delay configured + } + + const delaySeconds = timing.triggerDelays[ combination ]; + if ( delaySeconds > 0 ) { + // eslint-disable-next-line no-console + console.log( `โฑ๏ธ Applying ${ delaySeconds }s delay to prevent race condition...` ); + await new Promise( ( resolve ) => setTimeout( resolve, delaySeconds * 1000 ) ); + } +} + +/** + * Waits for a specific workflow run to complete + * @param {Object} github - GitHub API client + * @param {Object} context - GitHub Actions context + * @param {string} runId - The workflow run ID to wait for + * @param {Object} options - Options for polling + * @return {Object} - Final run status and details + */ +async function waitForWorkflowCompletion( github, context, runId, options = {} ) { + const { + maxAttempts = 60, + pollInterval = 30000, + logProgress = true, + } = options; + + // eslint-disable-next-line no-console + console.log( `โณ Waiting for workflow run ${ runId } to complete...` ); + + for ( let attempt = 1; attempt <= maxAttempts; attempt++ ) { + try { + const response = await github.rest.actions.getWorkflowRun( { + owner: context.repo.owner, + repo: context.repo.repo, + run_id: runId, + } ); + + const run = response.data; + + if ( 'completed' === run.status ) { + // eslint-disable-next-line no-console + console.log( `โœ… Run ${ runId } completed with conclusion: ${ run.conclusion }` ); + return { + runId, + status: run.status, + conclusion: run.conclusion, + url: run.html_url, + createdAt: run.created_at, + updatedAt: run.updated_at, + duration: calculateDuration( run.created_at, run.updated_at ), + }; + } + + if ( logProgress && ( 0 === attempt % 10 || attempt <= 5 ) ) { + // eslint-disable-next-line no-console + console.log( `โณ Run ${ runId } is ${ run.status } (attempt ${ attempt }/${ maxAttempts })` ); + } + + await new Promise( ( resolve ) => setTimeout( resolve, pollInterval ) ); + } catch ( error ) { + // eslint-disable-next-line no-console + console.error( `Error checking run ${ runId } status (attempt ${ attempt }):`, error.message ); + if ( attempt === maxAttempts ) { + throw error; + } + } + } + + throw new Error( `Workflow run ${ runId } did not complete within ${ maxAttempts * pollInterval / 1000 } seconds` ); +} + +/** + * Calculates duration between two timestamps + * @param {string} startTime - ISO timestamp + * @param {string} endTime - ISO timestamp + * @return {string} - Human-readable duration + */ +function calculateDuration( startTime, endTime ) { + const start = new Date( startTime ); + const end = new Date( endTime ); + const durationMs = end - start; + const durationMin = Math.round( durationMs / 60000 ); + return `${ durationMin }m`; +} + +/** + * Validates workflow inputs before triggering + * @param {Object} inputs - Workflow inputs to validate + * @return {boolean} - True if valid + */ +function validateWorkflowInputs( inputs ) { + const required = [ 'hello_theme_version', 'hello_plus_version', 'elementor_version' ]; + + for ( const field of required ) { + if ( ! inputs[ field ] || '' === inputs[ field ].trim() ) { + throw new Error( `Required input ${ field } is missing or empty` ); + } + } + + // Validate version format (basic check) + const versionRegex = /^(main|latest-stable|\d+\.\d+(\.\d+)?)$/; + for ( const field of required ) { + if ( ! versionRegex.test( inputs[ field ] ) ) { + throw new Error( `Invalid version format for ${ field }: ${ inputs[ field ] }` ); + } + } + + return true; +} + +module.exports = { + triggerWorkflowAndWait, + applyTriggerDelay, + waitForWorkflowCompletion, + calculateDuration, + validateWorkflowInputs, +}; diff --git a/.github/workflows/build-theme/action.yml b/.github/workflows/build-theme/action.yml new file mode 100644 index 00000000..025d9f11 --- /dev/null +++ b/.github/workflows/build-theme/action.yml @@ -0,0 +1,156 @@ +name: Build Hello Theme +description: Build Hello Theme with specified version + +inputs: + HELLO_THEME_VERSION: + description: 'The Hello Theme version to build (e.g. 3.4.4, main)' + required: true + BUILD_SCRIPT_PATH: + description: 'Path to build script' + required: false + default: "npm run zip" + +outputs: + hello-theme-version: + description: 'The Hello Theme version that was built' + value: ${{ steps.set-version.outputs.hello-theme-version }} + hello-theme-source: + description: 'Source type used for Hello Theme (github or wordpress-org)' + value: ${{ steps.determine-source.outputs.source-type }} + +runs: + using: "composite" + steps: + - name: Determine source type + id: determine-source + shell: bash + run: | + # Hello Theme builds from GitHub for workflow testing (like Hello Commerce) + # This ensures we have workflows and tests available + echo "source-type=github" >> $GITHUB_OUTPUT + echo "๐Ÿ” Using GitHub source for Hello Theme version: ${{ inputs.HELLO_THEME_VERSION }}" + + - name: Checkout Hello Theme source + shell: bash + env: + HELLO_THEME_VERSION: ${{ inputs.HELLO_THEME_VERSION }} + run: | + echo "๐Ÿ”„ Checking out Hello Theme version: ${HELLO_THEME_VERSION}" + + # Hello Theme builds from current repo like Hello Commerce + if [[ "$HELLO_THEME_VERSION" != "main" ]]; then + # Check if it's a semantic version (add 'v' prefix) + if [[ "$HELLO_THEME_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "๐Ÿ“‹ Checking out Git tag: v${HELLO_THEME_VERSION}" + git fetch --all --tags + git checkout "v${HELLO_THEME_VERSION}" || git checkout "${HELLO_THEME_VERSION}" || { + echo "โš ๏ธ Version v${HELLO_THEME_VERSION} not found, trying without 'v' prefix" + git checkout "${HELLO_THEME_VERSION}" || { + echo "โŒ Version ${HELLO_THEME_VERSION} not found, using current version" + } + } + else + # Branch name or other format + echo "๐Ÿ“‹ Checking out branch/ref: ${HELLO_THEME_VERSION}" + git fetch --all + git checkout "${HELLO_THEME_VERSION}" || { + echo "โŒ Branch ${HELLO_THEME_VERSION} not found, using current version" + } + fi + else + echo "๐Ÿ“‹ Using main branch (already checked out)" + fi + + # Verify we have the theme files + if [ ! -f "functions.php" ]; then + echo "โŒ Hello Theme functions.php not found after checkout" + exit 1 + fi + + - name: Set version outputs + id: set-version + shell: bash + env: + HELLO_THEME_VERSION: ${{ inputs.HELLO_THEME_VERSION }} + run: | + # Get actual version from the checked out code + if [ -f "functions.php" ]; then + ACTUAL_VERSION=$(grep "HELLO_ELEMENTOR_VERSION" functions.php | sed "s/.*'\([^']*\)'.*/\1/" | head -1) + if [[ -n "$ACTUAL_VERSION" ]]; then + echo "โœ… Detected Hello Theme version from functions.php: $ACTUAL_VERSION" + echo "hello-theme-version=$ACTUAL_VERSION" >> $GITHUB_OUTPUT + else + echo "โš ๏ธ Could not detect version from functions.php, using input: $HELLO_THEME_VERSION" + echo "hello-theme-version=$HELLO_THEME_VERSION" >> $GITHUB_OUTPUT + fi + else + echo "โš ๏ธ functions.php not found, using input: $HELLO_THEME_VERSION" + echo "hello-theme-version=$HELLO_THEME_VERSION" >> $GITHUB_OUTPUT + fi + + - name: Install npm dependencies + shell: bash + run: | + export PUPPETEER_SKIP_DOWNLOAD=true + npm ci + + - name: Install composer dependencies + shell: bash + run: | + composer install --no-dev --no-scripts --optimize-autoloader + + - name: Set package version + shell: bash + env: + HELLO_THEME_VERSION: ${{ steps.set-version.outputs.hello-theme-version }} + run: | + echo "HELLO_THEME_VERSION=${HELLO_THEME_VERSION}" >> $GITHUB_ENV + echo "Building Hello Theme version: ${HELLO_THEME_VERSION}" + + - name: Build Hello Theme + shell: bash + run: | + if [[ "${{ inputs.BUILD_SCRIPT_PATH }}" == npm* ]]; then + ${{ inputs.BUILD_SCRIPT_PATH }} + else + bash "${{ inputs.BUILD_SCRIPT_PATH }}" + fi + + - name: Create theme build directory + shell: bash + run: | + mkdir -p /tmp/hello-theme-builds + + - name: Package Hello Theme + shell: bash + env: + HELLO_THEME_VERSION: ${{ steps.set-version.outputs.hello-theme-version }} + run: | + # Create zip file with proper naming (following Hello Theme's pattern) + # Hello Theme generates: hello-elementor.{version}.zip + if [ -f "hello-elementor.${HELLO_THEME_VERSION}.zip" ]; then + # Use existing zip from npm run zip + mv "hello-elementor.${HELLO_THEME_VERSION}.zip" "/tmp/hello-theme-builds/" + echo "โœ… Found and moved existing zip: hello-elementor.${HELLO_THEME_VERSION}.zip" + elif [ -f "hello-elementor.zip" ]; then + # Rename generic zip to versioned + mv "hello-elementor.zip" "/tmp/hello-theme-builds/hello-elementor.${HELLO_THEME_VERSION}.zip" + echo "โœ… Renamed hello-elementor.zip to hello-elementor.${HELLO_THEME_VERSION}.zip" + else + # Create zip manually if npm run zip didn't work as expected + echo "โš ๏ธ No existing zip found, creating manually..." + zip -r "/tmp/hello-theme-builds/hello-elementor.${HELLO_THEME_VERSION}.zip" . \ + -x "node_modules/*" "test-results/*" "tests/*" ".git/*" "*.zip" \ + "playwright-report/*" ".wp-env.json.*" ".wp-env" "vendor/*" + fi + + - name: Move build to workspace + shell: bash + env: + HELLO_THEME_VERSION: ${{ steps.set-version.outputs.hello-theme-version }} + run: | + mv "/tmp/hello-theme-builds/hello-elementor.${HELLO_THEME_VERSION}.zip" \ + "./hello-elementor.${HELLO_THEME_VERSION}.zip" + + echo "โœ… Hello Theme build completed: hello-elementor.${HELLO_THEME_VERSION}.zip" + ls -la hello-elementor.*.zip diff --git a/.github/workflows/daily-test-matrix.yml b/.github/workflows/daily-test-matrix.yml new file mode 100644 index 00000000..12f82e2b --- /dev/null +++ b/.github/workflows/daily-test-matrix.yml @@ -0,0 +1,586 @@ +# ๐Ÿงช Hello Theme Daily Test Matrix +# +# This workflow runs daily automated testing across different version combinations: +# - Core Matrix: Hello Theme ร— Elementor versions +# - Plus Matrix: Hello Theme ร— Hello Plus versions +# - By default tests main branches (cutting-edge compatibility) +# - Can be manually triggered with specific versions for release testing +# +# Each matrix workflow handles its own version calculation and testing logic. +# +# ๐Ÿ“Š Targeted Matrix Generation: +# +# Hello Theme ร— Elementor: +# - Hello Theme (main) ร— Elementor (main, previous GA minor.x, previous-previous GA minor.x) +# - Hello Theme (ga, if exists) ร— Elementor (main, previous GA minor.x, previous-previous GA minor.x) +# +# Hello Theme ร— Hello Plus: +# - Hello Theme (main) ร— Hello Plus (latest, previous patch - WordPress.org releases only) +# - Hello Theme (ga, if exists) ร— Hello Plus (latest, previous patch - WordPress.org releases only) +# +# Example Output (HT GA=3.4.4, EL latest=3.30.4, HP latest=1.7.2): +# [ +# {"combination":"ht-main-el-main","name":"Hello Theme main + Elementor main","hello_theme_version":"main","elementor_version":"main","delay":0}, +# {"combination":"ht-main-el-prev","name":"Hello Theme main + Elementor 3.31.1 (GA)","hello_theme_version":"main","elementor_version":"3.31.1","delay":15}, +# {"combination":"ht-main-el-prev","name":"Hello Theme main + Elementor 3.30.4 (GA)","hello_theme_version":"main","elementor_version":"3.30.4","delay":30}, +# {"combination":"ht-ga-el-main","name":"Hello Theme 3.4.4 + Elementor main","hello_theme_version":"3.4.4","elementor_version":"main","delay":60}, +# {"combination":"ht-ga-el-prev","name":"Hello Theme 3.4.4 + Elementor 3.31.1 (GA)","hello_theme_version":"3.4.4","elementor_version":"3.31.1","delay":60}, +# {"combination":"ht-ga-el-prev","name":"Hello Theme 3.4.4 + Elementor 3.30.4 (GA)","hello_theme_version":"3.4.4","elementor_version":"3.30.4","delay":75}, +# {"combination":"ht-main-hp-latest","name":"Hello Theme main + Hello Plus 1.7.2","hello_theme_version":"main","hello_plus_version":"1.7.2","delay":105}, +# {"combination":"ht-main-hp-prev","name":"Hello Theme main + Hello Plus 1.7.1","hello_theme_version":"main","hello_plus_version":"1.7.1","delay":120} +# ] + +name: Daily Test Matrix + +on: + schedule: + # Run daily at 8:00 AM UTC (spread load from other themes) + - cron: '0 8 * * *' + workflow_dispatch: + inputs: + hello_theme_version: + description: 'Hello Theme version to test (e.g., 3.4.4) - defaults to main for scheduled runs' + required: false + default: 'main' + type: string + hello_plus_version: + description: 'Hello Plus version to test (e.g., 1.7.2) - defaults to latest-stable for scheduled runs' + required: false + default: 'latest-stable' + type: string + elementor_version: + description: 'Elementor version to test (e.g., 3.29.7) - defaults to main for scheduled runs' + required: false + default: 'main' + type: string + compatibility_depth: + description: 'Number of previous versions to test' + required: false + default: '2' + type: choice + options: ['1', '2', '3'] + run_targeted_tests: + description: 'Run targeted compatibility tests' + required: false + default: true + type: boolean + tag: + description: 'Provide @tag or a keyword for Playwright tests' + required: false + type: string + +permissions: + contents: read + actions: write + +env: + CORE_WORKFLOW: 'playwright-with-specific-elementor-version.yml' + PLUS_WORKFLOW: 'playwright-with-specific-hello-plus-version.yml' + +jobs: + calculate-test-matrix: + name: Calculate Dynamic Test Matrix + runs-on: ubuntu-22.04 + outputs: + hello-theme-version: ${{ steps.matrix.outputs.hello-theme-version }} + hello-plus-version: ${{ steps.matrix.outputs.hello-plus-version }} + elementor-version: ${{ steps.matrix.outputs.elementor-version }} + hello-theme-previous: ${{ steps.matrix.outputs.hello-theme-previous }} + targeted-tests: ${{ steps.matrix.outputs.targeted-tests }} + steps: + - name: Calculate version matrix + id: matrix + run: | + # Use defaults for scheduled runs, inputs for manual runs + HT_VERSION="${{ inputs.hello_theme_version || 'main' }}" + HP_VERSION="${{ inputs.hello_plus_version || 'latest-stable' }}" + EL_VERSION="${{ inputs.elementor_version || 'main' }}" + DEPTH="${{ inputs.compatibility_depth || '2' }}" + + # Function to get latest released version from GitHub + get_latest_version() { + local repo=$1 + local latest_version + + # Get latest release tag from GitHub API + latest_version=$(curl -s "https://api.github.com/repos/${repo}/releases/latest" | jq -r '.tag_name // empty') + + # Remove 'v' prefix if present + latest_version=${latest_version#v} + + # If no version found or empty, return empty + if [[ -z "$latest_version" || "$latest_version" == "null" ]]; then + echo "" + else + echo "$latest_version" + fi + } + + # Function to get latest version from WordPress.org API (plugins) + get_wp_org_latest_version() { + local plugin_slug=$1 + local latest_version + + # Get plugin info from WordPress.org API + latest_version=$(curl -s "https://api.wordpress.org/plugins/info/1.0/${plugin_slug}.json" | jq -r '.version // empty') + + # If no version found or empty, return empty + if [[ -z "$latest_version" || "$latest_version" == "null" ]]; then + echo "" + else + echo "$latest_version" + fi + } + + # Function to get latest version from WordPress.org API (themes) + get_wp_org_theme_latest_version() { + local theme_slug=$1 + local latest_version + + # Get theme info from WordPress.org API (URL-encoded array syntax) + latest_version=$(curl -s "https://api.wordpress.org/themes/info/1.1/?action=theme_information&request%5Bslug%5D=${theme_slug}" | jq -r '.version // empty') + + # If no version found or empty, return empty + if [[ -z "$latest_version" || "$latest_version" == "null" ]]; then + echo "" + else + echo "$latest_version" + fi + } + + echo "๐Ÿงฎ Calculating Hello Theme Daily Test Matrix" + echo "==============================================" + echo "Input versions:" + echo "- Hello Theme: $HT_VERSION" + echo "- Hello Plus: $HP_VERSION" + echo "- Elementor: $EL_VERSION" + echo "- Compatibility depth: $DEPTH" + echo "" + + # Resolve Hello Theme GA version for matrix calculation + if [[ "$HT_VERSION" == "main" ]]; then + # For scheduled runs, try to detect the latest GA version + HT_GA_LATEST="" + + # Hello Theme is published to WordPress.org, try there first + HT_GA_LATEST=$(get_wp_org_theme_latest_version "hello-elementor") + if [[ -n "$HT_GA_LATEST" ]]; then + echo "๐Ÿ” Detected Hello Theme GA -> ${HT_GA_LATEST} (from WordPress.org)" + HT_VERSION_FOR_MATRIX="$HT_GA_LATEST" + else + # Fallback to GitHub (may not be publicly available for themes) + HT_GA_LATEST=$(get_latest_version "elementor/hello-theme") + if [[ -n "$HT_GA_LATEST" ]]; then + echo "๐Ÿ” Detected Hello Theme GA -> ${HT_GA_LATEST} (from GitHub)" + HT_VERSION_FOR_MATRIX="$HT_GA_LATEST" + else + echo "โš ๏ธ Could not detect Hello Theme GA version, using main only" + HT_VERSION_FOR_MATRIX="main" + fi + fi + else + HT_VERSION_FOR_MATRIX="$HT_VERSION" + fi + + # Handle Hello Plus (WordPress.org releases only) + if [[ "$HP_VERSION" == "latest-stable" ]]; then + # For latest-stable, we need to resolve to actual version number for matrix calculation + HP_LATEST=$(get_wp_org_latest_version "hello-plus") + if [[ -n "$HP_LATEST" ]]; then + echo "๐Ÿ” Resolved Hello Plus latest-stable -> ${HP_LATEST} (from WordPress.org)" + HP_VERSION_FOR_MATRIX="$HP_LATEST" + else + echo "โš ๏ธ Could not resolve Hello Plus latest version from WordPress.org, using latest-stable" + HP_VERSION_FOR_MATRIX="latest-stable" + fi + else + HP_VERSION_FOR_MATRIX="$HP_VERSION" + fi + + # Validation: If WordPress version is required but not available, use main version + # This handles the case when no GA versions exist yet (e.g., releasing 3.5.0) + validate_version_availability() { + local version=$1 + local plugin_name=$2 + + # If version is "main", it's always available + if [[ "$version" == "main" ]]; then + echo "$version" + return 0 + fi + + # Special case for Hello Plus: "latest-stable" is valid + if [[ "$plugin_name" == "Hello Plus" && "$version" == "latest-stable" ]]; then + echo "$version" + return 0 + fi + + # Check if it's a semantic version format + if [[ $version =~ ^([0-9]+)\.([0-9]+) ]]; then + # For Hello Theme 3.5.0 (first release), previous versions won't exist + # For other plugins, we assume the version exists if it's in semantic format + echo "$version" + else + if [[ "$plugin_name" == "Hello Plus" ]]; then + echo "โš ๏ธ Invalid version format for $plugin_name: '$version', falling back to latest-stable" + echo "latest-stable" + else + echo "โš ๏ธ Invalid version format for $plugin_name: '$version', falling back to main" + echo "main" + fi + fi + } + + # Validate and potentially fallback versions + HT_VERSION=$(validate_version_availability "$HT_VERSION" "Hello Theme") + HP_VERSION=$(validate_version_availability "$HP_VERSION" "Hello Plus") + EL_VERSION=$(validate_version_availability "$EL_VERSION" "Elementor") + + echo "hello-theme-version=${HT_VERSION}" >> $GITHUB_OUTPUT + echo "hello-plus-version=${HP_VERSION}" >> $GITHUB_OUTPUT + echo "elementor-version=${EL_VERSION}" >> $GITHUB_OUTPUT + + # Function to calculate previous versions with different strategies + calculate_previous_versions() { + local version=$1 + local depth=$2 + local strategy=${3:-"elementor"} # "elementor" for minor-based, "theme" for patch-based + + if [[ "$version" == "main" || "$version" == "latest-stable" ]]; then + echo "" + return 0 + fi + + # Different strategies based on plugin type + if [[ "$strategy" == "elementor" ]]; then + # Elementor: Minor-level versioning (3.30.4 -> 3.29.x, 3.28.x) + if [[ $version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then + major=${BASH_REMATCH[1]} + minor=${BASH_REMATCH[2]} + patch=${BASH_REMATCH[3]} + + previous_versions="" + for i in $(seq 1 $depth); do + prev_minor=$((minor - i)) + if [ $prev_minor -ge 0 ]; then + # For Elementor, assume latest patch in previous minor (e.g., 3.29.7) + prev_version="${major}.${prev_minor}.7" + if [ -z "$previous_versions" ]; then + previous_versions="$prev_version" + else + previous_versions="$previous_versions,$prev_version" + fi + fi + done + echo "$previous_versions" + else + echo "" + return 0 + fi + else + # Hello Theme & Hello Plus: Patch-level versioning (3.4.4 -> 3.4.3, 3.4.2) + if [[ $version =~ ^([0-9]+)\.([0-9]+)\.([0-9]+) ]]; then + major=${BASH_REMATCH[1]} + minor=${BASH_REMATCH[2]} + patch=${BASH_REMATCH[3]} + + previous_versions="" + + # Strategy: Get patch versions in current minor, then latest of previous minor + # Example: 3.4.4 -> 3.4.3, then 3.3.x (latest) + + # First, get previous patch versions in current minor + local patches_added=0 + for i in $(seq 1 $depth); do + if [ $patches_added -ge $depth ]; then + break + fi + + prev_patch=$((patch - i)) + if [ $prev_patch -ge 0 ]; then + prev_version="${major}.${minor}.${prev_patch}" + if [ -z "$previous_versions" ]; then + previous_versions="$prev_version" + else + previous_versions="$previous_versions,$prev_version" + fi + patches_added=$((patches_added + 1)) + else + # If we run out of patches, try previous minor + prev_minor=$((minor - 1)) + if [ $prev_minor -ge 0 ] && [ $patches_added -lt $depth ]; then + # Assume latest patch in previous minor (e.g., 3.3.9) + prev_version="${major}.${prev_minor}.9" + if [ -z "$previous_versions" ]; then + previous_versions="$prev_version" + else + previous_versions="$previous_versions,$prev_version" + fi + patches_added=$((patches_added + 1)) + fi + break + fi + done + + echo "$previous_versions" + else + echo "" + return 0 + fi + fi + } + + # Calculate previous versions for each component + if [[ "${{ inputs.run_targeted_tests }}" == "true" ]]; then + ELEMENTOR_PREVIOUS=$(calculate_previous_versions "$EL_VERSION" "$DEPTH" "elementor") + HELLO_PLUS_PREVIOUS=$(calculate_previous_versions "$HP_VERSION" "$DEPTH" "theme") + HELLO_THEME_PREVIOUS=$(calculate_previous_versions "$HT_VERSION_FOR_MATRIX" "$DEPTH" "theme") + + echo "hello-theme-previous=${HELLO_THEME_PREVIOUS}" >> $GITHUB_OUTPUT + + echo "" + echo "๐Ÿ“‹ Version Matrix Calculation Complete:" + echo "- Hello Theme: $HT_VERSION (previous: ${HELLO_THEME_PREVIOUS:-none})" + echo "- Hello Plus: $HP_VERSION (previous: ${HELLO_PLUS_PREVIOUS:-none})" + echo "- Elementor: $EL_VERSION (previous: ${ELEMENTOR_PREVIOUS:-none})" + echo "" + + # Generate targeted test matrix + TARGETED_TESTS="[" + DELAY_COUNTER=0 + + # Helper function to add test combination + add_test_combination() { + local combination=$1 + local name=$2 + local ht_ver=$3 + local el_ver=$4 + local hp_ver=$5 + local delay=$6 + + if [[ "$TARGETED_TESTS" != "[" ]]; then + TARGETED_TESTS="${TARGETED_TESTS}," + fi + + if [[ -n "$hp_ver" ]]; then + # Hello Plus matrix + TARGETED_TESTS="${TARGETED_TESTS}{\"combination\":\"${combination}\",\"name\":\"${name}\",\"hello_theme_version\":\"${ht_ver}\",\"hello_plus_version\":\"${hp_ver}\",\"delay\":${delay}}" + else + # Elementor matrix + TARGETED_TESTS="${TARGETED_TESTS}{\"combination\":\"${combination}\",\"name\":\"${name}\",\"hello_theme_version\":\"${ht_ver}\",\"elementor_version\":\"${el_ver}\",\"delay\":${delay}}" + fi + } + + # Core Matrix: Hello Theme ร— Elementor + # Main Hello Theme combinations + add_test_combination "ht-main-el-main" "Hello Theme main + Elementor main" "main" "main" "" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + + if [[ -n "$ELEMENTOR_PREVIOUS" ]]; then + IFS=',' read -ra EL_PREV_ARRAY <<< "$ELEMENTOR_PREVIOUS" + for el_prev in "${EL_PREV_ARRAY[@]}"; do + add_test_combination "ht-main-el-prev" "Hello Theme main + Elementor ${el_prev} (GA)" "main" "$el_prev" "" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + done + fi + + # GA Hello Theme combinations (if detected) + if [[ "$HT_VERSION_FOR_MATRIX" != "main" ]]; then + add_test_combination "ht-ga-el-main" "Hello Theme ${HT_VERSION_FOR_MATRIX} + Elementor main" "$HT_VERSION_FOR_MATRIX" "main" "" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + + if [[ -n "$ELEMENTOR_PREVIOUS" ]]; then + IFS=',' read -ra EL_PREV_ARRAY <<< "$ELEMENTOR_PREVIOUS" + for el_prev in "${EL_PREV_ARRAY[@]}"; do + add_test_combination "ht-ga-el-prev" "Hello Theme ${HT_VERSION_FOR_MATRIX} + Elementor ${el_prev} (GA)" "$HT_VERSION_FOR_MATRIX" "$el_prev" "" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + done + fi + fi + + # Plus Matrix: Hello Theme ร— Hello Plus + # Main Hello Theme combinations + add_test_combination "ht-main-hp-latest" "Hello Theme main + Hello Plus ${HP_VERSION}" "main" "" "$HP_VERSION" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + + if [[ -n "$HELLO_PLUS_PREVIOUS" ]]; then + IFS=',' read -ra HP_PREV_ARRAY <<< "$HELLO_PLUS_PREVIOUS" + for hp_prev in "${HP_PREV_ARRAY[@]}"; do + add_test_combination "ht-main-hp-prev" "Hello Theme main + Hello Plus ${hp_prev}" "main" "" "$hp_prev" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + done + fi + + # GA Hello Theme combinations (if detected) + if [[ "$HT_VERSION_FOR_MATRIX" != "main" ]]; then + add_test_combination "ht-ga-hp-latest" "Hello Theme ${HT_VERSION_FOR_MATRIX} + Hello Plus ${HP_VERSION}" "$HT_VERSION_FOR_MATRIX" "" "$HP_VERSION" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + + if [[ -n "$HELLO_PLUS_PREVIOUS" ]]; then + IFS=',' read -ra HP_PREV_ARRAY <<< "$HELLO_PLUS_PREVIOUS" + for hp_prev in "${HP_PREV_ARRAY[@]}"; do + add_test_combination "ht-ga-hp-prev" "Hello Theme ${HT_VERSION_FOR_MATRIX} + Hello Plus ${hp_prev}" "$HT_VERSION_FOR_MATRIX" "" "$hp_prev" $((DELAY_COUNTER * 15)) + DELAY_COUNTER=$((DELAY_COUNTER + 1)) + done + fi + fi + + TARGETED_TESTS="${TARGETED_TESTS}]" + + echo "targeted-tests=${TARGETED_TESTS}" >> $GITHUB_OUTPUT + + echo "" + echo "๐ŸŽฏ Generated Targeted Test Matrix:" + echo "$TARGETED_TESTS" | jq '.' + else + echo "โญ๏ธ Skipping targeted test matrix generation (run_targeted_tests=false)" + echo "targeted-tests=[]" >> $GITHUB_OUTPUT + fi + + trigger-core-workflow: + name: Trigger Core Tests + needs: [calculate-test-matrix] + if: ${{ fromJson(needs.calculate-test-matrix.outputs.targeted-tests)[0].elementor_version != '' }} + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + test: ${{ fromJson(needs.calculate-test-matrix.outputs.targeted-tests) }} + steps: + - name: Filter and trigger Core workflows + if: contains(matrix.test.combination, 'el') + uses: actions/github-script@v7 + with: + script: | + const { triggerWorkflowAndWait, applyTriggerDelay } = require('./.github/scripts/workflow-trigger.js'); + const { getDailyTriggerTimingConfig } = require('./.github/scripts/workflow-reporting.js'); + + const timing = getDailyTriggerTimingConfig(); + + // Apply trigger delay to prevent race conditions + await applyTriggerDelay('${{ matrix.test.combination }}', timing); + + const workflowConfig = { + combination: '${{ matrix.test.combination }}', + name: '${{ matrix.test.name }}', + workflowId: '${{ env.CORE_WORKFLOW }}', + ref: 'main', + inputs: { + core_branch: '${{ matrix.test.elementor_version }}', + hello_theme_version: '${{ matrix.test.hello_theme_version }}', + tag: '${{ inputs.tag || '' }}' + } + }; + + const result = await triggerWorkflowAndWait(github, context, core, workflowConfig); + core.setOutput('run-id', result.runId); + core.setOutput('run-url', result.runUrl); + + - name: Store Core workflow run info + if: contains(matrix.test.combination, 'el') + run: | + echo "Core workflow triggered:" + echo "- Combination: ${{ matrix.test.combination }}" + echo "- Name: ${{ matrix.test.name }}" + echo "- Run ID: ${{ steps.trigger-core.outputs.run-id }}" + echo "- Run URL: ${{ steps.trigger-core.outputs.run-url }}" + + trigger-plus-workflow: + name: Trigger Plus Tests + needs: [calculate-test-matrix] + if: ${{ fromJson(needs.calculate-test-matrix.outputs.targeted-tests)[0].hello_plus_version != '' }} + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + test: ${{ fromJson(needs.calculate-test-matrix.outputs.targeted-tests) }} + steps: + - name: Filter and trigger Plus workflows + if: contains(matrix.test.combination, 'hp') + uses: actions/github-script@v7 + with: + script: | + const { triggerWorkflowAndWait, applyTriggerDelay } = require('./.github/scripts/workflow-trigger.js'); + const { getDailyTriggerTimingConfig } = require('./.github/scripts/workflow-reporting.js'); + + const timing = getDailyTriggerTimingConfig(); + + // Apply trigger delay to prevent race conditions + await applyTriggerDelay('${{ matrix.test.combination }}', timing); + + const workflowConfig = { + combination: '${{ matrix.test.combination }}', + name: '${{ matrix.test.name }}', + workflowId: '${{ env.PLUS_WORKFLOW }}', + ref: 'main', + inputs: { + hello_plus_version: '${{ matrix.test.hello_plus_version }}', + hello_theme_version: '${{ matrix.test.hello_theme_version }}', + tag: '${{ inputs.tag || '' }}' + } + }; + + const result = await triggerWorkflowAndWait(github, context, core, workflowConfig); + core.setOutput('run-id', result.runId); + core.setOutput('run-url', result.runUrl); + + - name: Store Plus workflow run info + if: contains(matrix.test.combination, 'hp') + run: | + echo "Plus workflow triggered:" + echo "- Combination: ${{ matrix.test.combination }}" + echo "- Name: ${{ matrix.test.name }}" + echo "- Run ID: ${{ steps.trigger-plus.outputs.run-id }}" + echo "- Run URL: ${{ steps.trigger-plus.outputs.run-url }}" + + daily-trigger-report: + name: Daily Trigger Report + needs: [calculate-test-matrix, trigger-core-workflow, trigger-plus-workflow] + runs-on: ubuntu-22.04 + if: always() + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Collect workflow run data + id: collect-data + uses: actions/github-script@v7 + with: + script: | + // Collect all workflow runs triggered by this daily matrix + const workflowRuns = []; + + // Get runs from both core and plus workflows + const coreJobs = ${{ toJson(needs.trigger-core-workflow.outputs) }} || {}; + const plusJobs = ${{ toJson(needs.trigger-plus-workflow.outputs) }} || {}; + + // Note: In a real implementation, we'd need to collect run IDs from the matrix jobs + // For now, we'll generate a placeholder report + + core.setOutput('workflow-runs', JSON.stringify(workflowRuns)); + + - name: Generate daily compatibility report + uses: actions/github-script@v7 + with: + script: | + const { generateDailyTriggerReport } = require('./.github/scripts/daily-report.js'); + const { getDailyTriggerTimingConfig } = require('./.github/scripts/workflow-reporting.js'); + + const workflowData = JSON.parse('${{ steps.collect-data.outputs.workflow-runs }}'); + const timing = getDailyTriggerTimingConfig(); + + await generateDailyTriggerReport(github, context, core, workflowData, timing); + + - name: Matrix calculation summary + run: | + echo "## ๐Ÿงฎ Hello Theme Daily Test Matrix Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Component | Version | Source |" >> $GITHUB_STEP_SUMMARY + echo "|-----------|---------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Hello Theme | ${{ needs.calculate-test-matrix.outputs.hello-theme-version }} | GitHub/WordPress.org |" >> $GITHUB_STEP_SUMMARY + echo "| Hello Plus | ${{ needs.calculate-test-matrix.outputs.hello-plus-version }} | WordPress.org |" >> $GITHUB_STEP_SUMMARY + echo "| Elementor | ${{ needs.calculate-test-matrix.outputs.elementor-version }} | WordPress.org/GitHub |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Previous versions tested:** ${{ needs.calculate-test-matrix.outputs.hello-theme-previous || 'none' }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Matrix generation time:** $(date -u +"%Y-%m-%d %H:%M:%S UTC")" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/playwright-with-specific-elementor-version.yml b/.github/workflows/playwright-with-specific-elementor-version.yml new file mode 100644 index 00000000..4d86212a --- /dev/null +++ b/.github/workflows/playwright-with-specific-elementor-version.yml @@ -0,0 +1,513 @@ +name: Playwright with Specific Elementor Version + +on: + workflow_dispatch: + inputs: + core_branch: + description: 'Elementor Core Branch' + required: true + hello_theme_version: + description: 'Hello Theme version to test (e.g., 3.4.4 or main)' + required: false + default: 'main' + tag: + description: 'Provide @tag or a keyword' + required: false + workflow_call: + inputs: + core_branch: + description: 'Elementor Core Branch' + required: true + type: string + hello_theme_version: + description: 'Hello Theme version to test (e.g., 3.4.4 or main)' + required: false + type: string + tag: + description: 'Provide @tag or a keyword' + required: false + type: string + secrets: + CLOUD_DEVOPS_TOKEN: + required: true + +concurrency: + group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('core-{0}-{1}', github.run_id, github.event.inputs.core_branch || github.ref) }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + actions: read + id-token: write + +jobs: + build-core-components: + name: Build Core Components (Hello Theme + Elementor) + runs-on: ubuntu-22.04 + outputs: + hello-theme-version: ${{ steps.set-versions.outputs.hello-theme-version }} + elementor-core-branch: ${{ steps.set-versions.outputs.elementor-core-branch }} + elementor-version: ${{ steps.set-versions.outputs.elementor-version }} + hello-theme-source: ${{ steps.set-versions.outputs.hello-theme-source }} + artifact-name: ${{ steps.set-versions.outputs.artifact-name }} + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set version outputs + id: set-versions + run: | + HELLO_THEME_VERSION="${{ inputs.hello_theme_version || 'main' }}" + ELEMENTOR_CORE_BRANCH="${{ inputs.core_branch }}" + + echo "hello-theme-version=${HELLO_THEME_VERSION}" >> $GITHUB_OUTPUT + echo "elementor-core-branch=${ELEMENTOR_CORE_BRANCH}" >> $GITHUB_OUTPUT + echo "elementor-version=${ELEMENTOR_CORE_BRANCH}" >> $GITHUB_OUTPUT + echo "hello-theme-source=github" >> $GITHUB_OUTPUT + + # Generate artifact name + ARTIFACT_NAME="core-ht${HELLO_THEME_VERSION}-el${ELEMENTOR_CORE_BRANCH}-${{ github.run_id }}" + echo "artifact-name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT + + echo "โœ… Set versions: Hello Theme=${HELLO_THEME_VERSION}, Elementor=${ELEMENTOR_CORE_BRANCH}" + + - name: Build Hello Theme + uses: ./.github/workflows/build-theme + with: + HELLO_THEME_VERSION: ${{ steps.set-versions.outputs.hello-theme-version }} + + - name: Create theme build directory + run: | + mkdir -p ./tmp/hello-theme + + # Extract the built theme zip to tmp directory + HELLO_THEME_VERSION="${{ steps.set-versions.outputs.hello-theme-version }}" + if [ -f "hello-elementor.${HELLO_THEME_VERSION}.zip" ]; then + unzip -q "hello-elementor.${HELLO_THEME_VERSION}.zip" -d ./tmp/ + + # The zip creates hello-elementor directory, rename to hello-theme for consistency + if [ -d "./tmp/hello-elementor" ]; then + mv "./tmp/hello-elementor" "./tmp/hello-theme" + echo "โœ… Extracted and renamed theme to ./tmp/hello-theme" + else + echo "โŒ Expected hello-elementor directory not found after extraction" + exit 1 + fi + else + echo "โŒ Theme zip file not found: hello-elementor.${HELLO_THEME_VERSION}.zip" + exit 1 + fi + + - name: Download Elementor Core + run: | + ELEMENTOR_CORE_BRANCH="${{ steps.set-versions.outputs.elementor-core-branch }}" + echo "Downloading Elementor Core branch: ${ELEMENTOR_CORE_BRANCH}" + + # Create Elementor build directory + mkdir -p ./tmp + + if [[ "$ELEMENTOR_CORE_BRANCH" == "latest-stable" ]]; then + # Download latest stable from WordPress.org + curl --location -o ./elementor-core.zip https://downloads.wordpress.org/plugin/elementor.latest-stable.zip + unzip -q ./elementor-core.zip + mv ./elementor ./tmp/elementor + echo "โœ… Elementor latest-stable downloaded and extracted" + elif [[ "$ELEMENTOR_CORE_BRANCH" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Download specific version from WordPress.org + curl --location -o ./elementor-core.zip "https://downloads.wordpress.org/plugin/elementor.${ELEMENTOR_CORE_BRANCH}.zip" + unzip -q ./elementor-core.zip + mv ./elementor ./tmp/elementor + echo "โœ… Elementor ${ELEMENTOR_CORE_BRANCH} downloaded and extracted" + else + # For branches like 'main', we need GitHub artifacts + # This should be provided by the calling workflow + echo "โŒ GitHub artifacts for Elementor branches not implemented yet" + echo "For now, using latest-stable as fallback" + curl --location -o ./elementor-core.zip https://downloads.wordpress.org/plugin/elementor.latest-stable.zip + unzip -q ./elementor-core.zip + mv ./elementor ./tmp/elementor + echo "โš ๏ธ Using Elementor latest-stable as fallback for branch: ${ELEMENTOR_CORE_BRANCH}" + fi + + - name: Upload core build artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.set-versions.outputs.artifact-name }} + path: | + ./tmp/hello-theme + ./tmp/elementor + retention-days: 3 + + - name: Generate build summary + run: | + echo "## ๐Ÿ”ง Build Summary" >> $GITHUB_STEP_SUMMARY + echo "|-----------------|----------------------------|-----------------------|" >> $GITHUB_STEP_SUMMARY + echo "| Component | Version | Source |" >> $GITHUB_STEP_SUMMARY + echo "|-----------------|----------------------------|-----------------------|" >> $GITHUB_STEP_SUMMARY + echo "| Hello Theme | ${{ steps.set-versions.outputs.hello-theme-version }} | ${{ steps.set-versions.outputs.hello-theme-source }} |" >> $GITHUB_STEP_SUMMARY + echo "| Elementor Core | ${{ steps.set-versions.outputs.elementor-version }} | WordPress.org/GitHub |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Strategy:** GitHub source for Hello Theme, WordPress.org for Elementor versions" >> $GITHUB_STEP_SUMMARY + + core-playwright-tests: + name: Hello Theme + Elementor Tests + needs: [build-core-components] + if: github.event.inputs.tag == '' + env: + HELLO_THEME_VERSION: ${{ needs.build-core-components.outputs.hello-theme-version }} + ELEMENTOR_VERSION: ${{ needs.build-core-components.outputs.elementor-version }} + RUN_IDENTIFIER: core-${{ github.event.inputs.hello_theme_version || github.ref }}-${{ github.run_id }} + runs-on: ubuntu-22.04 + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Hybrid Test Setup - Extract tests from target version + id: extract-version-tests + run: | + echo "๐ŸŽฏ HYBRID APPROACH: Always latest workflows + version-specific tests" + echo "๐Ÿ“‹ Workflow infrastructure: main branch (latest build-wp-env.js, scripts, etc.)" + echo "๐Ÿงช Test content: ${{ inputs.hello_theme_version || 'main' }} (matches theme functionality)" + + # Determine the target version for test extraction + TARGET_VERSION="${{ inputs.hello_theme_version || 'main' }}" + + if [ "$TARGET_VERSION" = "main" ]; then + echo "โœ… Using main branch tests (already available)" + TEST_VERSION="main" + TEST_SOURCE_TYPE="main-branch" + TESTS_AVAILABLE="true" + else + echo "๐Ÿ” Extracting tests from version: $TARGET_VERSION" + + # Fetch all tags and branches + git fetch --all --tags + + # Check if target version exists and has tests + TESTS_AVAILABLE="false" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="version-specific" + + # Try different tag formats + for TAG_FORMAT in "v$TARGET_VERSION" "$TARGET_VERSION"; do + echo "๐Ÿ” Checking for tag: $TAG_FORMAT" + if git rev-parse --verify "$TAG_FORMAT" >/dev/null 2>&1; then + echo "โœ… Found tag: $TAG_FORMAT" + + # Check if this version has playwright tests + if git ls-tree -r "$TAG_FORMAT" | grep -q "tests/playwright/"; then + echo "โœ… Playwright tests found in $TAG_FORMAT" + + # Extract tests directory from target version + echo "๐Ÿ“ฅ Extracting tests directory from $TAG_FORMAT..." + rm -rf ./tests/playwright/ + git archive "$TAG_FORMAT" tests/playwright/ | tar -x + + if [ -d "./tests/playwright/" ]; then + echo "โœ… Successfully extracted tests from $TAG_FORMAT" + TESTS_AVAILABLE="true" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="extracted-from-$TAG_FORMAT" + break + else + echo "โš ๏ธ Failed to extract tests from $TAG_FORMAT" + fi + else + echo "โš ๏ธ No playwright tests found in $TAG_FORMAT" + fi + else + echo "โš ๏ธ Tag $TAG_FORMAT not found" + fi + done + + if [ "$TESTS_AVAILABLE" = "false" ]; then + echo "โš ๏ธ No compatible tests found for version $TARGET_VERSION" + echo "๐Ÿ“‹ Will skip testing for this version (tests don't exist yet)" + TEST_VERSION="none" + TEST_SOURCE_TYPE="not-available" + fi + fi + + # Set outputs for workflow control + echo "test-version=$TEST_VERSION" >> $GITHUB_OUTPUT + echo "test-source-type=$TEST_SOURCE_TYPE" >> $GITHUB_OUTPUT + echo "tests-available=$TESTS_AVAILABLE" >> $GITHUB_OUTPUT + + echo "โœ… Hybrid setup complete:" + echo " ๐Ÿ—๏ธ Workflow infrastructure: main branch" + echo " ๐Ÿงช Test content: $TEST_VERSION ($TEST_SOURCE_TYPE)" + echo " โœ… Tests available: $TESTS_AVAILABLE" + + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + tools: composer + coverage: none + + - name: Install Dependencies + run: | + export PUPPETEER_SKIP_DOWNLOAD=true + npm ci + composer install --no-dev --no-scripts --optimize-autoloader + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-core-components.outputs.artifact-name }} + path: ./ + + - name: Update wp-env.json file + env: + PHP_VERSION: '8.1' + WP_CORE_VERSION: 'latest' + HELLO_THEME_VERSION: ${{ env.HELLO_THEME_VERSION }} + ELEMENTOR_VERSION: ${{ env.ELEMENTOR_VERSION }} + run: node ./.github/scripts/build-wp-env.js + + - name: Install WordPress environment + run: | + npx wp-env start + + - name: Setup WordPress environment (activate plugins and themes) + run: | + npx wp-env run cli bash hello-elementor-config/setup.sh + npx wp-env run tests-cli bash hello-elementor-config/setup.sh + + - name: WordPress and plugin information + run: | + npx wp-env run cli wp --info + + - name: Install Playwright + run: npx playwright install chromium + + - name: Run Hello Theme tests only (Core Matrix) + if: steps.extract-version-tests.outputs.tests-available == 'true' + run: | + export TEST_TITLE="Hello Theme Core Test - HT ${HELLO_THEME_VERSION} + EL ${ELEMENTOR_VERSION}" + echo "๐Ÿงช Running tests from: ${{ steps.extract-version-tests.outputs.test-source-type }}" + # Only run Hello Theme tests, not Elementor or other plugin tests + npm run test:playwright -- tests/playwright/tests/ + + - name: Skip tests - version incompatible + if: steps.extract-version-tests.outputs.tests-available != 'true' + run: | + echo "โญ๏ธ Skipping tests for Hello Theme ${{ inputs.hello_theme_version || 'main' }}" + echo "๐Ÿ“‹ Reason: ${{ steps.extract-version-tests.outputs.test-source-type }}" + echo "โœ… This is expected for older versions that predate Playwright testing" + + - name: Upload core test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: core-test-results-ht${{ env.HELLO_THEME_VERSION }}-el${{ env.ELEMENTOR_VERSION }} + path: test-results/ + if-no-files-found: ignore + retention-days: 3 + + - name: Upload Core Playwright HTML report + uses: actions/upload-artifact@v4 + if: always() + with: + name: core-playwright-report-ht${{ env.HELLO_THEME_VERSION }}-el${{ env.ELEMENTOR_VERSION }} + path: playwright-report/ + if-no-files-found: ignore + retention-days: 3 + + - name: Stop wp-env + if: always() + run: npx wp-env stop || true + + core-playwright-tagged-tests: + name: Core Tagged Tests + needs: [build-core-components] + if: ${{ github.event.inputs.tag }} + env: + HELLO_THEME_VERSION: ${{ needs.build-core-components.outputs.hello-theme-version }} + ELEMENTOR_VERSION: ${{ needs.build-core-components.outputs.elementor-version }} + runs-on: ubuntu-22.04 + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Hybrid Test Setup - Extract tests from target version + id: extract-version-tests + run: | + echo "๐ŸŽฏ HYBRID APPROACH: Always latest workflows + version-specific tests" + echo "๐Ÿ“‹ Workflow infrastructure: main branch (latest build-wp-env.js, scripts, etc.)" + echo "๐Ÿงช Test content: ${{ inputs.hello_theme_version || 'main' }} (matches theme functionality)" + + # Determine the target version for test extraction + TARGET_VERSION="${{ inputs.hello_theme_version || 'main' }}" + + if [ "$TARGET_VERSION" = "main" ]; then + echo "โœ… Using main branch tests (already available)" + TEST_VERSION="main" + TEST_SOURCE_TYPE="main-branch" + TESTS_AVAILABLE="true" + else + echo "๐Ÿ” Extracting tests from version: $TARGET_VERSION" + + # Fetch all tags and branches + git fetch --all --tags + + # Check if target version exists and has tests + TESTS_AVAILABLE="false" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="version-specific" + + # Try different tag formats + for TAG_FORMAT in "v$TARGET_VERSION" "$TARGET_VERSION"; do + echo "๐Ÿ” Checking for tag: $TAG_FORMAT" + if git rev-parse --verify "$TAG_FORMAT" >/dev/null 2>&1; then + echo "โœ… Found tag: $TAG_FORMAT" + + # Check if this version has playwright tests + if git ls-tree -r "$TAG_FORMAT" | grep -q "tests/playwright/"; then + echo "โœ… Playwright tests found in $TAG_FORMAT" + + # Extract tests directory from target version + echo "๐Ÿ“ฅ Extracting tests directory from $TAG_FORMAT..." + rm -rf ./tests/playwright/ + git archive "$TAG_FORMAT" tests/playwright/ | tar -x + + if [ -d "./tests/playwright/" ]; then + echo "โœ… Successfully extracted tests from $TAG_FORMAT" + TESTS_AVAILABLE="true" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="extracted-from-$TAG_FORMAT" + break + else + echo "โš ๏ธ Failed to extract tests from $TAG_FORMAT" + fi + else + echo "โš ๏ธ No playwright tests found in $TAG_FORMAT" + fi + else + echo "โš ๏ธ Tag $TAG_FORMAT not found" + fi + done + + if [ "$TESTS_AVAILABLE" = "false" ]; then + echo "โš ๏ธ No compatible tests found for version $TARGET_VERSION" + echo "๐Ÿ“‹ Will skip testing for this version (tests don't exist yet)" + TEST_VERSION="none" + TEST_SOURCE_TYPE="not-available" + fi + fi + + # Set outputs for workflow control + echo "test-version=$TEST_VERSION" >> $GITHUB_OUTPUT + echo "test-source-type=$TEST_SOURCE_TYPE" >> $GITHUB_OUTPUT + echo "tests-available=$TESTS_AVAILABLE" >> $GITHUB_OUTPUT + + echo "โœ… Hybrid setup complete:" + echo " ๐Ÿ—๏ธ Workflow infrastructure: main branch" + echo " ๐Ÿงช Test content: $TEST_VERSION ($TEST_SOURCE_TYPE)" + echo " โœ… Tests available: $TESTS_AVAILABLE" + + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + tools: composer + coverage: none + + - name: Install Dependencies + run: | + export PUPPETEER_SKIP_DOWNLOAD=true + npm ci + composer install --no-dev --no-scripts --optimize-autoloader + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-core-components.outputs.artifact-name }} + path: ./ + + - name: Update wp-env.json file + env: + PHP_VERSION: '8.1' + WP_CORE_VERSION: 'latest' + HELLO_THEME_VERSION: ${{ env.HELLO_THEME_VERSION }} + ELEMENTOR_VERSION: ${{ env.ELEMENTOR_VERSION }} + run: node ./.github/scripts/build-wp-env.js + + - name: Install WordPress environment + run: | + npx wp-env start + + - name: Setup WordPress environment (activate plugins and themes) + run: | + npx wp-env run cli bash hello-elementor-config/setup.sh + npx wp-env run tests-cli bash hello-elementor-config/setup.sh + + - name: Install Playwright + run: npx playwright install chromium + + - name: Run Hello Theme tagged tests only + if: steps.extract-version-tests.outputs.tests-available == 'true' + run: | + echo "๐Ÿงช Running tagged tests from: ${{ steps.extract-version-tests.outputs.test-source-type }}" + # Only run Hello Theme tests with the specified tag + npm run test:playwright -- tests/playwright/tests/ --grep="${{ github.event.inputs.tag }}" + + - name: Skip tagged tests - version incompatible + if: steps.extract-version-tests.outputs.tests-available != 'true' + run: | + echo "โญ๏ธ Skipping tagged tests for Hello Theme ${{ inputs.hello_theme_version || 'main' }}" + echo "๐Ÿ“‹ Reason: ${{ steps.extract-version-tests.outputs.test-source-type }}" + echo "โœ… This is expected for older versions that predate Playwright testing" + + - name: Upload core tagged test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: core-tagged-test-results-ht${{ env.HELLO_THEME_VERSION }}-el${{ env.ELEMENTOR_VERSION }} + path: test-results/ + if-no-files-found: ignore + retention-days: 3 + + - name: Upload Core Tagged Playwright HTML report + uses: actions/upload-artifact@v4 + if: always() + with: + name: core-tagged-playwright-report-ht${{ env.HELLO_THEME_VERSION }}-el${{ env.ELEMENTOR_VERSION }} + path: playwright-report/ + if-no-files-found: ignore + retention-days: 3 + + - name: Stop wp-env + if: always() + run: npx wp-env stop || true + + core-matrix-results: + needs: [core-playwright-tests, core-playwright-tagged-tests] + if: needs.core-playwright-tests.result != 'skipped' || needs.core-playwright-tagged-tests.result != 'skipped' + runs-on: ubuntu-22.04 + name: Core Matrix - Test Results + steps: + - name: Core Matrix test status + run: echo "Core Matrix test status is - ${{ needs.core-playwright-tests.result }}" + + - name: Check Core Matrix status + if: ${{ needs.core-playwright-tests.result != 'success' && needs.core-playwright-tests.result != 'skipped' }} + run: exit 1 diff --git a/.github/workflows/playwright-with-specific-hello-plus-version.yml b/.github/workflows/playwright-with-specific-hello-plus-version.yml new file mode 100644 index 00000000..af58b701 --- /dev/null +++ b/.github/workflows/playwright-with-specific-hello-plus-version.yml @@ -0,0 +1,504 @@ +name: Playwright with Specific Hello Plus Version + +on: + workflow_dispatch: + inputs: + hello_plus_version: + description: 'Hello Plus version to test (e.g., 1.7.2 or latest-stable)' + required: true + hello_theme_version: + description: 'Hello Theme version to test (e.g., 3.4.4 or main)' + required: false + default: 'main' + tag: + description: 'Provide @tag or a keyword' + required: false + workflow_call: + inputs: + hello_plus_version: + description: 'Hello Plus version to test (e.g., 1.7.2 or latest-stable)' + required: true + type: string + hello_theme_version: + description: 'Hello Theme version to test (e.g., 3.4.4 or main)' + required: false + type: string + tag: + description: 'Provide @tag or a keyword' + required: false + type: string + +concurrency: + group: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || format('plus-{0}-{1}', github.run_id, github.event.inputs.hello_plus_version || github.ref) }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +permissions: + contents: read + actions: read + +jobs: + build-plus-components: + name: Build Plus Components (Hello Theme + Hello Plus) + runs-on: ubuntu-22.04 + outputs: + hello-theme-version: ${{ steps.set-versions.outputs.hello-theme-version }} + hello-plus-version: ${{ steps.set-versions.outputs.hello-plus-version }} + hello-theme-source: ${{ steps.set-versions.outputs.hello-theme-source }} + artifact-name: ${{ steps.set-versions.outputs.artifact-name }} + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set version outputs + id: set-versions + run: | + HELLO_THEME_VERSION="${{ inputs.hello_theme_version || 'main' }}" + HELLO_PLUS_VERSION="${{ inputs.hello_plus_version }}" + + echo "hello-theme-version=${HELLO_THEME_VERSION}" >> $GITHUB_OUTPUT + echo "hello-plus-version=${HELLO_PLUS_VERSION}" >> $GITHUB_OUTPUT + echo "hello-theme-source=github" >> $GITHUB_OUTPUT + + # Generate artifact name + ARTIFACT_NAME="plus-ht${HELLO_THEME_VERSION}-hp${HELLO_PLUS_VERSION}-${{ github.run_id }}" + echo "artifact-name=${ARTIFACT_NAME}" >> $GITHUB_OUTPUT + + echo "โœ… Set versions: Hello Theme=${HELLO_THEME_VERSION}, Hello Plus=${HELLO_PLUS_VERSION}" + + - name: Build Hello Theme + uses: ./.github/workflows/build-theme + with: + HELLO_THEME_VERSION: ${{ steps.set-versions.outputs.hello-theme-version }} + + - name: Create theme build directory + run: | + mkdir -p ./tmp/hello-theme + + # Extract the built theme zip to tmp directory + HELLO_THEME_VERSION="${{ steps.set-versions.outputs.hello-theme-version }}" + if [ -f "hello-elementor.${HELLO_THEME_VERSION}.zip" ]; then + unzip -q "hello-elementor.${HELLO_THEME_VERSION}.zip" -d ./tmp/ + + # The zip creates hello-elementor directory, rename to hello-theme for consistency + if [ -d "./tmp/hello-elementor" ]; then + mv "./tmp/hello-elementor" "./tmp/hello-theme" + echo "โœ… Extracted and renamed theme to ./tmp/hello-theme" + else + echo "โŒ Expected hello-elementor directory not found after extraction" + exit 1 + fi + else + echo "โŒ Theme zip file not found: hello-elementor.${HELLO_THEME_VERSION}.zip" + exit 1 + fi + + - name: Download Hello Plus + run: | + HELLO_PLUS_VERSION="${{ steps.set-versions.outputs.hello-plus-version }}" + echo "Downloading Hello Plus version: ${HELLO_PLUS_VERSION}" + + # Create Hello Plus build directory + mkdir -p ./tmp + + if [[ "$HELLO_PLUS_VERSION" == "latest-stable" ]]; then + # Download latest stable from WordPress.org + curl --location -o ./hello-plus.zip https://downloads.wordpress.org/plugin/hello-plus.latest-stable.zip + unzip -q ./hello-plus.zip + mv ./hello-plus ./tmp/hello-plus + echo "โœ… Hello Plus latest-stable downloaded and extracted" + elif [[ "$HELLO_PLUS_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + # Download specific version from WordPress.org + curl --location -o ./hello-plus.zip "https://downloads.wordpress.org/plugin/hello-plus.${HELLO_PLUS_VERSION}.zip" + unzip -q ./hello-plus.zip + mv ./hello-plus ./tmp/hello-plus + echo "โœ… Hello Plus ${HELLO_PLUS_VERSION} downloaded and extracted" + else + echo "โŒ Unsupported Hello Plus version format: ${HELLO_PLUS_VERSION}" + echo "Supported formats: latest-stable, x.y.z (semantic version)" + exit 1 + fi + + - name: Upload plus build artifacts + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.set-versions.outputs.artifact-name }} + path: | + ./tmp/hello-theme + ./tmp/hello-plus + retention-days: 3 + + - name: Generate build summary + run: | + echo "## ๐Ÿ”ง Build Summary" >> $GITHUB_STEP_SUMMARY + echo "|-----------------|----------------------------|-----------------------|" >> $GITHUB_STEP_SUMMARY + echo "| Component | Version | Source |" >> $GITHUB_STEP_SUMMARY + echo "|-----------------|----------------------------|-----------------------|" >> $GITHUB_STEP_SUMMARY + echo "| Hello Theme | ${{ steps.set-versions.outputs.hello-theme-version }} | ${{ steps.set-versions.outputs.hello-theme-source }} |" >> $GITHUB_STEP_SUMMARY + echo "| Hello Plus | ${{ steps.set-versions.outputs.hello-plus-version }} | WordPress.org |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Strategy:** GitHub source for Hello Theme, WordPress.org for Hello Plus" >> $GITHUB_STEP_SUMMARY + + plus-playwright-tests: + name: Hello Theme + Hello Plus Tests + needs: [build-plus-components] + if: github.event.inputs.tag == '' + env: + HELLO_THEME_VERSION: ${{ needs.build-plus-components.outputs.hello-theme-version }} + HELLO_PLUS_VERSION: ${{ needs.build-plus-components.outputs.hello-plus-version }} + RUN_IDENTIFIER: plus-${{ github.event.inputs.hello_theme_version || github.ref }}-${{ github.run_id }} + runs-on: ubuntu-22.04 + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Hybrid Test Setup - Extract tests from target version + id: extract-version-tests + run: | + echo "๐ŸŽฏ HYBRID APPROACH: Always latest workflows + version-specific tests" + echo "๐Ÿ“‹ Workflow infrastructure: main branch (latest build-wp-env.js, scripts, etc.)" + echo "๐Ÿงช Test content: ${{ inputs.hello_theme_version || 'main' }} (matches theme functionality)" + + # Determine the target version for test extraction + TARGET_VERSION="${{ inputs.hello_theme_version || 'main' }}" + + if [ "$TARGET_VERSION" = "main" ]; then + echo "โœ… Using main branch tests (already available)" + TEST_VERSION="main" + TEST_SOURCE_TYPE="main-branch" + TESTS_AVAILABLE="true" + else + echo "๐Ÿ” Extracting tests from version: $TARGET_VERSION" + + # Fetch all tags and branches + git fetch --all --tags + + # Check if target version exists and has tests + TESTS_AVAILABLE="false" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="version-specific" + + # Try different tag formats + for TAG_FORMAT in "v$TARGET_VERSION" "$TARGET_VERSION"; do + echo "๐Ÿ” Checking for tag: $TAG_FORMAT" + if git rev-parse --verify "$TAG_FORMAT" >/dev/null 2>&1; then + echo "โœ… Found tag: $TAG_FORMAT" + + # Check if this version has playwright tests + if git ls-tree -r "$TAG_FORMAT" | grep -q "tests/playwright/"; then + echo "โœ… Playwright tests found in $TAG_FORMAT" + + # Extract tests directory from target version + echo "๐Ÿ“ฅ Extracting tests directory from $TAG_FORMAT..." + rm -rf ./tests/playwright/ + git archive "$TAG_FORMAT" tests/playwright/ | tar -x + + if [ -d "./tests/playwright/" ]; then + echo "โœ… Successfully extracted tests from $TAG_FORMAT" + TESTS_AVAILABLE="true" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="extracted-from-$TAG_FORMAT" + break + else + echo "โš ๏ธ Failed to extract tests from $TAG_FORMAT" + fi + else + echo "โš ๏ธ No playwright tests found in $TAG_FORMAT" + fi + else + echo "โš ๏ธ Tag $TAG_FORMAT not found" + fi + done + + if [ "$TESTS_AVAILABLE" = "false" ]; then + echo "โš ๏ธ No compatible tests found for version $TARGET_VERSION" + echo "๐Ÿ“‹ Will skip testing for this version (tests don't exist yet)" + TEST_VERSION="none" + TEST_SOURCE_TYPE="not-available" + fi + fi + + # Set outputs for workflow control + echo "test-version=$TEST_VERSION" >> $GITHUB_OUTPUT + echo "test-source-type=$TEST_SOURCE_TYPE" >> $GITHUB_OUTPUT + echo "tests-available=$TESTS_AVAILABLE" >> $GITHUB_OUTPUT + + echo "โœ… Hybrid setup complete:" + echo " ๐Ÿ—๏ธ Workflow infrastructure: main branch" + echo " ๐Ÿงช Test content: $TEST_VERSION ($TEST_SOURCE_TYPE)" + echo " โœ… Tests available: $TESTS_AVAILABLE" + + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + tools: composer + coverage: none + + - name: Install Dependencies + run: | + export PUPPETEER_SKIP_DOWNLOAD=true + npm ci + composer install --no-dev --no-scripts --optimize-autoloader + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-plus-components.outputs.artifact-name }} + path: ./ + + - name: Update wp-env.json file + env: + PHP_VERSION: '8.1' + WP_CORE_VERSION: 'latest' + HELLO_THEME_VERSION: ${{ env.HELLO_THEME_VERSION }} + HELLO_PLUS_VERSION: ${{ env.HELLO_PLUS_VERSION }} + ELEMENTOR_VERSION: 'latest-stable' + run: node ./.github/scripts/build-wp-env.js + + - name: Install WordPress environment + run: | + npx wp-env start + + - name: Setup WordPress environment (activate plugins and themes) + run: | + npx wp-env run cli bash hello-elementor-config/setup.sh + npx wp-env run tests-cli bash hello-elementor-config/setup.sh + + - name: WordPress and plugin information + run: | + npx wp-env run cli wp --info + + - name: Install Playwright + run: npx playwright install chromium + + - name: Run Hello Theme tests only (Plus Matrix) + if: steps.extract-version-tests.outputs.tests-available == 'true' + run: | + export TEST_TITLE="Hello Theme Plus Test - HT ${HELLO_THEME_VERSION} + HP ${HELLO_PLUS_VERSION}" + echo "๐Ÿงช Running tests from: ${{ steps.extract-version-tests.outputs.test-source-type }}" + # Only run Hello Theme tests, not Hello Plus or other plugin tests + npm run test:playwright -- tests/playwright/tests/ + + - name: Skip tests - version incompatible + if: steps.extract-version-tests.outputs.tests-available != 'true' + run: | + echo "โญ๏ธ Skipping tests for Hello Theme ${{ inputs.hello_theme_version || 'main' }}" + echo "๐Ÿ“‹ Reason: ${{ steps.extract-version-tests.outputs.test-source-type }}" + echo "โœ… This is expected for older versions that predate Playwright testing" + + - name: Upload plus test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: plus-test-results-ht${{ env.HELLO_THEME_VERSION }}-hp${{ env.HELLO_PLUS_VERSION }} + path: test-results/ + if-no-files-found: ignore + retention-days: 3 + + - name: Upload Plus Playwright HTML report + uses: actions/upload-artifact@v4 + if: always() + with: + name: plus-playwright-report-ht${{ env.HELLO_THEME_VERSION }}-hp${{ env.HELLO_PLUS_VERSION }} + path: playwright-report/ + if-no-files-found: ignore + retention-days: 3 + + - name: Stop wp-env + if: always() + run: npx wp-env stop || true + + plus-playwright-tagged-tests: + name: Plus Tagged Tests + needs: [build-plus-components] + if: ${{ github.event.inputs.tag }} + env: + HELLO_THEME_VERSION: ${{ needs.build-plus-components.outputs.hello-theme-version }} + HELLO_PLUS_VERSION: ${{ needs.build-plus-components.outputs.hello-plus-version }} + runs-on: ubuntu-22.04 + steps: + - name: Checkout Hello Theme + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Hybrid Test Setup - Extract tests from target version + id: extract-version-tests + run: | + echo "๐ŸŽฏ HYBRID APPROACH: Always latest workflows + version-specific tests" + echo "๐Ÿ“‹ Workflow infrastructure: main branch (latest build-wp-env.js, scripts, etc.)" + echo "๐Ÿงช Test content: ${{ inputs.hello_theme_version || 'main' }} (matches theme functionality)" + + # Determine the target version for test extraction + TARGET_VERSION="${{ inputs.hello_theme_version || 'main' }}" + + if [ "$TARGET_VERSION" = "main" ]; then + echo "โœ… Using main branch tests (already available)" + TEST_VERSION="main" + TEST_SOURCE_TYPE="main-branch" + TESTS_AVAILABLE="true" + else + echo "๐Ÿ” Extracting tests from version: $TARGET_VERSION" + + # Fetch all tags and branches + git fetch --all --tags + + # Check if target version exists and has tests + TESTS_AVAILABLE="false" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="version-specific" + + # Try different tag formats + for TAG_FORMAT in "v$TARGET_VERSION" "$TARGET_VERSION"; do + echo "๐Ÿ” Checking for tag: $TAG_FORMAT" + if git rev-parse --verify "$TAG_FORMAT" >/dev/null 2>&1; then + echo "โœ… Found tag: $TAG_FORMAT" + + # Check if this version has playwright tests + if git ls-tree -r "$TAG_FORMAT" | grep -q "tests/playwright/"; then + echo "โœ… Playwright tests found in $TAG_FORMAT" + + # Extract tests directory from target version + echo "๐Ÿ“ฅ Extracting tests directory from $TAG_FORMAT..." + rm -rf ./tests/playwright/ + git archive "$TAG_FORMAT" tests/playwright/ | tar -x + + if [ -d "./tests/playwright/" ]; then + echo "โœ… Successfully extracted tests from $TAG_FORMAT" + TESTS_AVAILABLE="true" + TEST_VERSION="$TARGET_VERSION" + TEST_SOURCE_TYPE="extracted-from-$TAG_FORMAT" + break + else + echo "โš ๏ธ Failed to extract tests from $TAG_FORMAT" + fi + else + echo "โš ๏ธ No playwright tests found in $TAG_FORMAT" + fi + else + echo "โš ๏ธ Tag $TAG_FORMAT not found" + fi + done + + if [ "$TESTS_AVAILABLE" = "false" ]; then + echo "โš ๏ธ No compatible tests found for version $TARGET_VERSION" + echo "๐Ÿ“‹ Will skip testing for this version (tests don't exist yet)" + TEST_VERSION="none" + TEST_SOURCE_TYPE="not-available" + fi + fi + + # Set outputs for workflow control + echo "test-version=$TEST_VERSION" >> $GITHUB_OUTPUT + echo "test-source-type=$TEST_SOURCE_TYPE" >> $GITHUB_OUTPUT + echo "tests-available=$TESTS_AVAILABLE" >> $GITHUB_OUTPUT + + echo "โœ… Hybrid setup complete:" + echo " ๐Ÿ—๏ธ Workflow infrastructure: main branch" + echo " ๐Ÿงช Test content: $TEST_VERSION ($TEST_SOURCE_TYPE)" + echo " โœ… Tests available: $TESTS_AVAILABLE" + + - name: Install Node.js 20.x + uses: actions/setup-node@v4 + with: + node-version: 20.x + cache: 'npm' + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + tools: composer + coverage: none + + - name: Install Dependencies + run: | + export PUPPETEER_SKIP_DOWNLOAD=true + npm ci + composer install --no-dev --no-scripts --optimize-autoloader + + - name: Download build artifacts + uses: actions/download-artifact@v4 + with: + name: ${{ needs.build-plus-components.outputs.artifact-name }} + path: ./ + + - name: Update wp-env.json file + env: + PHP_VERSION: '8.1' + WP_CORE_VERSION: 'latest' + HELLO_THEME_VERSION: ${{ env.HELLO_THEME_VERSION }} + HELLO_PLUS_VERSION: ${{ env.HELLO_PLUS_VERSION }} + ELEMENTOR_VERSION: 'latest-stable' + run: node ./.github/scripts/build-wp-env.js + + - name: Install WordPress environment + run: | + npx wp-env start + + - name: Setup WordPress environment (activate plugins and themes) + run: | + npx wp-env run cli bash hello-elementor-config/setup.sh + npx wp-env run tests-cli bash hello-elementor-config/setup.sh + + - name: Install Playwright + run: npx playwright install chromium + + - name: Run Hello Theme tagged tests only + if: steps.extract-version-tests.outputs.tests-available == 'true' + run: | + echo "๐Ÿงช Running tagged tests from: ${{ steps.extract-version-tests.outputs.test-source-type }}" + # Only run Hello Theme tests with the specified tag + npm run test:playwright -- tests/playwright/tests/ --grep="${{ github.event.inputs.tag }}" + + - name: Skip tagged tests - version incompatible + if: steps.extract-version-tests.outputs.tests-available != 'true' + run: | + echo "โญ๏ธ Skipping tagged tests for Hello Theme ${{ inputs.hello_theme_version || 'main' }}" + echo "๐Ÿ“‹ Reason: ${{ steps.extract-version-tests.outputs.test-source-type }}" + echo "โœ… This is expected for older versions that predate Playwright testing" + + - name: Upload plus tagged test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: plus-tagged-test-results-ht${{ env.HELLO_THEME_VERSION }}-hp${{ env.HELLO_PLUS_VERSION }} + path: test-results/ + if-no-files-found: ignore + retention-days: 3 + + - name: Upload Plus Tagged Playwright HTML report + uses: actions/upload-artifact@v4 + if: always() + with: + name: plus-tagged-playwright-report-ht${{ env.HELLO_THEME_VERSION }}-hp${{ env.HELLO_PLUS_VERSION }} + path: playwright-report/ + if-no-files-found: ignore + retention-days: 3 + + - name: Stop wp-env + if: always() + run: npx wp-env stop || true + + plus-matrix-results: + needs: [plus-playwright-tests, plus-playwright-tagged-tests] + if: needs.plus-playwright-tests.result != 'skipped' || needs.plus-playwright-tagged-tests.result != 'skipped' + runs-on: ubuntu-22.04 + name: Plus Matrix - Test Results + steps: + - name: Plus Matrix test status + run: echo "Plus Matrix test status is - ${{ needs.plus-playwright-tests.result }}" + + - name: Check Plus Matrix status + if: ${{ needs.plus-playwright-tests.result != 'success' && needs.plus-playwright-tests.result != 'skipped' }} + run: exit 1