@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
263 lines • 10.9 kB
JavaScript
;
/*
* Copyright (c) 2025, 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.extensions = void 0;
exports.showRunningExtensions = showRunningExtensions;
exports.reloadAndEnableExtensions = reloadAndEnableExtensions;
exports.getExtensionsToVerifyActive = getExtensionsToVerifyActive;
exports.verifyExtensionsAreRunning = verifyExtensionsAreRunning;
exports.findExtensionsInRunningExtensionsList = findExtensionsInRunningExtensionsList;
exports.checkForUncaughtErrors = checkForUncaughtErrors;
const miscellaneous_1 = require("../core/miscellaneous");
const commandPrompt_1 = require("../ui-interaction/commandPrompt");
const vscode_extension_tester_1 = require("vscode-extension-tester");
const chai_1 = require("chai");
const ui_interaction_1 = require("../ui-interaction");
const VERIFY_EXTENSIONS_TIMEOUT = miscellaneous_1.Duration.seconds(60);
exports.extensions = [
{
extensionId: 'salesforcedx-vscode',
name: 'Salesforce Extension Pack',
vsixPath: '',
shouldInstall: 'never',
shouldVerifyActivation: false
},
{
extensionId: 'salesforcedx-vscode-expanded',
name: 'Salesforce Extension Pack (Expanded)',
vsixPath: '',
shouldInstall: 'never',
shouldVerifyActivation: false
},
{
extensionId: 'salesforcedx-vscode-soql',
name: 'SOQL',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: false
},
{
extensionId: 'salesforcedx-einstein-gpt',
name: 'Einstein for Developers (Beta)',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: false
},
{
extensionId: 'salesforcedx-vscode-core',
name: 'Salesforce CLI Integration',
vsixPath: '',
shouldInstall: 'always',
shouldVerifyActivation: true
},
{
extensionId: 'salesforcedx-vscode-apex',
name: 'Apex',
vsixPath: '',
shouldInstall: 'always',
shouldVerifyActivation: true
},
{
extensionId: 'salesforcedx-vscode-apex-debugger',
name: 'Apex Interactive Debugger',
vsixPath: '',
shouldInstall: 'never',
shouldVerifyActivation: false
},
{
extensionId: 'salesforcedx-vscode-apex-replay-debugger',
name: 'Apex Replay Debugger',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: true
},
{
extensionId: 'salesforcedx-vscode-lightning',
name: 'Lightning Web Components',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: true
},
{
extensionId: 'salesforcedx-vscode-lwc',
name: 'Lightning Web Components',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: true
},
{
extensionId: 'salesforcedx-vscode-visualforce',
name: 'salesforcedx-vscode-visualforce',
vsixPath: '',
shouldInstall: 'optional',
shouldVerifyActivation: true
}
];
/**
* Shows the list of running extensions in VS Code
* @returns The editor showing running extensions, or undefined if not found
*/
async function showRunningExtensions() {
(0, miscellaneous_1.log)('');
(0, miscellaneous_1.log)(`Starting showRunningExtensions()...`);
await (0, commandPrompt_1.executeQuickPick)('Developer: Show Running Extensions');
let re = undefined;
await (0, ui_interaction_1.getBrowser)().wait(async () => {
const wb = (0, ui_interaction_1.getWorkbench)();
const ev = wb.getEditorView();
re = await ev.openEditor('Running Extensions');
return re.isDisplayed();
}, 5000, // Timeout after 5 seconds
'Expected "Running Extensions" tab to be visible after 5 seconds', 500);
(0, miscellaneous_1.log)(`... Finished showRunningExtensions()`);
(0, miscellaneous_1.log)('');
return re;
}
/**
* Reloads the VS Code window and enables all extensions
*/
async function reloadAndEnableExtensions() {
await (0, ui_interaction_1.reloadWindow)();
await (0, ui_interaction_1.enableAllExtensions)();
}
/**
* Gets a list of extensions that need to be verified as active
* @param predicate - Optional filter function to apply to the extensions
* @returns Array of extensions that should be verified as active
*/
function getExtensionsToVerifyActive(predicate = ext => !!ext) {
return exports.extensions
.filter(ext => {
return ext.shouldVerifyActivation;
})
.filter(predicate);
}
/**
* Verifies that specified extensions are running in VS Code
* @param extensions - Array of extensions to verify
* @param timeout - Optional timeout (defaults to VERIFY_EXTENSIONS_TIMEOUT)
* @returns True if all extensions are activated successfully
*/
async function verifyExtensionsAreRunning(extensions, timeout = VERIFY_EXTENSIONS_TIMEOUT) {
(0, miscellaneous_1.log)('');
(0, miscellaneous_1.log)(`Starting verifyExtensionsAreRunning()...`);
if (extensions.length === 0) {
(0, miscellaneous_1.log)('verifyExtensionsAreRunning - No extensions to verify, continuing test run w/o extension verification');
return true;
}
const extensionIDsToVerify = extensions.map(extension => extension.extensionId);
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(15));
await (0, ui_interaction_1.zoom)('Out', 4, miscellaneous_1.Duration.seconds(1));
let extensionsStatus = [];
let allActivated = false;
const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('findExtensionsInRunningExtensionsList timeout')), timeout.milliseconds));
try {
await Promise.race([
(async () => {
do {
extensionsStatus = await findExtensionsInRunningExtensionsList(extensionIDsToVerify);
extensions.map(e => {
const found = extensionsStatus.find(es => es.extensionId === e.extensionId);
if (found) {
// Log the current state of the activation check for each extension
(0, miscellaneous_1.log)(`Extension ${found.extensionId}: ${found.activationTime ?? 'No activation time'}`);
}
else {
(0, miscellaneous_1.log)(`Extension ${e.extensionId}: ${e.name} is not activated yet`);
}
});
allActivated = extensionIDsToVerify.every(extensionId => extensionsStatus.find(extensionStatus => extensionStatus.extensionId === extensionId)
?.isActivationComplete);
} while (!allActivated);
})(),
timeoutPromise
]);
}
catch (error) {
(0, miscellaneous_1.log)(`Error while waiting for extensions to activate: ${error}`);
}
await (0, ui_interaction_1.zoomReset)();
(0, miscellaneous_1.log)('... Finished verifyExtensionsAreRunning()');
(0, miscellaneous_1.log)('');
return allActivated;
}
/**
* Finds and returns information about running extensions
* @param extensionIds - Array of extension IDs to look for
* @returns Array of extension activation information
* @throws If the running extensions editor cannot be found
*/
async function findExtensionsInRunningExtensionsList(extensionIds) {
(0, miscellaneous_1.log)('');
(0, miscellaneous_1.log)('Starting findExtensionsInRunningExtensionsList()...');
// This function assumes the Extensions list was opened.
// Close the panel and clear notifications so we can see as many of the running extensions as we can.
try {
const center = await (0, ui_interaction_1.getWorkbench)().openNotificationsCenter();
await center.clearAllNotifications();
await center.close();
}
catch (error) {
if (error instanceof Error) {
(0, miscellaneous_1.log)(`Failed clearing all notifications ${error.message}`);
}
}
const runningExtensionsEditor = await showRunningExtensions();
if (!runningExtensionsEditor) {
throw new Error('Could not find the running extensions editor');
}
// Get all extensions
const allExtensions = await runningExtensionsEditor.findElements(vscode_extension_tester_1.By.css('div.monaco-list-row > div.extension'));
const runningExtensions = [];
for (const extension of allExtensions) {
const parent = await extension.findElement(vscode_extension_tester_1.By.xpath('..'));
const extensionId = await parent.getAttribute('aria-label');
const version = await extension.findElement(vscode_extension_tester_1.By.css('.version')).getText();
const activationTime = await extension.findElement(vscode_extension_tester_1.By.css('.activation-time')).getText();
const isActivationComplete = /\:\s*?[0-9]{1,}ms/.test(activationTime);
let hasBug;
try {
await parent.findElement(vscode_extension_tester_1.By.css('span.codicon-bug error'));
}
catch (error) {
if (error instanceof Error) {
hasBug = error.message.startsWith('no such element') ? false : true;
}
else {
hasBug = true;
}
}
runningExtensions.push({
extensionId,
activationTime,
version,
isPresent: true,
hasBug,
isActivationComplete
});
}
(0, miscellaneous_1.log)('... Finished findExtensionsInRunningExtensionsList()');
(0, miscellaneous_1.log)('');
// limit runningExtensions to those whose property extensionId is in the list of extensionIds
return runningExtensions.filter(extension => extensionIds.includes(extension.extensionId));
}
/**
* Checks for uncaught errors in extensions and fails the test if any are found
*/
async function checkForUncaughtErrors() {
await showRunningExtensions();
// Zoom out so all the extensions are visible
await (0, ui_interaction_1.zoom)('Out', 4, miscellaneous_1.Duration.seconds(1));
const uncaughtErrors = (await findExtensionsInRunningExtensionsList(getExtensionsToVerifyActive().map(ext => ext.extensionId))).filter(ext => ext.hasBug);
await (0, ui_interaction_1.zoomReset)();
uncaughtErrors.forEach(ext => {
(0, miscellaneous_1.log)(`Extension ${ext.extensionId}:${ext.version ?? 'unknown'} has a bug`);
});
(0, chai_1.expect)(uncaughtErrors.length).equal(0);
}
//# sourceMappingURL=extensionUtils.js.map