Skip to content

Commit

Permalink
Adding the Java Inspector command across the board - opening from the…
Browse files Browse the repository at this point in the history
… side panel & command palette. Fixing small issues in API. Removing dead code related to old inspector
  • Loading branch information
custompointofview committed Mar 5, 2024
1 parent 0f02b36 commit 07845ed
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 481 deletions.
7 changes: 7 additions & 0 deletions robocorp-code/codegen/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,13 @@ def __init__(
server_handled=False,
icon="$(add)",
),
Command(
"robocorp.newRobocorpInspectorJava",
"Add Java Locator",
add_to_package_json=True,
server_handled=False,
icon="$(add)",
),
Command(
"robocorp.openPlaywrightRecorder",
"Open Playwright Recorder",
Expand Down
9 changes: 8 additions & 1 deletion robocorp-code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"onCommand:robocorp.newRobocorpInspectorBrowser",
"onCommand:robocorp.newRobocorpInspectorWindows",
"onCommand:robocorp.newRobocorpInspectorImage",
"onCommand:robocorp.newRobocorpInspectorJava",
"onCommand:robocorp.openPlaywrightRecorder",
"onCommand:robocorp.openPlaywrightRecorder.internal",
"onCommand:robocorp.editRobocorpInspectorLocator",
Expand Down Expand Up @@ -519,6 +520,12 @@
"category": "Robocorp",
"icon": "$(add)"
},
{
"command": "robocorp.newRobocorpInspectorJava",
"title": "Add Java Locator",
"category": "Robocorp",
"icon": "$(add)"
},
{
"command": "robocorp.openPlaywrightRecorder",
"title": "Open Playwright Recorder",
Expand Down Expand Up @@ -1314,4 +1321,4 @@
"vscode-languageclient": "^7.0.0",
"vscode-nls": "^4.1.2"
}
}
}
1 change: 1 addition & 0 deletions robocorp-code/src/robocorp_code/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
ROBOCORP_NEW_ROBOCORP_INSPECTOR_BROWSER = "robocorp.newRobocorpInspectorBrowser" # Add Web Locator
ROBOCORP_NEW_ROBOCORP_INSPECTOR_WINDOWS = "robocorp.newRobocorpInspectorWindows" # Add Windows Locator
ROBOCORP_NEW_ROBOCORP_INSPECTOR_IMAGE = "robocorp.newRobocorpInspectorImage" # Add Image Locator
ROBOCORP_NEW_ROBOCORP_INSPECTOR_JAVA = "robocorp.newRobocorpInspectorJava" # Add Java Locator
ROBOCORP_OPEN_PLAYWRIGHT_RECORDER = "robocorp.openPlaywrightRecorder" # Open Playwright Recorder
ROBOCORP_OPEN_PLAYWRIGHT_RECORDER_INTERNAL = "robocorp.openPlaywrightRecorder.internal" # Open Playwright Recorder Internal
ROBOCORP_EDIT_ROBOCORP_INSPECTOR_LOCATOR = "robocorp.editRobocorpInspectorLocator" # Edit locator
Expand Down
8 changes: 1 addition & 7 deletions robocorp-code/src/robocorp_code/inspector/inspector_api.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import threading
<<<<<<< HEAD
from typing import TYPE_CHECKING, Tuple
from queue import Queue
from typing import Literal, Optional, Callable
=======
from queue import Queue
from typing import Literal, Optional, TYPE_CHECKING
>>>>>>> f5da5c0e ([skip ci] Finished scaffolding for the Java Inspector.)
from typing import Literal, Optional, TYPE_CHECKING, Callable, Tuple

from robocorp_ls_core.basic import overrides
from robocorp_ls_core.protocols import ActionResultDict, IConfig, IEndPoint
Expand Down
5 changes: 5 additions & 0 deletions robocorp-code/vscode-client/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ import {
ROBOCORP_DEBUG_ACTION_FROM_ACTION_PACKAGE,
ROBOCORP_CREATE_ACTION_PACKAGE,
ROBOCORP_CREATE_TASK_OR_ACTION_PACKAGE,
ROBOCORP_NEW_ROBOCORP_INSPECTOR_JAVA,
} from "./robocorpCommands";
import { installPythonInterpreterCheck } from "./pythonExtIntegration";
import { refreshCloudTreeView } from "./viewsRobocorp";
Expand Down Expand Up @@ -679,6 +680,10 @@ export async function doActivate(context: ExtensionContext, C: CommandRegistry)
ROBOCORP_NEW_ROBOCORP_INSPECTOR_IMAGE,
async () => await showInspectorUI(context, IAppRoutes.IMAGE_INSPECTOR)
);
C.register(
ROBOCORP_NEW_ROBOCORP_INSPECTOR_JAVA,
async () => await showInspectorUI(context, IAppRoutes.JAVA_INSPECTOR)
);

// i.e.: allow other extensions to also use our submit issue api.
C.registerWithoutStub(
Expand Down
298 changes: 9 additions & 289 deletions robocorp-code/vscode-client/src/inspector.ts
Original file line number Diff line number Diff line change
@@ -1,298 +1,18 @@
import * as path from "path";
import { env, ProgressLocation, Uri, window } from "vscode";
import { getLanguageServerPythonInfo } from "./extension";
import { verifyFileExists } from "./files";
import { listAndAskRobotSelection } from "./activities";
import { getSelectedLocator, getSelectedRobot, LocatorEntry, RobotEntry } from "./viewsCommon";
import { execFilePromise, mergeEnviron } from "./subprocess";
import { logError, OUTPUT_CHANNEL } from "./channel";
import { ChildProcess } from "child_process";
import { feedback } from "./rcc";
import { LocalRobotMetadataInfo } from "./protocols";
import { Mutex } from "./mutex";

// InspectorType - needs to respect the types from vscode-client/src/inspector/types.ts
export enum InspectorType {
Browser = "browser",
Windows = "windows",
Image = "image",
WebRecorder = "web-recorder",
WebInspector = "browser",
WindowsInspector = "windows",
ImageInspector = "image",
JavaInspector = "java",
PlaywrightRecorder = "playwright-recorder",
}

export type InspectorTypes = `${InspectorType}`;

export const DEFAULT_INSPECTOR_VALUE = {
browser: false,
image: false,
windows: false,
"web-recorder": false,
"browser": false,
"windows": false,
"image": false,
"java": false,
"playwright-recorder": false,
};

let _openingInspector: { [K in InspectorTypes]: boolean } = DEFAULT_INSPECTOR_VALUE;
let _startingRootWindowNotified: { [K in InspectorTypes]: boolean } = DEFAULT_INSPECTOR_VALUE;

let globalVerifiedRequirementsForInspectorCli: boolean = false;
const globalVerifiedRequirementsForInspectorCliMutex = new Mutex();

export async function verifyWebview2Installed(
pythonExecutable: string,
cwd?: string,
environ?: { [key: string]: string }
): Promise<boolean> {
if (process.platform !== "win32") {
return true; // If non-win32 that's Ok.
}
if (globalVerifiedRequirementsForInspectorCli) {
return globalVerifiedRequirementsForInspectorCli;
}
return await globalVerifiedRequirementsForInspectorCliMutex.dispatch(async () => {
while (!globalVerifiedRequirementsForInspectorCli) {
try {
const args = [
"-c",
"from webview.platforms.winforms import _is_chromium;print(_is_chromium());import sys;sys.stdout.flush()",
];
const result = await execFilePromise(pythonExecutable, args, {
env: environ,
cwd,
});

const stdoutTrimmed = result.stdout.trim();
if (stdoutTrimmed === "True") {
// Great, it's already there.
globalVerifiedRequirementsForInspectorCli = true;
return globalVerifiedRequirementsForInspectorCli;
} else if (stdoutTrimmed === "False") {
// The user needs to install it.
const INSTALL_WEBVIEW2_OPTION = "Open Download Page";
let choice = await window.showWarningMessage(
`WebView2 Runtime not detected. To use locators the WebView2 Runtime is required.`,
{
"modal": true,
"detail": `Please download the installer from "https://developer.microsoft.com/en-us/microsoft-edge/webview2/".
Note: the "Evergreen Bootstrapper" is recommended, but the "Evergreen Standalone Installer" is also Ok.`,
},
INSTALL_WEBVIEW2_OPTION
);
if (choice == INSTALL_WEBVIEW2_OPTION) {
env.openExternal(
Uri.parse("https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section")
);
await window.showInformationMessage("Press Ok after installing WebView2 to proceed.", {
"modal": true,
});

// Keep on in the while loop to recheck.
} else {
// Cancelled.
return globalVerifiedRequirementsForInspectorCli;
}
} else {
// This is unexpected.
throw new Error(
"Expected either 'True' or 'False' checking whether 'Webview2' is installed. Found: stdout:\n" +
result.stdout +
"\nstderr:\n" +
result.stderr
);
}
} catch (err) {
logError("Error verifying if webview2 is available.", err, "ERROR_CHECK_WEBVIEW2");
await window.showErrorMessage(
"There was an error verifying if webview2 is installed. Please submit an issue report to Robocorp."
);
return globalVerifiedRequirementsForInspectorCli;
}
}
return globalVerifiedRequirementsForInspectorCli;
});
}

export async function openRobocorpInspector(locatorType?: InspectorTypes, locator?: LocatorEntry): Promise<void> {
let localLocatorType = locatorType;
if (locatorType === undefined) {
if (locator !== undefined) {
localLocatorType = locator.type as InspectorTypes;
} else {
window.showErrorMessage("Internal error: either the locatorType or the locator entry must be specified.");
return;
}
}
if (localLocatorType === InspectorType.Windows && process.platform !== "win32") {
window.showInformationMessage("This feature is Windows specific and not supported on other platforms.");
return; // Windows only feature
}

if (_openingInspector[localLocatorType]) {
if (!_startingRootWindowNotified[localLocatorType]) {
return; // We should be showing the progress already, so, don't do anything.
}
window.showInformationMessage(
"The Locators UI is already opened, so, please use the existing UI (or close it/wait for it to be closed)."
);
return;
}
try {
_openingInspector[localLocatorType] = true;
return await _internalOpenRobocorpInspector(localLocatorType, locator);
} finally {
_openingInspector[localLocatorType] = false;
_startingRootWindowNotified[localLocatorType] = false;
}
}

export async function _internalOpenRobocorpInspector(
locatorType?: InspectorTypes,
locator?: LocatorEntry
): Promise<void> {
let locatorJson;
const args: string[] = [];
let selectedEntry: RobotEntry = getSelectedRobot();
let robot: LocalRobotMetadataInfo | undefined = selectedEntry?.robot;
if (robot === undefined) {
// Ask for the robot to be used and then show dialog with the options.
robot = await listAndAskRobotSelection(
"Please select the Task or Action Package where the locators should be saved.",
"Unable to open Inspector (no Task nor Action Package detected in the Workspace).",
{ showTaskPackages: true, showActionPackages: true }
);
if (!robot) {
return;
}
}
locatorJson = path.join(robot.directory, "locators.json");
locatorJson = verifyFileExists(locatorJson, false) ? locatorJson : undefined;
const inspectorLaunchInfo = await getLanguageServerPythonInfo();
if (!inspectorLaunchInfo) {
OUTPUT_CHANNEL.appendLine("Unable to get Robocorp Inspector launch info.");
return;
}

// add locators.json path to args
if (locatorJson) {
args.push("--database", locatorJson);
}

if (locator !== undefined) {
if (locator.type === "error") {
OUTPUT_CHANNEL.appendLine("Trying to edit non-existing (error) locator.");
return;
}
args.push("edit", locator.name);
} else if (locatorType) {
// if locatorType is given prioritize that. Else Ensure that a locator is selected!
args.push("open");
args.push(locatorType);
} else {
const locatorSelected: LocatorEntry | undefined =
locator ??
(await getSelectedLocator({
noSelectionMessage: "Please select a locator first.",
moreThanOneSelectionMessage: "Please select only one locator.",
}));
if (locatorSelected.type === "error") {
OUTPUT_CHANNEL.appendLine("Trying to edit non-existing (error) locator.");
return;
}
if (locatorSelected) {
args.push("edit", locatorSelected.name);
} else {
OUTPUT_CHANNEL.appendLine("Unable to open Robocorp Inspector. Select a locator first.");
return;
}
}

let resolveProgress = undefined;
window.withProgress(
{
location: ProgressLocation.Notification,
title: "Robocorp",
cancellable: false,
},
(progress) => {
progress.report({ message: "Opening Inspector..." });
return new Promise<void>((resolve) => {
resolveProgress = resolve;
});
}
);

try {
// Required due to how conda packages python, and MacOS requiring
// a signed package for displaying windows (supplied through python.app)
function replaceNewLines(s) {
return s.replace(/(?:\r\n|\r|\n)/g, "\n i> ");
}
let first = true;
function append(s: string) {
if (first) {
OUTPUT_CHANNEL.append(" i> ");
first = false;
}
OUTPUT_CHANNEL.append(replaceNewLines(s));
}
const configChildProcess = function (childProcess: ChildProcess) {
childProcess.stderr.on("data", function (data: any) {
const s = "" + data;
append(s);
if (s.includes("Starting root window")) {
_startingRootWindowNotified[locatorType] = true;
resolveProgress();
}
});
childProcess.stdout.on("data", function (data: any) {
append("" + data);
});
};

feedback("vscode.inspector.opened", locatorType);

const pythonExecutablePath =
process.platform === "darwin"
? path.join(path.dirname(inspectorLaunchInfo.pythonExe), "pythonw")
: inspectorLaunchInfo.pythonExe;
await startInspectorCLI(
pythonExecutablePath,
args,
robot.directory,
inspectorLaunchInfo.environ,
configChildProcess
);
} finally {
resolveProgress();
}
}

async function startInspectorCLI(
pythonExecutable: string,
args: string[],
cwd?: string,
environ?: { [key: string]: string },
configChildProcess?: (childProcess: ChildProcess) => void
): Promise<void> {
const installed = await verifyWebview2Installed(pythonExecutable, cwd, environ);
if (!installed) {
return;
}
const inspectorCmd = ["-m", "inspector.cli"];
const completeArgs = inspectorCmd.concat(args);
OUTPUT_CHANNEL.appendLine(`Using cwd root for inspector: "${cwd}"`);
try {
await execFilePromise(
pythonExecutable,
completeArgs,
{
env: mergeEnviron(environ),
cwd,
},
{
"configChildProcess": configChildProcess,
}
);
} catch (err) {
// As the process is force-killed, we may have an error code in the return.
// That's ok, just ignore it.
}
}
1 change: 1 addition & 0 deletions robocorp-code/vscode-client/src/robocorpCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const ROBOCORP_OPEN_CLOUD_HOME = "robocorp.openCloudHome"; // Open cloud
export const ROBOCORP_NEW_ROBOCORP_INSPECTOR_BROWSER = "robocorp.newRobocorpInspectorBrowser"; // Add Web Locator
export const ROBOCORP_NEW_ROBOCORP_INSPECTOR_WINDOWS = "robocorp.newRobocorpInspectorWindows"; // Add Windows Locator
export const ROBOCORP_NEW_ROBOCORP_INSPECTOR_IMAGE = "robocorp.newRobocorpInspectorImage"; // Add Image Locator
export const ROBOCORP_NEW_ROBOCORP_INSPECTOR_JAVA = "robocorp.newRobocorpInspectorJava"; // Add Java Locator
export const ROBOCORP_OPEN_PLAYWRIGHT_RECORDER = "robocorp.openPlaywrightRecorder"; // Open Playwright Recorder
export const ROBOCORP_OPEN_PLAYWRIGHT_RECORDER_INTERNAL = "robocorp.openPlaywrightRecorder.internal"; // Open Playwright Recorder Internal
export const ROBOCORP_EDIT_ROBOCORP_INSPECTOR_LOCATOR = "robocorp.editRobocorpInspectorLocator"; // Edit locator
Expand Down
Loading

0 comments on commit 07845ed

Please sign in to comment.