Skip to content

Commit 61d6b4b

Browse files
authored
feat: debug with selector option added (#8066)
<!-- Thank you for your contribution. Before making a PR, please read our contributing guidelines at https://github.com/DevExpress/testcafe/blob/master/CONTRIBUTING.md#code-contribution We recommend creating a *draft* PR, so that you can mark it as 'ready for review' when you are done. --> ## Purpose The purpose of this PR is to allow passing selector to a t.debug call. Something like this t.debug(Selector). Then the selector will be shown in debug mode and highlighted. ## Approach So overall, the main goal was to pass the selector argument from server-side debug call to client-side selector-input-contaner. 1) src/test-run/index.ts Inside this file we have _internalExecuteCommand method. There is debug command type handling block. Inside of it we call _enqueueSetBreakpointCommand to which we pass command.selector as a second argument. This way we transfer Selector from debugCommand to breakpointCommand. 2) After the breakpointCommand is added to driverTaskQueue we can work with it on client-side. 3) On client-side we pass selector to _debugSelector method of SelectorInputContainer class. 4) In _debugSelector method we: - remove any previously highlighted elements, - get Selector name and assign it to this.value for SelectorInputContainer, - execute Selector to get elements for highlighting and indication, - highlight and indicate elements of the Selector ## References DevExpress/testcafe-private#163 ## Pre-Merge TODO - [x] Write tests for your proposed changes - [x] Make sure that existing tests do not fail
1 parent 51b54c7 commit 61d6b4b

File tree

27 files changed

+179
-102
lines changed

27 files changed

+179
-102
lines changed

src/api/skip-js-errors/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {
22
Dictionary, SkipJsErrorsCallback, SkipJsErrorsCallbackWithOptionsObject, SkipJsErrorsOptionsObject,
33
} from '../../configuration/interfaces';
44
import { parseRegExpString } from '../../utils/make-reg-exp';
5-
import { ExecuteClientFunctionCommand } from '../../test-run/commands/observation';
5+
import { ExecuteClientFunctionCommand } from '../../test-run/commands/execute-client-function';
66
import ClientFunctionBuilder from '../../client-functions/client-function-builder';
77

88
const SKIP_JS_ERRORS_OBJECT_FUNCTION = `

src/api/test-controller/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,8 @@ export default class TestController {
602602
return new Assertion(actual, this, callsite);
603603
}
604604

605-
[delegatedAPI(DebugCommand.methodName)] () {
606-
return this.enqueueCommand(DebugCommand);
605+
[delegatedAPI(DebugCommand.methodName)] (selector) {
606+
return this.enqueueCommand(DebugCommand, { selector });
607607
}
608608

609609
[delegatedAPI(SetTestSpeedCommand.methodName)] (speed) {

src/client-functions/client-function-builder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { isNil as isNullOrUndefined, assign } from 'lodash';
22
import testRunTracker from '../api/test-run-tracker';
33
import functionBuilderSymbol from './builder-symbol';
44
import { createReplicator, FunctionTransform } from './replicator';
5-
import { ExecuteClientFunctionCommand } from '../test-run/commands/observation';
5+
import { ExecuteClientFunctionCommand } from '../test-run/commands/execute-client-function';
66
import compileClientFunction from '../compiler/compile-client-function';
77
import { APIError, ClientFunctionAPIError } from '../errors/runtime';
88
import { assertType, is } from '../errors/runtime/type-assertions';

src/client-functions/selectors/selector-builder.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ClientFunctionAPIError } from '../../errors/runtime';
66
import functionBuilderSymbol from '../builder-symbol';
77
import { RUNTIME_ERRORS } from '../../errors/types';
88
import { assertType, is } from '../../errors/runtime/type-assertions';
9-
import { ExecuteSelectorCommand } from '../../test-run/commands/observation';
9+
import { ExecuteSelectorCommand } from '../../test-run/commands/execute-client-function';
1010
import defineLazyProperty from '../../utils/define-lazy-property';
1111
import { addAPI, addCustomMethods } from './add-api';
1212
import createSnapshotMethods from './create-snapshot-methods';

src/client/driver/command-executors/action-executor/elements-retriever.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ExecuteSelectorCommand } from '../../../../test-run/commands/observation';
1+
import { ExecuteSelectorCommand } from '../../../../test-run/commands/execute-client-function';
22
import { ExecuteSelectorFn } from '../../../../shared/types';
33
import NODE_TYPE_DESCRIPTIONS from '../../node-type-descriptions';
44
import {

src/client/driver/command-executors/client-functions/client-function-executor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import ClientFunctionNodeTransform from './replicator/transforms/client-function
44
import evalFunction from './eval-function';
55
import { UncaughtErrorInClientFunctionCode } from '../../../../shared/errors/index';
66
import Replicator from 'replicator';
7-
import { ExecuteClientFunctionCommand, ExecuteClientFunctionCommandBase } from '../../../../test-run/commands/observation';
7+
import { ExecuteClientFunctionCommand, ExecuteClientFunctionCommandBase } from '../../../../test-run/commands/execute-client-function';
88
import { Dictionary } from '../../../../configuration/interfaces';
99
// @ts-ignore
1010
import { Promise } from '../../deps/hammerhead';

src/client/driver/command-executors/client-functions/selector-executor/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import ClientFunctionExecutor from '../client-function-executor';
22
import createReplicator from '../replicator/index';
33
import FunctionTransform from '../replicator/transforms/function-transform';
44
import SelectorNodeTransform from '../replicator/transforms/selector-node-transform';
5-
import { ExecuteSelectorCommand } from '../../../../../test-run/commands/observation';
5+
import { ExecuteSelectorCommand } from '../../../../../test-run/commands/execute-client-function';
66
import {
77
SelectorErrorParams,
88
SelectorDependencies,

src/client/driver/driver.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,10 +1527,10 @@ export default class Driver extends serviceUtils.EventEmitter {
15271527
});
15281528
}
15291529

1530-
_onSetBreakpointCommand ({ isTestError }) {
1530+
_onSetBreakpointCommand ({ isTestError, selector }) {
15311531
const showDebuggingStatusPromise = this.statusBar.showDebuggingStatus(isTestError);
15321532

1533-
this.selectorInspectorPanel.show();
1533+
this.selectorInspectorPanel.show(selector);
15341534

15351535
showDebuggingStatusPromise.then(debug => {
15361536
const stopAfterNextAction = debug === STATUS_BAR_DEBUG_ACTION.step;

src/client/ui/selector-inspector-panel/index.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@ export default class SelectorInspectorPanel {
1414
elementPicker = elementPicker;
1515

1616
constructor () {
17-
this.element = createElementFromDescriptor(panel);
18-
17+
this.element = createElementFromDescriptor(panel);
18+
this.selectorInputContainer = new SelectorInputContainer();
1919
const pickButton = new PickButton();
20-
const selectorInputContainer = new SelectorInputContainer();
21-
const copyButton = new CopyButton(selectorInputContainer);
22-
const container = new MainContainer(pickButton.element, selectorInputContainer.element, copyButton.element);
20+
const copyButton = new CopyButton(this.selectorInputContainer);
21+
const container = new MainContainer(pickButton.element, this.selectorInputContainer.element, copyButton.element);
2322
const hideButton = new HideButton(this.element);
2423

2524
this.element.appendChild(container.element);
2625
this.element.appendChild(hideButton.element);
2726
}
2827

29-
show () {
28+
show (selector) {
29+
this.selectorInputContainer.debugSelector(selector);
30+
3031
if (!this.element.parentElement)
3132
uiRoot.insertFirstChildToPanelsContainer(this.element);
3233

src/client/ui/selector-inspector-panel/selector-input-container.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import hammerhead from './../deps/hammerhead';
33
import testCafeCore from './../deps/testcafe-core';
44

55
import { createElementFromDescriptor } from './utils/create-element-from-descriptor';
6-
import { getElementsBySelector } from './utils/get-elements-by-selector';
6+
import { getElementsBySelector, executeSelector } from './utils/get-elements-by-selector';
7+
import { castArray } from './utils/cast-array';
78

89
import * as descriptors from './descriptors';
910
import { elementPicker, ELEMENT_PICKED } from './element-picker';
@@ -159,4 +160,14 @@ export class SelectorInputContainer {
159160
this._indicateMatches(elements);
160161
this._highlightElements(elements);
161162
}
163+
164+
async debugSelector (selector) {
165+
highlighter.stopHighlighting();
166+
167+
this.value = selector.apiFnChain.join('');
168+
const elements = castArray(await executeSelector(selector));
169+
170+
this._indicateMatches(elements);
171+
this._highlightElements(elements);
172+
}
162173
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import hammerhead from '../../deps/hammerhead';
2+
3+
const nativeMethods = hammerhead.nativeMethods;
4+
5+
export function castArray (elements) {
6+
return nativeMethods.isArray(elements) ? elements : [elements];
7+
}

src/client/ui/selector-inspector-panel/utils/get-elements-by-selector.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ async function parseSelector (selector) {
2020
return browser.parseSelector(communicationUrls.parseSelector, createNativeXHR, selector);
2121
}
2222

23-
async function executeSelector (parsedSelector) {
23+
export async function executeSelector (parsedSelector) {
2424
const startTime = nativeMethods.date();
2525
const selectorExecutor = new SelectorExecutor(parsedSelector, GLOBAL_TIMEOUT, startTime, createNotFoundError, createIsInvisibleError);
2626
const elements = await selectorExecutor.getResult();

src/reporter/command/command-formatter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isEmpty } from 'lodash';
2-
import { ExecuteSelectorCommand, ExecuteClientFunctionCommand } from '../../test-run/commands/observation';
2+
import { ExecuteSelectorCommand, ExecuteClientFunctionCommand } from '../../test-run/commands/execute-client-function';
33
import {
44
NavigateToCommand,
55
PressKeyCommand,

src/shared/types.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* global globalThis */
22

3-
import { ExecuteSelectorCommand } from '../test-run/commands/observation';
3+
import { ExecuteSelectorCommand } from '../test-run/commands/execute-client-function';
44

55
export interface NativeMethods {
66
setTimeout: typeof globalThis.setTimeout;

src/test-run/bookmark.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212

1313
import { CurrentIframeNotFoundError, CurrentIframeIsNotLoadedError } from '../errors/test-run';
1414
import TestRun from './index';
15-
import { ExecuteClientFunctionCommand, ExecuteSelectorCommand } from './commands/observation';
15+
import { ExecuteClientFunctionCommand, ExecuteSelectorCommand } from './commands/execute-client-function';
1616
import Role, { RedirectUrl } from '../role/role';
1717
import { DEFAULT_SPEED_VALUE } from '../configuration/default-values';
1818
import BrowserConsoleMessages from './browser-console-messages';

src/test-run/commands/actions.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ActionCommandBase, CommandBase } from './base';
2-
import { ExecuteClientFunctionCommand, ExecuteSelectorCommand } from './observation';
2+
import { ExecuteClientFunctionCommand, ExecuteSelectorCommand } from './execute-client-function';
33

44
import {
55
ActionOptions,

src/test-run/commands/actions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {
4848
} from './validations/argument';
4949

5050
import { SetNativeDialogHandlerCodeWrongTypeError } from '../../errors/test-run';
51-
import { ExecuteClientFunctionCommand } from './observation';
51+
import { ExecuteClientFunctionCommand } from './execute-client-function';
5252
import { camelCase } from 'lodash';
5353
import {
5454
prepareSkipJsErrorsOptions,
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { CommandBase } from './base';
2+
import TestRun from '../index';
3+
4+
declare class ExecuteClientFunctionCommandBase extends CommandBase {
5+
public constructor(obj: object, testRun: TestRun, type: string);
6+
public instantiationCallsiteName: string;
7+
public fnCode: string;
8+
public args: string[];
9+
public dependencies: string[];
10+
}
11+
12+
export class ExecuteClientFunctionCommand extends ExecuteClientFunctionCommandBase {
13+
public constructor(obj: object, testRun: TestRun);
14+
}
15+
16+
export class ExecuteSelectorCommand extends ExecuteClientFunctionCommandBase {
17+
public constructor(obj: object, testRun: TestRun);
18+
public visibilityCheck: boolean;
19+
public timeout?: number;
20+
public apiFnChain: string[];
21+
public needError: boolean;
22+
public index: number;
23+
public strictError: boolean;
24+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import TYPE from './type';
2+
import { ActionCommandBase } from './base';
3+
4+
export class ExecuteClientFunctionCommandBase extends ActionCommandBase {
5+
constructor (obj, testRun, type) {
6+
super(obj, testRun, type, false);
7+
}
8+
9+
getAssignableProperties () {
10+
return [
11+
{ name: 'instantiationCallsiteName', defaultValue: '' },
12+
{ name: 'fnCode', defaultValue: '' },
13+
{ name: 'args', defaultValue: [] },
14+
{ name: 'dependencies', defaultValue: [] },
15+
];
16+
}
17+
}
18+
19+
export class ExecuteClientFunctionCommand extends ExecuteClientFunctionCommandBase {
20+
static methodName = TYPE.executeClientFunction;
21+
22+
constructor (obj, testRun) {
23+
super(obj, testRun, TYPE.executeClientFunction);
24+
}
25+
}
26+
27+
export class ExecuteSelectorCommand extends ExecuteClientFunctionCommandBase {
28+
static methodName = TYPE.executeSelector;
29+
30+
constructor (obj, testRun) {
31+
super(obj, testRun, TYPE.executeSelector);
32+
}
33+
34+
getAssignableProperties () {
35+
return [
36+
{ name: 'visibilityCheck', defaultValue: false },
37+
{ name: 'timeout', defaultValue: null },
38+
{ name: 'apiFnChain' },
39+
{ name: 'needError' },
40+
{ name: 'index', defaultValue: 0 },
41+
{ name: 'strictError' },
42+
];
43+
}
44+
}
Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,13 @@
1-
import { CommandBase, ActionCommandBase } from './base';
1+
import { ActionCommandBase } from './base';
2+
import { ExecuteClientFunctionCommand } from './execute-client-function';
23
import TestRun from '../index';
34

4-
declare class ExecuteClientFunctionCommandBase extends CommandBase {
5-
public constructor(obj: object, testRun: TestRun, type: string);
6-
public instantiationCallsiteName: string;
7-
public fnCode: string;
8-
public args: string[];
9-
public dependencies: string[];
10-
}
11-
12-
export class ExecuteClientFunctionCommand extends ExecuteClientFunctionCommandBase {
13-
public constructor(obj: object, testRun: TestRun);
14-
}
15-
16-
export class ExecuteSelectorCommand extends ExecuteClientFunctionCommandBase {
17-
public constructor(obj: object, testRun: TestRun);
18-
public visibilityCheck: boolean;
19-
public timeout?: number;
20-
public apiFnChain: string[];
21-
public needError: boolean;
22-
public index: number;
23-
public strictError: boolean;
24-
}
25-
265
export class WaitCommand extends ActionCommandBase {
276
public constructor(obj: object, testRun: TestRun);
287
public timeout: number;
298
}
309

31-
export class DebugCommand extends ActionCommandBase { }
10+
export class DebugCommand extends ActionCommandBase {
11+
public constructor(obj: object, testRun: TestRun, validateProperties?: boolean);
12+
public selector?: ExecuteClientFunctionCommand;
13+
}

src/test-run/commands/observation.js

Lines changed: 13 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@ import TYPE from './type';
22
import { ActionCommandBase } from './base';
33
import { positiveIntegerArgument } from './validations/argument';
44
import { camelCase } from 'lodash';
5+
import { initSelector } from './validations/initializers';
6+
7+
8+
// Initializers
9+
function initDebugOptions (name, val, options) {
10+
return initSelector(name, val, Object.assign({}, options,
11+
{ skipVisibilityCheck: true, collectionMode: true }
12+
));
13+
}
514

615
// Commands
716
export class WaitCommand extends ActionCommandBase {
@@ -18,52 +27,16 @@ export class WaitCommand extends ActionCommandBase {
1827
}
1928
}
2029

21-
export class ExecuteClientFunctionCommandBase extends ActionCommandBase {
22-
constructor (obj, testRun, type) {
23-
super(obj, testRun, type, false);
24-
}
25-
26-
getAssignableProperties () {
27-
return [
28-
{ name: 'instantiationCallsiteName', defaultValue: '' },
29-
{ name: 'fnCode', defaultValue: '' },
30-
{ name: 'args', defaultValue: [] },
31-
{ name: 'dependencies', defaultValue: [] },
32-
];
33-
}
34-
}
35-
36-
export class ExecuteClientFunctionCommand extends ExecuteClientFunctionCommandBase {
37-
static methodName = TYPE.executeClientFunction;
38-
39-
constructor (obj, testRun) {
40-
super(obj, testRun, TYPE.executeClientFunction);
41-
}
42-
}
43-
44-
export class ExecuteSelectorCommand extends ExecuteClientFunctionCommandBase {
45-
static methodName = TYPE.executeSelector;
30+
export class DebugCommand extends ActionCommandBase {
31+
static methodName = camelCase(TYPE.debug);
4632

4733
constructor (obj, testRun) {
48-
super(obj, testRun, TYPE.executeSelector);
34+
super(obj, testRun, TYPE.debug);
4935
}
5036

5137
getAssignableProperties () {
5238
return [
53-
{ name: 'visibilityCheck', defaultValue: false },
54-
{ name: 'timeout', defaultValue: null },
55-
{ name: 'apiFnChain' },
56-
{ name: 'needError' },
57-
{ name: 'index', defaultValue: 0 },
58-
{ name: 'strictError' },
39+
{ name: 'selector', init: initDebugOptions, required: false },
5940
];
6041
}
6142
}
62-
63-
export class DebugCommand extends ActionCommandBase {
64-
static methodName = camelCase(TYPE.debug);
65-
66-
constructor () {
67-
super(null, null, TYPE.debug);
68-
}
69-
}

src/test-run/commands/service.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,10 @@ export class HideAssertionRetriesStatusCommand {
1616
}
1717

1818
export class SetBreakpointCommand {
19-
constructor (isTestError) {
19+
constructor (isTestError, selector) {
2020
this.type = TYPE.setBreakpoint;
2121
this.isTestError = isTestError;
22+
this.selector = selector;
2223
}
2324
}
2425

src/test-run/commands/validations/initializers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import SelectorBuilder from '../../../client-functions/selectors/selector-builder';
22
import { ActionSelectorError } from '../../../errors/test-run';
33
import { APIError } from '../../../errors/runtime';
4-
import { ExecuteSelectorCommand } from '../observation';
4+
import { ExecuteSelectorCommand } from '../execute-client-function';
55
import { executeJsExpression } from '../../execute-js-expression';
66
import { isJSExpression } from '../utils';
77

0 commit comments

Comments
 (0)