Files
vscode/test/automation/src/quickaccess.ts
Sandeep Somavarapu bee0ea18bd Move wait for workbench restoration to Application (#197053)
#195850: Move wait for workbench restoration to
Application#checkWindowReady
2023-10-31 09:00:40 +01:00

244 lines
7.3 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Editors } from './editors';
import { Code } from './code';
import { QuickInput } from './quickinput';
import { basename, isAbsolute } from 'path';
enum QuickAccessKind {
Files = 1,
Commands,
Symbols
}
export class QuickAccess {
constructor(private code: Code, private editors: Editors, private quickInput: QuickInput) { }
async openFileQuickAccessAndWait(searchValue: string, expectedFirstElementNameOrExpectedResultCount: string | number): Promise<void> {
// make sure the file quick access is not "polluted"
// with entries from the editor history when opening
await this.runCommand('workbench.action.clearEditorHistory');
const PollingStrategy = {
Stop: true,
Continue: false
};
let retries = 0;
let success = false;
while (++retries < 10) {
let retry = false;
try {
await this.openQuickAccessWithRetry(QuickAccessKind.Files, searchValue);
await this.quickInput.waitForQuickInputElements(elementNames => {
this.code.logger.log('QuickAccess: resulting elements: ', elementNames);
// Quick access seems to be still running -> retry
if (elementNames.length === 0) {
this.code.logger.log('QuickAccess: file search returned 0 elements, will continue polling...');
return PollingStrategy.Continue;
}
// Quick access does not seem healthy/ready -> retry
const firstElementName = elementNames[0];
if (firstElementName === 'No matching results') {
this.code.logger.log(`QuickAccess: file search returned "No matching results", will retry...`);
retry = true;
return PollingStrategy.Stop;
}
// Expected: number of results
if (typeof expectedFirstElementNameOrExpectedResultCount === 'number') {
if (elementNames.length === expectedFirstElementNameOrExpectedResultCount) {
success = true;
return PollingStrategy.Stop;
}
this.code.logger.log(`QuickAccess: file search returned ${elementNames.length} results but was expecting ${expectedFirstElementNameOrExpectedResultCount}, will retry...`);
retry = true;
return PollingStrategy.Stop;
}
// Expected: string
else {
if (firstElementName === expectedFirstElementNameOrExpectedResultCount) {
success = true;
return PollingStrategy.Stop;
}
this.code.logger.log(`QuickAccess: file search returned ${firstElementName} as first result but was expecting ${expectedFirstElementNameOrExpectedResultCount}, will retry...`);
retry = true;
return PollingStrategy.Stop;
}
});
} catch (error) {
this.code.logger.log(`QuickAccess: file search waitForQuickInputElements threw an error ${error}, will retry...`);
retry = true;
}
if (!retry) {
break;
}
await this.quickInput.closeQuickInput();
}
if (!success) {
if (typeof expectedFirstElementNameOrExpectedResultCount === 'string') {
throw new Error(`Quick open file search was unable to find '${expectedFirstElementNameOrExpectedResultCount}' after 10 attempts, giving up.`);
} else {
throw new Error(`Quick open file search was unable to find ${expectedFirstElementNameOrExpectedResultCount} result items after 10 attempts, giving up.`);
}
}
}
async openFile(path: string): Promise<void> {
if (!isAbsolute(path)) {
// we require absolute paths to get a single
// result back that is unique and avoid hitting
// the search process to reduce chances of
// search needing longer.
throw new Error('QuickAccess.openFile requires an absolute path');
}
const fileName = basename(path);
// quick access shows files with the basename of the path
await this.openFileQuickAccessAndWait(path, basename(path));
// open first element
await this.quickInput.selectQuickInputElement(0);
// wait for editor being focused
await this.editors.waitForActiveTab(fileName);
await this.editors.selectTab(fileName);
}
private async openQuickAccessWithRetry(kind: QuickAccessKind, value?: string): Promise<void> {
let retries = 0;
// Other parts of code might steal focus away from quickinput :(
while (retries < 5) {
// Open via keybinding
switch (kind) {
case QuickAccessKind.Files:
await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+p' : 'ctrl+p');
break;
case QuickAccessKind.Symbols:
await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+shift+o' : 'ctrl+shift+o');
break;
case QuickAccessKind.Commands:
await this.code.dispatchKeybinding(process.platform === 'darwin' ? 'cmd+shift+p' : 'ctrl+shift+p');
break;
}
// Await for quick input widget opened
try {
await this.quickInput.waitForQuickInputOpened(10);
break;
} catch (err) {
if (++retries > 5) {
throw new Error(`QuickAccess.openQuickAccessWithRetry(kind: ${kind}) failed: ${err}`);
}
// Retry
await this.code.dispatchKeybinding('escape');
}
}
// Type value if any
if (value) {
await this.quickInput.type(value);
}
}
async runCommand(commandId: string, options?: { keepOpen?: boolean; exactLabelMatch?: boolean }): Promise<void> {
const keepOpen = options?.keepOpen;
const exactLabelMatch = options?.exactLabelMatch;
const openCommandPalletteAndTypeCommand = async (): Promise<boolean> => {
// open commands picker
await this.openQuickAccessWithRetry(QuickAccessKind.Commands, `>${commandId}`);
// wait for best choice to be focused
await this.quickInput.waitForQuickInputElementFocused();
// Retry for as long as the command not found
const text = await this.quickInput.waitForQuickInputElementText();
if (text === 'No matching commands' || (exactLabelMatch && text !== commandId)) {
return false;
}
return true;
};
let hasCommandFound = await openCommandPalletteAndTypeCommand();
if (!hasCommandFound) {
this.code.logger.log(`QuickAccess: No matching commands, will retry...`);
await this.quickInput.closeQuickInput();
let retries = 0;
while (++retries < 5) {
hasCommandFound = await openCommandPalletteAndTypeCommand();
if (hasCommandFound) {
break;
} else {
this.code.logger.log(`QuickAccess: No matching commands, will retry...`);
await this.quickInput.closeQuickInput();
await this.code.wait(1000);
}
}
if (!hasCommandFound) {
throw new Error(`QuickAccess.runCommand(commandId: ${commandId}) failed to find command.`);
}
}
// wait and click on best choice
await this.quickInput.selectQuickInputElement(0, keepOpen);
}
async openQuickOutline(): Promise<void> {
let retries = 0;
while (++retries < 10) {
// open quick outline via keybinding
await this.openQuickAccessWithRetry(QuickAccessKind.Symbols);
const text = await this.quickInput.waitForQuickInputElementText();
// Retry for as long as no symbols are found
if (text === 'No symbol information for the file') {
this.code.logger.log(`QuickAccess: openQuickOutline indicated 'No symbol information for the file', will retry...`);
// close and retry
await this.quickInput.closeQuickInput();
continue;
}
}
}
}