Skip to content

Commit

Permalink
Merge pull request #1601 from jdi-testing/issue_1594_fix-ap-crash-on-…
Browse files Browse the repository at this point in the history
…validation-new-locator

Issue 1594: fix ap crash on validation new locator
  • Loading branch information
Iogsotot authored Dec 13, 2023
2 parents e584cc5 + 8e0ce16 commit b5d88b2
Show file tree
Hide file tree
Showing 12 changed files with 207 additions and 181 deletions.
4 changes: 2 additions & 2 deletions src/__tests__/getLocator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
});
38 changes: 19 additions & 19 deletions src/features/locators/components/LocatorEditDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,32 +142,32 @@ export const LocatorEditDialog: React.FC<Props> = ({
};

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();
};

Expand All @@ -190,7 +190,7 @@ export const LocatorEditDialog: React.FC<Props> = ({

// 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));
}
Expand Down
16 changes: 8 additions & 8 deletions src/features/locators/locators.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -102,7 +103,6 @@ const locatorsSlice = createSlice({
if (rest.message === LocatorValidationWarnings.NotFound) {
newValue.jdnHash = '';
}

locatorsAdapter.upsertOne(state, newValue);
},
changeIdentificationStatus(state, { payload }: PayloadAction<IdentificationStatus>) {
Expand Down
78 changes: 39 additions & 39 deletions src/features/locators/reducers/addCustomLocator.thunk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -31,30 +33,23 @@ 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) {
foundHash = element_id.split('_')[0];
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;
Expand All @@ -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 }));
Expand Down
98 changes: 54 additions & 44 deletions src/features/locators/reducers/changeLocatorElement.thunk.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<LocatorsState>) => {
return builder
builder
.addCase(changeLocatorElement.fulfilled, (state, { payload }) => {
// @ts-ignore
locatorsAdapter.upsertOne(state, payload);
Expand Down
4 changes: 2 additions & 2 deletions src/features/locators/selectors/locators.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
},
};
Expand All @@ -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),
},
};
Expand Down
Loading

0 comments on commit b5d88b2

Please sign in to comment.