@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
342 lines • 18.1 kB
JavaScript
"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 });
exports.TestSetup = void 0;
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
const path = __importStar(require("path"));
const fs_1 = __importDefault(require("fs"));
const environmentSettings_1 = require("./environmentSettings");
const core = __importStar(require("./core/index"));
const types_1 = require("./core/types");
const system_operations_1 = require("./system-operations");
const testing_1 = require("./testing");
const ui_interaction_1 = require("./ui-interaction");
const salesforce_components_1 = require("./salesforce-components");
const retryUtils_1 = require("./retryUtils");
class TestSetup {
testSuiteSuffixName = '';
// this needs to be defined in EnvironmentSettings with the default being os.tmpDir()
tempFolderPath = path.join(process.cwd(), 'e2e-temp');
projectFolderPath;
testDataFolderPath;
aliasAndUserNameWereVerified = false;
scratchOrgAliasName;
scratchOrgId;
configuredExtensions = [];
constructor() {
// Private constructor to prevent direct instantiation - use TestSetup.setUp() instead
}
get tempProjectName() {
return 'TempProject-' + this.testSuiteSuffixName;
}
static async setUp(testReqConfig) {
const testSetup = new TestSetup();
testSetup.testSuiteSuffixName = testReqConfig.testSuiteSuffixName;
core.log('');
core.log(`${testSetup.testSuiteSuffixName} - Starting TestSetup.setUp()...`);
// Configure extensions based on test requirements
testSetup.configureExtensions(testReqConfig);
/* The expected workspace will be open up after setUpTestingWorkspace */
await testSetup.setUpTestingWorkspace(testReqConfig.projectConfig);
await (0, ui_interaction_1.executeQuickPick)('View: Close All Editors');
if (testReqConfig.projectConfig.projectShape !== types_1.ProjectShapeOption.NONE) {
await (0, testing_1.verifyExtensionsAreRunning)(testSetup.getExtensionsToVerify());
if (process.platform === 'darwin')
testSetup.setJavaHomeConfigEntry(); // Extra config needed for Apex LSP on GHA
if (testReqConfig.isOrgRequired) {
await (0, salesforce_components_1.setUpScratchOrg)(testSetup);
}
await (0, testing_1.reloadAndEnableExtensions)(); // This is necessary in order to update JAVA home path
}
testSetup.setWindowDialogStyle();
testSetup.setWorkbenchHoverDelay();
testSetup.setMaximumWindowSize();
await (0, ui_interaction_1.reloadWindow)(); // reload window to apply settings
core.log(`${testSetup.testSuiteSuffixName} - ...finished TestSetup.setUp()`);
return testSetup;
}
/**
* Configure extensions based on test requirements
* @param testReqConfig Test requirement configuration
*/
configureExtensions(testReqConfig) {
// Start with all default extensions from salesforceExtensions
this.configuredExtensions = testReqConfig.extensionConfigs || [];
testReqConfig.extensionConfigs = testReqConfig.extensionConfigs || testing_1.extensions;
// If extensionConfigs is provided, configure the specific extensions
if (testReqConfig.extensionConfigs) {
if (testReqConfig.extensionConfigs.length === 0) {
// Empty array means no extensions
this.configuredExtensions = [];
}
else {
// For each configured extension, update the corresponding default extension or add a new one
for (const extConfig of testReqConfig.extensionConfigs) {
const existingExtIndex = this.configuredExtensions.findIndex(ext => ext.extensionId === extConfig.extensionId);
if (existingExtIndex >= 0) {
// Update existing extension configuration
this.configuredExtensions[existingExtIndex] = {
...this.configuredExtensions[existingExtIndex],
shouldInstall: extConfig.shouldInstall,
shouldVerifyActivation: extConfig.shouldVerifyActivation,
vsixPath: extConfig.vsixPath || this.configuredExtensions[existingExtIndex].vsixPath
};
}
else {
// Add new extension configuration
this.configuredExtensions.push({
extensionId: extConfig.extensionId,
name: extConfig.name || extConfig.extensionId, // Use provided name or extensionId as name
vsixPath: extConfig.vsixPath || '',
shouldInstall: extConfig.shouldInstall,
shouldVerifyActivation: extConfig.shouldVerifyActivation
});
}
}
}
}
// For backward compatibility: handle excludedExtensions by setting shouldInstall to 'never'
if (testReqConfig.excludedExtensions && testReqConfig.excludedExtensions.length > 0) {
for (const excludedExtId of testReqConfig.excludedExtensions) {
const extIndex = this.configuredExtensions.findIndex(ext => ext.extensionId === excludedExtId);
if (extIndex >= 0) {
this.configuredExtensions[extIndex].shouldInstall = 'never';
this.configuredExtensions[extIndex].shouldVerifyActivation = false;
}
}
}
// Update the global extensions variable to use our configured extensions
// This ensures compatibility with any code that still uses the global extensions
Object.assign(testing_1.extensions, this.configuredExtensions);
core.log(`${this.testSuiteSuffixName} - Configured ${this.configuredExtensions.length} extensions for testing`);
}
/**
* Get extensions that should be verified during test setup
*/
getExtensionsToVerify() {
return this.configuredExtensions.filter(ext => ext.shouldVerifyActivation);
}
/**
* Get extensions that should be installed with a specific installation option
* @param option Installation option to filter by
*/
getExtensionsToInstall(option) {
return this.configuredExtensions.filter(ext => ext.shouldInstall === option);
}
async tearDown(shouldCheckForUncaughtErrors = true) {
if (shouldCheckForUncaughtErrors)
await (0, testing_1.checkForUncaughtErrors)();
try {
await (0, system_operations_1.deleteScratchOrg)(this.scratchOrgAliasName);
}
catch (error) {
core.log(`Deleting scratch org (or info) failed with Error: ${error.message}`);
}
}
async initializeNewSfProject() {
if (!fs_1.default.existsSync(this.tempFolderPath)) {
(0, system_operations_1.createFolder)(this.tempFolderPath);
}
await (0, system_operations_1.generateSfProject)(this.tempProjectName, this.tempFolderPath); // generate a sf project for 'new'
this.projectFolderPath = path.join(this.tempFolderPath, this.tempProjectName);
}
async setUpTestingWorkspace(projectConfig) {
core.log(`${this.testSuiteSuffixName} - Starting setUpTestingWorkspace()...`);
let projectName;
switch (projectConfig.projectShape) {
case types_1.ProjectShapeOption.NEW:
await this.initializeNewSfProject();
break;
case types_1.ProjectShapeOption.NAMED:
if (projectConfig.githubRepoUrl) {
// verify if folder matches the github repo url
const repoExists = await (0, system_operations_1.gitRepoExists)(projectConfig.githubRepoUrl);
if (!repoExists) {
this.throwError(`Repository does not exist or is inaccessible: ${projectConfig.githubRepoUrl}`);
}
const repoName = (0, system_operations_1.getRepoNameFromUrl)(projectConfig.githubRepoUrl);
if (!repoName) {
this.throwError(`Unable to determine repository name from URL: ${projectConfig.githubRepoUrl}`);
}
else {
projectName = repoName;
if (projectConfig.folderPath) {
const localProjName = (0, system_operations_1.getFolderName)(projectConfig.folderPath);
if (localProjName !== repoName) {
this.throwError(`The local project ${localProjName} does not match the required Github repo ${repoName}`);
}
else {
// If it is a match, use the local folder directly. Local dev use only.
this.projectFolderPath = projectConfig.folderPath;
}
}
else {
// Clone the project from Github URL directly
this.projectFolderPath = path.join(this.tempFolderPath, repoName);
await (0, system_operations_1.gitClone)(projectConfig.githubRepoUrl, this.projectFolderPath);
}
}
}
else {
// missing info, throw an error
this.throwError(`githubRepoUrl is required for named project shape`);
}
break;
case types_1.ProjectShapeOption.ANY:
// ANY: workspace is designated to open when redhat vscode-extension-tester is initialized
if (projectConfig.folderPath) {
this.projectFolderPath = projectConfig.folderPath;
projectName = (0, system_operations_1.getFolderName)(projectConfig.folderPath);
}
else {
// Fallback: if no folder specified, create a new sf project instead
await this.initializeNewSfProject();
}
break;
case types_1.ProjectShapeOption.NONE:
// NONE: no project open in the workspace by default
/* create the e2e-temp folder to benefit further testing */
this.projectFolderPath = path.join(this.tempFolderPath, this.tempProjectName);
if (!fs_1.default.existsSync(this.tempFolderPath)) {
(0, system_operations_1.createFolder)(this.tempFolderPath);
}
break;
default:
this.throwError(`Invalid project shape: ${projectConfig.projectShape}`);
}
if ([types_1.ProjectShapeOption.NAMED, types_1.ProjectShapeOption.NEW].includes(projectConfig.projectShape)) {
core.log(`Project folder to open: ${this.projectFolderPath}`);
// Verify the project was loaded.
await (0, retryUtils_1.retryOperation)(async () => {
if (this.projectFolderPath) {
await core.openFolder(this.projectFolderPath);
await (0, ui_interaction_1.verifyProjectLoaded)(projectName ?? this.tempProjectName);
}
else {
this.throwError('Project folder path is not set');
}
});
}
}
throwError(message) {
core.log(message);
throw new Error(message);
}
updateScratchOrgDefWithEdition(scratchOrgEdition) {
if (scratchOrgEdition === 'enterprise') {
if (!this.projectFolderPath) {
this.throwError('Project folder path is not set');
return; // This line will never be reached, but helps TypeScript understand
}
const projectScratchDefPath = path.join(this.projectFolderPath, 'config', 'project-scratch-def.json');
let projectScratchDef = fs_1.default.readFileSync(projectScratchDefPath, 'utf8');
projectScratchDef = projectScratchDef.replace(`"edition": "Developer"`, `"edition": "Enterprise"`);
fs_1.default.writeFileSync(projectScratchDefPath, projectScratchDef, 'utf8');
}
}
setJavaHomeConfigEntry() {
if (!this.projectFolderPath) {
this.throwError('Project folder path is not set');
return; // This line will never be reached, but helps TypeScript understand
}
const vscodeSettingsPath = path.join(this.projectFolderPath, '.vscode', 'settings.json');
if (!environmentSettings_1.EnvironmentSettings.getInstance().javaHome) {
return;
}
if (!fs_1.default.existsSync(path.dirname(vscodeSettingsPath))) {
fs_1.default.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true });
}
let settings = fs_1.default.existsSync(vscodeSettingsPath) ? JSON.parse(fs_1.default.readFileSync(vscodeSettingsPath, 'utf8')) : {};
settings = {
...settings,
...(process.env.JAVA_HOME ? { 'salesforcedx-vscode-apex.java.home': process.env.JAVA_HOME } : {})
};
fs_1.default.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
core.log(`${this.testSuiteSuffixName} - Set 'salesforcedx-vscode-apex.java.home' to '${process.env.JAVA_HOME}' in ${vscodeSettingsPath}`);
}
setWorkbenchHoverDelay() {
if (!this.projectFolderPath) {
this.throwError('Project folder path is not set');
return; // This line will never be reached, but helps TypeScript understand
}
const vscodeSettingsPath = path.join(this.projectFolderPath, '.vscode', 'settings.json');
if (!fs_1.default.existsSync(path.dirname(vscodeSettingsPath))) {
fs_1.default.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true });
}
let settings = fs_1.default.existsSync(vscodeSettingsPath) ? JSON.parse(fs_1.default.readFileSync(vscodeSettingsPath, 'utf8')) : {};
// Update settings to set workbench.hover.delay
settings = {
...settings,
'workbench.hover.delay': 300000
};
fs_1.default.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
core.log(`${this.testSuiteSuffixName} - Set 'workbench.hover.delay' to '300000' in ${vscodeSettingsPath}`);
}
setMaximumWindowSize() {
if (!this.projectFolderPath) {
return;
}
const vscodeSettingsPath = path.join(this.projectFolderPath, '.vscode', 'settings.json');
if (!fs_1.default.existsSync(path.dirname(vscodeSettingsPath))) {
fs_1.default.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true });
}
let settings = fs_1.default.existsSync(vscodeSettingsPath) ? JSON.parse(fs_1.default.readFileSync(vscodeSettingsPath, 'utf8')) : {};
// Update settings to set window.newWindowDimensions
settings = {
...settings,
'window.newWindowDimensions': 'maximized'
};
fs_1.default.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
core.log(`${this.testSuiteSuffixName} - Set 'window.newWindowDimensions' to 'maximized' in ${vscodeSettingsPath}`);
}
setWindowDialogStyle() {
if (!this.projectFolderPath) {
this.throwError('Project folder path is not set');
return; // This line will never be reached, but helps TypeScript understand
}
const vscodeSettingsPath = path.join(this.projectFolderPath, '.vscode', 'settings.json');
if (!fs_1.default.existsSync(path.dirname(vscodeSettingsPath))) {
fs_1.default.mkdirSync(path.dirname(vscodeSettingsPath), { recursive: true });
}
let settings = fs_1.default.existsSync(vscodeSettingsPath) ? JSON.parse(fs_1.default.readFileSync(vscodeSettingsPath, 'utf8')) : {};
// Update settings to set window.dialogStyle
settings = {
...settings,
'window.dialogStyle': 'custom'
};
fs_1.default.writeFileSync(vscodeSettingsPath, JSON.stringify(settings, null, 2), 'utf8');
core.log(`${this.testSuiteSuffixName} - Set 'window.dialogStyle' to 'custom' in ${vscodeSettingsPath}`);
}
}
exports.TestSetup = TestSetup;
//# sourceMappingURL=testSetup.js.map