Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 1371: Set Locator Active optimizations #1412

Merged
merged 7 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "JDN",
"description": "JDN – helps Test Automation Engineer to create Page Objects in the test automation framework and speed up test development",
"devtools_page": "index.html",
"version": "3.13.534",
"version": "3.13.535",
"icons": {
"128": "icon128.png"
},
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "jdn-ai-chrome-extension",
"version": "3.13.534",
"version": "3.13.535",
"description": "jdn-ai chrome extension",
"scripts": {
"start": "webpack --watch --env devenv",
Expand Down
3 changes: 2 additions & 1 deletion src/features/locators/Locator.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { isLocatorListPage } from "../../app/utils/heplers";
import { selectCurrentPageObject } from "../pageObjects/selectors/pageObjects.selectors";
import { AnnotationType, LocatorType } from "../../common/types/common";
import { getLocatorPrefix } from "./utils/locatorOutput";
import { ScriptMsg } from "../../pageServices/scriptMsg.constants";

interface Props {
element: LocatorInterface;
Expand Down Expand Up @@ -91,7 +92,7 @@ export const Locator: React.FC<Props> = ({ element, currentPage, searchState, de
const param = scriptMessage?.param;

switch (message) {
case "OPEN_EDIT_LOCATOR":
case ScriptMsg.OpenEditLocator:
if (param?.value.element_id !== element_id) return;
setIsEditModalOpen(true);
dispatch(setScriptMessage({}));
Expand Down
4 changes: 2 additions & 2 deletions src/features/locators/locators.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ const locatorsSlice = createSlice({
{ payload }: PayloadAction<Array<Locator> | { locators: Array<Locator>; fromScript: boolean }>
) {
const locators = Array.isArray(payload) ? payload : payload.locators;
const newValue = locators.map((_locator) => ({ ..._locator, active: false }));
locatorsAdapter.upsertMany(state, newValue);
const newValue = locators.map(({ element_id }) => ({ element_id, active: false }));
locatorsAdapter.upsertMany(state, newValue as Locator[]);
},
elementSetActive(state, { payload }: PayloadAction<Locator>) {
locatorsAdapter.upsertOne(state, { element_id: payload.element_id, active: true } as Locator);
Expand Down
35 changes: 21 additions & 14 deletions src/features/locators/reducers/onSetActive.middleware.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,37 @@
import { Middleware } from "@reduxjs/toolkit";
import { Locator } from "../types/locator.types";
import { runLocatorsGeneration } from "./runLocatorsGeneration.thunk";
import { getNoLocatorsElements, hasAllLocators } from "../utils/utils";

export const onSetActive: Middleware = (store) => (next) => (action) => {
const { type, payload } = action;

switch (type) {
case "locators/elementGroupSetActive": {
store.dispatch(
// @ts-ignore
runLocatorsGeneration({
locators: payload.locators,
generateMissingLocator: true,
})
);
const noLocators = getNoLocatorsElements(payload.locators);
if (noLocators.length) {
store.dispatch(
// @ts-ignore
runLocatorsGeneration({
locators: noLocators,
generateMissingLocator: true,
})
);
}
break;
}
case "locators/setActiveSingle":
case "locators/elementSetActive": {
store.dispatch(
// @ts-ignore
runLocatorsGeneration({
locators: [payload as Locator],
generateMissingLocator: true,
})
);
const noLocators = !hasAllLocators(payload);
if (noLocators) {
store.dispatch(
// @ts-ignore
runLocatorsGeneration({
locators: [payload as Locator],
generateMissingLocator: true,
})
);
}
break;
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/features/locators/reducers/runLocatorsGeneration.thunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ export const runLocatorsGeneration = createAsyncThunk(
}));

const state = thunkAPI.getState() as RootState;
if (setPendingXpaths)
if (setPendingXpaths.length)
thunkAPI.dispatch(
updateLocatorGroup({
locators: setPendingXpaths,
pageObject: selectCurrentPageObject(state)!,
})
);

if (setPendingCss)
if (setPendingCss.length)
thunkAPI.dispatch(
updateLocatorGroup({
locators: setPendingCss,
Expand Down
6 changes: 6 additions & 0 deletions src/features/locators/utils/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getLocatorString, getLocatorWithJDIAnnotation, getLocatorWithSelenium }

export const isValidJavaVariable = (value: string) => /^[a-zA-Z_$]([a-zA-Z0-9_])*$/.test(value);

// wishes for future refactorings: get rid these three functions and call sendMessage<> directly
export const evaluateXpath = (xPath: string, element_id?: ElementId, originJdnHash?: string) =>
sendMessage.evaluateXpath({ xPath, element_id, originJdnHash });

Expand Down Expand Up @@ -175,3 +176,8 @@ export const getTaskStatus = (locator: LocatorValue) => {
// fallback for any unhandled cases
return xPathStatus || cssSelectorStatus;
};

export const hasAllLocators = ({ locator }: Locator) =>
locator && locator.xPath !== locator.fullXpath && locator.cssSelector;

export const getNoLocatorsElements = (locators: Locator[]) => locators.filter((locator) => !hasAllLocators(locator));
2 changes: 1 addition & 1 deletion src/pageServices/connector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Connector {
port?: chrome.runtime.Port;
onerror: (err: Error) => void;
onmessage: (
payload: { message: string; param: Record<string, never> },
payload: { message: ScriptMsg; param: Record<string, never> },
sender: chrome.runtime.MessageSender,
sendResponse: (response: any) => void
) => void;
Expand Down
28 changes: 20 additions & 8 deletions src/pageServices/contentScripts/selectable.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,16 @@ export const selectable = () => {
const selected = new Set();
const deselected = new Set();
const a = rb();
if (!a) {
return;
}
if (!a) return;

delete self.ipos;
document.body.classList.remove("s-noselect");
document.body.removeEventListener("mousemove", self.rectDraw);
document.body.removeEventListener("mouseup", self.select);
document.body.removeEventListener("mouseleave", self.select);

const s = self.options.selectedClass;

const toggleActiveClass = function (el) {
if (el.classList.contains(s)) {
deselected.add(el.id);
Expand All @@ -237,12 +237,23 @@ export const selectable = () => {
};
if (isPlainClick(a)) {
const highlightTarget = e.target.closest("[jdn-highlight=true]:not([id^='jdn-overlay'])");
const isActiveTarget = highlightTarget && highlightTarget.classList.contains(self.options.selectedClass);
const isActiveGroup =
document.querySelectorAll(`[jdn-highlight=true].${self.options.selectedClass}`).length > 0;

if (highlightTarget && !self.isContextForGroup(e)) {
// simple click on any highlight
if (!e[self.options.moreUsing]) self.removePreviousSelection();
toggleActiveClass(highlightTarget);
} else if (!highlightTarget) self.removePreviousSelection(); // simple click outside highlight
/** single click on any highlight **/
if (!e[self.options.moreUsing]) {
/** remove selection from all items, except just clicked **/
if (isActiveGroup) self.removePreviousSelection([highlightTarget.id]);
/** make target active, if it's still not **/
if (!isActiveTarget) toggleActiveClass(highlightTarget);
} else {
/** with "more button" used, active class is always converted **/
toggleActiveClass(highlightTarget);
}
/* single click outside highlight cancels all selections */
} else if (!highlightTarget) self.removePreviousSelection();
} else {
self.selectedItems = new Set();
self.foreach(self.items, function (el) {
Expand All @@ -263,9 +274,10 @@ export const selectable = () => {
if (self.options.stop) self.options.stop(e);
};

this.removePreviousSelection = function () {
this.removePreviousSelection = function (exceptIds) {
const deselected = new Set();
self.foreach(self.items, function (el) {
if (exceptIds && exceptIds.includes(el.id)) return;
const s = self.options.selectedClass;
if (el.classList.contains(s)) {
el.classList.remove(s);
Expand Down
8 changes: 4 additions & 4 deletions src/pageServices/scriptMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ import { rerunGeneration } from "../features/locators/reducers/rerunGeneration.t
import { stopGenerationGroup } from "../features/locators/reducers/stopGenerationGroup.thunk";
import { copyLocator } from "../features/locators/utils/utils";
import { selectLocatorByJdnHash } from "../features/locators/selectors/locators.selectors";
import { ScriptMsg } from "./scriptMsg.constants";
import { ScriptMsg, dispatchingMessages } from "./scriptMsg.constants";
import { selectPresentActiveLocators } from "../features/locators/selectors/locatorsByPO.selectors";
import { selectCurrentPageObject } from "../features/pageObjects/selectors/pageObjects.selectors";

export type ScriptMessagePayload = { message: keyof Actions; param: Record<string, never> };

type Response<T> = (payload: T, sender: chrome.runtime.MessageSender, sendResponse: (response: any) => void) => void;

type Actions<P = any> = Record<string, Response<P>>;
type Actions<P = any> = Partial<Record<ScriptMsg, Response<P>>>;

export const updateMessageHandler = (
dispatch: Dispatch<{ payload?: any; type?: string } | AsyncThunkAction<any, any, any>>,
Expand Down Expand Up @@ -101,8 +101,8 @@ export const updateMessageHandler = (
_actions: Actions
) => {
if (_actions[message]) {
_actions[message](param, sender, sendResponse);
dispatch(setScriptMessage({ message, param }));
_actions[message]!(param, sender, sendResponse);
if (dispatchingMessages.includes(message)) dispatch(setScriptMessage({ message, param }));
}
};

Expand Down
2 changes: 2 additions & 0 deletions src/pageServices/scriptMsg.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,5 @@ export enum ScriptMsg {
StopGroupGeneration = "StopGroupGeneration",
ToggleElementGroup = "ToggleElementGroup",
}

export const dispatchingMessages = [ScriptMsg.OpenEditLocator];
Loading