diff --git a/manifest.json b/manifest.json index 3914342b..021f3911 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.13.561", + "version": "3.13.562", "icons": { "128": "icon128.png" }, diff --git a/package.json b/package.json index c6e745af..3ed9ed42 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jdn-ai-chrome-extension", - "version": "3.13.561", + "version": "3.13.562", "description": "jdn-ai chrome extension", "scripts": { "start": "webpack --watch --env devenv", diff --git a/src/__tests__/__mocks__/pageObjectMocks/pageObject.mock.js b/src/__tests__/__mocks__/pageObjectMocks/pageObject.mock.js index 4ea39e96..c4c3ed9f 100644 --- a/src/__tests__/__mocks__/pageObjectMocks/pageObject.mock.js +++ b/src/__tests__/__mocks__/pageObjectMocks/pageObject.mock.js @@ -206,6 +206,137 @@ export const locatorsWithFindBy = [ }, ]; +export const locatorsVividus = [ + { + element_id: "0057770603115098154872149076_0", + predicted_label: "label", + childs: ["1052464727115098158766915230"], + displayed: false, + jdnHash: "0057770603115098154872149076", + pageObj: 0, + elemName: "", + elemId: "", + elemText: "Simple Table", + elemAriaLabel: null, + locator: { + xPath: "//*[@index='3']//*[@index='4']/a", + fullXpath: "/html/body/div/div[1]/div/div[1]/div/div[1]/ul/li[3]/ul/li[4]/a", + cssSelector: '[index="\\33 "] [index="\\34 "] > a', + cssSelectorStatus: "SUCCESS", + xPathStatus: "SUCCESS", + output: '[index="\\33 "] [index="\\34 "] > a', + taskStatus: "SUCCESS", + }, + name: "simpleTable", + type: "Label", + parent_id: "", + children: [], + active: false, + generate: true, + annotationType: "@FindBy", + locatorType: "CSS selector", + }, + { + element_id: "0735357117115098156691382591_0", + predicted_label: "label", + childs: null, + displayed: false, + jdnHash: "0735357117115098156691382591", + pageObj: 0, + elemName: "", + elemId: "", + elemText: "User Table ", + elemAriaLabel: null, + locator: { + xPath: "//*[contains(text(), 'User Table ')]", + fullXpath: "/html/body/header/div/nav/ul[1]/li[3]/ul/li[6]/a", + cssSelector: '[role="menu"] > li:nth-child(6) > a', + cssSelectorStatus: "SUCCESS", + xPathStatus: "SUCCESS", + output: "//*[contains(text(), 'User Table ')]", + taskStatus: "SUCCESS", + }, + name: "userTable", + type: "Label", + parent_id: "", + children: [], + active: false, + locatorType: "xPath", + annotationType: "@FindBy", + library: "HTML5", + message: "", + isCustomName: true, + isCustomLocator: true, + generate: true, + }, + { + element_id: "0790139442115098150038335533_0", + predicted_label: "textarea", + childs: null, + displayed: false, + jdnHash: "0790139442115098150038335533", + pageObj: 0, + elemName: "", + elemId: "password", + elemText: "", + elemAriaLabel: null, + locator: { + xPath: "//*[@id='password']", + fullXpath: "/html/body/header/div/nav/ul[2]/li/div/form/div/div[2]/div/input", + cssSelector: "#password", + cssSelectorStatus: "SUCCESS", + xPathStatus: "SUCCESS", + output: "#password", + taskStatus: "SUCCESS", + }, + name: "password", + type: "TextArea", + parent_id: "", + children: [], + active: false, + locatorType: "CSS selector", + annotationType: "@UI", + library: "HTML5", + message: "", + isCustomName: true, + isCustomLocator: true, + generate: true, + }, + { + element_id: "0928942213115098152027249027_0", + predicted_label: "label", + childs: null, + displayed: false, + jdnHash: "0928942213115098152027249027", + pageObj: 0, + elemName: "", + elemId: "", + elemText: "Dates", + elemAriaLabel: null, + locator: { + xPath: "//a[contains(text(), 'Dates')]", + fullXpath: "/html/body/header/div/nav/ul[1]/li[3]/ul/li[2]/a", + cssSelector: '[role="menu"] > li:nth-child(2) > a', + cssSelectorStatus: "SUCCESS", + xPathStatus: "SUCCESS", + output: "//a[contains(text(), 'Dates')]", + taskStatus: "SUCCESS", + }, + name: "dates", + type: "Label", + parent_id: "", + children: [], + active: true, + locatorType: "xPath", + annotationType: "@UI", + library: "HTML5", + message: "", + isCustomName: true, + isCustomLocator: true, + generate: true, + }, +]; + export const pageObjectHTML = `package site.pages; import com.epam.jdi.light.elements.pageobjects.annotations.locators.*; @@ -321,3 +452,10 @@ public class HomePage extends WebPage { public badge badge; } `; + +export const pageObjectVividus = `variables.HomePage.url=(/jdi-light/index.html) +variables.HomePage.Label.simpleTable=By.cssSelector(xpath = [index="\\33 "] [index="\\34 "] > a) +variables.HomePage.Label.userTable=By.xPath(xpath = //*[contains(text(), 'User Table ')]) +variables.HomePage.TextArea.password=By.cssSelector(#password) +variables.HomePage.Label.dates=By.xPath(//a[contains(text(), 'Dates')]) +`; diff --git a/src/__tests__/pageObject/__mocks__/selectLocatorsByPageObject.mock.ts b/src/__tests__/pageObject/__mocks__/selectLocatorsByPageObject.mock.ts index efe16afb..c7ac0054 100644 --- a/src/__tests__/pageObject/__mocks__/selectLocatorsByPageObject.mock.ts +++ b/src/__tests__/pageObject/__mocks__/selectLocatorsByPageObject.mock.ts @@ -1,4 +1,4 @@ -import { LocatorType } from "../../../common/types/common"; +import { FrameworkType, LocatorType } from "../../../common/types/common"; import { ElementLibrary } from "../../../features/locators/types/generationClasses.types"; import { PageObject } from "../../../features/pageObjects/types/pageObjectSlice.types"; @@ -6,6 +6,7 @@ export const pageObject0: PageObject = { id: 0, name: "HomePage", url: "https://jdi-testing.github.io/jdi-light/index.html", + framework: FrameworkType.JdiLight, library: ElementLibrary.MUI, pathname: "/jdi-light/index.html", search: "", @@ -168,7 +169,7 @@ export const selectMockedLocators = (pageObject: PageObject) => [ elemId: "", elemText: "Home", elemAriaLabel: null, - ...(pageObject.locatorType === LocatorType.cssSelector ? { locatorType: LocatorType.cssSelector } : null), + locatorType: pageObject.locatorType || LocatorType.xPath, locator: { taskStatus: "PENDING", xPathStatus: "SUCCESS", diff --git a/src/__tests__/pageObject/pageObject.test.js b/src/__tests__/pageObject/pageObject.test.js index b6672e51..128e746c 100644 --- a/src/__tests__/pageObject/pageObject.test.js +++ b/src/__tests__/pageObject/pageObject.test.js @@ -1,9 +1,12 @@ import { + locators, pageObjectMUI, pageObjectHTML, pageObjectVuetify, pageObjectHTMLWithFindBy, getLocatorsByAnnotationType, + locatorsVividus, + pageObjectVividus, } from "../__mocks__/pageObjectMocks/pageObject.mock"; import { elementsWithoutNames } from "../__mocks__/pageObjectMocks/elementsWithoutNames"; import { elementsWithNames } from "../__mocks__/pageObjectMocks/elementsWithNames"; @@ -12,33 +15,59 @@ import { ElementLibrary } from "../../features/locators/types/generationClasses. import { pageObjectTemplate } from "../../features/pageObjects/utils/pageObjectTemplate"; import { createLocatorNames } from "../../features/pageObjects/utils/pageObject"; import { getClassName } from "../../features/pageObjects/utils/pageObjectTemplate"; -import { AnnotationType } from "../../common/types/common"; +import { AnnotationType, LocatorType } from "../../common/types/common"; const templateTestData = [ { - input: "HTML", + pageObject: { + framework: "JDI Light", + library: "HTML", + name: "HomePage", + }, output: pageObjectHTML, }, { - input: "MUI", + pageObject: { + framework: "JDI Light", + library: "MUI", + name: "HomePage", + }, output: pageObjectMUI, }, { - input: "Vuetify", + pageObject: { + framework: "JDI Light", + library: "Vuetify", + name: "HomePage", + }, output: pageObjectVuetify, }, ]; const templateTestDataWithFindBy = { - input: "HTML", + pageObject: { + framework: "JDI Light", + library: "HTML", + name: "HomePage", + }, output: pageObjectHTMLWithFindBy, }; +const templateTestDataVividus = { + pageObject: { + framework: "Vividus", + library: "HTML", + name: "HomePage", + pathname: "/jdi-light/index.html", + locatorType: LocatorType.cssSelector, + }, + output: pageObjectVividus, +}; + describe("page object code generation", () => { - const locators = getLocatorsByAnnotationType(AnnotationType.UI); - templateTestData.forEach(({ input, output }) => { - test(`page object generated with ${input}`, () => { - const page = pageObjectTemplate(locators, "HomePage", input); + templateTestData.forEach(({ pageObject, output }) => { + test(`page object generated with ${pageObject.library}`, () => { + const page = pageObjectTemplate(locators, pageObject); expect(page.pageCode).toBe(output); expect(page.title).toBe("HomePage"); }); @@ -50,10 +79,16 @@ describe("page object code generation", () => { }); }); + test("generate page object template for Vividus", () => { + const { pageObject, output } = templateTestDataVividus; + const page = pageObjectTemplate(locatorsVividus, pageObject); + expect(page.pageCode).toBe(output); + }); + describe("pageObjectTemplate should return pageObjectHTML with FindBy import", () => { const locators = getLocatorsByAnnotationType(AnnotationType.FindBy); - test(`when page object generated with ${templateTestDataWithFindBy.input} and locators has Annotation Type === 'FindBy'`, () => { - const page = pageObjectTemplate(locators, "HomePage", templateTestDataWithFindBy.input); + test(`when page object generated with ${templateTestDataWithFindBy.pageObject.library} and locators has Annotation Type === 'FindBy'`, () => { + const page = pageObjectTemplate(locators, templateTestDataWithFindBy.pageObject); expect(page.pageCode).toBe(templateTestDataWithFindBy.output); expect(page.title).toBe("HomePage"); }); diff --git a/src/__tests__/pageObject/utils/editPomContent.test.ts b/src/__tests__/pageObject/utils/editPomContent.test.ts index 31ed3d6e..15df7db2 100644 --- a/src/__tests__/pageObject/utils/editPomContent.test.ts +++ b/src/__tests__/pageObject/utils/editPomContent.test.ts @@ -1,3 +1,4 @@ +import { FrameworkType, LocatorType } from "../../../common/types/common"; import { ElementLibrary } from "../../../features/locators/types/generationClasses.types"; import { editPomContent } from "../../../features/pageObjects/utils/templateFileContent"; import { html5Result, muiResult, vuetifyResult } from "../__mocks__/pomTemplates.mock"; @@ -23,15 +24,30 @@ describe("editPomContent function", () => { const testData = [ { output: html5Result, - po: { ...pageObject0, library: ElementLibrary.HTML5 }, + po: { + ...pageObject0, + framework: FrameworkType.JdiLight, + locator: LocatorType.xPath, + library: ElementLibrary.HTML5, + }, }, { output: muiResult, - po: { ...pageObject0, library: ElementLibrary.MUI }, + po: { + ...pageObject0, + framework: FrameworkType.JdiLight, + locator: LocatorType.xPath, + library: ElementLibrary.MUI, + }, }, { output: vuetifyResult, - po: { ...pageObject0, library: ElementLibrary.Vuetify }, + po: { + ...pageObject0, + framework: FrameworkType.JdiLight, + locator: LocatorType.xPath, + library: ElementLibrary.Vuetify, + }, }, ]; diff --git a/src/app/App.tsx b/src/app/App.tsx index 62294c74..41d73743 100644 --- a/src/app/App.tsx +++ b/src/app/App.tsx @@ -25,7 +25,8 @@ import { OnboardingProvider } from "../features/onboarding/OnboardingProvider"; import { isPageObjectPage } from "./utils/heplers"; const App = () => { - const [template, setTemplate] = useState(undefined); + const [jdiTemplate, setJdiTemplate] = useState(undefined); + const [vividusTemplate, setVividusTemplate] = useState(undefined); const backendAvailable = useSelector((state: RootState) => state.main.backendAvailable); const xpathConfig = useSelector((state: RootState) => state.main.xpathConfig); const currentPage = useSelector(selectCurrentPage); @@ -43,19 +44,20 @@ const App = () => { }, []); useEffect(() => { - const fetchTemplate = async () => { - setTemplate(await request.getBlob(HttpEndpoint.DOWNLOAD_TEMPLATE)); + const fetchTemplates = async () => { + setJdiTemplate(await request.getBlob(HttpEndpoint.DOWNLOAD_TEMPLATE)); + setVividusTemplate(await request.getBlob(HttpEndpoint.DOWNLOAD_TEMPLATE_VIVIDUS)); }; if (backendAvailable === BackendStatus.Accessed) { - fetchTemplate(); + fetchTemplates(); initLocatorSocketController(xpathConfig); } }, [backendAvailable]); const renderPage = () => { const { page } = currentPage; - return isPageObjectPage(page) ? : ; + return isPageObjectPage(page) ? : ; }; return ( diff --git a/src/common/utils/localStorage.ts b/src/common/utils/localStorage.ts index 44ab3d8d..e35dcd90 100644 --- a/src/common/utils/localStorage.ts +++ b/src/common/utils/localStorage.ts @@ -4,6 +4,7 @@ export enum LocalStorageKey { AnnotationType = "JDN_ANNOTATION_TYPE", LocatorType = "JDN_LOCATOR_TYPE", Library = "JDN_LIBRARY", + Framework = "JDN_FRAMEWORK", Filter = "JDN_FILTER", } diff --git a/src/features/locators/selectors/locatorsByPO.selectors.ts b/src/features/locators/selectors/locatorsByPO.selectors.ts index b9c40d19..17279cad 100644 --- a/src/features/locators/selectors/locatorsByPO.selectors.ts +++ b/src/features/locators/selectors/locatorsByPO.selectors.ts @@ -36,12 +36,13 @@ export const selectPresentLocatorsByPO = createSelector( .filter((loc) => locByPageObj.includes(loc.element_id)) .map((loc) => { const annotationType = loc.annotationType || pageObject?.annotationType; + const locatorType = loc.locatorType || pageObject?.locatorType || LocatorType.xPath; const isDefaultLocatorType = () => !loc.locatorType && pageObject?.locatorType === LocatorType.cssSelector; return { ...loc, ...(annotationType && { annotationType }), - ...(isDefaultLocatorType() && { locatorType: pageObject?.locatorType }), + ...(locatorType && { locatorType }), ...(isDefaultLocatorType() && { locator: { ...loc.locator, output: getLocator(loc.locator, pageObject?.locatorType) }, }), diff --git a/src/features/pageObjects/PageObjectPage.tsx b/src/features/pageObjects/PageObjectPage.tsx index 15c6bc48..264cfe81 100644 --- a/src/features/pageObjects/PageObjectPage.tsx +++ b/src/features/pageObjects/PageObjectPage.tsx @@ -2,7 +2,8 @@ import React from "react"; import { PageObjList } from "./components/PageObjList"; interface Props { - template?: Blob; + jdiTemplate?: Blob; + vividusTemplate?: Blob; } export const PageObjectPage: React.FC = (props) => { diff --git a/src/features/pageObjects/components/PageObjGenerationBar.tsx b/src/features/pageObjects/components/PageObjGenerationBar.tsx index 2645c7d9..4f7b8899 100644 --- a/src/features/pageObjects/components/PageObjGenerationBar.tsx +++ b/src/features/pageObjects/components/PageObjGenerationBar.tsx @@ -9,6 +9,7 @@ import { setHideUnadded, setLocatorType, setAnnotationType, + changeFrameworkType, } from "../pageObject.slice"; import { PageObjectId } from "../types/pageObjectSlice.types"; import { ElementLibrary, libraryNames } from "../../locators/types/generationClasses.types"; @@ -65,8 +66,7 @@ const frameworkTypeOptions = [ { value: FrameworkType.Vividus, label: FrameworkType.Vividus, - title: IN_DEVELOPMENT_TITLE, - disabled: true, + disabled: false, }, ]; @@ -106,11 +106,23 @@ export const PageObjGenerationBar: React.FC = ({ pageObj, library, url }) const dispatch = useDispatch(); + const currentLibrary = currentPageObject?.library; + const isCurrentFrameworkVividus = currentPageObject?.framework === FrameworkType.Vividus; + const onLibraryChange = (library: ElementLibrary) => { dispatch(changeElementLibrary({ id: pageObj, library })); setLocalStorage(LocalStorageKey.Library, library); }; + const onFrameworkChange = async (framework: FrameworkType) => { + dispatch(changeFrameworkType({ id: pageObj, framework })); + setLocalStorage(LocalStorageKey.Framework, framework); + + if (framework === FrameworkType.Vividus) { + onLibraryChange(ElementLibrary.HTML5); + } + }; + const onAnnotationTypeChange = (annotationType: AnnotationType) => { dispatch(setAnnotationType({ id: pageObj, annotationType })); setLocalStorage(LocalStorageKey.AnnotationType, annotationType); @@ -143,9 +155,10 @@ export const PageObjGenerationBar: React.FC = ({ pageObj, library, url }) = (props) => { +export const PageObjList: React.FC = ({ jdiTemplate, vividusTemplate }) => { const state = useSelector((state) => state); // due to antd types: onChange?: (key: string | string[]) => void; - const currentPageObject = useSelector((state: RootState): string | undefined => + const currentPageObjectIndex = useSelector((state: RootState): string | undefined => state.pageObject.present.currentPageObject?.toString() ); + const currentPageObject = useSelector(selectCurrentPageObject); const pageObjects = useSelector(selectPageObjects); const [activePanel, setActivePanel] = useState([DEFAULT_ACTIVE_KEY]); @@ -42,12 +45,12 @@ export const PageObjList: React.FC = (props) => { const isExpanded = !!size(activePanel); useEffect(() => { - if (currentPageObject) { - setActivePanel([currentPageObject]); + if (currentPageObjectIndex) { + setActivePanel([currentPageObjectIndex]); } else { setActivePanel([DEFAULT_ACTIVE_KEY]); } - }, [currentPageObject]); + }, [currentPageObjectIndex]); const renderLocators = (elements: LocatorType[], library: ElementLibrary) => { if (size(elements)) { @@ -84,9 +87,18 @@ export const PageObjList: React.FC = (props) => { } }; + const template = currentPageObject?.framework === FrameworkType.Vividus ? vividusTemplate : jdiTemplate; + return (
- +
{size(pageObjects) ? ( @@ -105,7 +117,8 @@ export const PageObjList: React.FC = (props) => { {...(!isNil(activePanel) ? { activeKey: activePanel } : {})} onChange={(key) => setActivePanel([...key])} > - {pageObjects.map(({ id, name, url, locators, library }) => { + {pageObjects.map((pageObject) => { + const { id, locators, url, name, library } = pageObject; const elements = selectConfirmedLocators(state as RootState, id); const isPageObjectNotEmpty = !!size(locators); return ( @@ -125,7 +138,7 @@ export const PageObjList: React.FC = (props) => { extra={ <> {isPageObjectNotEmpty && } - + } > diff --git a/src/features/pageObjects/components/PageObjMenu.tsx b/src/features/pageObjects/components/PageObjMenu.tsx index c763d82d..2a6f9d57 100644 --- a/src/features/pageObjects/components/PageObjMenu.tsx +++ b/src/features/pageObjects/components/PageObjMenu.tsx @@ -16,8 +16,7 @@ import { import { ElementId, Locator } from "../../locators/types/locator.types"; import { removeLocators } from "../../locators/locators.slice"; import { removePageObject, setCurrentPageObj } from "../pageObject.slice"; -import { PageObjectId } from "../types/pageObjectSlice.types"; -import { ElementLibrary } from "../../locators/types/generationClasses.types"; +import { PageObject } from "../types/pageObjectSlice.types"; import { generatePageObject, generatePageObjectPerfTest } from "../../pageObjects/utils/pageObject"; import { RenamePageObjectDialog } from "./RenamePageObjDialog"; import { checkLocatorsValidity } from "../../locators/reducers/checkLocatorValidity.thunk"; @@ -27,28 +26,19 @@ import { OnbrdTooltip } from "../../onboarding/components/OnbrdTooltip"; import { OnboardingContext } from "../../onboarding/OnboardingProvider"; interface Props { - id: PageObjectId; - name: string; - url: string; - locators?: ElementId[]; + pageObject: PageObject; elements: Locator[]; - library: ElementLibrary; } -export const PageObjMenu: React.FC = ({ id, name, url, locators, elements, library }) => { +export const PageObjMenu: React.FC = ({ pageObject, elements }) => { const dispatch = useDispatch(); + const { id, locators, name } = pageObject; const [isRenameModalOpen, setIsRenameModalOpen] = useState(false); const { isOpen: isOnboardingOpen } = useContext(OnboardingContext); - const getMenuItems = ( - id: PageObjectId, - locatorIds: ElementId[] | undefined, - locatorObjects: Locator[], - name: string, - url: string - ) => { + const getMenuItems = (pageObject: PageObject, locatorIds: ElementId[] | undefined, locatorObjects: Locator[]) => { const handleRename = () => setIsRenameModalOpen(true); const handleRemove = () => { @@ -57,13 +47,13 @@ export const PageObjMenu: React.FC = ({ id, name, url, locators, elements }; const handleDownload = () => { - generatePageObject(locatorObjects, name, library).then(() => + generatePageObject(locatorObjects, pageObject).then(() => dispatch(pushNotification({ action: { type: "downloadFile" } })) ); }; const handleDownloadPerfTest = () => { - generatePageObjectPerfTest(locatorObjects, name, url).then(() => + generatePageObjectPerfTest(locatorObjects, pageObject).then(() => dispatch(pushNotification({ action: { type: "downloadJSFile" } })) ); }; @@ -94,7 +84,7 @@ export const PageObjMenu: React.FC = ({ id, name, url, locators, elements disabled={isOnboardingOpen} align={{ offset: [15, 0] }} trigger={["click"]} - menu={getMenuItems(id, locators, elements, name, url)} + menu={getMenuItems(pageObject, locators, elements)} getPopupContainer={(triggerNode) => triggerNode} destroyPopupOnHide > diff --git a/src/features/pageObjects/pageObject.slice.ts b/src/features/pageObjects/pageObject.slice.ts index ab5ad689..14d5b834 100644 --- a/src/features/pageObjects/pageObject.slice.ts +++ b/src/features/pageObjects/pageObject.slice.ts @@ -5,7 +5,7 @@ import { PageObjectState, PageObject, PageObjectId } from "./types/pageObjectSli import { ElementId } from "../locators/types/locator.types"; import { addPageObjReducer } from "./reducers/addPageObject.thunk"; import { ElementLibrary } from "../locators/types/generationClasses.types"; -import { LocatorType, AnnotationType } from "../../common/types/common"; +import { LocatorType, AnnotationType, FrameworkType } from "../../common/types/common"; const initialState: PageObjectState = {}; @@ -35,6 +35,10 @@ const pageObjSlice = createSlice({ const { id, library } = payload; pageObjAdapter.upsertOne(state, { id, library } as PageObject); }, + changeFrameworkType(state, { payload }: PayloadAction<{ id: PageObjectId; framework: FrameworkType }>) { + const { id, framework } = payload; + pageObjAdapter.upsertOne(state, { id, framework } as PageObject); + }, clearLocators(state, { payload }) { const id = payload || state.currentPageObject; pageObjAdapter.upsertOne(state, { id, locators: undefined } as PageObject); @@ -75,6 +79,7 @@ export const { addLocatorToPageObj, addLocatorsToPageObj, changeElementLibrary, + changeFrameworkType, changeName, clearLocators, removeAll, diff --git a/src/features/pageObjects/reducers/addPageObject.thunk.js b/src/features/pageObjects/reducers/addPageObject.thunk.js index 8241706a..6bab9746 100644 --- a/src/features/pageObjects/reducers/addPageObject.thunk.js +++ b/src/features/pageObjects/reducers/addPageObject.thunk.js @@ -7,12 +7,13 @@ import { selectLastAnnotationType, selectMaxId, simpleSelectPageObjects, + selectLastFrameworkType, } from "../selectors/pageObjects.selectors"; import { defaultLibrary } from "../../locators/types/generationClasses.types"; import { getPageAttributes, isPONameUnique } from "../utils/pageObject"; import { getClassName } from "../utils/pageObjectTemplate"; import { LocalStorageKey, getLocalStorage } from "../../../common/utils/localStorage"; -import { AnnotationType, LocatorType } from "../../../common/types/common"; +import { AnnotationType, LocatorType, FrameworkType } from "../../../common/types/common"; export const addPageObj = createAsyncThunk("pageObject/addPageObj", async (payload, { getState }) => { const res = await getPageAttributes(); @@ -21,19 +22,35 @@ export const addPageObj = createAsyncThunk("pageObject/addPageObj", async (paylo const state = getState(); + const lastSelectedFrameworkType = + getLocalStorage(LocalStorageKey.Framework) || selectLastFrameworkType(state) || FrameworkType.JdiLight; const lastSelectedLibrary = getLocalStorage(LocalStorageKey.Library) || selectLastElementLibrary(state) || defaultLibrary; const lastSelectedLocatorType = getLocalStorage(LocalStorageKey.LocatorType) || selectLastLocatorType(state); const lastSelectedAnnotationType = getLocalStorage(LocalStorageKey.AnnotationType) || selectLastAnnotationType(state) || AnnotationType.UI; - return { className, url, lastSelectedLibrary, lastSelectedLocatorType, lastSelectedAnnotationType }; + return { + className, + url, + lastSelectedFrameworkType, + lastSelectedLibrary, + lastSelectedLocatorType, + lastSelectedAnnotationType, + }; }); export const addPageObjReducer = (builder) => { return builder .addCase(addPageObj.fulfilled, (state, { payload }) => { - const { className, url, lastSelectedLibrary, lastSelectedLocatorType, lastSelectedAnnotationType } = payload; + const { + className, + url, + lastSelectedFrameworkType, + lastSelectedLibrary, + lastSelectedLocatorType, + lastSelectedAnnotationType, + } = payload; // create unique PO name let maxExistingId = selectMaxId(state); @@ -58,6 +75,7 @@ export const addPageObjReducer = (builder) => { id, name, url, + framework: lastSelectedFrameworkType, library: lastSelectedLibrary, annotationType: lastSelectedAnnotationType, pathname, diff --git a/src/features/pageObjects/selectors/pageObjects.selectors.ts b/src/features/pageObjects/selectors/pageObjects.selectors.ts index d79e35ea..ea60296b 100644 --- a/src/features/pageObjects/selectors/pageObjects.selectors.ts +++ b/src/features/pageObjects/selectors/pageObjects.selectors.ts @@ -33,6 +33,8 @@ export const selectMaxId = createSelector(simpleSelectPageObjects, (items) => { return res !== -Infinity ? res : null; }); +export const selectLastFrameworkType = createSelector(selectPageObjects, (pageObjects) => last(pageObjects)?.framework); + export const selectLastElementLibrary = createSelector(selectPageObjects, (pageObjects) => last(pageObjects)?.library); export const selectLastLocatorType = createSelector(selectPageObjects, (pageObjects) => last(pageObjects)?.locatorType); diff --git a/src/features/pageObjects/types/pageObjectSlice.types.ts b/src/features/pageObjects/types/pageObjectSlice.types.ts index 1f88466e..3b537306 100644 --- a/src/features/pageObjects/types/pageObjectSlice.types.ts +++ b/src/features/pageObjects/types/pageObjectSlice.types.ts @@ -1,6 +1,6 @@ import { ElementId } from "../../locators/types/locator.types"; import { ElementLibrary } from "../../locators/types/generationClasses.types"; -import { LocatorType, AnnotationType } from "../../../common/types/common"; +import { FrameworkType, LocatorType, AnnotationType } from "../../../common/types/common"; export type PageObjectId = number; @@ -11,6 +11,7 @@ export interface PageObjectState { export interface PageObject { hideUnadded?: boolean; // whether to show in list Locators with "generate" set to false id: PageObjectId; + framework: FrameworkType; library: ElementLibrary; locators?: ElementId[]; annotationType?: AnnotationType; diff --git a/src/features/pageObjects/utils/pageObject.ts b/src/features/pageObjects/utils/pageObject.ts index 6fc1f72f..84860fb6 100644 --- a/src/features/pageObjects/utils/pageObject.ts +++ b/src/features/pageObjects/utils/pageObject.ts @@ -125,28 +125,32 @@ export const getPageAttributes = async () => { }); }; -export const getPage = async (locators: Array, title: string, library: ElementLibrary) => { - const pageObject = pageObjectTemplate(locators, title, library); - return pageObject; +export const getPage = async ( + locators: Array, + pageObject: PageObject +): Promise<{ pageCode: string; title: string }> => { + return pageObjectTemplate(locators, pageObject); }; -export const getPagePerfTest = async (locators: Array, title: string, url: string) => { - const pageObject = pageObjectTemplatePerfTest(locators, title, url); - return pageObject; +export const getPagePerfTest = async ( + locators: Array, + pageObject: PageObject +): Promise<{ pageCode: string; name: string }> => { + return pageObjectTemplatePerfTest(locators, pageObject); }; -export const generatePageObject = async (elements: Array, title: string, library: ElementLibrary) => { - const page = await getPage(elements, title, library); +export const generatePageObject = async (elements: Array, pageObject: PageObject): Promise => { + const page = await getPage(elements, pageObject); const blob = new Blob([page.pageCode], { type: "text/plain;charset=utf-8", }); saveAs(blob, `${page.title}.java`); }; -export const generatePageObjectPerfTest = async (elements: Array, title: string, url: string) => { - const page = await getPagePerfTest(elements, title, url); +export const generatePageObjectPerfTest = async (elements: Array, pageObject: PageObject): Promise => { + const page = await getPagePerfTest(elements, pageObject); const blob = new Blob([page.pageCode], { type: "text/plain;charset=utf-8", }); - saveAs(blob, `${page.title}.js`); + saveAs(blob, `${page.name}.js`); }; diff --git a/src/features/pageObjects/utils/pageObjectTemplate.ts b/src/features/pageObjects/utils/pageObjectTemplate.ts index f81c7113..a70b454f 100644 --- a/src/features/pageObjects/utils/pageObjectTemplate.ts +++ b/src/features/pageObjects/utils/pageObjectTemplate.ts @@ -3,8 +3,10 @@ import { camelCase, upperFirst } from "lodash"; import transliterate from "@sindresorhus/transliterate"; import { Locator } from "../../locators/types/locator.types"; import { getLocatorPrefix } from "../../locators/utils/locatorOutput"; -import { AnnotationType } from "../../../common/types/common"; +import { AnnotationType, LocatorType, FrameworkType } from "../../../common/types/common"; import { hasAnnotationType } from "./hasAnnotationType"; +import { PageObject } from "../types/pageObjectSlice.types"; +import _ from "lodash"; export const getClassName = (title: string) => { let className = transliterate(title); @@ -18,8 +20,33 @@ export const getClassName = (title: string) => { return className; }; -export const pageObjectTemplate = (locators: Locator[], title: string, library: ElementLibrary) => { - const className = title; +export const vividusTemplate = (locators: Locator[], pageObject: PageObject): { pageCode: string; title: string } => { + const { name, pathname, annotationType, locatorType } = pageObject; + let pageCode = `variables.${name}.url=(${pathname})\n`; + + locators.forEach((it) => { + const currentAnnotationType = it.annotationType || annotationType || AnnotationType.UI; + const currentLocatorType = _.camelCase(it.locatorType || locatorType || LocatorType.xPath); + pageCode += `variables.${name}.${it.type}.${it.name}=By.${currentLocatorType}(${getLocatorPrefix( + currentAnnotationType, + currentLocatorType as LocatorType + // @ts-ignore + )}${it.locator[currentLocatorType]})\n`; + }); + + return { pageCode, title: name }; +}; + +export const pageObjectTemplate = ( + locators: Locator[], + pageObject: PageObject +): { pageCode: string; title: string } => { + const { framework, name: className, library } = pageObject; + + if (framework === FrameworkType.Vividus) { + return vividusTemplate(locators, pageObject); + } + const locatorsCode = locators.map((loc) => { const locatorEscaped = loc.locator.output?.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); return ` ${loc.annotationType}(${getLocatorPrefix( diff --git a/src/features/pageObjects/utils/pageObjectTemplatePerfTest.ts b/src/features/pageObjects/utils/pageObjectTemplatePerfTest.ts index 6370f72d..2d6cc214 100644 --- a/src/features/pageObjects/utils/pageObjectTemplatePerfTest.ts +++ b/src/features/pageObjects/utils/pageObjectTemplatePerfTest.ts @@ -1,6 +1,10 @@ import { Locator } from "../../locators/types/locator.types"; +import { PageObject } from "../types/pageObjectSlice.types"; -export const pageObjectTemplatePerfTest = (locators: Locator[], title: string, url: string) => { +export const pageObjectTemplatePerfTest = ( + locators: Locator[], + pageObject: PageObject +): { pageCode: string; name: string } => { const locatorsCode = locators.map((loc) => ` this.${loc.name} = new ${loc.type}("${loc.locator.output}", page)`); const pageCode = `const Page = require("../../core/page"); @@ -8,18 +12,18 @@ export const pageObjectTemplatePerfTest = (locators: Locator[], title: string, u const Button = require("../../core/elements/button"); const UploadField = require("../../core/elements/uploadField"); const UIElement = require("../../core/elements/element");\n -class ${title} extends Page {\n +class ${pageObject.name} extends Page {\n constructor(page) { super(page) - this.url = "${url}" + this.url = "${pageObject.url}" }\n init(page){ super.init(page) ${locatorsCode.join("\n")} }\n }\n -module.exports = ${title}; +module.exports = ${pageObject.name}; `; - return { pageCode, title }; + return { pageCode, name: pageObject.name }; }; diff --git a/src/features/pageObjects/utils/projectTemplate.ts b/src/features/pageObjects/utils/projectTemplate.ts index df082eb4..e5ecf222 100644 --- a/src/features/pageObjects/utils/projectTemplate.ts +++ b/src/features/pageObjects/utils/projectTemplate.ts @@ -9,9 +9,27 @@ import { PageObject } from "../types/pageObjectSlice.types"; import { ElementLibrary } from "../../locators/types/generationClasses.types"; import { editPomContent } from "./templateFileContent"; import { selectConfirmedLocators } from "../../locators/selectors/locatorsFiltered.selectors"; +import { FrameworkType } from "../../../common/types/common"; -const generatePoFile = (newZip: JSZip, page: { pageCode: string; title: string }) => - newZip.file(`src/main/java/site/pages/${page.title}.java`, page.pageCode, { binary: false }); +const VIVIDUS = { + PAGES_PROPERTIES_PATH: "src/main/resources/properties/suite/web_app/pages.properties", + SITE_PROPERTIES_PATH: "src/main/resources/properties/suite/web_app/site.properties", +}; + +const JDI = { + MY_SITE_PATH: "src/main/java/site/MySite.java", + EXAMPLE_STRING: "// ADD SITE PAGES WITH URLS", +}; + +const isVividusFramework = (framework: FrameworkType) => framework === FrameworkType.Vividus; + +const generatePoFile = (newZip: JSZip, framework: FrameworkType, page: { pageCode: string; title: string }) => { + const path = isVividusFramework(framework) + ? VIVIDUS.PAGES_PROPERTIES_PATH + : `src/main/java/site/pages/${page.title}.java`; + + newZip.file(path, page.pageCode, { binary: false }); +}; const editTestPropertiesFile = (newZip: JSZip, po: PageObject) => newZip @@ -23,21 +41,23 @@ const editTestPropertiesFile = (newZip: JSZip, po: PageObject) => return newZip.file(`src/test/resources/test.properties`, newContent, { binary: true }); }); -const editMySiteFile = (newZip: JSZip, po: PageObject, instanceName: string) => +const editMySiteFile = (newZip: JSZip, po: PageObject, instanceName: string) => { + if (isVividusFramework(po.framework)) return; newZip - .file("src/main/java/site/MySite.java")! + .file(JDI.MY_SITE_PATH)! .async("string") .then((content) => { if (content.includes(instanceName)) instanceName = `${instanceName}1`; const urlSearchParams = po.search; const testUrl = urlSearchParams.length ? po.pathname + urlSearchParams : po.pathname; const newContent = content.replace( - "// ADD SITE PAGES WITH URLS", - `// ADD SITE PAGES WITH URLS\n @Url("${testUrl}")\n public static ${po.name} ${instanceName}; + JDI.EXAMPLE_STRING, + `${JDI.EXAMPLE_STRING}\n @Url("${testUrl}")\n public static ${po.name} ${instanceName}; ` ); - return newZip.file(`src/main/java/site/MySite.java`, newContent, { binary: true }); + return newZip.file(JDI.MY_SITE_PATH, newContent, { binary: true }); }); +}; const editTestsFile = (newZip: JSZip, po: PageObject, instanceName: string) => newZip.file(`src/test/java/tests/${po.name}Tests.java`, testFileTemplate(instanceName, po.name)); @@ -81,15 +101,20 @@ export const generateAndDownloadZip = async (state: RootState, template: Blob) = // create page object files const locators = selectConfirmedLocators(state, po.id); if (!size(locators)) continue; - const page = await getPage(locators, po.name, po.library); + const page = await getPage(locators, po); const instanceName = lowerFirst(po.name); - await generatePoFile(newZip, page); - await editTestPropertiesFile(newZip, po); - await editMySiteFile(newZip, po, instanceName); - await editTestsFile(newZip, po, instanceName); - await editPomFile(newZip, po); + await generatePoFile(newZip, po.framework, page); + + if (!isVividusFramework(po.framework)) { + await editTestPropertiesFile(newZip, po); + await editMySiteFile(newZip, po, instanceName); + await editTestsFile(newZip, po, instanceName); + await editPomFile(newZip, po); + } else { + newZip.file(VIVIDUS.SITE_PROPERTIES_PATH, `variables.siteURL=${po.url}`, { binary: true }); + } } saveZip(); diff --git a/src/services/backend.ts b/src/services/backend.ts index 38172285..81d161fd 100644 --- a/src/services/backend.ts +++ b/src/services/backend.ts @@ -8,6 +8,8 @@ export enum HttpEndpoint { NGMAT_PREDICT = "angular-predict", // TODO: check when implemented at BE REPORT_PROBLEM = "report_problem", DOWNLOAD_TEMPLATE = "download_template", + DOWNLOAD_TEMPLATE_VIVIDUS = "download_template?repo_zip_url=https://github.com/vividus-framework/vividus-sample-tests/archive/refs/heads/main.zip", + SESSION_ID = "get_session_id", PING_SMTP = "ping_smtp", }