Basics - Web Automation
Introduction
In this documentation, we will explore web automation fundamentals using WebdriverIO. Here you'll delve into the essential techniques and concepts for automating a webpage using WebdriverIO.
Concept 1: Mocha Framework
The below is the basic boilerplate code for a test case in mocha framework.
describe('Test Suite Name', () => {
it('Test Case Name', async () => {
await browser.url('www.google.com')
})
})
Let's look at each statements in detail:
describe('Test Suite Name' , () => {...})
: The describe function is a test suite container provided by Mocha. It groups together related test cases. In this case, 'test suite name' is passed as the description.it('Test Case Name', async () => {...})
: The it function is a test case definition provided by Mocha. It specifies an individual test case. In this case, 'Test Case Name' is passed as the description.async () => {...}
: The arrow function is an asynchronous function used to define the test case. This allows us to use await to handle promises.await browser.url('www.google.com')
: This line uses the WebDriverIO's browser.url() method to navigate to the specified URL. In this case, it navigates to 'www.google.com'.- All the executable statements for the automation script should be inside the it block.
- You can add multiple it and describe blocks for multiple test cases or test suites.
To explore more about the mocha framework, refer to this documentation by webdriver.io
Concept 2: Selectors
Below are the list of selectors used to locate an element on a webpage.
Selector | Usage | HTML Code | Description |
---|---|---|---|
By id | let user = await $('#username'); | <input id='username'/> | To locate an element using id, we use # symbol with the id of the element |
By class name | let pass = await $('.password'); | <input class='password'/> | To locate an element using class name, we have to '.' symbol with the name of the class |
By element text | let header = await $('h1=Welcome to my Page') | <h1>Welcome to my Page</h1> | To locate an element using the text inside the element, we have use tag name with the element text |
By partial element text | let header = await $('h1*=Welcome') | <h1>Welcome to my Page</h1> | We can also locate an element using the partial text inside it using the '*' symbol |
By attribute value | const elem = await $('[name="username"]') | <input name="username"/> | To locate an element using the attribute name and value, we have to use this template await $('attribute_name="attribute_value"') |
By link text | const link = await $('=WebdriverIO') | <a href="https://driver.io">WebdriverIO</a> | If an element is a link on the webpage then we can locate the element using the link text |
By partial link text | const link = await $('*=driver') | <a href="https://driver.io">WebdriverIO</a> | We can also locate the element using the partial link text using '*' symbol with =. |
For more selectors, refer to this documentation by webdriver.io.
Concept 3: Browser Methods
In this section, we will list down all the methods we use for the browser. Let's look at these one by one.
url()
This method is used to navigate to a specific URL in the browser.
// navigate to a new URL
await browser.url('https://webdriver.io');
// receive url
console.log(await browser.getUrl()); // outputs: "https://webdriver.io"
If a baseUrl is specified in the config, it will be prepended to the url parameter using node's url.resolve() method. Calling browser.url('...') with the same url as last time will trigger a page reload.
pause()
Pauses execution for a specific amount of time. It is recommended to not use this command to wait for an element to show up. In order to avoid flaky test results it is better to use commands like waitForExist or other waitFor* commands.
it('should pause the execution', async () => {
await browser.url('https://webdriver.io');
await browser.pause(3000);
});
keys()
Send a sequence of key strokes to the "active" element. You can make an input element active by just clicking on it.
import { Key } from 'webdriverio'
it('should copy&paste from one input to another', async () => {
const $copyInput = await $('#copy')
await $copyInput.setValue('some text')
await browser.keys([Key.Ctrl, 'a'])
await browser.keys([Key.Ctrl, 'c'])
await $pasteInput.click()
await browser.keys([Key.Ctrl, 'v'])
await expect($pasteInput).toHaveValue('some text')
})
maximizeWindow()
Maximizes the current browser window.
it('should pause the execution', async () => {
await browser.maximizeWindow();
await browser.url('https://webdriver.io');
await browser.pause(3000);
});
getUrl()
The Get Current URL command returns the URL of the current top-level browsing context.
await browser.url('https://webdriver.io')
console.log(await browser.getUrl())
getTitle()
The Get Title command returns the document title of the current top-level browsing context.
await browser.navigateTo('https://webdriver.io')
console.log(await browser.getTitle())
back()
The Back command causes the browser to traverse one step backward in the joint session history.
await browser.navigateTo('https://webdriver.io')
await $('=API Reference').click()
await expect(browser).toHaveUrl('https://webdriver.io/docs/api')
await browser.back()
await expect(browser).toHaveUrl('https://webdriver.io/')
forward()
The Forward command causes the browser to traverse one step forward in the joint session history.
await browser.navigateTo('https://webdriver.io')
await $('=API Reference').click()
await expect(browser).toHaveUrl('https://webdriver.io/docs/api')
await browser.back()
await expect(browser).toHaveUrl('https://webdriver.io/')
await browser.forward()
await expect(browser).toHaveUrl('https://webdriver.io/docs/api')
refresh()
The Refresh command causes the browser to reload the page in current top-level browsing context.
await browser.navigateTo('https://webdriver.io')
await browser.refresh();
scroll()
Scroll within the browser viewport. Note that x and y coordinates are relative to the current scroll position, therefore browser.scroll(0, 0) is a non operation.
it('should demonstrate the scroll command', async () => {
await browser.url('https://webdriver.io')
console.log(await browser.execute(() => window.scrollY)) // returns 0
await browser.scroll(0, 200)
console.log(await browser.execute(() => window.scrollY)) // returns 200
});
There are many more methods available for browsers in webdriverio. Refer to this documentation to explore more about them.
Concept 4: Element Methods
In this section, we will list down all the methods we use for the element in webdriverio. Let's look at these one by one.
setValue()
Send a sequence of key strokes to an element after the input has been cleared before. If the element doesn't need to be cleared first then use addValue.
const input = await $('.input');
await input.setValue('test')
await input.setValue(123)
console.log(await input.getValue()); // outputs: '123'
getValue()
Get the value of a textarea, select or text input found by given selector. If multiple elements are found via the given selector, an array of values is returned instead.
const inputUser = await $('#username');
const value = await inputUser.getValue();
console.log(value); // outputs: "John Doe"
clearValue()
Clear the value of an input or textarea element. Make sure you can interact with the element before using this command.
const elem = await $('.input')
await elem.setValue('test123')
await elem.clearValue()
click()
This issues a WebDriver click command for the selected element , which generally scrolls to and then clicks the selected element.
const myButton = await $('#myButton')
await myButton.click()
getText()
Get the text content from a DOM-element. Make sure the element you want to request the text from is interactable.
<div id="elem">
Lorem ipsum <strong>dolor</strong> sit amet,<br />
consetetur sadipscing elitr
</div>
const elem = await $('#elem');
console.log(await elem.getText());
scrollIntoView()
Scroll element into viewport
const elem = await $('#myElement');
// scroll to specific element
await elem.scrollIntoView();
// center element within the viewport
await elem.scrollIntoView({ block: 'center', inline: 'center' });
There are several more element method available in webdriverio. To know more, kindly refer to this documentation by webdriver.io.
Concept 5: Implicit and Explicit Waits
In WebDriverIO, waits play a crucial role in ensuring robust and reliable test automation. WebDriverIO provides two primary types of waits: implicit and explicit.
Implicit Waits
Implicit waits in WebDriverIO define a default timeout duration during which WebDriverIO will wait for an element to appear before throwing an exception
waitForExist
Wait for an element for the provided amount of milliseconds to be present within the DOM. Returns true if the selector matches at least one element that exists in the DOM, otherwise throws an error.
const form = await $('form');
const notification = await $('.notification');
await form.$(".send").click();
await notification.waitForExist({ timeout: 5000 });
expect(await notification.getText()).to.be.equal('Data transmitted successfully!')
waitForExist offers several parameters as well. Refer to this link to know more.
waitForDisplayed
Wait for an element for the provided amount of milliseconds to be displayed or not displayed.
const elem = await $('#elem')
await elem.waitForDisplayed({ timeout: 3000 })
waitForDisplayed offers several parameters as well. Refer to this link to know more.
waitForClickable
Wait for an element for the provided amount of milliseconds to be clickable or not clickable.
const elem = await $('#elem')
await elem.waitForClickable({ timeout: 3000 });
There are few more implicit waits available in webdriverio. Refer to this link to know more.
Explicit Waits
Explicit waits allow developers to define custom conditions and timeouts for waiting on specific elements or events in WebDriverIO.
waitUntil
This wait command is your universal weapon if you want to wait on something. It expects a condition and waits until that condition is fulfilled with a truthy value.
const elem = await $('#someText')
await elem.waitUntil(async function () {
return (await this.getText()) === 'I am now different'
}, {
timeout: 5000,
timeoutMsg: 'expected text to be different after 5s'
})
Concept 6: Assertions
In the section we will talk about the different ways to add assertions in your automation script.
Browser Matchers
These assertions are related to the browsers.
toHaveUrl
Checks if browser is on a specific page.
await browser.url('https://webdriver.io/')
await expect(browser).toHaveUrl('https://webdriver.io')
await expect(browser).toHaveUrl(expect.stringContaining('webdriver')) //Checks if browser is on a page URL that contains a value.
toHaveTitle
Checks if website has a specific title.
await browser.url('https://webdriver.io/')
await expect(browser).toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js')
await expect(browser).toHaveTitle(expect.stringContaining('WebdriverIO')) // Checks if website's title contains a value.
Element Matchers
These assertions are related to the elements on the page.
toBeDisplayed()
Check if the passed element is displayed on the page or not.
const elem = await $('#someElem')
await expect(elem).toBeDisplayed()
toExist()
Returns true if element exists in the DOM.
const elem = await $('#someElem')
await expect(elem).toExist()
toBeFocused
Checks if element has focus. This assertion only works in a web context.
const elem = await $('#someElem')
await expect(elem).toBeFocused()
toHaveAttribute
Checks if an element has a certain attribute with a specific value.
const myInput = await $('input')
await expect(myInput).toHaveAttribute('class', 'form-control')
await expect(myInput).toHaveAttribute('class', expect.stringContaining('form')) // using partial attribute value
toHaveValue
Checks if an input element has a certain value.
const myInput = await $('input')
await expect(myInput).toHaveValue('user', { ignoreCase: true })
await expect(myInput).toHaveValue(expect.stringContaining('us')) // using partial value
toHaveText
Checks if element has a specific text. Can also be called with an array as parameter in the case where the element can have different texts.
await browser.url('https://webdriver.io/')
const elem = await $('.container')
await expect(elem).toHaveText('Next-gen browser and mobile automation test framework for Node.js')
await expect(elem).toHaveText(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'])
//using partial text in an element
await expect(elem).toHaveText(expect.stringContaining('browser and mobile automation test framework'))
await expect(elem).toHaveText([
expect.stringContaining('browser and mobile automation test framework'),
expect.stringContaining('Started')
])
toBeClickable
Checks if an element can be clicked by calling isClickable on the element.
const elem = await $('#elem')
await expect(elem).toBeClickable()
toBeEnabled
Checks if an element is enabled by calling isEnabled on the element.
const elem = await $('#elem')
await expect(elem).toBeEnabled()
// same as
await expect(elem).not.toBeDisabled()
toBeDisabled
Checks if an element is disabled by calling isEnabled on the element.
const elem = await $('#elem')
await expect(elem).toBeDisabled()
// same as
await expect(elem).not.toBeEnabled()
There are many more different expect matcher available in webdriver. Refer to this documentation by wendriver.io to explore more a them.
Sample script
Below is the sample script which uses many of the functions and concepts explained sections above. The script tries to log into facebook using invalid credential and verifies whether facebook allows us to login or not.
describe("Facebook Login Test", () => {
it("Login using invalid credentials", async () => {
await browser.maximizeWindow();
await browser.url('https://www.facebook.com/');
await browser.pause(2000);
const username = await $('#email');
await username.setValue('invalid_user');
const password = await $('#pass');
await password.setValue('invalid_pass');
const login_btn = await $('[name="login"]');
await login_btn.click();
const error_message = await $("div*=The email address or mobile number you entered");
try {
error_message.waitForDisplayed({ timeout: 5000 });
} catch (error) {
console.log('Error message not displayed');
}
await expect(error_message).toBeDisplayed();
await expect(error_message).toHaveText(expect.stringContaining("The email address or mobile number you entered"));
})
})