UNPKG

@salesforce/salesforcedx-vscode-test-tools

Version:
764 lines 38.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const vscode_extension_tester_1 = require("vscode-extension-tester"); const environmentSettings_1 = require("./environmentSettings"); const path_1 = __importDefault(require("path")); const promises_1 = __importDefault(require("fs/promises")); const extensionUtils_1 = require("./testing/extensionUtils"); const yargs_1 = __importDefault(require("yargs")); const helpers_1 = require("yargs/helpers"); const codeUtil_1 = require("vscode-extension-tester/out/util/codeUtil"); const chai_1 = require("chai"); const console_1 = require("console"); const cliCommands_1 = require("./system-operations/cliCommands"); const system_operations_1 = require("./system-operations"); const helpers_2 = require("./core/helpers"); const authorization_1 = require("./salesforce-components/authorization"); const retryUtils_1 = require("./retryUtils"); // Set Ubuntu Chrome arguments immediately when module loads if (process.platform === 'linux') { const uniqueId = `${Date.now()}-${process.pid}`; const tempDir = path_1.default.join(process.cwd(), 'test-resources', 'chrome-user-data', uniqueId); const ubuntuChromeArgs = [ `--user-data-dir=${tempDir}`, '--no-sandbox', '--disable-dev-shm-usage', '--disable-web-security', '--disable-features=VizDisplayCompositor', '--disable-gpu', '--disable-software-rasterizer', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', '--disable-field-trial-config', '--disable-ipc-flooding-protection', '--single-process', '--no-zygote' ].join(' '); process.env.VSCODE_EXTENSION_TESTER_CHROMEDRIVER_ARGS = ubuntuChromeArgs; process.env.CHROME_OPTIONS = ubuntuChromeArgs; process.env.CHROMIUM_FLAGS = ubuntuChromeArgs; (0, console_1.log)(`Early Ubuntu Chrome setup: ${ubuntuChromeArgs}`); } // Set Chrome-related environment variables early process.env.CHROME_OPTIONS = '--no-sandbox --disable-dev-shm-usage --disable-gpu --remote-debugging-port=9222 --disable-web-security --disable-features=VizDisplayCompositor --single-process --no-zygote --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --user-data-dir=/tmp/chrome-user-data-' + Date.now(); process.env.CHROMIUM_FLAGS = '--no-sandbox --disable-dev-shm-usage --disable-gpu --remote-debugging-port=9222 --disable-web-security --disable-features=VizDisplayCompositor --single-process --no-zygote --disable-background-timer-throttling --disable-backgrounding-occluded-windows --disable-renderer-backgrounding --user-data-dir=/tmp/chrome-user-data-' + Date.now(); process.env.CHROME_BIN = '/usr/bin/google-chrome'; process.env.CHROMIUM_BIN = '/usr/bin/google-chrome'; // Skip Husky installation during test setup process.env.HUSKY_SKIP_INSTALL = '1'; // Set NPM to use legacy peer dependencies process.env.NPM_CONFIG_LEGACY_PEER_DEPS = 'true'; // For Ubuntu CI environments if (process.platform === 'linux') { // Set up virtual display for headless operation process.env.DISPLAY = ':99'; process.env.CHROME_NO_SANDBOX = 'true'; process.env.PUPPETEER_SKIP_CHROMIUM_DOWNLOAD = 'true'; process.env.PUPPETEER_EXECUTABLE_PATH = '/usr/bin/google-chrome'; // Additional Chrome flags for CI process.env.CHROME_DEVEL_SANDBOX = '/usr/lib/chromium-browser/chrome-sandbox'; } class TestSetupAndRunner extends vscode_extension_tester_1.ExTester { static _exTestor; testConfig; spec; static MAX_RETRIES = 3; static RETRY_DELAY = 5000; constructor(testConfig, spec) { (0, console_1.log)(`Init testConfig with testConfig: ${JSON.stringify(testConfig)}`); (0, console_1.log)(`Init testConfig with spec: ${spec}`); // Create config with defaults and overrides const config = (0, helpers_2.createDefaultTestConfig)(testConfig); // Validate config and set defaults for missing values (0, helpers_2.validateTestConfig)(config); super(config?.testResources, codeUtil_1.ReleaseQuality.Stable); this.testConfig = config; this.spec = spec; } /** * Helper method to log important test information using standard terminology */ logTestEnvironment() { (0, console_1.log)(`Setting up test environment with:`); (0, console_1.log)(`- Test Resources: ${this.testConfig.testResources}`); (0, console_1.log)(`- Extensions Folder: ${this.testConfig.extensionsFolder}`); (0, console_1.log)(`- VS Code Version: ${this.testConfig.codeVersion}`); // Log Chrome driver arguments if available const chromeDriverArgs = environmentSettings_1.EnvironmentSettings.getInstance().chromeDriverArgs; if (chromeDriverArgs) { (0, console_1.log)(`- ChromeDriver Arguments: ${chromeDriverArgs}`); } // Log the throttle factor const throttleFactor = environmentSettings_1.EnvironmentSettings.getInstance().throttleFactor; (0, console_1.log)(`- Throttle Factor: ${throttleFactor}`); } /** * Sets up Ubuntu-specific Chrome driver arguments to avoid user data directory conflicts */ async setupUbuntuChromeArgs() { // Only apply Ubuntu-specific settings if we're on Linux and no custom args are already set if (process.platform !== 'linux') { console.log(`Not on Linux, skipping setupUbuntuChromeArgs: ${process.platform}`); return; } const existingArgs = environmentSettings_1.EnvironmentSettings.getInstance().chromeDriverArgs; if (existingArgs) { (0, console_1.log)(`Chrome driver arguments already set: ${existingArgs}`); return; } // Generate a unique user data directory for this test run const uniqueId = `${Date.now()}-${process.pid}`; const tempDir = path_1.default.join(this.testConfig.testResources || 'test-resources', 'chrome-user-data', uniqueId); // Ensure the directory exists try { await promises_1.default.mkdir(tempDir, { recursive: true }); (0, console_1.log)(`Created Chrome user data directory: ${tempDir}`); } catch (error) { (0, console_1.log)(`Warning: Could not create Chrome user data directory: ${error}`); } const ubuntuChromeArgs = [ `--user-data-dir=${tempDir}`, '--no-sandbox', '--disable-dev-shm-usage', '--disable-web-security', '--disable-features=VizDisplayCompositor', '--disable-gpu', '--disable-software-rasterizer', '--disable-background-timer-throttling', '--disable-backgrounding-occluded-windows', '--disable-renderer-backgrounding', '--disable-field-trial-config', '--disable-ipc-flooding-protection', '--single-process', '--no-zygote' ].join(' '); // Set the environment variable for vscode-extension-tester to pick up process.env.VSCODE_EXTENSION_TESTER_CHROMEDRIVER_ARGS = ubuntuChromeArgs; // Also try other possible environment variables process.env.CHROME_OPTIONS = ubuntuChromeArgs; process.env.CHROMIUM_FLAGS = ubuntuChromeArgs; process.env.GOOGLE_CHROME_OPTS = ubuntuChromeArgs; (0, console_1.log)(`Set Ubuntu Chrome driver arguments: ${ubuntuChromeArgs}`); (0, console_1.log)(`Environment variable VSCODE_EXTENSION_TESTER_CHROMEDRIVER_ARGS: ${process.env.VSCODE_EXTENSION_TESTER_CHROMEDRIVER_ARGS}`); // Also try setting the user data dir in a more explicit way const userDataDir = tempDir; process.env.CHROME_USER_DATA_DIR = userDataDir; process.env.CHROMIUM_USER_DATA_DIR = userDataDir; (0, console_1.log)(`Set CHROME_USER_DATA_DIR: ${userDataDir}`); } /** * Kills existing Chrome processes that might be using the user data directory */ async killExistingChromeProcesses() { try { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execAsync = promisify(exec); (0, console_1.log)('Attempting to kill existing Chrome processes...'); // Kill Chrome processes const commands = [ 'pkill -f "chrome.*--test-type"', 'pkill -f "chromium.*--test-type"', 'pkill -f "google-chrome.*--test-type"', 'pkill -f "chrome.*--user-data-dir"', 'pkill -f "chromium.*--user-data-dir"' ]; for (const command of commands) { try { await execAsync(command); (0, console_1.log)(`Executed: ${command}`); } catch (error) { // It's normal for pkill to exit with code 1 if no processes are found (0, console_1.log)(`Command ${command} completed (processes may not have been running)`); } } // Wait a moment for processes to fully terminate await new Promise(resolve => setTimeout(resolve, 2000)); (0, console_1.log)('Chrome process cleanup completed'); } catch (error) { (0, console_1.log)(`Warning: Could not kill Chrome processes: ${error}`); } } /** * Kills existing VS Code processes on Windows to prevent file locking issues during extension installation */ async killExistingVSCodeProcesses() { if (process.platform !== 'win32') { return; } try { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execAsync = promisify(exec); (0, console_1.log)('Attempting to kill existing VS Code processes on Windows...'); // Kill VS Code processes on Windows const commands = [ 'taskkill /f /im Code.exe', 'taskkill /f /im code.exe', 'taskkill /f /im VSCode.exe', 'taskkill /f /im electron.exe /fi "WINDOWTITLE eq Visual Studio Code*"', 'taskkill /f /im node.exe /fi "COMMANDLINE eq *vscode*"' ]; for (const command of commands) { try { await execAsync(command); (0, console_1.log)(`Executed: ${command}`); } catch (error) { // It's normal for taskkill to exit with code 1 if no processes are found (0, console_1.log)(`Command ${command} completed (processes may not have been running)`); } } // Wait a moment for processes to fully terminate and release file locks await new Promise(resolve => setTimeout(resolve, 3000)); (0, console_1.log)('VS Code process cleanup completed'); } catch (error) { (0, console_1.log)(`Warning: Could not kill VS Code processes: ${error}`); } } /** * Cleans up Windows VS Code extension temporary files that may cause locking issues */ async cleanupWindowsExtensionTempFiles() { if (process.platform !== 'win32') { return; } try { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execAsync = promisify(exec); (0, console_1.log)('Cleaning up Windows VS Code extension temporary files...'); // Clean up VS Code extension temporary files const cleanupCommands = [ 'del /f /q "%USERPROFILE%\\.vscode\\extensions\\*.vsctmp" 2>nul', 'del /f /q "%USERPROFILE%\\.vscode\\extensions\\*.tmp" 2>nul', 'rmdir /s /q "%TEMP%\\vscode-*" 2>nul', 'rmdir /s /q "%TEMP%\\Code-*" 2>nul' ]; for (const command of cleanupCommands) { try { await execAsync(command); (0, console_1.log)(`Executed cleanup: ${command}`); } catch (error) { // It's normal for cleanup commands to fail if files don't exist (0, console_1.log)(`Cleanup command completed: ${command}`); } } (0, console_1.log)('Windows extension temp file cleanup completed'); } catch (error) { (0, console_1.log)(`Warning: Could not cleanup Windows extension temp files: ${error}`); } } /** * Creates a Chrome wrapper script that forces Ubuntu-specific arguments */ async createChromeWrapper() { try { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execAsync = promisify(exec); // Find Chrome executable const chromeCommands = [ 'which google-chrome', 'which google-chrome-stable', 'which chromium-browser', 'which chromium' ]; let chromeExePath = ''; for (const command of chromeCommands) { try { const result = await execAsync(command); chromeExePath = result.stdout.trim(); if (chromeExePath) { (0, console_1.log)(`Found Chrome at: ${chromeExePath}`); break; } } catch (error) { // Command failed, try next one } } if (!chromeExePath) { (0, console_1.log)('Warning: Could not find Chrome executable for wrapper'); return; } // Create unique user data directory const uniqueId = `${Date.now()}-${process.pid}`; const tempDir = path_1.default.join(this.testConfig.testResources || 'test-resources', 'chrome-user-data', uniqueId); await promises_1.default.mkdir(tempDir, { recursive: true }); // Create wrapper script const wrapperPath = path_1.default.join(this.testConfig.testResources || 'test-resources', 'chrome-wrapper.sh'); const wrapperScript = `#!/bin/bash # Chrome wrapper script for Ubuntu testing exec "${chromeExePath}" \\ --user-data-dir="${tempDir}" \\ --no-sandbox \\ --disable-dev-shm-usage \\ --disable-web-security \\ --disable-features=VizDisplayCompositor \\ --disable-gpu \\ --disable-software-rasterizer \\ --disable-background-timer-throttling \\ --disable-backgrounding-occluded-windows \\ --disable-renderer-backgrounding \\ --disable-field-trial-config \\ --disable-ipc-flooding-protection \\ --single-process \\ --no-zygote \\ "$@" `; (0, system_operations_1.createOrOverwriteFile)(wrapperPath, wrapperScript); await execAsync(`chmod +x "${wrapperPath}"`); // Override Chrome path in environment process.env.CHROME_BIN = wrapperPath; process.env.CHROMIUM_BIN = wrapperPath; process.env.GOOGLE_CHROME_BIN = wrapperPath; // Create wrapper scripts for common Chrome names in our own directory const wrapperDir = path_1.default.join(this.testConfig.testResources || 'test-resources', 'chrome-wrappers'); await promises_1.default.mkdir(wrapperDir, { recursive: true }); const chromeNames = ['google-chrome', 'google-chrome-stable', 'chromium-browser', 'chromium']; for (const name of chromeNames) { const nameWrapperPath = path_1.default.join(wrapperDir, name); (0, system_operations_1.createOrOverwriteFile)(nameWrapperPath, wrapperScript); await execAsync(`chmod +x "${nameWrapperPath}"`); (0, console_1.log)(`Created ${name} wrapper at: ${nameWrapperPath}`); } // Prepend our wrapper directory to PATH so our wrappers are found first process.env.PATH = `${wrapperDir}:${process.env.PATH}`; (0, console_1.log)(`Prepended ${wrapperDir} to PATH`); (0, console_1.log)(`Created Chrome wrapper at: ${wrapperPath}`); (0, console_1.log)(`Set CHROME_BIN to: ${wrapperPath}`); } catch (error) { (0, console_1.log)(`Warning: Could not create Chrome wrapper: ${error}`); } } async setup() { // Log the test environment configuration this.logTestEnvironment(); // Set Ubuntu-specific Chrome driver arguments if needed await this.setupUbuntuChromeArgs(); // Kill any existing Chrome processes on Linux if (process.platform === 'linux') { await this.killExistingChromeProcesses(); } await this.downloadCode(this.testConfig.codeVersion); await this.downloadChromeDriver(this.testConfig.codeVersion); // Create Chrome wrapper script for Ubuntu if (process.platform === 'linux') { await this.createChromeWrapper(); } // Kill any existing VS Code processes on Windows before installing extensions if (process.platform === 'win32') { await this.killExistingVSCodeProcesses(); await this.cleanupWindowsExtensionTempFiles(); } try { await this.installExtensions(); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); (0, console_1.log)(`Warning: Failed to install extensions: ${errorMessage}. Continuing setup.`); } await this.setupAndAuthorizeOrg(); } async runTests() { const useExistingProject = environmentSettings_1.EnvironmentSettings.getInstance().useExistingProject; const resources = useExistingProject ? [useExistingProject] : []; (0, console_1.log)(`starting runTests with useExistingProject: ${useExistingProject}`); (0, console_1.log)(`starting runTests with resources: ${resources}`); (0, console_1.log)(`starting runTests with spec: ${this.spec}`); // Set Ubuntu Chrome arguments just before running tests const isLinux = process.platform === 'linux'; if (isLinux) { await this.setupUbuntuChromeArgs(); await this.killExistingChromeProcesses(); // Use Ubuntu-specific test runner (0, console_1.log)('Using Ubuntu-specific test runner...'); return await this.runTestsUbuntu(); } // Try to pass additional launch arguments for Ubuntu const runOptions = { resources }; if (isLinux) { runOptions.launchArgs = [ '--disable-web-security', '--disable-features=VizDisplayCompositor', '--no-sandbox', '--disable-dev-shm-usage' ]; } const exitCode = await super.runTests(this.spec || environmentSettings_1.EnvironmentSettings.getInstance().specFiles, runOptions); await this.validateTestExecution(exitCode); return exitCode; } async installExtension(extension) { (0, console_1.log)(`SetUp - Started Install extension ${path_1.default.basename(extension)}`); await this.installVsix({ useYarn: false, vsixFile: extension }); } async installExtensions(excludeExtensions = []) { const vsixToInstallDir = this.testConfig.vsixToInstallDir || environmentSettings_1.EnvironmentSettings.getInstance().vsixToInstallDir; (0, console_1.log)(`Installing extension from vsixToInstallDir: ${vsixToInstallDir}`); if (!vsixToInstallDir) { (0, console_1.log)(`No VSIX_TO_INSTALL directory specified, the tests will run without installing any extensions`); return; } // Check if the directory exists try { await promises_1.default.access(vsixToInstallDir); } catch (error) { throw new Error(`VSIX_TO_INSTALL directory does not exist or is not accessible: ${vsixToInstallDir}`); } // Check for already installed extensions const extensionPattern = /^(?<publisher>.+?)\.(?<extensionId>.+?)-(?<version>\d+\.\d+\.\d+)(?:\.\d+)*$/; const extensionsDirEntries = (await promises_1.default.readdir(vsixToInstallDir)).map(entry => path_1.default.resolve((0, helpers_2.normalizePath)(path_1.default.join(vsixToInstallDir, entry)))); const foundInstalledExtensions = await Promise.all(extensionsDirEntries .filter(async (entry) => { try { const stats = await promises_1.default.stat(entry); return stats.isDirectory(); } catch (e) { (0, console_1.log)(`stat failed for file ${entry}`); return false; } }) .map(entry => { const match = path_1.default.basename(entry).match(extensionPattern); if (match?.groups) { return { publisher: match.groups.publisher, extensionId: match.groups.extensionId, version: match.groups.version, path: entry }; } return null; }) .filter(Boolean) .filter(ext => extensionUtils_1.extensions.find(refExt => { return refExt.extensionId === ext?.extensionId; }))); if (foundInstalledExtensions.length > 0 && foundInstalledExtensions.every(ext => extensionUtils_1.extensions.find(refExt => refExt.extensionId === ext?.extensionId))) { (0, console_1.log)(`Found the following pre-installed extensions in dir ${vsixToInstallDir}, skipping installation of vsix`); foundInstalledExtensions.forEach(ext => { (0, console_1.log)(`Extension ${ext?.extensionId} version ${ext?.version}`); }); return; } const extensionsVsixs = (0, system_operations_1.getVsixFilesFromDir)(vsixToInstallDir); if (extensionsVsixs.length === 0) { (0, console_1.log)(`No vsix files were found in dir ${vsixToInstallDir}, skipping extension installation`); return; // Skip installation instead of throwing an error } (0, console_1.log)(`VSIX files count: ${extensionsVsixs.length} were found in dir ${vsixToInstallDir}, skipping extension installation`); const mergeExcluded = Array.from(new Set([ ...excludeExtensions, ...extensionUtils_1.extensions.filter(ext => ext.shouldInstall === 'never').map(ext => ext.extensionId) ])); // Refactored part to use the extensions array extensionsVsixs.forEach(vsix => { const match = path_1.default.basename(vsix).match(/^(?<extension>.*?)(-(?<version>\d+\.\d+\.\d+))?\.vsix$/); (0, console_1.log)(`Found extension: ${vsix} with match ${match}`); if (match?.groups) { const { extension, version } = match.groups; const foundExtension = extensionUtils_1.extensions.find(e => e.extensionId === extension); if (foundExtension) { foundExtension.vsixPath = vsix; // assign 'never' to this extension if its id is included in excluedExtensions foundExtension.shouldInstall = mergeExcluded.includes(foundExtension.extensionId) ? 'never' : 'always'; // if not installing, don't verify, otherwise use default value foundExtension.shouldVerifyActivation = foundExtension.shouldInstall === 'never' ? false : foundExtension.shouldVerifyActivation; (0, console_1.log)(`SetUp - Found extension ${extension} version ${version} with vsixPath ${foundExtension.vsixPath}`); } else { (0, console_1.log)(`SetUp - Not found extension ${extension} version ${version}`); } } }); if (extensionUtils_1.extensions.length === 0) { (0, console_1.log)(`No extensions found, cannot proceed with install`); } // Iterate over the extensions array to install extensions for (const extensionObj of extensionUtils_1.extensions.filter(ext => ext.vsixPath !== '' && ext.shouldInstall !== 'never')) { let vsixPath = path_1.default.resolve(extensionObj.vsixPath); // Avoid Windows paths being parsed as URLs by the tester if (process.platform === 'win32') { vsixPath = vsixPath.replace(/\\/g, '/'); if (/^[a-zA-Z]:\//.test(vsixPath)) { vsixPath = '/' + vsixPath; // Prevent `new URL()` from treating it as a URL } } // Use retry logic for Windows to handle file locking issues if (process.platform === 'win32') { await (0, retryUtils_1.retryOperation)(async () => { // Add a small delay before each retry attempt to allow file handles to be released await new Promise(resolve => setTimeout(resolve, TestSetupAndRunner.RETRY_DELAY)); await this.installExtension(vsixPath); }, TestSetupAndRunner.MAX_RETRIES, `Failed to install extension ${path_1.default.basename(vsixPath)} after retries due to Windows file locking issues`); } else { await this.installExtension(vsixPath); } } } async setupAndAuthorizeOrg() { const environmentSettings = environmentSettings_1.EnvironmentSettings.getInstance(); const devHubUserName = environmentSettings.devHubUserName; const devHubAliasName = environmentSettings.devHubAliasName; const SFDX_AUTH_URL = environmentSettings.sfdxAuthUrl; try { // First try to verify if the org with alias and username exists (0, console_1.log)('Checking if org with matching alias and username is already authenticated...'); await (0, authorization_1.verifyAliasAndUserName)(); (0, console_1.log)(`Org with alias ${devHubAliasName} and username ${devHubUserName} is already authenticated`); return; } catch (error) { // If verification fails, continue with SFDX_AUTH_URL const errorMessage = error instanceof Error ? error.message : String(error); (0, console_1.log)(`No existing authenticated org found: ${errorMessage}`); // If no SFDX_AUTH_URL is provided, rethrow the error if (!SFDX_AUTH_URL) { throw new Error('No SFDX_AUTH_URL provided and no existing authentication found. Unable to authorize org.'); } } // The verifyAliasAndUserName function already checks that both alias and username are set, // so at this point we know they are defined (or we would have thrown earlier). // TypeScript doesn't know this though, so we'll add the non-null assertion if (!devHubUserName || !devHubAliasName) { throw new Error('DevHub username or alias name is not set. This should not happen as verifyAliasAndUserName should have caught this.'); } // At this point, neither alias nor username matched an existing org, // and we have an SFDX_AUTH_URL to use (0, console_1.log)('Authenticating using SFDX_AUTH_URL from environment variable...'); // Step 1: Authorize to Testing Org const authorizeOrg = await (0, cliCommands_1.orgLoginSfdxUrl)(); (0, chai_1.expect)(authorizeOrg.stdout).to.contain(`Successfully authorized ${devHubUserName}`); (0, console_1.log)(`Successfully authorized ${devHubUserName}`); // Step 2: Set Alias for the Org const setAliasResult = await (0, cliCommands_1.setAlias)(devHubAliasName, devHubUserName); (0, chai_1.expect)(setAliasResult.stdout).to.contain(devHubAliasName); (0, chai_1.expect)(setAliasResult.stdout).to.contain(devHubUserName); (0, chai_1.expect)(setAliasResult.stdout).to.contain('true'); (0, console_1.log)(`Successfully setAliasResult ${setAliasResult.stdout}`); } async downloadCode(version = 'latest') { await super.downloadCode(version); } static get exTester() { if (TestSetupAndRunner.exTester) { return TestSetupAndRunner._exTestor; } TestSetupAndRunner._exTestor = new TestSetupAndRunner(); return TestSetupAndRunner._exTestor; } async setupVirtualDisplay() { if (process.platform !== 'linux') { return; } try { console.log('Setting up virtual display for Ubuntu...'); // Start Xvfb virtual display await this.execShellCommand('Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &').catch(() => { console.log('Xvfb might already be running or not available'); }); // Wait for display to be ready await new Promise(resolve => setTimeout(resolve, 2000)); // Verify display is working await this.execShellCommand('export DISPLAY=:99 && xdpyinfo > /dev/null 2>&1').catch(() => { console.log('Virtual display verification failed, continuing anyway'); }); console.log('Virtual display setup completed'); } catch (error) { console.warn('Failed to setup virtual display:', error); } } async execShellCommand(command) { const { exec } = await Promise.resolve().then(() => __importStar(require('child_process'))); const { promisify } = await Promise.resolve().then(() => __importStar(require('util'))); const execAsync = promisify(exec); await execAsync(command); } async runTestsUbuntu() { // Setup virtual display for Ubuntu await this.setupVirtualDisplay(); console.log('Running tests...'); // Kill any existing Chrome processes before running if (process.platform === 'linux') { await this.killChromeProcesses(); } // Create a unique user data directory const uniqueUserDataDir = `/tmp/vscode-chrome-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`; // Update environment variables with unique directory process.env.CHROME_USER_DATA_DIR = uniqueUserDataDir; // Call the existing runTests method const useExistingProject = environmentSettings_1.EnvironmentSettings.getInstance().useExistingProject; const resources = useExistingProject ? [useExistingProject] : []; const exitCode = await super.runTests(this.spec || environmentSettings_1.EnvironmentSettings.getInstance().specFiles, { resources }); await this.validateTestExecution(exitCode); if (exitCode === 0) { console.log('Tests completed successfully!'); } return exitCode; } /** * Validates that tests were actually executed by checking for test output * Throws an error if no tests were found or executed */ async validateTestExecution(exitCode) { if (exitCode !== 0) { return; // If tests failed, don't check for test count (tests were found but failed) } // Check if any spec files were provided const specFiles = this.spec || environmentSettings_1.EnvironmentSettings.getInstance().specFiles; if (!specFiles || (Array.isArray(specFiles) && specFiles.length === 0)) { throw new Error('No E2E test spec files were provided. Please specify test files to run.'); } // Check if spec files actually exist const specsToCheck = Array.isArray(specFiles) ? specFiles : [specFiles]; let foundTestFiles = false; for (const spec of specsToCheck) { try { // Check if the spec file/pattern matches any files if (spec.includes('*') || spec.includes('?')) { // It's a glob pattern - we can't easily check without implementing glob matching // For now, assume it's valid if it contains wildcards (0, console_1.log)(`Spec pattern provided: ${spec}`); foundTestFiles = true; } else { // It's a specific file path await promises_1.default.access(spec); foundTestFiles = true; (0, console_1.log)(`Found test file: ${spec}`); } } catch (error) { (0, console_1.log)(`Test file not found: ${spec}`); } } if (!foundTestFiles && exitCode === 0) { throw new Error(`No E2E test files were found for the provided spec: ${JSON.stringify(specFiles)}. This likely indicates a configuration issue - tests should not pass when no test files exist.`); } (0, console_1.log)('Test execution validation completed - test files were found and executed'); } async killChromeProcesses() { try { console.log('Killing existing Chrome processes...'); const commands = [ 'pkill -f "chrome.*--test-type"', 'pkill -f "chrome.*--remote-debugging-port"', 'pkill -f "chrome.*--user-data-dir"', 'pkill -f "chrome.*--disable-dev-shm-usage"', 'pkill -f chromium', 'pkill -f google-chrome', 'pkill -f chrome', 'pkill -f "Code.*--extensionDevelopmentPath"', 'pkill -f "code.*--extensionDevelopmentPath"' ]; for (const cmd of commands) { try { await this.execShellCommand(cmd); } catch (error) { // Ignore errors - processes might not exist } } // Clean up any leftover user data directories and lock files await this.execShellCommand('rm -rf /tmp/chrome-user-data-* /tmp/vscode-chrome-* ~/.config/Code/SingletonLock ~/.vscode/extensions/*/node_modules/.bin/chromedriver').catch(() => { console.log('Failed to clean up leftover files, continuing anyway'); }); // Wait a moment for processes to fully terminate await new Promise(resolve => setTimeout(resolve, 3000)); } catch (error) { console.warn('Failed to kill Chrome processes:', error); } } } // Parse command-line arguments const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)) .option('spec', { alias: 's', type: 'string', description: 'Glob pattern for test files or list of test files', demandOption: false, array: true }) .option('workspace-path', { alias: 'w', type: 'string', description: 'Path to workspace directory', demandOption: false }) .option('vscode-version', { alias: 'v', type: 'string', description: 'VS Code version to use (e.g., latest, stable, 1.85.0)', demandOption: false }) .help().argv; // Create test config from command line arguments const testConfig = {}; if (argv.workspacePath) { testConfig.workspacePath = argv.workspacePath; testConfig.extensionsPath = path_1.default.join(argv.workspacePath, 'extensions'); } if (argv.vscodeVersion) { testConfig.codeVersion = argv.vscodeVersion; } if (argv.spec) { (0, console_1.log)(`Spec passed in: ${argv.spec}`); } const testSetupAndRunner = new TestSetupAndRunner(testConfig, argv.spec); async function run() { try { await testSetupAndRunner.setup(); const result = await testSetupAndRunner.runTests(); console.log(result); process.exit(result); } catch (error) { console.error(error); process.exit(1); } } void run(); //# sourceMappingURL=test-setup-and-runner.js.map