@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
242 lines (219 loc) • 8.4 kB
text/typescript
/*
* 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
*/
import { Duration, log, pause } from '../core/miscellaneous';
import { retryOperation } from '../retryUtils';
import { getBrowser, getWorkbench } from './workbench';
import { By, InputBox, Key, QuickOpenBox, Workbench } from 'vscode-extension-tester';
/**
* Opens the command prompt and enters a command
* @param workbench - The VSCode workbench instance
* @param command - The command to enter in the prompt
* @returns The InputBox or QuickOpenBox representing the command prompt
*/
export async function openCommandPromptWithCommand(
workbench: Workbench,
command: string
): Promise<InputBox | QuickOpenBox> {
const prompt = await (await workbench.openCommandPrompt()).wait();
await (await prompt.wait()).setText(`>${command}`);
return prompt;
}
/**
* Runs a command from the command prompt
* @param workbench - The VSCode workbench instance
* @param command - The command to run
* @param durationInSeconds - Optional duration to wait after running the command
* @returns The InputBox or QuickOpenBox representing the command prompt
*/
export async function runCommandFromCommandPrompt(
workbench: Workbench,
command: string,
durationInSeconds: Duration = Duration.seconds(0)
): Promise<InputBox | QuickOpenBox> {
const prompt = await (await openCommandPromptWithCommand(workbench, command)).wait();
await selectQuickPickItem(prompt, command);
if (durationInSeconds.milliseconds > 0) {
await pause(durationInSeconds);
}
return prompt;
}
/**
* Selects a quick pick option with the specified text
* @param prompt - The command prompt
* @param text - The text of the quick pick option to select
*/
export async function selectQuickPickWithText(prompt: InputBox | QuickOpenBox, text: string) {
// Set the text in the command prompt. Only selectQuickPick() needs to be called, but setting
// the text in the command prompt is a nice visual feedback to anyone watching the tests run.
await prompt.setText(text);
await pause(Duration.seconds(1));
await prompt.selectQuickPick(text);
await pause(Duration.seconds(1));
// After the text has been entered and selectQuickPick() is called, you might see the last few characters
// in the input box be deleted. This is b/c selectQuickPick() calls resetPosition(), which for some reason
// deletes the last two characters. This doesn't seem to affect the outcome though.
}
/**
* Selects a quick pick item with the exact text
* @param prompt - The command prompt
* @param text - The exact text of the quick pick item to select
* @throws Error if the prompt is undefined or if the quick pick item is not found
*/
export async function selectQuickPickItem(prompt: InputBox | QuickOpenBox | undefined, text: string): Promise<void> {
if (!prompt) {
throw new Error('Prompt cannot be undefined');
}
const quickPick = await prompt.findQuickPick(text);
if (!quickPick || (await quickPick.getLabel()) !== text) {
throw new Error(`Quick pick item ${text} was not found`);
}
await quickPick.select();
await pause(Duration.seconds(1));
}
/**
* Finds a quick pick item in the command prompt
* @param inputBox - The command prompt
* @param quickPickItemTitle - The text to search for in quick pick items
* @param useExactMatch - If true, looks for exact match; if false, checks if item contains the text
* @param selectTheQuickPickItem - If true, selects the item when found
* @returns True if the item was found, false otherwise
*/
export async function findQuickPickItem(
inputBox: InputBox | QuickOpenBox | undefined,
quickPickItemTitle: string,
useExactMatch: boolean,
selectTheQuickPickItem: boolean
): Promise<boolean> {
if (!inputBox) {
return false;
}
// Type the text into the filter. Do this in case the pick list is long and
// the target item is not visible (and one needs to scroll down to see it).
await inputBox.setText(quickPickItemTitle);
await pause(Duration.seconds(1));
let itemWasFound = false;
const quickPicks = await inputBox.getQuickPicks();
for (const quickPick of quickPicks) {
const label = await quickPick.getLabel();
if (useExactMatch && label === quickPickItemTitle) {
itemWasFound = true;
} else if (!useExactMatch && label.includes(quickPickItemTitle)) {
itemWasFound = true;
}
if (itemWasFound) {
if (selectTheQuickPickItem) {
await quickPick.select();
await pause(Duration.seconds(1));
}
return true;
}
}
return false;
}
/**
* Waits for a quick pick item to appear in the command prompt
* @param prompt - The command prompt
* @param pickListItem - The text of the quick pick item to wait for
* @param options - Optional configuration including timeout and error message
*/
export async function waitForQuickPick(
prompt: InputBox | QuickOpenBox | undefined,
pickListItem: string,
options: { msg?: string; timeout?: Duration } = { timeout: Duration.milliseconds(10_000) }
) {
await getBrowser().wait(
async () => {
try {
await findQuickPickItem(prompt, pickListItem, false, true);
return true;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (_) {
return false;
}
},
options.timeout?.milliseconds,
options.msg ?? `Expected to find option ${pickListItem} before ${options.timeout} milliseconds`,
500 // Check every 500 ms
);
}
/**
* Runs exact command from command palette
* @param command - The command to execute
* @param wait - Duration to wait after executing the command; default is 1 second
* @returns The command prompt interface
* @throws Error if the command is not found or execution fails
*/
export async function executeQuickPick(
command: string,
wait: Duration = Duration.seconds(1)
): Promise<InputBox | QuickOpenBox> {
log(`executeQuickPick command: ${command}`);
return await retryOperation(async () => {
const workbench = getWorkbench();
const inputBox = await workbench.openCommandPrompt();
await pause(Duration.seconds(2));
await inputBox.wait();
await inputBox.setText(`>${command}`);
await inputBox.selectQuickPick(command);
await pause(wait);
log(`executeQuickPick command: ${command} - done`);
return inputBox;
},
3,
`executeQuickPick command: ${command} - failed`
);
}
/**
* Clicks the OK button in a file path dialog
* Also handles the Overwrite confirmation if a folder already exists
* @throws Error if the OK button is not found
*/
export async function clickFilePathOkButton(): Promise<void> {
const browser = getBrowser();
const okButton = await browser.findElement(
By.css('*:not([style*="display: none"]).quick-input-action .monaco-button')
);
if (!okButton) {
throw new Error('Ok button not found');
}
await pause(Duration.milliseconds(500));
await okButton.sendKeys(Key.ENTER);
await pause(Duration.seconds(1));
const buttons = await browser.findElements(By.css('a.monaco-button.monaco-text-button'));
for (const item of buttons) {
const text = await item.getText();
if (text.includes('Overwrite')) {
log('clickFilePathOkButton() - folder already exists');
await browser.wait(
async () => (await item.isDisplayed()) && (await item.isEnabled()),
Duration.seconds(5).milliseconds,
`Overwrite button not clickable within 5 seconds`
);
await item.click();
break;
}
}
await pause(Duration.seconds(2));
}
/**
* Checks if a VSCode command is available.
* @param commandName - Name of the VSCode command to check
* @returns boolean - true if the command is available, false otherwise
*/
export const isCommandAvailable = async (commandName: string): Promise<boolean> => {
log('Checking if command is available: ' + commandName);
const workbench = getWorkbench();
const prompt = await workbench.openCommandPrompt();
await prompt.setText(`>${commandName}`);
const availableCommands = await prompt.getQuickPicks();
for (const item of availableCommands) {
if ((await item.getLabel()) === commandName) {
return true;
}
}
return false;
};