Package: zelenium
zeleniumNew Selenium framework for Python with base pages and elementsInstallationpipinstallzeleniumUsageZelenium offers several features that could be combined with classical
selenium usage:Driver singleton configuration;BasePage with BaseElements;Suffix and formatting mechanisms for BaseElements;It also should be useful for Appium testing.zelenium configurationTo setup configuration for zelenium you could just useConfig:fromseleniumimportwebdriverfromzeleniumimportConfigconfig=Config.get_instance()config.driver=webdriver.Chrome()Because Config is singleton - you could not use it with two different
webdrivers at one moment. But if you need it, you could use private class:fromzeleniumimportConfigfromzelenium.base.configimport_Configconfig1=Config.get_instance()config2=_Config()assertnot(config1isconfig2)# No assertionBasePage and BaseElementWhat offers you BasePage:No need to pass webdriver instance - it would be passed from
configuration automaticallySome predefined methods, which are useful in testingSuffix mechanismDefine new PageLet's imagine that we have already setup webdriver for Config,
and starting to create new page:fromselenium.webdriver.common.byimportByfromzeleniumimportBasePageclassLoginPage(BasePage):title=(By.CSS_SELECTOR,"[data-test='title']")username=(By.CSS_SELECTOR,"[data-test='username']")password=(By.CSS_SELECTOR,"[data-test='password']")submit=(By.CSS_SELECTOR,"[data-test='submit']")defmain():login_page=LoginPage()print(login_page.title().text)main()If we execute it after opening something in browser - it will find
element and print text inside of it.How it works?Well, BasePage also has ametaclassthat will go all over page class
fields and if field is tuple with two strings - it would replace it withBaseElement.BaseElementitself has magic__call__method,  which executes when
you 'call' class instance:fromzeleniumimportBEelem=BE("by","selector")web_element=elem()# Here you calls class instance and it will return# WebElement for you. Just classic WebElementInherit pagesFor example, you have several pages, which have same structure, but some
different logic, for example:fromselenium.webdriver.common.byimportByfromzeleniumimportBasePageclassLoginPage(BasePage):title=(By.CSS_SELECTOR,"[data-test='title']")username=(By.CSS_SELECTOR,"[data-test='username']")password=(By.CSS_SELECTOR,"[data-test='password']")submit=(By.CSS_SELECTOR,"[data-test='submit']")deflogin(self,username,password):self.username().send_keys(username)self.password().send_keys(password)self.submit().click()classRegisterPage(LoginPage):full_name=(By.CSS_SELECTOR,"[data-test='full_name']")defregister(self,full_name,username,password):self.full_name().send_key(full_name)self.username().send_keys(username)self.password().send_keys(password)self.submit().click()Using this - you have no need to redefine elements on different pages -
you could just inherit them, if they have same locators (or quite the same).Format elementsSometimes you need to define a lot of elements with similar locators.
Zelenium offers two way to solve this. First is BaseElement formatting:fromselenium.webdriver.common.byimportByfromzeleniumimportBasePage,BEclassDevicesPage(BasePage):_cell=BE(By.CSS_SELECTOR,"[data-test='devicesPageCell_{}']")user=_cell.format("user")imei=_cell.format("imei")iccid=_cell.format("iccid")model=_cell.format("model").format()method formats locator as a string and returns new instance
of BaseElement.Second mechanism is suffix:fromselenium.webdriver.common.byimportByfromzeleniumimportBasePageclassDevicesPage(BasePage):__suffix="devicesPageCell_"user=(By.CSS_SELECTOR,"[data-test='{s}_user']")imei=(By.CSS_SELECTOR,"[data-test='{s}_imei']")iccid=(By.CSS_SELECTOR,"[data-test='{s}_iccid']")model=(By.CSS_SELECTOR,"[data-test='{s}_model']")Main differences of this two mechanisms are:Suffix adds to locator automatically;Suffix could be inherited;Format could be used anywhere outside classes - you could format
element in some functions according to changes on page.Format requires usage of BaseElement class itselfExample of suffix inheritance:fromselenium.webdriver.common.byimportByfromzeleniumimportBasePageclassLoginPage(BasePage):__suffix="loginPageForm_"title=(By.CSS_SELECTOR,"[data-test='{s}title']")username=(By.CSS_SELECTOR,"[data-test='{s}username']")password=(By.CSS_SELECTOR,"[data-test='{s}password']")submit=(By.CSS_SELECTOR,"[data-test='{s}submit']")classRegisterPage(LoginPage):__suffix="registerPageForm_"email=(By.CSS_SELECTOR,"[data-test='{s}email']")confirm=(By.CSS_SELECTOR,"[data-test='{s}confirm']")classRenamedRegisterPage(RegisterPage):__suffix="renamedRegisterPageForm_"defmain():log=LoginPage()reg=RegisterPage()ren=RenamedRegisterPage()print(log.title)print(log.username)print(log.password)print(log.submit)print(reg.title)print(reg.username)print(reg.password)print(reg.submit)print(reg.email)print(reg.confirm)print(ren.title)print(ren.username)print(ren.password)print(ren.submit)print(ren.email)print(ren.confirm)if__name__=='__main__':main()This code will output:Element [data-test='loginPageForm_title'] (css selector)
Element [data-test='loginPageForm_username'] (css selector)
Element [data-test='loginPageForm_password'] (css selector)
Element [data-test='loginPageForm_submit'] (css selector)
Element [data-test='registerPageForm_title'] (css selector)
Element [data-test='registerPageForm_username'] (css selector)
Element [data-test='registerPageForm_password'] (css selector)
Element [data-test='registerPageForm_submit'] (css selector)
Element [data-test='registerPageForm_email'] (css selector)
Element [data-test='registerPageForm_confirm'] (css selector)
Element [data-test='renamedRegisterPageForm_title'] (css selector)
Element [data-test='renamedRegisterPageForm_username'] (css selector)
Element [data-test='renamedRegisterPageForm_password'] (css selector)
Element [data-test='renamedRegisterPageForm_submit'] (css selector)
Element [data-test='renamedRegisterPageForm_email'] (css selector)
Element [data-test='renamedRegisterPageForm_confirm'] (css selector)