PopperFramework is inspired by springframework, so it is non-invasive and doesn't rely on special interfaces or complexe infrastructure you have to implement in your project. All PopperFramework does is:
- Instantiate an PageObject
- override / implement methods annotated with PopperFW-Annotations
If it doesn't find any PopperFW-Annotatios it doesn't change the existing object-definition at all.
So the first thing you do is to let PopperFW create your existing PageObjects for you. You don't have to do that for all of your PageObjects. As a start you might want to popperfy only
- new PageObjects
- not often used PageObjects
- less complexe PageObjects
- PageObjects created on a wendsday
- ...
The importend part is, that you change your own creation of the PO against a PopperFW call: Change
new MyPoToMigrate();
against
new popperFactory.createPO(MyPoToMigrate.class);
Let's say you have the given structure:
public class MyLoginPo { public MyLoginPo(WebDriver driver, SearchContext searchContext) { // do some stuff to initialize } public WebElement getUsernameTextbox() { return // some WebDriver-Stuff } public WebElement getPasswordTextbox() { return // some WebDriver-Stuff } public WebElement getLoginButton() { return // some WebDriver-Stuff } public Header getHeader() { WebElement searchContextForHeader = // some crazy stuff return new Header(driver, searchContextForHeader); } } public class MyLoginTest { @Test public void testLogin() { MyLoginPo login = new MyLoginPo(WebDriverHolder.getDriver(), null); login.getUsernameTextbox().sendKeys("user"); login.getPasswordTextbox().sendKeys("secret"); login.getLoginButton().click(); } }
So all you have to do is change the creation of you PO.
public class MyLoginTest { @Test public void testLogin() { // ---> MyLoginPo login = popperFactorynew.createPO(MyLoginPo.class); login.getUsernameTextbox().sendKeys("user"); login.getPasswordTextbox().sendKeys("secret"); login.getLoginButton().click(); } }
As you see creating a an instance of MyLoginPo needs a WebDriver instance as constructor argument. PopperFW is able to resolve itself some Types used as construcotor arguments, WebDriver ist one of them (see [Legacy support] for the others). But at least it needs to know where to find an instance of WebDriver. PopperFW is able to manage the creation of WebDriver instances itself, but in this case we don't want that. We need to reuse the existing WebDriver. For that you need to tell PopperFW how to find your WebDriver
public class WebdriverProxyConfig implements IWebdriverConfig { @Override public WebDriver createDriver() { return WebDriverHolder.getDriver(); } @Override public Browser getBrowser() { return WebDriverHolder.getBrowser(); } @Override public String getBaseUrl() { return WebDriverHolder.getBaseUrl(); } } public class MyLoginTest { @Test public void testLogin() { WebdriverContext context = new WebdriverContext(); context.setConfig(new WebdriverProxyConfig()); IPoFactory popperFactory = context.getFactory(); MyLoginPo login = popperFactorynew.createPO(MyLoginPo.class, "MyLogin"); login.getUsernameTextbox().sendKeys("user"); login.getPasswordTextbox().sendKeys("secret"); login.getLoginButton().click(); } }
That was easy! But for now it won't do much more for you than creating the same object in a differnet manner.
But as your PageObject is now popperfied you might want to start declaring new elements instead of implementing them.
- First of all you have to declare your PageObject class abstract* (that comes with the positive effect that your IDE will show you all occurences of the instations where you didn't change the instantiation to popperFactory.
- Then you have to add an @Page-annotation to your class
- Then you can define your elements
@Page(name="MyLoginPo") public abstract class MyLoginPo { public MyLoginPo(WebDriver driver, SearchContext searchContext) { // do some stuff to initialize } public WebElement getUsernameTextbox() { return // some WebDriver-Stuff } public WebElement getPasswordTextbox() { return // some WebDriver-Stuff } public WebElement getLoginButton() { return // some WebDriver-Stuff } @Locator(name="Register Link", id="register") public abstract WebElement getRegisterLink(); public Header getHeader() { WebElement searchContextForHeader = // some crazzy stuff return new Header(driver, searchContextForHeader); } }
Next you may want to replace all your implementation in your PO by declarations
@Page(name="MyLoginPo") public abstract class MyLoginPo { public MyLoginPo(WebDriver driver, SearchContext searchContext) { // do some stuff to initialize } @Locator(name="Username", id="username") public abstract WebElement getUsernameTextbox(); @Locator(name="Password", id="password") public abstract WebElement getPasswordTextbox(); @Locator(name="Submit Login", id="submit") public abstract WebElement getLoginButton(); @Locator(name="Register Link", id="register") public abstract WebElement getRegisterLink(); @Locator(name="Header", id="header") public abstract Header getHeader(); }
At this point your complete class is described declarativly (and by the way your Header instance is popperfied, too). So there is no more need for more boilerplate code
@Page(name="MyLoginPo") public interface MyLoginPo { @Locator(name="Username", id="username") WebElement getUsernameTextbox(); @Locator(name="Password", id="password") WebElement getPasswordTextbox(); @Locator(name="Submit Login", id="submit") WebElement getLoginButton(); @Locator(name="Register Link", id="register") public abstract WebElement getRegisterLink(); @Locator(name="Header", id="header") Header getHeader(); }
So lets take a look what happened to our test code:
public class MyLoginTest { @Test public void testLogin() { WebdriverContext context = new WebdriverContext(); context.setConfig(new WebdriverProxyConfig()); IPoFactory popperFactory = context.getFactory(); MyLoginPo login = popperFactorynew.createPO(MyLoginPo.class); login.getUsernameTextbox().sendKeys("user"); login.getPasswordTextbox().sendKeys("secret"); login.getLoginButton().click(); } }
Oh my goodness, it didn't change at all. We converted our whole PageObject from an implemented class to a declarative interface and it didn't have any effect to our testcode. That's amazing!
If you are not afraid of changing your testcode you can change the return-types of your PageObject to the PopperFW default ones. To get even more flexibility for future:
@Page(name="MyLoginPo") public interface MyLoginPo { @Locator(name="Username", id="username") ITextbox getUsernameTextbox(); @Locator(name="Password", id="password") ITextbox getPasswordTextbox(); @Locator(name="Submit Login", id="submit") ITextbox getLoginButton(); @Locator(name="Login Button", id="submitButton") ITextbox getLoginButton(); @Locator(name="Header", id="header") Header getHeader(); }
As a conclusion you have a smooth transition from your old implemented PageObjects to new declarative ones. It's up to you where you start, how many objects you replace and when you stop changing your PageObjects to the PopperFW way.