@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
202 lines • 10.6 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
*/
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;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.selectOutputChannel = selectOutputChannel;
exports.getOutputViewText = getOutputViewText;
exports.verifyOutputPanelText = verifyOutputPanelText;
exports.attemptToFindOutputPanelText = attemptToFindOutputPanelText;
exports.getOperationTime = getOperationTime;
exports.clearOutputView = clearOutputView;
const miscellaneous_1 = require("../core/miscellaneous");
const notifications_1 = require("./notifications");
const commandPrompt_1 = require("./commandPrompt");
const vscode_extension_tester_1 = require("vscode-extension-tester");
const chai_1 = require("chai");
const retryUtils_1 = require("../retryUtils");
async function selectOutputChannel(name) {
// Wait for all notifications to go away. If there is a notification that is overlapping and hiding the Output channel's
// dropdown menu, calling select.click() doesn't work, so dismiss all notifications first before clicking the dropdown
// menu and opening it.
await (0, notifications_1.dismissAllNotifications)();
// Find the given channel in the Output view
const outputView = await new vscode_extension_tester_1.BottomBarPanel().openOutputView();
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(1));
if (!!name) {
await outputView.selectChannel(name);
}
return outputView;
}
async function getOutputViewText(outputChannelName = '') {
// Set the output channel, but only if the value is passed in.
const outputView = await selectOutputChannel(outputChannelName);
// Set focus to the contents in the Output panel.
await (0, commandPrompt_1.executeQuickPick)('Output: Focus on Output View', miscellaneous_1.Duration.seconds(2));
try {
return await outputView.getText();
}
catch (error) {
// Fallback when outputView.getText() fails
(0, miscellaneous_1.log)(`outputView.getText() failed, trying fallback selector: ${error.message}`);
const clipboard = (await Promise.resolve().then(() => __importStar(require('clipboardy')))).default;
let originalClipboard = '';
try {
originalClipboard = clipboard.readSync();
}
catch (clipboardError) {
// workaround issue when clipboard is empty
}
try {
// Try to find the textarea using the fallback selector
const textarea = await outputView.findElement(vscode_extension_tester_1.By.css('.inputarea.monaco-mouse-cursor-text, .inputarea textarea, .monaco-editor textarea'));
// Select all text and copy to clipboard
await textarea.sendKeys(vscode_extension_tester_1.Key.chord(process.platform === 'darwin' ? vscode_extension_tester_1.Key.META : vscode_extension_tester_1.Key.CONTROL, 'a'));
await textarea.sendKeys(vscode_extension_tester_1.Key.chord(process.platform === 'darwin' ? vscode_extension_tester_1.Key.META : vscode_extension_tester_1.Key.CONTROL, 'c'));
const text = clipboard.readSync();
// Restore original clipboard content
if (originalClipboard.length > 0) {
clipboard.writeSync(originalClipboard);
}
return text;
}
catch (fallbackError) {
(0, miscellaneous_1.log)(`Fallback getText also failed: ${fallbackError.message}`);
// If fallback also fails, rethrow the original error
throw error;
}
}
}
/**
* Verifies that the output panel contains all expected text snippets.
*
* @param {string} outputPanelText - The output panel text as a string that needs to be verified.
* @param {string[]} expectedTexts - An array of strings representing the expected text snippets that should be present in the output panel.
*
* @example
* await verifyOutputPanelText(
* testResult,
* [
* '=== Test Summary',
* 'Outcome Passed',
* 'Tests Ran 1',
* 'Pass Rate 100%',
* 'TEST NAME',
* 'ExampleTest1 Pass',
* 'Ended SFDX: Run Apex Tests'
* ]
* );
*/
async function verifyOutputPanelText(outputPanelText, expectedTexts) {
(0, miscellaneous_1.log)(`verifyOutputPanelText() - ${outputPanelText}`);
for (const expectedText of expectedTexts) {
(0, miscellaneous_1.log)(`Expected text:\n ${expectedText}`);
(0, chai_1.expect)(outputPanelText).to.include(expectedText);
}
}
// If found, this function returns the entire text that's in the Output panel.
async function attemptToFindOutputPanelText(outputChannelName, searchString, attempts = 10) {
(0, miscellaneous_1.log)(`attemptToFindOutputPanelText in channel "${outputChannelName}: with string "${searchString}"`);
return await (0, retryUtils_1.retryOperation)(async () => {
const outputViewText = await getOutputViewText(outputChannelName);
(0, miscellaneous_1.log)(`outputViewText:\n ${outputViewText}`);
if (outputViewText.includes(searchString)) {
return outputViewText;
}
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(2));
throw new Error(`Failed to find output panel text: ${searchString} in ${outputChannelName} on attempt ${attempts}`);
}, attempts, 'Failed to find output panel text');
}
async function getOperationTime(outputText) {
const tRegex = /((?<hours>\d+):(?<minutes>\d+):(?<seconds>\d+)(?<secondFraction>\.\d+))/g;
let matches;
const times = [];
while ((matches = tRegex.exec(outputText)) !== null) {
if (matches.groups) {
const { hours, minutes, seconds, secondFraction } = matches.groups;
const time = new Date(1970, 0, 1, Number(hours), Number(minutes), Number(seconds), Number(secondFraction) * 1000);
times.push(time);
}
}
if (times.length < 2) {
return 'Insufficient timestamps found.';
}
const [startTime, endTime] = times;
let diff = endTime.getTime() - startTime.getTime();
const hours = Math.floor(diff / 3600000); // 1000 * 60 * 60
diff %= 3600000;
const minutes = Math.floor(diff / 60000); // 1000 * 60
diff %= 60000;
const seconds = Math.floor(diff / 1000);
const milliseconds = diff % 1000;
return `${formatTimeComponent(hours)}:${formatTimeComponent(minutes)}:${formatTimeComponent(seconds)}.${formatTimeComponent(milliseconds, 3)}`;
}
async function clearOutputView(wait = miscellaneous_1.Duration.seconds(1)) {
if (process.platform === 'linux') {
// In Linux, clear the output by clicking the "Clear Output" button in the Output Tab
// Use retry logic with longer wait for Linux due to timing issues
(0, miscellaneous_1.log)('clearOutputView() - Linux: Attempting to find and click clear button with retries');
await (0, retryUtils_1.retryOperation)(async () => {
(0, miscellaneous_1.log)('clearOutputView() - Linux: Looking for clear button...');
const outputView = await new vscode_extension_tester_1.BottomBarPanel().openOutputView();
// Wait a bit longer for UI to be ready on Linux
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(2));
const clearButton = await outputView.findElement(vscode_extension_tester_1.By.className('codicon-clear-all'));
(0, miscellaneous_1.log)('clearOutputView() - Linux: Clear button found, attempting to click');
// Try multiple click methods for better Linux compatibility
try {
await outputView.getDriver().executeScript('arguments[0].click();', clearButton);
(0, miscellaneous_1.log)('clearOutputView() - Linux: Click via executeScript successful');
}
catch (scriptError) {
(0, miscellaneous_1.log)(`clearOutputView() - Linux: executeScript failed, trying direct click: ${scriptError}`);
await clearButton.click();
(0, miscellaneous_1.log)('clearOutputView() - Linux: Direct click successful');
}
// Wait a moment to ensure the action completes
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(1));
// Verify the output was actually cleared by checking if there's minimal content
const outputText = await outputView.getText();
if (outputText.trim().length > 100) {
(0, miscellaneous_1.log)(`clearOutputView() - Linux: Output not cleared (${outputText.length} chars), retrying...`);
throw new Error('Output view was not cleared successfully');
}
(0, miscellaneous_1.log)('clearOutputView() - Linux: Output cleared successfully');
}, 3, 'Failed to clear output view on Linux after retries');
}
else {
// In Mac and Windows, clear the output by calling the "View: Clear Output" command in the command palette
(0, miscellaneous_1.log)('clearOutputView() - Mac/Windows: Using command palette to clear output');
await (0, commandPrompt_1.executeQuickPick)('View: Clear Output', wait);
}
}
function formatTimeComponent(component, padLength = 2) {
return component.toString().padStart(padLength, '0');
}
//# sourceMappingURL=outputView.js.map