Skip to content

Commit

Permalink
Merge pull request #1761 from jdi-testing/issue_1659-data-attributes-…
Browse files Browse the repository at this point in the history
…in-dropdown

Issue 1659: data attributes in dropdown
  • Loading branch information
Iogsotot authored Aug 1, 2024
2 parents 464a435 + 7328eb8 commit 2fc9924
Show file tree
Hide file tree
Showing 15 changed files with 204 additions and 76 deletions.
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 — Page Object Generator",
"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.16.1",
"version": "3.16.2",
"icons": {
"128": "icon128.png"
},
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

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.16.1",
"version": "3.16.2",
"description": "jdn-ai chrome extension",
"scripts": {
"start": "webpack --watch --env devenv",
Expand Down
5 changes: 3 additions & 2 deletions src/common/components/DialogWithForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface OnboardingRefProps {
onNextClickHandler: () => void;
isOkButtonDisabled: boolean;
}

interface DialogFormProps {
modalProps: JDNModalProps;
children?: ReactNode;
Expand Down Expand Up @@ -79,11 +80,11 @@ export const DialogWithForm: React.FC<DialogFormProps> = ({ modalProps, formProp
onOk={onOk}
open={open}
okButtonProps={okButtonProps}
{...{ ...restModal }}
{...restModal}
>
{open ? (
<div className="jdn__dialog-with-form" ref={modalRef as React.LegacyRef<HTMLDivElement>}>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} {...{ form }} {...{ ...restForm }}>
<Form labelCol={{ span: 4 }} wrapperCol={{ span: 20 }} form={form} {...restForm}>
{children}
</Form>
</div>
Expand Down
3 changes: 0 additions & 3 deletions src/features/locators/Locator.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// ToDo fix naming according to naming-convention
/* eslint-disable @typescript-eslint/naming-convention */
import React, { FC, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import { Button, Checkbox } from 'antd';
import { DotsThree } from '@phosphor-icons/react';
Expand Down Expand Up @@ -52,7 +50,6 @@ interface Props {

export const Locator: FC<Props> = ({ element, currentPage, searchState, depth, searchString, index }) => {
const dispatch = useDispatch();

const [isEditModalOpen, setIsEditModalOpen] = useState(false);

const {
Expand Down
65 changes: 44 additions & 21 deletions src/features/locators/components/LocatorEditDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// ToDo: fix TS, remove the comment when there is time in the sprint
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck
import React, { useEffect, useState } from 'react';
import { Form, Input, Select, Space } from 'antd';
import Icon from '@ant-design/icons';
Expand Down Expand Up @@ -80,6 +83,7 @@ export const LocatorEditDialog: React.FC<Props> = ({
const [isEditedName, setIsEditedName] = useState<boolean>(isCustomName);

const [form] = Form.useForm<FormValues>();

const isCurrentFrameworkVividus = pageObjectFramework === FrameworkType.Vividus;
// ToDo rewrite all related logic and tests for defaultLocatorType: string (because it's string)
const defaultLocatorType: LocatorType = locatorType || pageObjectLocatorType || LocatorType.xPath;
Expand All @@ -105,6 +109,25 @@ export const LocatorEditDialog: React.FC<Props> = ({
};

const notShownElementIds = useSelector(selectNotShownElementIds);

const [textareaValue, setTextareaValue] = useState('');

const handleTypeChangeBySelect = (value: string, option: { desc: React.SetStateAction<string> }) => {
console.log('handleTypeChangeBySelect ', option.desc);
setTextareaValue(option.desc);
form.setFieldValue('locator', option.desc);

if (isEditedName) return;

const newName = createNewName(
{ elementId, isCustomName, type, name, elemId, elemName, elemText } as ILocator,
value,
library,
locators,
);
form.setFieldValue('name', newName);
};

const getLocatorValidationRules: () => Rule[] = () =>
createLocatorValidationRules(
isCreatingForm,
Expand All @@ -116,19 +139,8 @@ export const LocatorEditDialog: React.FC<Props> = ({
elementId,
notShownElementIds,
);
const [locatorValidationRules, setLocatorValidationRules] = useState<Rule[]>(getLocatorValidationRules());

const handleTypeChange = (value: string) => {
if (isEditedName) return;

const newName = createNewName(
{ elementId, isCustomName, type, name, elemId, elemName, elemText } as ILocator,
value,
library,
locators,
);
form.setFieldValue('name', newName);
};
const [locatorValidationRules, setLocatorValidationRules] = useState<Rule[]>(getLocatorValidationRules());

const handleNameChange = () => {
setIsEditedName(true);
Expand Down Expand Up @@ -191,7 +203,7 @@ export const LocatorEditDialog: React.FC<Props> = ({
if ((!validationMessage.length && !jdnHash) || validationMessage === LocatorValidationWarnings.NewElement) {
await dispatch(changeLocatorElement(updatedLocator));
} else {
dispatch(changeLocatorAttributes(updatedLocator));
dispatch(changeLocatorAttributes({ ...updatedLocator, isCurrentFrameworkVividus }));
}

closeDialog();
Expand Down Expand Up @@ -223,12 +235,12 @@ export const LocatorEditDialog: React.FC<Props> = ({
</div>
) : null;

const onLocatorTypeChange = async () => {
const onLocatorTypeChange = async (newLocatorType: LocatorType) => {
setLocatorValidationRules(getLocatorValidationRules());
const newLocatorType = form.getFieldValue('locatorType');
const newLocator = form.getFieldValue('locator');

if (newLocator !== '') {
const previousLocatorValue = form.getFieldValue('locator');

if (previousLocatorValue !== '') {
const newLocatorValue = await getLocatorValueOnTypeSwitch(
newLocatorType,
validationMessage,
Expand All @@ -243,7 +255,10 @@ export const LocatorEditDialog: React.FC<Props> = ({

const onFieldsChange = async (changedValues: FieldData[]) => {
const isLocatorTypeChanged = changedValues.some((value) => value.name.toString().includes('locatorType'));
if (isLocatorTypeChanged) await onLocatorTypeChange();
if (isLocatorTypeChanged) {
const newLocatorType = changedValues.find((value) => value.name.toString().includes('locatorType'))?.value;
await onLocatorTypeChange(newLocatorType);
}
setIsOkButtonDisabled(computeIsOkButtonDisabled());
};

Expand All @@ -264,6 +279,7 @@ export const LocatorEditDialog: React.FC<Props> = ({
value: value,
}));
const [locatorTypeOptions, setLocatorTypeOptions] = useState<ILocatorTypeOptions[]>([]);

useEffect(() => {
if (!isCreatingForm) {
const fetchLocatorTypeOptions = async () => {
Expand All @@ -281,7 +297,8 @@ export const LocatorEditDialog: React.FC<Props> = ({
}
}, [locatorValue, locators, elementId]);

const handleLocatorDropdownOnChange = async () => {
const handleLocatorDropdownOnChange = async (_: any, option: { desc: React.SetStateAction<string> }) => {
setTextareaValue(option.desc);
setValidationMessage('');

try {
Expand Down Expand Up @@ -344,7 +361,7 @@ export const LocatorEditDialog: React.FC<Props> = ({
]}
>
<Select
onChange={handleTypeChange}
onChange={handleTypeChangeBySelect}
showSearch
filterOption={(input, option) => isFilteredSelect(input, option)}
options={getBlockTypeOptions()}
Expand Down Expand Up @@ -379,7 +396,13 @@ export const LocatorEditDialog: React.FC<Props> = ({
help={renderValidationMessage()}
extra={renderValidationWarning()}
>
<Input.TextArea spellCheck={false} disabled={isLocatorDisabled} autoSize className="input input__textarea" />
<Input.TextArea
value={textareaValue}
spellCheck={false}
disabled={isLocatorDisabled}
autoSize
className="input input__textarea"
/>
</Form.Item>
</DialogWithForm>
);
Expand Down
14 changes: 3 additions & 11 deletions src/features/locators/components/LocatorsTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux';
import { CaretDown } from '@phosphor-icons/react';
import { selectCurrentPage } from '../../../app/main.selectors';
import { RootState } from '../../../app/store/store';
import { ElementId, ILocator } from '../types/locator.types';
import { ElementId } from '../types/locator.types';
import { defaultLibrary } from '../types/generationClasses.types';
import { LocatorsProgress } from './LocatorsProgress';
import { useSize } from '../utils/useSize';
Expand All @@ -19,7 +19,7 @@ import { checkForEscaped, fullEscapeLocatorString } from '../utils/escapeLocator
import { LocatorType } from '../../../common/types/common';
import type RcTree from 'rc-tree';
import cn from 'classnames';
import { getTaskStatus } from '../utils/utils';
import { createLocatorsMap, getTaskStatus } from '../utils/utils';

export enum SearchState {
None = 'none',
Expand Down Expand Up @@ -94,15 +94,7 @@ export const LocatorsTree: React.FC<LocatorTreeProps> = ({ locatorIds, viewProps
setExpandAll(ExpandState.Custom);
};

const createLocatorsMap = () => {
const map: Record<ElementId, ILocator> = {};
for (let index = 0; index < locators.length; index++) {
map[locators[index].elementId] = locators[index];
}
return map;
};

const locatorsMap = createLocatorsMap();
const locatorsMap = createLocatorsMap(locators);

const locatorsTree = useMemo(
() => convertListToTree(locators, searchString),
Expand Down
1 change: 1 addition & 0 deletions src/features/locators/locators.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ChangeLocatorAttributesPayload {
isCustomName?: boolean;
isGeneratedName?: boolean;
locatorType: LocatorType;
isCurrentFrameworkVividus?: boolean;
}

const locatorsSlice = createSlice({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export const changeLocatorElement = createAsyncThunk(
elemText: foundElementText || '',
jdnHash: foundHash,
};
// почему здесь так, почему не отправляется на бэк WS message? для генерации xPath (и cssSelector)

if (isCSSLocator) {
fullXpath = await getElementFullXpath(foundHash);
newValue.locatorValue.cssSelector = locatorValue;
Expand Down
61 changes: 41 additions & 20 deletions src/features/locators/utils/createLocatorTypeOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ interface IOptionsWithLabel {
}

export interface ILocatorTypeOptions {
value?: string;
label: string;
value?: string;
desc?: string;
options?: IOptionsWithLabel[];
}

const generateOptionsWithLabel = (attributes: ElementAttributes): IOptionsWithLabel[] => {
const generateOptionsWithLabel = (attributes: ElementAttributes, isVividusFramework: boolean): IOptionsWithLabel[] => {
const generateLabel = (locatorType: string, attribute: string | null) => {
if (attribute === null || attribute === '') {
return (
Expand All @@ -37,21 +38,43 @@ const generateOptionsWithLabel = (attributes: ElementAttributes): IOptionsWithLa
return <>{locatorType}</>;
};

return Object.keys(attributes).map((key) => {
let locatorType = key;
const options: IOptionsWithLabel[] = [];

for (const dataKey of Object.keys(attributes)) {
let locatorType = dataKey;
if (locatorType === 'cssSelector') locatorType = 'CSS Selector';
const option: IOptionsWithLabel = {
label: generateLabel(locatorType, attributes[key as keyof ElementAttributes] as string),
value: locatorType,
desc: attributes[key as keyof ElementAttributes] as string,
};

if (attributes[key as keyof ElementAttributes] === null) {
option.disabled = true;

if (isVividusFramework && dataKey.startsWith('data-') && attributes[dataKey as keyof ElementAttributes]) {
const dataValue: string = attributes[dataKey as keyof ElementAttributes] as string;

if (dataValue !== null && dataValue !== '') {
options.push({
label: generateLabel('CSS Selector', dataValue),
value: `cssSelector-data-${dataKey}`,
desc: `*[${dataKey}="${dataValue}"]`,
});
options.push({
label: generateLabel('xPath', dataValue),
value: `xPath-data-${dataKey}`,
desc: `//*[@${dataKey}='${dataValue}']`,
});
}
} else {
const option: IOptionsWithLabel = {
label: generateLabel(locatorType, attributes[dataKey as keyof ElementAttributes] as string),
value: locatorType,
desc: attributes[dataKey as keyof ElementAttributes] as string,
};

if (attributes[dataKey as keyof ElementAttributes] === null) {
option.disabled = true;
}

options.push(option);
}
}

return option;
});
return options;
};

const addRestAttributes = (
Expand All @@ -65,7 +88,6 @@ const addRestAttributes = (
const updatedUniqueAttributes: ExtendedElementAttributes = { ...uniqueAttributes, xPath, cssSelector };
const updatedNonUniqueAttributes: ElementAttributes = nonUniqueAttributes;

// Add noData fields:
Object.entries(allLocatorAttributes).forEach(([key, value]) => {
if (!updatedUniqueAttributes.hasOwnProperty(key) && !updatedNonUniqueAttributes.hasOwnProperty(key)) {
updatedNonUniqueAttributes[key as keyof ElementAttributes] = value;
Expand All @@ -79,13 +101,14 @@ const getLocatorTypeOptions = (
attributes: ElementAttributes[],
cssSelector: string | null,
xPath: string | null,
isVividusFramework: boolean,
): ILocatorTypeOptions[] => {
const [updatedUniqueAttributes, updatedNonUniqueAttributes] = addRestAttributes(attributes, cssSelector, xPath);

const uniqueOptions = generateOptionsWithLabel(updatedUniqueAttributes)
const uniqueOptions = generateOptionsWithLabel(updatedUniqueAttributes, isVividusFramework)
.slice()
.sort((a, b) => a.value.localeCompare(b.value));
const nonUniqueOptions = generateOptionsWithLabel(updatedNonUniqueAttributes)
const nonUniqueOptions = generateOptionsWithLabel(updatedNonUniqueAttributes, isVividusFramework)
.slice()
.sort((a, b) => a.value.localeCompare(b.value));

Expand Down Expand Up @@ -155,14 +178,12 @@ const splitUniqueAndNonUniqueAttributes = async (attributes: ElementAttributes):
export const createLocatorTypeOptions = async (locatorValue: LocatorValue, isVividusFramework: boolean) => {
const attributes: ElementAttributes = {};
Object.assign(attributes, locatorValue.attributes);
if (isVividusFramework) {
delete attributes.dataAttributes;
}

const optionsData = await splitUniqueAndNonUniqueAttributes(attributes);
return getLocatorTypeOptions(
optionsData,
locatorValue.cssSelector ?? locatorValue.originalCssSelector ?? null,
locatorValue.xPath,
isVividusFramework,
);
};
2 changes: 1 addition & 1 deletion src/features/locators/utils/escapeLocatorString.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ export const fullEscapeLocatorString = (str: string = ''): string => {

export const checkForEscaped = (str: string = ''): boolean => {
const found = str.match(/\\/);
return found ? true : false;
return !!found;
};
Loading

0 comments on commit 2fc9924

Please sign in to comment.