diff --git a/manifest.json b/manifest.json index 0692b855..28f66597 100644 --- a/manifest.json +++ b/manifest.json @@ -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.14.35", + "version": "3.14.36", "icons": { "128": "icon128.png" }, diff --git a/package-lock.json b/package-lock.json index 236e5646..4af6e34b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "jdn-ai-chrome-extension", - "version": "3.14.35", + "version": "3.14.36", "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package.json b/package.json index 38dcbeba..80bd8f39 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jdn-ai-chrome-extension", - "version": "3.14.35", + "version": "3.14.36", "description": "jdn-ai chrome extension", "scripts": { "start": "webpack --watch --env devenv", diff --git a/src/features/locators/components/LocatorEditDialog.tsx b/src/features/locators/components/LocatorEditDialog.tsx index 0e2ddc93..ccdbcffb 100644 --- a/src/features/locators/components/LocatorEditDialog.tsx +++ b/src/features/locators/components/LocatorEditDialog.tsx @@ -1,4 +1,4 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Form, Input, Select } from 'antd'; import Icon from '@ant-design/icons'; import WarningFilled from '../assets/warning-filled.svg'; @@ -31,7 +31,7 @@ import { changeLocatorElement } from '../reducers/changeLocatorElement.thunk'; import { addCustomLocator } from '../reducers/addCustomLocator.thunk'; import { selectPresentLocatorsByPO } from '../selectors/locatorsByPO.selectors'; import { LocatorMessageForDuplicate } from './LocatorMessageForDuplicate'; -import { createLocatorTypeOptions } from '../utils/createLocatorTypeOptions'; +import { ILocatorTypeOptions, createLocatorTypeOptions } from '../utils/createLocatorTypeOptions'; import { validateLocator } from '../utils/locatorValidation'; interface Props extends ILocator { @@ -258,7 +258,19 @@ export const LocatorEditDialog: React.FC = ({ const isLocatorDisabled = form.getFieldValue('locator') === CALCULATING; - const locatorTypeOptions = createLocatorTypeOptions(locatorValue, locators, element_id); + const [locatorTypeOptions, setLocatorTypeOptions] = useState([]); + useEffect(() => { + const fetchLocatorTypeOptions = async () => { + try { + const options = await createLocatorTypeOptions(locatorValue); + setLocatorTypeOptions(options); + } catch (error) { + console.error('Error: can`t get options for locator:', error); + } + }; + + void fetchLocatorTypeOptions(); + }, [locatorValue, locators, element_id]); const handleLocatorDropdownOnChange = async () => { setValidationMessage(''); diff --git a/src/features/locators/utils/createLocatorTypeOptions.tsx b/src/features/locators/utils/createLocatorTypeOptions.tsx index 0b85097b..16f6ff44 100644 --- a/src/features/locators/utils/createLocatorTypeOptions.tsx +++ b/src/features/locators/utils/createLocatorTypeOptions.tsx @@ -1,13 +1,15 @@ import React from 'react'; -import { ILocator, LocatorValue } from '../types/locator.types'; +import { LocatorValue } from '../types/locator.types'; import { ElementAttributes, ExtendedElementAttributes, + LocatorType, locatorAttributesInitialState, } from '../../../common/types/common'; import { mergeObjects } from './mergeObjects'; import { Tooltip } from 'antd'; +import { evaluateLocator } from './utils'; interface IOptionsWithLabel { label: React.JSX.Element; @@ -15,7 +17,7 @@ interface IOptionsWithLabel { disabled?: boolean; } -interface ILocatorTypeOptions { +export interface ILocatorTypeOptions { label: string; options: IOptionsWithLabel[]; } @@ -23,7 +25,11 @@ interface ILocatorTypeOptions { const generateOptionsWithLabel = (attributes: ElementAttributes): IOptionsWithLabel[] => { const generateLabel = (locatorType: string, attribute: string | null) => { if (attribute === null || attribute === '') { - return {locatorType}; + return ( + + {locatorType} + + ); } return ( @@ -96,104 +102,58 @@ const getLocatorTypeOptions = ( ]; }; -const createHashMap = (locators: LocatorValue[]): Map> => { - const hashMap = new Map>(); - - const updateHashMap = (key: string, value: string | null) => { - if (!hashMap.has(key)) { - hashMap.set(key, new Set()); - } - - const valueSet = hashMap.get(key); - if (value !== null && value !== undefined) { - valueSet?.add(value); - } - }; - - locators.forEach((locator) => { - const attributes = locator.attributes; - - if (attributes) { - Object.keys(attributes).forEach((key) => { - if (key === 'dataAttributes' && attributes.dataAttributes) { - const dataAttributes = attributes.dataAttributes; - Object.keys(dataAttributes).forEach((dataKey) => { - const value = dataAttributes[dataKey]; - updateHashMap(dataKey, value); - }); - } else { - const value = attributes[key as keyof Omit]; - if (value) updateHashMap(key, value); - else { - throw new Error(`The key value of the ${key} value is undefined!`); - } - } - }); - } - }); - - return hashMap; -}; - -const splitUniqueAndNonUniqueAttributes = ( - attributes: ElementAttributes, - attributesHashMap: Map>, -): ElementAttributes[] => { +const splitUniqueAndNonUniqueAttributes = async (attributes: ElementAttributes): Promise => { const uniqueAttributes: ElementAttributes = {}; const nonUniqueAttributes: ElementAttributes = {}; - Object.entries(attributes).forEach(([key, value]) => { + for (const [key, value] of Object.entries(attributes)) { const keyType = key as keyof ElementAttributes; const isValueEmpty = value === '' || value === null || value === undefined; if (keyType === 'dataAttributes' && value) { - // Check and unpacked data-attributes: const dataAttributes: { [key: string]: string | null } = value; const uniqueDataAttributes: { [key: string]: string | null } = {}; const nonUniqueDataAttributes: { [key: string]: string | null } = {}; - Object.entries(dataAttributes).forEach(([dataKey, dataValue]) => { - const isDataValueEmpty = dataValue === '' || dataValue === null || dataValue === undefined; - const isNonUnique = - isDataValueEmpty || !attributesHashMap.has(dataKey) || !attributesHashMap.get(dataKey)?.has(dataValue); - if (isNonUnique) { + for (const [dataKey, dataValue] of Object.entries(dataAttributes)) { + if (dataValue === '' || dataValue === null || dataValue === undefined) { nonUniqueDataAttributes[dataKey] = dataValue; } else { - uniqueDataAttributes[dataKey] = dataValue; + const res = JSON.parse(await evaluateLocator(dataValue, dataKey as LocatorType)); + const isDataAttributeUnique = res.length === 1; + + if (isDataAttributeUnique) { + uniqueDataAttributes[dataKey] = dataValue; + } else { + nonUniqueDataAttributes[dataKey] = dataValue; + } } - }); + } if (Object.keys(uniqueDataAttributes).length > 0) { mergeObjects(uniqueAttributes, uniqueDataAttributes); } - if (Object.keys(nonUniqueDataAttributes).length > 0) { mergeObjects(nonUniqueAttributes, nonUniqueDataAttributes); } - } else { - const isNonUnique = isValueEmpty || !attributesHashMap.has(key) || !attributesHashMap.get(key)?.has(value); - if (isNonUnique) { - nonUniqueAttributes[keyType] = value; - } else { + } else if (!isValueEmpty) { + const res = JSON.parse(await evaluateLocator(value, key as LocatorType)); + const isAttributeUnique = res.length === 1; + + if (isAttributeUnique) { uniqueAttributes[keyType] = value; + } else { + nonUniqueAttributes[keyType] = value; } + } else { + nonUniqueAttributes[keyType] = value; } - }); + } return [uniqueAttributes, nonUniqueAttributes]; }; -export const createLocatorTypeOptions = ( - locatorValue: LocatorValue, - locators: ILocator[], - currentElementId: string, -) => { - const preparedData: LocatorValue[] = locators - .filter((element) => element.element_id !== currentElementId) - .map((element) => element.locatorValue); - - const hashMap: Map> = createHashMap(preparedData); - const optionsData = splitUniqueAndNonUniqueAttributes(locatorValue.attributes, hashMap); - +export const createLocatorTypeOptions = async (locatorValue: LocatorValue) => { + const optionsData = await splitUniqueAndNonUniqueAttributes(locatorValue.attributes); return getLocatorTypeOptions(optionsData, locatorValue.cssSelector, locatorValue.xPath); }; diff --git a/src/features/locators/utils/utils.ts b/src/features/locators/utils/utils.ts index 95250f11..333f58e7 100644 --- a/src/features/locators/utils/utils.ts +++ b/src/features/locators/utils/utils.ts @@ -51,7 +51,7 @@ const prepareLocatorStringForEvaluation = (type: LocatorType, string: string): s export const evaluateLocator = async ( locatorString: string, locatorType: LocatorType, - elementId: ElementId, + elementId?: ElementId, jdnHash?: string, ) => { if (locatorType === LocatorType.xPath) return evaluateXpath(locatorString, elementId, jdnHash);