@salesforce/salesforcedx-vscode-test-tools
Version:
Test automation framework for Salesforce Extensions for VS Code
341 lines • 17.8 kB
JavaScript
;
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.moveCursorWithFallback = void 0;
exports.getTextEditor = getTextEditor;
exports.checkFileOpen = checkFileOpen;
exports.waitForFileOpen = waitForFileOpen;
exports.attemptToFindTextEditorText = attemptToFindTextEditorText;
exports.overrideTextInFile = overrideTextInFile;
exports.waitForAndGetCodeLens = waitForAndGetCodeLens;
exports.replaceLineInFile = replaceLineInFile;
const vscode_extension_tester_1 = require("vscode-extension-tester");
const commandPrompt_1 = require("./commandPrompt");
const miscellaneous_1 = require("../core/miscellaneous");
const workbench_1 = require("./workbench");
const retryUtils_1 = require("../retryUtils");
const fs = __importStar(require("fs/promises"));
const fileSystem_1 = require("../system-operations/fileSystem");
/**
* Gets a text editor for a specific file
* @param workbench - The VSCode workbench instance
* @param fileName - Name of the file to open and edit
* @returns A TextEditor instance for the specified file
*/
async function getTextEditor(workbench, fileName) {
(0, miscellaneous_1.log)(`calling getTextEditor(${fileName})`);
(0, miscellaneous_1.log)('getTextEditor() - Attempting to open file');
const inputBox = await (0, commandPrompt_1.executeQuickPick)('Go to File...', miscellaneous_1.Duration.seconds(1));
(0, miscellaneous_1.log)('getTextEditor() - executeQuickPick() - inputBox');
await inputBox.setText(fileName);
(0, miscellaneous_1.log)(`getTextEditor() - executeQuickPick() - inputBox.setText(${fileName})`);
await inputBox.confirm();
(0, miscellaneous_1.log)('getTextEditor() - executeQuickPick() - inputBox.confirm()');
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(1));
(0, miscellaneous_1.log)('getTextEditor() - File opened, getting editor view');
const editorView = workbench.getEditorView();
// Add retry logic specifically around the openEditor call that fails
const textEditor = await (0, retryUtils_1.retryOperation)(async () => {
(0, miscellaneous_1.log)('getTextEditor() - Attempting to open editor...');
return (await editorView.openEditor(fileName));
}, 3, 'Failed to open editor after retries');
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(2));
return textEditor;
}
/**
* Checks if a file is open in the editor
* @param workbench - The VSCode workbench instance
* @param name - The name of the file to check
* @param options - Optional configuration including timeout and error message
* @throws Timeout error if the file isn't open within the specified timeout
*/
async function checkFileOpen(workbench, name, options = { timeout: miscellaneous_1.Duration.milliseconds(30_000) }) {
await (0, workbench_1.getBrowser)().wait(async () => {
try {
const editorView = workbench.getEditorView();
const activeTab = await editorView.getActiveTab();
if (activeTab != undefined && name == (await activeTab.getTitle())) {
return true;
}
else
return false;
}
catch (error) {
return false;
}
}, options.timeout?.milliseconds, options.msg ?? `Expected to find file ${name} open in TextEditor before ${options.timeout}`, 500 // Check every 500 ms
);
}
/**
* Waits for a file to be open in the editor with a 30-second timeout
* @param workbench - The VSCode workbench instance
* @param fileName - Name of the file to wait for
* @returns Promise that resolves when file is open or rejects on timeout
*/
async function waitForFileOpen(workbench, fileName) {
const timeout = 20_000; // 20 seconds
const checkInterval = 500; // Check every 500ms
const startTime = Date.now();
const endTime = startTime + timeout;
let attemptCount = 0;
(0, miscellaneous_1.log)(`waitForFileOpen() - Starting wait for file: ${fileName}`);
(0, miscellaneous_1.log)(`waitForFileOpen() - Timeout: ${timeout}ms, Check interval: ${checkInterval}ms`);
while (Date.now() < endTime) {
attemptCount++;
const currentTime = Date.now();
const elapsedTime = currentTime - startTime;
const remainingTime = endTime - currentTime;
(0, miscellaneous_1.log)(`waitForFileOpen() - Attempt ${attemptCount}, Elapsed: ${elapsedTime}ms, Remaining: ${remainingTime}ms`);
try {
const editorView = workbench.getEditorView();
(0, miscellaneous_1.log)(`waitForFileOpen() - Got editor view`);
// Try to get the active tab with a shorter timeout
let activeTab;
try {
// Use a promise race to implement our own timeout for getActiveTab
activeTab = await Promise.race([
editorView.getActiveTab(),
new Promise((_, reject) => setTimeout(() => reject(new Error('getActiveTab timeout')), 5000))
]);
}
catch (tabError) {
(0, miscellaneous_1.log)(`waitForFileOpen() - getActiveTab failed: ${tabError}`);
// Try alternative approach: check if any tabs exist
try {
const tabs = await editorView.getOpenTabs();
(0, miscellaneous_1.log)(`waitForFileOpen() - Found ${tabs.length} open tabs`);
if (tabs.length > 0) {
// Try to find our target file in the open tabs
for (const tab of tabs) {
try {
const tabTitle = await tab.getTitle();
(0, miscellaneous_1.log)(`waitForFileOpen() - Checking tab: "${tabTitle}"`);
if (tabTitle === fileName) {
(0, miscellaneous_1.log)(`waitForFileOpen() - SUCCESS: Found target file in open tabs after ${elapsedTime}ms and ${attemptCount} attempts`);
return;
}
}
catch (titleError) {
(0, miscellaneous_1.log)(`waitForFileOpen() - Error getting tab title: ${titleError}`);
}
}
}
}
catch (tabsError) {
(0, miscellaneous_1.log)(`waitForFileOpen() - Error getting open tabs: ${tabsError}`);
}
// Continue to next iteration if all approaches failed
(0, miscellaneous_1.log)(`waitForFileOpen() - All tab approaches failed, continuing to wait...`);
await new Promise(resolve => setTimeout(resolve, checkInterval));
continue;
}
if (activeTab) {
const tabTitle = await activeTab.getTitle();
(0, miscellaneous_1.log)(`waitForFileOpen() - Active tab title: "${tabTitle}", Expected: "${fileName}"`);
if (tabTitle === fileName) {
(0, miscellaneous_1.log)(`waitForFileOpen() - SUCCESS: File ${fileName} is now open in editor after ${elapsedTime}ms and ${attemptCount} attempts`);
return; // File is open, success!
}
else {
(0, miscellaneous_1.log)(`waitForFileOpen() - File not matched, continuing to wait...`);
}
}
else {
(0, miscellaneous_1.log)(`waitForFileOpen() - No active tab found, continuing to wait...`);
}
}
catch (error) {
// Continue checking if there's an error getting editor state
(0, miscellaneous_1.log)(`waitForFileOpen() - Error checking file open status (attempt ${attemptCount}): ${error}`);
}
// Wait before next check
(0, miscellaneous_1.log)(`waitForFileOpen() - Waiting ${checkInterval}ms before next check...`);
await new Promise(resolve => setTimeout(resolve, checkInterval));
}
// If we reach here, timeout occurred
const totalElapsed = Date.now() - startTime;
const errorMsg = `Timeout: File ${fileName} was not found open in editor after ${totalElapsed}ms (${attemptCount} attempts)`;
(0, miscellaneous_1.log)(`waitForFileOpen() - TIMEOUT: ${errorMsg}`);
throw new Error(errorMsg);
}
/**
* Opens a file and retrieves its text content
* @param filePath - The path to the file to open
* @returns The text content of the file
*/
async function attemptToFindTextEditorText(filePath) {
await (0, miscellaneous_1.openFile)(filePath);
const fileName = filePath.substring(filePath.lastIndexOf(process.platform === 'win32' ? '\\' : '/') + 1);
const editorView = new vscode_extension_tester_1.EditorView();
const editor = await editorView.openEditor(fileName);
return await editor.getText();
}
/**
* Overwrites the entire content of a file using filesystem operations
* @param textEditor - The text editor instance containing the file to modify
* @param classText - The new content to write to the file
* @throws Error if file write operation fails or content verification fails
*/
async function overrideTextInFile(textEditor, classText) {
// Use fs.writeFileSync() to write the new content to the file
try {
const filePath = await textEditor.getFilePath();
// Write new content to file
(0, miscellaneous_1.log)(`overrideTextInFile() - Writing content to file: ${filePath}`);
(0, fileSystem_1.createOrOverwriteFile)(filePath, classText);
(0, miscellaneous_1.log)(`overrideTextInFile() - Successfully wrote ${classText.length} characters to ${filePath}`);
// Give the editor time to detect the file change and reload
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.seconds(1));
// Verify the write was successful by reading it back
const writtenContent = await fs.readFile(filePath, 'utf8');
const normalizeText = (str) => str.replace(/\s+/g, '');
const normalizedWritten = normalizeText(writtenContent);
const normalizedExpected = normalizeText(classText);
if (normalizedWritten !== normalizedExpected) {
throw new Error(`File write verification failed. Written content does not match expected content.`);
}
(0, miscellaneous_1.log)('overrideTextInFile() - File content verified successfully');
}
catch (error) {
(0, miscellaneous_1.log)(`overrideTextInFile() - File system operation failed: ${error}`);
throw new Error(`File system text replacement failed: ${error}`);
}
}
async function waitForAndGetCodeLens(textEditor, codeLensName) {
(0, miscellaneous_1.log)(`waitForAndGetCodeLens() - Waiting for code lens: ${codeLensName}`);
return await (0, retryUtils_1.retryOperation)(async () => {
const lens = await textEditor.getCodeLens(codeLensName);
if (!lens) {
(0, miscellaneous_1.log)(`waitForAndGetCodeLens() - Code lens ${codeLensName} NOT FOUND`);
throw new Error(`Code lens ${codeLensName} not found`);
}
(0, miscellaneous_1.log)(`waitForAndGetCodeLens() - Code lens ${codeLensName} found`);
return lens;
}, 3);
}
/**
* Wrapper function for moveCursor with fallback mechanism using inputarea monaco-mouse-cursor-text selector
* @param textEditor - The text editor instance
* @param line - The line number to move to
* @param column - The column number to move to
*/
const moveCursorWithFallback = async (textEditor, line, column) => {
try {
// First try the original moveCursor approach
await textEditor.moveCursor(line, column);
(0, miscellaneous_1.log)(`moveCursorWithFallback() - Successfully moved cursor to line ${line}, column ${column} using original method`);
}
catch (error) {
// Fallback when moveCursor fails due to .native-edit-context selector issues
(0, miscellaneous_1.log)(`moveCursorWithFallback() - Original moveCursor failed, trying fallback approach: ${String(error)}`);
try {
// Find the editor element using the fallback selector
const browser = (0, workbench_1.getBrowser)();
const editorElement = await browser.findElement(vscode_extension_tester_1.By.css('.inputarea.monaco-mouse-cursor-text'));
if (!editorElement) {
throw new Error('Could not find editor element using fallback selector .inputarea.monaco-mouse-cursor-text');
}
(0, miscellaneous_1.log)('moveCursorWithFallback() - Found editor element using fallback selector');
// Get current coordinates (fallback method)
const getCurrentCoordinates = async () => {
try {
return await textEditor.getCoordinates();
}
catch (coordError) {
(0, miscellaneous_1.log)(`moveCursorWithFallback() - getCoordinates() failed: ${String(coordError)}`);
// Return a default position if coordinates can't be retrieved
return [1, 1];
}
};
// Check if we're at the target line
const isAtLine = async (targetLine) => {
try {
const coords = await getCurrentCoordinates();
return coords[0] === targetLine;
}
catch {
return false;
}
};
// Check if we're at the target column
const isAtColumn = async (targetColumn) => {
try {
const coords = await getCurrentCoordinates();
return coords[1] === targetColumn;
}
catch {
return false;
}
};
// Move to target line (mimic moveCursorToLine)
const coordinates = await getCurrentCoordinates();
const lineGap = coordinates[0] - line;
const lineKey = lineGap >= 0 ? vscode_extension_tester_1.Key.UP : vscode_extension_tester_1.Key.DOWN;
for (let i = 0; i < Math.abs(lineGap); i++) {
if (await isAtLine(line)) {
break;
}
await editorElement.sendKeys(lineKey);
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.milliseconds(50));
}
(0, miscellaneous_1.log)(`moveCursorWithFallback() - Successfully moved cursor to line ${line} using fallback method`);
// Move to target column (mimic moveCursorToColumn)
const currentCoords = await getCurrentCoordinates();
const columnGap = currentCoords[1] - column;
const columnKey = columnGap >= 0 ? vscode_extension_tester_1.Key.LEFT : vscode_extension_tester_1.Key.RIGHT;
for (let i = 0; i < Math.abs(columnGap); i++) {
if (await isAtColumn(column)) {
break;
}
await editorElement.sendKeys(columnKey);
await (0, miscellaneous_1.pause)(miscellaneous_1.Duration.milliseconds(50));
// Check if we moved to a different line (mimic original error handling)
const newCoords = await getCurrentCoordinates();
if (newCoords[0] !== currentCoords[0]) {
throw new Error(`Column number ${column} is not accessible on line ${currentCoords[0]}`);
}
}
(0, miscellaneous_1.log)(`moveCursorWithFallback() - Successfully moved cursor to column ${column} using fallback method`);
}
catch (fallbackError) {
(0, miscellaneous_1.log)(`moveCursorWithFallback() - Fallback approach also failed: ${String(fallbackError)}`);
throw new Error(`Failed to move cursor using both original and fallback methods: ${String(fallbackError)}`);
}
}
};
exports.moveCursorWithFallback = moveCursorWithFallback;
/** Replace a specific line in a file using fs operations
* @param filePath - The path to the file to modify
* @param lineNumber - The line number to replace (1-based index)
* @param newContent - The new content to write
*/
async function replaceLineInFile(filePath, lineNumber, newContent) {
const fileContent = await fs.readFile(filePath, 'utf8');
const lines = fileContent.split('\n');
// Replace the specific line (1-based to 0-based index)
lines[lineNumber - 1] = newContent;
(0, fileSystem_1.createOrOverwriteFile)(filePath, lines.join('\n'));
}
//# sourceMappingURL=textEditorView.js.map