Step Definitions are the next layer of our Cucumber container.
This is where we will define exactly what to do with each of our steps.
Just like Feature Files, WebDriverIO expects to find our Step Definitions in a specific location.
Let's see where that is in our wdio.conf.js
file.
Scroll down to the cucumberOpts object
.
The first property here is require
.
This is used to load files that are needed before executing any Feature Files. Step definitions definitely fall into that category. This path is where WebDriverIO expects us to put our Step Definitions, but I'm going to change this just a bit.
require: [
"./steps/**/given.js",
"./steps/**/when.js",
"./steps/**/then.js"
], // <string[]> (file/dir) require files before executing features
I've updated this property to expect our Step Definitions to be in the root of our project in a folder called "steps", and within that, it should look for files named “given.js”, “when.js” and “then.js”.
This will allow us to manage Step Definitions quite easily, with each file responsible for either Given
, When
or Then
steps.
Let's create that folder.
TIP
Just like our Feature folder, I'd recommend using sub folders to manage your Step Definitions as well. For Steps, I usually create sub folders based on the pages or areas of the system being tested.
For the testing of Google Search, I'll create a folder for the “HomePage” and the “ResultsPage”.
In each folder I'll create 3 files — “given.js”, “when.js” and “then.js”.
Let's bring up our Feature
file on the screen, so we know what steps we need to be defining and where they should go.
Given
step.Since this step is talking about going to the home page, it's safe to define what that step should do in the “given.js” in the “HomePage” folder.
Do you remember that WDIO Cucumber Framework module [@wdio/cucumber-framework] that was installed by WebDriverIO? That also installed a Cucumber module for us that gives us some helper methods that make defining what our steps should do pretty easy.
So, what we can do is import Given
from Cucumber to start.
Now, Given
here is a function that takes 2 parameters: the first parameter can be a String or regular expression that maps to the step, and the second is a function.
Typically, you would use a String for the first parameter of the function if there is nothing you want to parameterize in your step.
The regular expression option is best to use when there are elements of your step that may change, but you still want the action to be the same. This will become a bit clearer shortly.
Since our Given
step is just a static statement, let's just copy directly from our Feature File and use a String option for defining this.
The second parameter is what action should be performed for our step, so let's write that up.
All this step requires is for the browser to go to the Google homepage; that's pretty easy with WebDriverIO.
We can just use the WebDriverIO global browser and call the URL function. This function can take 1 String parameter, which is the URL you want to go to.
Since we already set up our base URL in WebDriverIO to be google.com, we can simply use a forward slash because WebDriverIO is actually smart enough to know that this a relative URL, not an absolute one — so it'll navigate to google.com.
And with that we have our first step defined.
import { Given } from "cucumber";
Given("A web browser is at the Google home page", () => {
browser.url("/");
});
Technically, we should be able to run this and see some action being performed, but before we do that, let's go over what we have here.
We have this Feature
file with this Given
step.
Given A web browser is at the Google home page
WebDriverIO sees this and starts looking for a matching step in our Step Definitions folder. It finds a match in one here and then executes the action we have provided. Pretty simple, right?
Let's actually try to run our test.
The command is
./node_modules/bin/wdio ./wdio.conf.js
So, we're using the WDIO binary to actually run our configuration file.
And there we have it, we ran our first test and we briefly saw it actually load the Google homepage.
But it looks like we have 2 errors in our console, that's not good.
If we inspect this further, we see the error is saying, 2 of our steps are not defined, which is actually true.
Now if you want, you can disable these types of warnings in the WebDriverIO configuration file, by changing the property, ignoreUndefinedDefinitions
to “true”.
Let's do that now and see the result.
So that property is actually in the cucumberOpts
object right here.
Let's run our test again.
There we go, those warnings are all gone, but now we have 2 skipped tests.
Turning those warnings off can be helpful, but I'd recommend and keeping them on.
So, let's set that back to “false”.
When
step.For this one, I'll use a regular expression as the first parameter.
I'm doing this because I want to be able to parameterize the step, specifically where “cucumber” is in the When
step and make it reusable, allowing me to effectively pass any keyword in place of cucumber.
So, for instance, I'll be able to say when the user enters “ball” into the search bar, or when the user enters “phone” into the search bar.
TIP
The use of quotations is not necessarily mandatory, but it makes the statement easier to read and allows others to know that the value can be replaced. But we have to construct our regular expression to match that step.
This is where a little knowledge of regular expressions can be really powerful.
I like to use a site called regexr.com, to validate the expressions I come up with.
Let's do that now.
Now I want an expression that will match our statement, The user enters "cucumber" into the search bar
.
Let's start by putting that in the text section. And delete what's in the expression section.
Now, I want to ensure my step starts with “The”, so I use this symbol [^] to do that. And I'll just type out the rest of the step, word for word. I want to ensure it ends with “bar”, so I'll use this symbol [$].
Now as we can see in the text section, our regular expression matches our text exactly, but how we have written it doesn't allow for us to replace “cucumber”.
Let's fix that.
Delete “cucumber” and replace it with a wildcard symbol ["(.*_"
] — in a regular expression that will match anything we use. I'll also wrap it with brackets.
This will essentially match any number of characters that are within quotations, and we can tweak our text to see if this is true.
Great, so it seems to match anything if we write it, but if we change anything else in the text we see it no longer matches, which is exactly what we want.
NOTE
This is probably the trickiest part of Cucumber, crafting regular expressions to match our steps and make them reusable.
Now that we have a working regular expression — /^The user enters "(.*)" into the search bar$/
— let's set up our “when.js” file, and copy this expression in.
Now we have to create our action.
Since we have a wildcard, we need to allow this function to expect a variable, which we will use as our search keyword.
I have this selector [$(“selector”)
] for the input field, so I'll just use the WebDriverIO API to accomplish these steps.
Let's wait for the element to be displayed, for a maximum of 5 seconds
Click in the field
Then enter the search keyword
These steps match what a typical user would do when they go to search for an item on Google.
And that should be all we need for this step.
import { When } from "cucumber";
When(/^The user enters "(.*)" into the search bar$/, keyword => {
$(".gLFyf.gsfi").waitForDisplayed(5000);
$(".gLFyf.gsfi").click();
$(".gLFyf.gsfi").setValue(keyword);
$(".aajZCb .gNO89b").waitForDisplayed(5000);
$(".aajZCb .gNO89b").click();
});
Let's see if we can run these tests. Great!
Then
step.What we want to do here is grab all the links from the results page and verify that each of them contain the word “cucumber”.
I'll open up the “then.js” file in the search results folder (ResultsPage) this time, since that's the page we would be on.
Import Then
from Cucumber and start defining our function.
This is the selector for the links [$$(“selector”)
] and this is how we can use WebDriverIO to get multiple elements from the page.
Now that we have that data, we can iterate over them, get the texts from each one, convert it to lowercase and check that each of them contain our keyword.
I'm going to be using the native assert
module that comes with Node to verify this.
And that should be it.
steps/ResultsPage/then.js
import { Then } from "cucumber";
import assert from "assert";
Then(/^links related to "(.*)" are shown on the results page$/, keyword => {
const links = $$(".LC20lb");
links.forEach(link => {
const linkText = link.getText().toLowerCase();
if (linkText) {
assert(
linkText.includes(keyword),
`Link text does not include ${keyword}`
);
}
});
});
We defined all of our steps.
Let's see if we have a passing test.
Great!
Let's move on to the next section.
Quiz
The quiz for this chapter can be found in section 4.5