Skip to content

Commit caab7a3

Browse files
authored
feat: retrieve commands and execute methods from active extensions (#2414)
* chore: move static commands list to own file * chore: pass generateCommandNotes as prop * only show static commands list if there is no method map support * add tab switcher for dynamic commands grid * add UI for rendering command/method buttons * filter empty values * do not render collapses if only one available * also filter execute methods with no content * remove hardcoded input value types, try to detect them * convert static commands list to use a format compatible with endpoint result also remove handling for notes and driver-specific command exclusion * store pending command in react hooks instead of redux * dynamic execute methods are now working * dynamic commands (with matching names) are working * filter execute methods before saving them * improve execute method filtering and add tests * add filtering for standard commands - full map WIP * add full map of supported commands * commands and execute methods are both working * rename property for clarity * extract parameters from paths for applicable commands * add element commands to driver, to allow calling them as commands * remove driver dependency for filterAvailableCommands * add unit tests for filterAvailableCommands * add support for required attribute * add support for deprecated attribute * add support for info attribute * add descriptions for both tabs * move CommandResultModal to Commands component * split runCommand into two methods for clarity * store command result in react hooks instead of redux * refactor MethodMapCommandsList components * add searchbar for dynamic method map * combine all commands in one list & support plugins * reduce sluggishness when entering command parameter values * simplify handling for special methods * add extra handling for certain commands * adjust tab descriptions * update deprecated command background in light mode * update commands documentation * update types * run prettier * add a few more unit tests * add extra handling for printPage * improve test for filtering out unsupported commands * add back stopRecordingScreen * use a map for handling mismatching commands * fix destructuring * add some memoization * add various workarounds for antd performance issues * fix lint * use useRef for full command lists * remove unneeded cancel button * make parameter input smoother * remove isMemo due to react compiler * speed up method filtering by avoiding dynamic toPairs calls * simplify table to row/col * ensure linebreaks for long method names * fix css * update translation strings * update docs * handle edge case for overrides * tune previous edge case handling * address copilot comments * update translation string * define local constants for method names * address comments in Commands component * address comments in commands tab utils * address comments in commands unit test file * label args for executeScript as optional * address comments * update docs image due to updated texts
1 parent c9fa392 commit caab7a3

File tree

22 files changed

+1353
-364
lines changed

22 files changed

+1353
-364
lines changed

app/common/public/locales/en/translation.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,5 +320,9 @@
320320
"toggleTableFormatting": "Toggle Table Formatting",
321321
"copyResultToClipboard": "Copy Result to Clipboard",
322322
"Property": "Property",
323-
"noCapsFound": "No session capabilities found. Please use the Capability Builder to edit, add, and save your capability set. Refer to the tutorial at {{url}}"
323+
"noCapsFound": "No session capabilities found. Please use the Capability Builder to edit, add, and save your capability set. Refer to the tutorial at {{url}}",
324+
"executeMethods": "Execute Methods",
325+
"dynamicCommandsDescription": "Run commands supported by the currently active driver and plugin(s). Note that some commands may only work in specific contexts. Any commands not supported by WebdriverIO are not included in this list.",
326+
"dynamicExecuteMethodsDescription": "Run any execute method supported by the currently active driver and plugin(s).",
327+
"methodDeprecated": "This method is deprecated"
324328
}

app/common/renderer/actions/SessionInspector.js

Lines changed: 25 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -81,10 +81,6 @@ export const HIDE_PROMPT_KEEP_ALIVE = 'HIDE_PROMPT_KEEP_ALIVE';
8181

8282
export const SELECT_INSPECTOR_TAB = 'SELECT_INSPECTOR_TAB';
8383

84-
export const ENTERING_COMMAND_ARGS = 'ENTERING_COMMAND_ARGS';
85-
export const CANCEL_PENDING_COMMAND = 'CANCEL_PENDING_COMMAND';
86-
export const SET_COMMAND_ARG = 'SET_COMMAND_ARG';
87-
8884
export const SET_CONTEXT = 'SET_CONTEXT';
8985

9086
export const SET_APP_ID = 'SET_APP_ID';
@@ -95,8 +91,6 @@ export const SET_KEEP_ALIVE_INTERVAL = 'SET_KEEP_ALIVE_INTERVAL';
9591
export const SET_USER_WAIT_TIMEOUT = 'SET_USER_WAIT_TIMEOUT';
9692
export const SET_LAST_ACTIVE_MOMENT = 'SET_LAST_ACTIVE_MOMENT';
9793

98-
export const SET_VISIBLE_COMMAND_RESULT = 'SET_VISIBLE_COMMAND_RESULT';
99-
10094
export const SET_AWAITING_MJPEG_STREAM = 'SET_AWAITING_MJPEG_STREAM';
10195

10296
export const SHOW_GESTURE_EDITOR = 'SHOW_GESTURE_EDITOR';
@@ -343,7 +337,7 @@ export function restartSession(error, params) {
343337
});
344338
const quitSes = quitSession('Window closed');
345339
const newSes = newSession(getState().builder.caps);
346-
const getPageSrc = applyClientMethod({methodName: 'getPageSource', ignoreResult: true});
340+
const getPageSrc = applyClientMethod({methodName: 'getPageSource'});
347341
const storeSessionSet = storeSessionSettings();
348342
const getSavedClientFrame = getSavedClientFramework();
349343
const runKeepAliveLp = runKeepAliveLoop();
@@ -458,7 +452,6 @@ export function storeSessionSettings(updatedSessionSettings = null) {
458452
const action = applyClientMethod({
459453
methodName: 'getSettings',
460454
skipRefresh: true,
461-
ignoreResult: true,
462455
});
463456
sessionSettings = await action(dispatch, getState);
464457
}
@@ -587,7 +580,6 @@ export function setLocatorTestElement(elementId) {
587580
methodName: 'getElementRect',
588581
skipRefresh: true,
589582
skipRecord: true,
590-
ignoreResult: true,
591583
});
592584
const {commandRes} = await action(dispatch, getState);
593585
dispatch({
@@ -826,21 +818,23 @@ export function selectInspectorTab(interaction) {
826818
};
827819
}
828820

829-
export function startEnteringCommandArgs(commandName, command) {
830-
return (dispatch) => {
831-
dispatch({type: ENTERING_COMMAND_ARGS, commandName, command});
832-
};
833-
}
834-
835-
export function cancelPendingCommand() {
836-
return (dispatch) => {
837-
dispatch({type: CANCEL_PENDING_COMMAND});
838-
};
839-
}
821+
export function getSupportedSessionMethods() {
822+
return async (_dispatch, getState) => {
823+
async function safelyCallCommand(methodName) {
824+
try {
825+
const action = executeDriverCommand({methodName});
826+
const {commandRes} = await action(getState);
827+
return commandRes;
828+
} catch {
829+
return [];
830+
}
831+
}
840832

841-
export function setCommandArg(index, value) {
842-
return (dispatch) => {
843-
dispatch({type: SET_COMMAND_ARG, index, value});
833+
const [commands, executeMethods] = await Promise.all([
834+
safelyCallCommand('getAppiumCommands'),
835+
safelyCallCommand('getAppiumExtensions'),
836+
]);
837+
return {commands, executeMethods};
844838
};
845839
}
846840

@@ -909,7 +903,6 @@ export function callClientMethod(params) {
909903
return async (dispatch, getState) => {
910904
const {driver, appMode, isUsingMjpegMode, isSourceRefreshOn, autoSessionRestart} =
911905
getState().inspector;
912-
const {methodName, ignoreResult = true} = params;
913906
params.appMode = appMode;
914907
params.autoSessionRestart = autoSessionRestart;
915908

@@ -929,22 +922,6 @@ export function callClientMethod(params) {
929922
action(dispatch, getState);
930923
const inspectorDriver = InspectorDriver.instance(driver);
931924
const res = await inspectorDriver.run(params);
932-
let {commandRes} = res;
933-
934-
// Ignore empty objects
935-
if (_.isObject(res) && _.isEmpty(res)) {
936-
commandRes = null;
937-
}
938-
939-
if (!ignoreResult) {
940-
// if the user is running actions manually, we want to show the full response with the
941-
// ability to scroll etc...
942-
const result = JSON.stringify(commandRes, null, ' ');
943-
const truncatedResult = _.truncate(result, {length: 2000});
944-
log.info(`Result of client command was:`);
945-
log.info(truncatedResult);
946-
setVisibleCommandResult(result, methodName)(dispatch);
947-
}
948925
res.elementId = res.id;
949926
return res;
950927
} catch (error) {
@@ -959,9 +936,14 @@ export function callClientMethod(params) {
959936
};
960937
}
961938

962-
export function setVisibleCommandResult(result, methodName) {
963-
return (dispatch) => {
964-
dispatch({type: SET_VISIBLE_COMMAND_RESULT, result, methodName});
939+
// Simple alternative to callClientMethod, for when we only want to
940+
// run the command without any side-effects
941+
export function executeDriverCommand(params) {
942+
return async (getState) => {
943+
const {driver} = getState().inspector;
944+
params.skipRefresh = true;
945+
const inspectorDriver = InspectorDriver.instance(driver);
946+
return await inspectorDriver.run(params);
965947
};
966948
}
967949

app/common/renderer/components/SessionInspector/CommandsTab/CommandResultModal.jsx

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const CommandResultRawTable = ({result}) => {
173173
};
174174

175175
const CommandResultModalFooter = ({
176-
visibleCommandResult,
176+
commandResult,
177177
closeCommandModal,
178178
setFormatResult,
179179
formatResult,
@@ -195,7 +195,7 @@ const CommandResultModalFooter = ({
195195
<Button
196196
icon={<CopyOutlined />}
197197
disabled={formatResult}
198-
onClick={() => copyToClipboard(visibleCommandResult)}
198+
onClick={() => copyToClipboard(commandResult)}
199199
/>
200200
</Tooltip>
201201
</Space>
@@ -208,31 +208,26 @@ const CommandResultModalFooter = ({
208208
</Row>
209209
);
210210

211-
const CommandResultModal = ({
212-
visibleCommandMethod,
213-
visibleCommandResult,
214-
setVisibleCommandResult,
215-
t,
216-
}) => {
211+
const CommandResultModal = ({commandName, commandResult, clearCurrentCommand, t}) => {
217212
const [formatResult, setFormatResult] = useState(false);
218213

219-
const {parsedResult, isPrimitive} = parseCommandResult(visibleCommandResult);
214+
const {parsedResult, isPrimitive} = parseCommandResult(commandResult);
220215

221216
const closeCommandModal = () => {
222-
setVisibleCommandResult(null);
217+
clearCurrentCommand();
223218
setFormatResult(false);
224219
};
225220

226221
return (
227222
<Modal
228-
title={t('methodCallResult', {methodName: visibleCommandMethod})}
229-
open={!!visibleCommandResult}
223+
title={t('methodCallResult', {methodName: commandName})}
224+
open={!!commandResult}
230225
onCancel={() => closeCommandModal()}
231226
width={{md: '80%', lg: '70%', xl: '60%', xxl: '50%'}}
232227
className={styles.commandResultModal}
233228
footer={
234229
<CommandResultModalFooter
235-
visibleCommandResult={visibleCommandResult}
230+
commandResult={commandResult}
236231
closeCommandModal={closeCommandModal}
237232
setFormatResult={setFormatResult}
238233
formatResult={formatResult}
@@ -244,7 +239,7 @@ const CommandResultModal = ({
244239
{formatResult ? (
245240
<CommandResultFormattedTable result={parsedResult} isPrimitive={isPrimitive} t={t} />
246241
) : (
247-
<CommandResultRawTable result={visibleCommandResult} />
242+
<CommandResultRawTable result={commandResult} />
248243
)}
249244
</Modal>
250245
);

0 commit comments

Comments
 (0)