From 4d270c5ee6eb9c1acdb507eb07e990aac5bee352 Mon Sep 17 00:00:00 2001 From: Anna Iustus Date: Wed, 13 Dec 2023 21:36:36 +0100 Subject: [PATCH 1/5] refactor: code style, types --- src/features/locators/locators.slice.ts | 16 ++++----- src/features/locators/types/locator.types.ts | 24 ++++++------- .../locators/utils/locatorValidation.ts | 23 ++---------- src/features/locators/utils/utils.ts | 35 +++++++++++++++++-- 4 files changed, 54 insertions(+), 44 deletions(-) diff --git a/src/features/locators/locators.slice.ts b/src/features/locators/locators.slice.ts index c14b6c2b..cfb4fff5 100644 --- a/src/features/locators/locators.slice.ts +++ b/src/features/locators/locators.slice.ts @@ -70,13 +70,14 @@ const locatorsSlice = createSlice({ const newValue: ILocator = { ...currentLocator, locator: { ...currentLocator.locator }, locatorType, ...rest }; const isStandardLocator: boolean = - locatorType === LocatorType.cssSelector || - locatorType === LocatorType.className || - locatorType === LocatorType.id || - locatorType === LocatorType.linkText || - locatorType === LocatorType.name || - locatorType === LocatorType.tagName || - locatorType.startsWith('data-'); + [ + LocatorType.cssSelector, + LocatorType.className, + LocatorType.id, + LocatorType.linkText, + LocatorType.name, + LocatorType.tagName, + ].includes(locatorType) || locatorType.startsWith('data-'); if (locatorType === LocatorType.cssSelector) { newValue.locator.cssSelector = locator; @@ -102,7 +103,6 @@ const locatorsSlice = createSlice({ if (rest.message === LocatorValidationWarnings.NotFound) { newValue.jdnHash = ''; } - locatorsAdapter.upsertOne(state, newValue); }, changeIdentificationStatus(state, { payload }: PayloadAction) { diff --git a/src/features/locators/types/locator.types.ts b/src/features/locators/types/locator.types.ts index 4d3e4799..93f142f4 100644 --- a/src/features/locators/types/locator.types.ts +++ b/src/features/locators/types/locator.types.ts @@ -54,18 +54,6 @@ export interface LocatorsState { export type ElementId = string; -export interface LocatorValue { - fullXpath?: string; - xPath: string | null; - attributes: ElementAttributes; - cssSelector: string | null; - cssSelectorStatus?: LocatorTaskStatus; // status of cssSelector calculation - xPathStatus?: LocatorTaskStatus; // status of xPath calculation - taskStatus?: LocatorTaskStatus; // status of both calculations - errorMessage?: string; // comes during the locator generation - output?: string; -} - export enum LocatorValidationErrors { DuplicatedName = 'This name already exists in the page object.', DuplicatedLocator = 'The locator for this element already exists.', @@ -82,6 +70,18 @@ export enum LocatorValidationWarnings { export type LocatorValidationErrorType = LocatorValidationErrors | LocatorValidationWarnings | ''; export type JDNHash = string; +export interface LocatorValue { + fullXpath?: string; + xPath: string | null; + attributes: ElementAttributes; + cssSelector: string | null; + cssSelectorStatus?: LocatorTaskStatus; // status of cssSelector calculation + xPathStatus?: LocatorTaskStatus; // status of xPath calculation + taskStatus?: LocatorTaskStatus; // status of both calculations + errorMessage?: string; // comes during the locator generation + output?: string; +} + export interface ILocator extends PredictedEntity { children?: ElementId[]; deleted?: boolean; diff --git a/src/features/locators/utils/locatorValidation.ts b/src/features/locators/utils/locatorValidation.ts index d838d4b9..2719bf10 100644 --- a/src/features/locators/utils/locatorValidation.ts +++ b/src/features/locators/utils/locatorValidation.ts @@ -8,27 +8,8 @@ import { JDNHash, ElementId, } from '../types/locator.types'; -import { checkDuplicates, evaluateStandardLocator, evaluateXpath } from './utils'; +import { checkDuplicates, evaluateLocator } from './utils'; -const prepareLocatorStringForEvaluation = (type: LocatorType, string: string): string => { - if (type === LocatorType.id) return `#${string}`; - if (type === LocatorType.className) return `.${string}`; - if (type === LocatorType.name) return `[name="${string}"]`; - return string; -}; - -const evaluateLocator = async ( - locatorString: string, - elementId: ElementId, - jdnHash: string, - locatorType: LocatorType, -) => { - if (locatorType === LocatorType.xPath) return evaluateXpath(locatorString, elementId, jdnHash); - else { - const preparedValue = prepareLocatorStringForEvaluation(locatorType, locatorString); - return evaluateStandardLocator(preparedValue, locatorType, elementId, jdnHash); - } -}; // ToDo: logic refactoring needed export const validateLocator = async ( locatorString: string, @@ -44,7 +25,7 @@ export const validateLocator = async ( let validatedJdnHash; let validationMessage: LocatorValidationErrorType = ''; - const locatorValue = await evaluateLocator(locatorString, element_id, jdnHash, locatorType); + const locatorValue = await evaluateLocator(locatorString, locatorType, element_id, jdnHash); if (locatorValue === LocatorValidationWarnings.NotFound || !locatorValue) { validationMessage = LocatorValidationWarnings.NotFound; //validationStatus: WARNING diff --git a/src/features/locators/utils/utils.ts b/src/features/locators/utils/utils.ts index 7fa6c0dc..62cbb103 100644 --- a/src/features/locators/utils/utils.ts +++ b/src/features/locators/utils/utils.ts @@ -41,6 +41,26 @@ export const evaluateStandardLocator = ( originJdnHash?: string, ) => sendMessage.evaluateStandardLocator({ selector, locatorType, element_id, originJdnHash }); +const prepareLocatorStringForEvaluation = (type: LocatorType, string: string): string => { + if (type === LocatorType.id) return `#${string}`; + if (type === LocatorType.className) return `.${string}`; + if (type === LocatorType.name) return `[name="${string}"]`; + return string; +}; + +export const evaluateLocator = async ( + locatorString: string, + locatorType: LocatorType, + elementId: ElementId, + jdnHash?: string, +) => { + if (locatorType === LocatorType.xPath) return evaluateXpath(locatorString, elementId, jdnHash); + else { + const preparedValue = prepareLocatorStringForEvaluation(locatorType, locatorString); + return evaluateStandardLocator(preparedValue, locatorType, elementId, jdnHash); + } +}; + export const generateSelectorByHash = (element_id: ElementId, jdnHash: string) => sendMessage.generateSelectorByHash({ element_id, jdnHash }); @@ -86,6 +106,7 @@ export const setIndents = (ref: React.RefObject, depth: number) } }; +// used in the coverage panel in the Copy option of the Context Menu: export const copyLocator = (framework: FrameworkType, locatorsForCopy: ILocator[], option?: LocatorOption) => (): void => { const isVividusFramework = framework === FrameworkType.Vividus; @@ -98,7 +119,9 @@ export const copyLocator = value = locatorsForCopy.map(({ locator }) => getLocatorWithSelenium(locator.xPath ?? '', 'xpath')); break; case LocatorOption.XpathAndJDI: - value = locatorsForCopy.map(({ locator }) => getLocatorWithJDIAnnotation(locator.xPath ?? '')); + value = locatorsForCopy.map(({ locator }) => + getLocatorWithJDIAnnotation(locator.xPath ?? '', LocatorType.xPath), + ); break; case LocatorOption.CSSSelector: value = locatorsForCopy.map(({ locator }) => `"${locator.cssSelector}"`); @@ -107,7 +130,9 @@ export const copyLocator = value = locatorsForCopy.map(({ locator }) => getLocatorWithSelenium(locator.cssSelector ?? '', 'css')); break; case LocatorOption.CSSAndJDI: - value = locatorsForCopy.map(({ locator }) => getLocatorWithJDIAnnotation(locator.cssSelector ?? '')); + value = locatorsForCopy.map(({ locator }) => + getLocatorWithJDIAnnotation(locator.cssSelector ?? '', LocatorType.cssSelector), + ); break; default: value = locatorsForCopy.map((element) => { @@ -184,7 +209,11 @@ export const getLocatorValueOnTypeSwitch = async ( ({ cssSelector: newLocatorValue } = await generateSelectorByHash(element_id, foundHash)); } else { if (newLocatorType === LocatorType.cssSelector) newLocatorValue = locatorValue.cssSelector; - newLocatorValue = getLocatorValueByType(locatorValue, newLocatorType); + try { + newLocatorValue = getLocatorValueByType(locatorValue, newLocatorType); + } catch (error) { + console.log('error: ', error); + } } } else { if (isLocatorLeadsToNewElement || !locatorValue.xPath) { From d747d902e20338e76e13525d903259ec1f77ba8e Mon Sep 17 00:00:00 2001 From: Anna Iustus Date: Wed, 13 Dec 2023 21:41:15 +0100 Subject: [PATCH 2/5] fix: update handle Create Custom Locator & add Custom Locator Thunk --- .../locators/components/LocatorEditDialog.tsx | 38 ++++----- .../reducers/addCustomLocator.thunk.ts | 78 +++++++++---------- 2 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/features/locators/components/LocatorEditDialog.tsx b/src/features/locators/components/LocatorEditDialog.tsx index d52bf573..db60e3ea 100644 --- a/src/features/locators/components/LocatorEditDialog.tsx +++ b/src/features/locators/components/LocatorEditDialog.tsx @@ -142,32 +142,32 @@ export const LocatorEditDialog: React.FC = ({ }; const handleCreateCustomLocator = async () => { - let newLocator: ILocator = { - ...newLocatorStub, - pageObj: pageObjectId, - isCustomName: isEditedName, - }; - const isLocatorFieldTouched = form.isFieldTouched('locator'); // in case if user didn't touch locator field to avoid forceUpdate const locatorMessage = isLocatorFieldTouched ? validationMessage : LocatorValidationWarnings.NotFound; - // ToDo: fix legacy: - // eslint-disable-next-line @typescript-eslint/no-shadow - const { name, type, locator, locatorType, annotationType } = await form.validateFields(); - const isCSSLocator = locatorType === LocatorType.cssSelector; - newLocator = { - ...newLocator, - locator: { ...newLocator.locator, ...{ [isCSSLocator ? 'cssSelector' : 'xPath']: locator } }, + const { + name: locatorFormName, + type: locatorFormElementClass, + locator: locatorFormValue, + locatorType: locatorFormType, + annotationType: locatorFormAnnotationType, + } = await form.validateFields(); + + const newLocatorData: ILocator & { locatorFormValue: string } = { + ...newLocatorStub, + pageObj: pageObjectId, + isCustomName: isEditedName, predicted_label: type.toLowerCase(), - annotationType, - locatorType, + annotationType: locatorFormAnnotationType, + locatorType: locatorFormType, message: locatorMessage, - name, - type, + name: locatorFormName, + type: locatorFormElementClass, + locatorFormValue, }; - dispatch(addCustomLocator({ newLocator, pageObjectId })); + await dispatch(addCustomLocator({ newLocatorData })); closeDialog(); }; @@ -190,7 +190,7 @@ export const LocatorEditDialog: React.FC = ({ // we need this check for the case when user edits custom invalid locator, that doesn't have jdnHash if ((!validationMessage.length && !jdnHash) || validationMessage === LocatorValidationWarnings.NewElement) { - dispatch(changeLocatorElement(updatedLocator)); + await dispatch(changeLocatorElement(updatedLocator)); } else { dispatch(changeLocatorAttributes(updatedLocator)); } diff --git a/src/features/locators/reducers/addCustomLocator.thunk.ts b/src/features/locators/reducers/addCustomLocator.thunk.ts index 7262f5ac..bee8bf35 100644 --- a/src/features/locators/reducers/addCustomLocator.thunk.ts +++ b/src/features/locators/reducers/addCustomLocator.thunk.ts @@ -3,25 +3,27 @@ import { ILocator, LocatorsState, ValidationStatus } from '../types/locator.type import { generateId, getElementFullXpath } from '../../../common/utils/helpers'; import { addLocatorToPageObj } from '../../pageObjects/pageObject.slice'; import { addLocators, setScrollToLocator, setActiveSingle } from '../locators.slice'; -import { - getLocatorValidationStatus, - evaluateXpath, - generateSelectorByHash, - evaluateStandardLocator, -} from '../utils/utils'; -import { PageObjectId } from '../../pageObjects/types/pageObjectSlice.types'; +import { getLocatorValidationStatus, evaluateXpath, generateSelectorByHash, evaluateLocator } from '../utils/utils'; import { sendMessage } from '../../../pageServices/connector'; import { locatorsAdapter } from '../selectors/locators.selectors'; import { LocatorType } from '../../../common/types/common'; export const addCustomLocator = createAsyncThunk( 'locators/addCustomLocator', - async (payload: { newLocator: ILocator; pageObjectId: PageObjectId }, thunkAPI) => { - let { newLocator } = payload; - const { pageObjectId } = payload; - const { message, locator, locatorType } = newLocator; + async (payload: { newLocatorData: ILocator & { locatorFormValue: string } }, thunkAPI) => { + const { newLocatorData } = payload; + const { message, locator, locatorType, pageObj: pageObjectId, locatorFormValue: locatorValue } = newLocatorData; - const isCSSLocator = locatorType === LocatorType.cssSelector; + const isXPathLocator = locatorType === LocatorType.xPath; + const isStandardLocator: boolean = + [ + LocatorType.cssSelector, + LocatorType.className, + LocatorType.id, + LocatorType.linkText, + LocatorType.name, + LocatorType.tagName, + ].includes(locatorType) || locatorType.startsWith('data-'); let foundHash; let foundElementText; @@ -31,21 +33,14 @@ export const addCustomLocator = createAsyncThunk( // eslint-disable-next-line @typescript-eslint/naming-convention const element_id = `${generateId()}_${pageObjectId}`; - newLocator = { - ...newLocator, - element_id, - }; - if (getLocatorValidationStatus(message) === ValidationStatus.SUCCESS) { try { - if (isCSSLocator && locator.cssSelector) { - ({ foundHash, foundElementText } = JSON.parse( - await evaluateStandardLocator(locator.cssSelector, LocatorType.cssSelector, element_id), - )); - } else { - if (locator.xPath) { - ({ foundHash, foundElementText } = JSON.parse(await evaluateXpath(locator.xPath, element_id))); - } + if ((isXPathLocator && locator.xPath) || (isStandardLocator && locatorValue)) { + const locatorData = await (isXPathLocator && locator.xPath + ? evaluateXpath(locator.xPath, element_id) + : evaluateLocator(locatorValue, locatorType, element_id)); + + ({ foundHash, foundElementText } = JSON.parse(locatorData)); } if (!foundHash) { @@ -53,8 +48,8 @@ export const addCustomLocator = createAsyncThunk( await sendMessage .assignJdnHash({ jdnHash: foundHash, - ...{ locator: isCSSLocator ? locator.cssSelector ?? '' : locator.xPath ?? '' }, - isCSSLocator, + ...{ locator: isStandardLocator ? locatorValue : locator.xPath ?? '' }, + isCSSLocator: isStandardLocator, }) .then((res) => { if (res === 'success') return res; @@ -65,24 +60,29 @@ export const addCustomLocator = createAsyncThunk( }); } - isCSSLocator - ? (fullXpath = await getElementFullXpath(foundHash)) - : ({ cssSelector } = await generateSelectorByHash(element_id, foundHash)); + if (isXPathLocator) { + ({ cssSelector } = await generateSelectorByHash(element_id, foundHash)); + } - newLocator = { - ...newLocator, - locator: { - ...newLocator.locator, - ...(isCSSLocator ? { xPath: fullXpath } : { cssSelector }), - }, - elemText: foundElementText || '', - jdnHash: foundHash, - }; + if (isStandardLocator) { + fullXpath = await getElementFullXpath(foundHash); + } } catch (err) { console.warn(err); } } + const newLocator: ILocator = { + ...newLocatorData, + element_id, + locator: { + ...newLocatorData.locator, + ...(isStandardLocator ? { xPath: fullXpath } : { cssSelector }), + }, + elemText: foundElementText || '', + jdnHash: foundHash, + }; + const dispatch = thunkAPI.dispatch; dispatch(addLocators([newLocator])); dispatch(addLocatorToPageObj({ pageObjId: pageObjectId, locatorId: newLocator.element_id })); From 81b44d7ddee899b0c1278e30f74d1548e7ebfb97 Mon Sep 17 00:00:00 2001 From: Anna Iustus Date: Wed, 13 Dec 2023 21:41:57 +0100 Subject: [PATCH 3/5] feat: rewrite change locator element thunk --- .../reducers/changeLocatorElement.thunk.ts | 98 ++++++++++--------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/src/features/locators/reducers/changeLocatorElement.thunk.ts b/src/features/locators/reducers/changeLocatorElement.thunk.ts index 97cd44eb..65580931 100644 --- a/src/features/locators/reducers/changeLocatorElement.thunk.ts +++ b/src/features/locators/reducers/changeLocatorElement.thunk.ts @@ -1,9 +1,9 @@ import { ActionReducerMapBuilder, createAsyncThunk } from '@reduxjs/toolkit'; -import { LocatorTaskStatus, LocatorsState } from '../types/locator.types'; +import { ILocator, LocatorTaskStatus, LocatorsState } from '../types/locator.types'; import { locatorsAdapter, selectLocatorById } from '../selectors/locators.selectors'; import { RootState } from '../../../app/store/store'; import { ChangeLocatorAttributesPayload } from '../locators.slice'; -import { evaluateXpath, generateSelectorByHash, evaluateStandardLocator } from '../utils/utils'; +import { generateSelectorByHash, evaluateLocator } from '../utils/utils'; import { generateId, getElementFullXpath } from '../../../common/utils/helpers'; import { sendMessage } from '../../../pageServices/connector'; import { LocatorType } from '../../../common/types/common'; @@ -16,71 +16,81 @@ export const changeLocatorElement = createAsyncThunk( // ToDo: fix legacy naming // eslint-disable-next-line @typescript-eslint/naming-convention const { locator, element_id, locatorType, ...rest } = payload; - const isCSSLocator = locatorType === LocatorType.cssSelector; - let foundHash; - let foundElementText; - let cssSelector; - let fullXpath; - const isStandardLocator: boolean = - locatorType === LocatorType.cssSelector || - locatorType === LocatorType.className || - locatorType === LocatorType.id || - locatorType === LocatorType.linkText || - locatorType === LocatorType.name || - locatorType === LocatorType.tagName || - locatorType.startsWith('data-'); + const state = thunkAPI.getState() as RootState; + const currentLocator = selectLocatorById(state, element_id); - if (isStandardLocator) { - ({ foundHash, foundElementText } = JSON.parse(await evaluateStandardLocator(locator, locatorType, element_id))); - } else { - ({ foundHash, foundElementText } = JSON.parse(await evaluateXpath(locator, element_id))); - } + if (!currentLocator) return; - const state = thunkAPI.getState() as RootState; - // ToDo: fix legacy naming - // eslint-disable-next-line @typescript-eslint/naming-convention - const _locator = selectLocatorById(state, element_id); + const isDataAttributeLocator = locatorType === LocatorType.dataAttributes || locatorType.startsWith('data-'); + const isCSSLocator = locatorType === LocatorType.cssSelector; + const isXPathLocator = locatorType === LocatorType.xPath; + const isStandardLocator = + [ + LocatorType.cssSelector, + LocatorType.className, + LocatorType.id, + LocatorType.linkText, + LocatorType.name, + LocatorType.tagName, + LocatorType.dataAttributes, + ].includes(locatorType) || locatorType.startsWith('data-'); - if (!_locator) return; + let foundHash, foundElementText, cssSelector, fullXpath; + + ({ foundHash, foundElementText } = JSON.parse(await await evaluateLocator(locator, locatorType, element_id))); if (!foundHash) { foundHash = generateId(); await sendMessage .assignJdnHash({ jdnHash: foundHash, locator, isCSSLocator }) .then((res) => { - if (res === 'success') return res; - else throw new Error('Failed to assign jdnHash'); + if (res !== 'success') throw new Error('Failed to assign jdnHash'); }) - .catch((err) => { - console.log(err); - }); + .catch((err) => console.log(err)); } - isCSSLocator - ? (fullXpath = await getElementFullXpath(foundHash)) - : ({ cssSelector } = await generateSelectorByHash(element_id, foundHash)); - - const newValue = { - ..._locator, - ...rest, + const newValue: ILocator = { + ...currentLocator, + locator: { ...currentLocator.locator, output: locator }, locatorType, + ...rest, elemText: foundElementText || '', jdnHash: foundHash, - locator: { - ...(isCSSLocator ? { cssSelector: locator, xPath: fullXpath } : { xPath: locator, cssSelector }), - ...(isCSSLocator - ? { cssSelectorStatus: LocatorTaskStatus.SUCCESS } - : { xPathStatus: LocatorTaskStatus.SUCCESS }), - }, }; + if (isCSSLocator) { + fullXpath = await getElementFullXpath(foundHash); + newValue.locator.cssSelector = locator; + newValue.locator.xPath = fullXpath; + newValue.locator.cssSelectorStatus = LocatorTaskStatus.SUCCESS; + } else if (isXPathLocator) { + cssSelector = (await generateSelectorByHash(element_id, foundHash)).cssSelector; + newValue.locator.cssSelector = cssSelector; + newValue.locator.xPath = locator; + newValue.locator.xPathStatus = LocatorTaskStatus.SUCCESS; + } else if (isStandardLocator) { + if (isDataAttributeLocator) { + newValue.locator.attributes = { + ...newValue.locator.attributes, + dataAttributes: { ...(newValue.locator.attributes?.dataAttributes || {}) }, + }; // unfreeze object + + if (newValue.locator.attributes.dataAttributes) { + newValue.locator.attributes.dataAttributes[locatorType] = locator; + } + } else { + const attributes = { ...newValue.locator.attributes }; // unfreeze object + attributes[locatorType] = locator; + newValue.locator.attributes = attributes; + } + } return newValue; }, ); export const changeLocatorElementReducer = (builder: ActionReducerMapBuilder) => { - return builder + builder .addCase(changeLocatorElement.fulfilled, (state, { payload }) => { // @ts-ignore locatorsAdapter.upsertOne(state, payload); From 9f6fc4879539819d73f0d9b333ab1cc656a4e038 Mon Sep 17 00:00:00 2001 From: Anna Iustus Date: Wed, 13 Dec 2023 21:43:39 +0100 Subject: [PATCH 4/5] fix: update getLocator fn, fix getting locator output string --- .../locators/selectors/locators.selectors.ts | 4 +- .../selectors/locatorsByPO.selectors.ts | 16 +++---- src/features/locators/utils/locatorOutput.tsx | 48 +++++++++++-------- src/pageServices/contentScripts/utils.ts | 4 +- 4 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/features/locators/selectors/locators.selectors.ts b/src/features/locators/selectors/locators.selectors.ts index 7f11ae3b..cac25f00 100644 --- a/src/features/locators/selectors/locators.selectors.ts +++ b/src/features/locators/selectors/locators.selectors.ts @@ -18,7 +18,7 @@ export const selectLocatorById = createSelector(selectById, (_item?: ILocator) = ..._item, locator: { ..._item.locator, - output: getLocator(_item.annotationType, _item.locator, _item.locatorType), + output: getLocator(_item.locator, _item.locatorType), taskStatus: getTaskStatus(_item.locator), }, }; @@ -32,7 +32,7 @@ export const selectLocators = createSelector(selectAll, (items: ILocator[]) => ..._item, locator: { ..._item.locator, - output: getLocator(_item.annotationType, _item.locator, _item.locatorType), + output: getLocator(_item.locator, _item.locatorType), taskStatus: getTaskStatus(_item.locator), }, }; diff --git a/src/features/locators/selectors/locatorsByPO.selectors.ts b/src/features/locators/selectors/locatorsByPO.selectors.ts index 04baf1ec..2128cb81 100644 --- a/src/features/locators/selectors/locatorsByPO.selectors.ts +++ b/src/features/locators/selectors/locatorsByPO.selectors.ts @@ -33,21 +33,21 @@ export const selectPresentLocatorsByPO = createSelector( (locators, pageObject) => { const locByPageObj = pageObject?.locators || []; return locators - .filter((loc) => locByPageObj.includes(loc.element_id)) - .map((loc) => { - const annotationType = loc.annotationType || pageObject?.annotationType; - const locatorType = loc.locatorType || pageObject?.locatorType || LocatorType.xPath; + .filter((locator) => locByPageObj.includes(locator.element_id)) + .map((locator) => { + const annotationType = locator.annotationType || pageObject?.annotationType; + const locatorType = locator.locatorType || pageObject?.locatorType || LocatorType.xPath; // ToDo: isDefaultLocatorType ??? - const isDefaultLocatorType = () => !loc.locatorType && pageObject?.locatorType === LocatorType.cssSelector; + const isDefaultLocatorType = () => !locator.locatorType && pageObject?.locatorType === LocatorType.cssSelector; return { - ...loc, + ...locator, ...(annotationType && { annotationType }), ...(locatorType && { locatorType }), ...(isDefaultLocatorType() && { locator: { - ...loc.locator, - output: getLocator(loc.annotationType, loc.locator, pageObject?.locatorType), + ...locator.locator, + output: getLocator(locator.locator, pageObject?.locatorType), }, }), }; diff --git a/src/features/locators/utils/locatorOutput.tsx b/src/features/locators/utils/locatorOutput.tsx index 0eed2ff4..ece2e397 100644 --- a/src/features/locators/utils/locatorOutput.tsx +++ b/src/features/locators/utils/locatorOutput.tsx @@ -7,18 +7,27 @@ import { camelCase } from 'lodash'; const getLocatorAnnotationStringByType = (value: string, locatorType: LocatorType, annotationType: AnnotationType) => { if (annotationType === AnnotationType.FindBy) return value; + const isDataAttributes = locatorType.startsWith('data-'); + // else return annotation string for UI Annotation Type: const annotations = { - 'CSS Selector': value, xPath: value, + 'CSS Selector': value, id: `#${value}`, - name: `[name='${value}']`, + name: `[name="${value}"]`, tagName: value, - className: `.${value}`, + className: value + .split(/\s+/) + .map((className) => `.${className}`) + .join(''), linkText: value, - dataAttributes: value, + dataAttributes: `[${locatorType}='${value}']`, }; + if (isDataAttributes) { + return annotations.dataAttributes; + } + return annotations[locatorType]; }; @@ -50,23 +59,14 @@ export const getLocatorValueByType = (locatorValue: LocatorValue, type: LocatorT } }; -export const getLocator = ( - annotationType: AnnotationType, - locatorValue: LocatorValue, - locatorType: LocatorType = LocatorType.xPath, -) => { - const preparedLocatorType = locatorType.startsWith('data-') ? LocatorType.dataAttributes : locatorType; - const annotationString: string = getLocatorAnnotationStringByType( - getLocatorValueByType(locatorValue, locatorType), - preparedLocatorType, - annotationType, - ); +export const getLocator = (locatorValue: LocatorValue, locatorType: LocatorType = LocatorType.xPath) => { + const locatorValueByType = getLocatorValueByType(locatorValue, locatorType); if (locatorType === LocatorType.cssSelector) { - return annotationString || CALCULATING; + return locatorValueByType || CALCULATING; } - return annotationString; + return locatorValueByType; }; export const getLocatorPrefix = (annotationType: AnnotationType, locatorType: LocatorType): string => { @@ -113,7 +113,12 @@ export const renderColorizedJdiString = ( {annotationType}({getLocatorPrefix(annotationType, locatorType)} - {`"${locatorOutput}"`}) + {`"${getLocatorAnnotationStringByType( + locatorOutput, + locatorType, + annotationType, + )}"`} + )
public  {type}  @@ -121,9 +126,10 @@ export const renderColorizedJdiString = ( ); }; - -export const getLocatorWithJDIAnnotation = (locator: string): string => `${AnnotationType.UI}("${locator}")`; - +// used in the coverage panel in the Copy option of the Context Menu: +export const getLocatorWithJDIAnnotation = (locator: string, locatorType: LocatorType): string => + `${AnnotationType.UI}("${getLocatorAnnotationStringByType(locator, locatorType, AnnotationType.UI)}")`; +// used in the coverage panel in the Copy option of the Context Menu: export const getLocatorWithSelenium = (locator: string, option: string): string => `${AnnotationType.FindBy}(${option} = "${locator}")`; diff --git a/src/pageServices/contentScripts/utils.ts b/src/pageServices/contentScripts/utils.ts index 528d7c3d..0ed03227 100644 --- a/src/pageServices/contentScripts/utils.ts +++ b/src/pageServices/contentScripts/utils.ts @@ -56,8 +56,8 @@ export const evaluateStandardLocator = ({ } const length = foundElements.length; - const foundHash = foundElements && foundElements[0].getAttribute('jdn-hash'); - const foundElementText = foundElements && foundElements[0].textContent; + const foundHash = foundElements[0] && foundElements[0].getAttribute('jdn-hash'); + const foundElementText = foundElements[0] && foundElements[0].textContent; return JSON.stringify({ length, foundHash, elementId, foundElementText, originJdnHash }); } catch (error) { console.error('error: ', error); From 8e0ce163f82843cdef04ed5eca01c5e6d3e6a24d Mon Sep 17 00:00:00 2001 From: Anna Iustus Date: Wed, 13 Dec 2023 21:44:47 +0100 Subject: [PATCH 5/5] fix: update tests for getLocator --- src/__tests__/getLocator.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/__tests__/getLocator.test.js b/src/__tests__/getLocator.test.js index 8f430826..7746c278 100644 --- a/src/__tests__/getLocator.test.js +++ b/src/__tests__/getLocator.test.js @@ -35,10 +35,10 @@ const data = [ describe('locator presentation by getLocator()', () => { data.forEach((_data) => { test(`converts ${JSON.stringify(_data.input, LocatorType.xPath)} to ${_data.xpathOutput}`, () => { - expect(getLocator(AnnotationType.UI, _data.input)).toBe(_data.xpathOutput); + expect(getLocator(_data.input)).toBe(_data.xpathOutput); }); test(`converts ${JSON.stringify(_data.input, LocatorType.cssSelector)} to ${_data.cssOutput}`, () => { - expect(getLocator(AnnotationType.UI, _data.input, LocatorType.cssSelector)).toBe(_data.cssOutput); + expect(getLocator(_data.input, LocatorType.cssSelector)).toBe(_data.cssOutput); }); }); });