Lets take an example , When we have a New Tag (my-app) in HTML and we have to build a case to Login using webdriverIO
,
Assume this is the HTML :
What we would do is using the component object pattern , component object pattern attempts to reduce that repetition and move the component's api into an object of its own. We know that in order to interact with an element's shadow DOM, we first need the host element. Using a base class for your component objects makes this pretty straightforward.
Here's a bare-bones component base class that takes the host element in its constructor and unrolls that element's queries up to the browser object, so it can be reused in many page objects (or other component objects), without having to know anything about the page itself.
class Component {
constructor(host) {
const selectors = [];
// Crawl back to the browser object, and cache all selectors
while (host.elementId && host.parent) {
selectors.push(host.selector);
host = host.parent;
}
selectors.reverse();
this.selectors_ = selectors;
}
get host() {
// Beginning with the browser object, reselect each element
return this.selectors_.reduce((element, selector) => element.$(selector), browser);
}
}
module.exports = Component;
then what we would do is , We will write a subclass for our app-login component:
const Component = require('./component');
class Login extends Component {
get usernameInput() {
return this.host.shadow$('input #username');
}
get passwordInput() {
return this.host.shadow$('input[type=password]');
}
get submitButton() {
return this.login.shadow$('button[type=submit]');
}
login(username, password) {
this.usernameInput.setValue(username);
this.passwordInput.setValue(password);
this.submitButton.click();
}
}
module.exports = Login;
Finally, we can use the component object inside our login page object:
const Login = require('./components/login');
class LoginPage {
open() {
browser.url('/login');
}
get app() {
return browser.$('my-app');
}
get loginComponent() {
// return a new instance of our login component object
return new Login(this.app.$('app-login'));
}
}
Now this component object can now be used in tests for any page or section of your app that uses an app-login web component, without having to know about how that component is structured. If you later decide to change the internal structure of the web component, you only need to update the component object.
Now we apply the same approach with the Check Box Component by using Shadow Dom Support :
public class CheckBox extends Component {
public CheckBox(element) {
this.element = element;
}
get checkBoxSelector() {
return this.host.shadow$(element);
}
get void toggle() {
checkBoxSelector().click();
}
get void check() {
if (!isChecked()) {
toggle();
}
}
get void uncheck() {
if (isChecked()) {
toggle();
}
}
get boolean isChecked() {
return checkBoxSelector().isSelected();
}
}
Then We can write a Check Box Controller component that can get the instance of check box using id and verify what every is necessary.
const CheckBox= require('./components/CheckBox');
class CheckBoxController{
open() {
browser.url('/login');
}
get checkboxComponent() {
// Using this we can verify whether the Specific Check Box has been Selected or Not
let element = browser.$('[id="lpagecheckbox"]');
return new CheckBox(element);
}
}
Note :
Please bear this is not the actual code , This is just a part of the template which can help us to move towards the solution of the Problem .
Source Contends :
https://webdriver.io/docs/api/element/isSelected.html
https://webdriver.io/blog/2019/02/22/shadow-dom-support.html
https://webdriver.io/blog/2019/04/03/react-selectors.html
https://webdriver.io/docs/pageobjects.html
Moreover if we are using Selenium Webdriver , This can help us to Achieve it
Here we have an interface which actually combines all the webdriver
interfaces , then we create a Specific implementation by inheriting the Element
Class , finally Lets assume of any component you need we should be inheriting and using it with its own implementation , In this case lets assume the Check box that should be inherited from then Element Implementation Class and finally a Cranky way of using it by instantiating the object. CheckBox cb = new CheckBox(element);cb.uncheck();
Step 1:
Create an Interface that combines all of the WebDriver interfaces:
public interface Element extends WebElement, WrapsElement, Locatable {}
Step 2:
Element Implementation Inheriting the element class:
public class ElementImpl implements Element {
private final WebElement element;
public ElementImpl(final WebElement element) {
this.element = element;
}
@Override
public void click() {
element.click();
}
@Override
public void sendKeys(CharSequence... keysToSend) {
element.sendKeys(keysToSend);
}
// And so on, delegates all the way down...
}
Step 3:
Consider any component you use , Lets assume Check Box in this case
public class CheckBox extends ElementImpl {
public CheckBox(WebElement element) {
super(element);
}
public void toggle() {
getWrappedElement().click();
}
public void check() {
if (!isChecked()) {
toggle();
}
}
public void uncheck() {
if (isChecked()) {
toggle();
}
}
public boolean isChecked() {
return getWrappedElement().isSelected();
}
}
Way of Using It :
CheckBox cb = new CheckBox(element);
cb.uncheck();
If you want More Clear way of Implementing Something Like this : refer the third Link
public class Part2ExampleTest {
private final WebDriver driver;
@FindBy(id = "checkbox")
CheckBox checkBox;
protected Part2ExampleTest(WebDriver driver) {
this.driver = driver;
}
protected static Part2ExampleTest initialize(WebDriver driver) {
return ElementFactory.initElements(driver, Part2ExampleTest.class);
}
@Test
public void simple() {
WebDriver driver = new FirefoxDriver();
Part2ExampleTest page = initialize(driver);
PageLoader.get(driver, "forms.html");
Assert.assertFalse(page.checkBox.isChecked());
page.checkBox.check();
Assert.assertTrue(page.checkBox.isChecked());
driver.close();
}
}
Sources :
Extend Selenium WebDriver WebElement?
http://elisarver.com/2012/12/09/wrapping-webelement-1/
http://elisarver.com/2012/12/10/wrapping-webelement-2
Checkbox
class, to demonstrate up to which level you need types support? – Soll