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 1659: data attributes in dropdown #1761

Merged
merged 7 commits into from
Aug 1, 2024
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 — 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
Loading