Learning about the TestCafe API
Since the server-side code runs in Node.js, tests should be written in JavaScript (TypeScript and CoffeeScript are also supported, but eventually, everything should be transpiled into JavaScript).
TestCafe utilizes a minimalistic API that provides less than a few dozen methods, which are then transformed into user actions on the page. As our tests will be using the TestCafe API methods to interact with the pages, let's review the main interaction groups supported in TestCafe:
- Elements selection.
- Actions.
- Assertions.
- User roles.
Let's discover each of these interactions in more detail.
Elements selection
TestCafe utilizes an advanced mechanism with built-in waiting to locate target elements for an action or assertion. To perform an action (such as click, hover, type, and so on) or to make an assertion, you should first identify the target page element. This is as easy as specifying a standard CSS selector. For more complex situations, you can chain methods (such as, for example, getting an element by class name, then getting its second child, and then finally, getting its third sibling). Selector strings should be passed inside chainable Selector
constructors to create a selector.
For example, you can click on a button with the button-test
class, as follows:
const { Selector } = require('testcafe');const buttonTest = Selector('.button-test');
For more complex situations, you can traverse the DOM tree by chaining selectors:
const { Selector } = require('testcafe');const linkTest = Selector('#block-test') .child('a') .withAttribute('href', 'https://test-site.com/main.html') .withText('Second link');
What this chain of selectors does is the following:
- Selects an element with the
block-test
id. - Selects its child elements.
- Filters them by the
a
tag. - Selects elements with the
href
attribute that includeshttps://test-site.com/main.html
. - Selects elements that include the
Second link
text.Note
If a selector matches several elements, the subsequent methods return results for all the elements that were matched.
TestCafe provides a number of methods that search for elements relative to the selected element (keep in mind that all of these methods should be prepended with Selector(cssSelector)
). Most of these methods accept index
as an argument, which should be a zero-based number (0 will be the closest relative element in the set). If the number is negative, the index is counted from the end of the matched set. Here are the methods:
.find(cssSelector)
: Finds the descendant nodes of all the nodes in the matched set and uses a CSS selector to filter them (the CSS selector should be a string) (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/find.html)..parent(index)
: Finds the parents of all the nodes in the matched set (the first element in the set is the closest parent) (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/parent.html)..child(index)
: Finds the child elements of all nodes in the matched set (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/child.html)..sibling(index)
: Finds the sibling elements of all the nodes in the matched set (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/sibling.html)..nextSibling(index)
: Finds the succeeding sibling elements of all the nodes in the matched set (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/nextsibling.html)..prevSibling(index)
: Finds the preceding sibling elements of all nodes in the matched set and filters them by index (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/prevsibling.html).
Now, let's look at the methods that filter elements from the selector. The same as before, all of these methods should be prepended with Selector(cssSelector)
. Here are the methods:
.nth(index)
: Selects an element with the specified index in the matched set. Here, theindex
argument should be a zero-based number (0 will be the closest relative element in the set). If it is negative, the index is counted from the end of the matched set (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/nth.html)..withText(text)
: Selects elements that contain the specified text. Here,text
is the element's text content (thetext
argument is a case-sensitive string) or a regular expression (RegExp) that should match the element's text (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/withtext.html)..withExactText(text)
: Selects elements whose text content strictly matches the specified text. Here,text
is the element's text content (thetext
argument is a case-sensitive string) (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/withexacttext.html)..withAttribute(attrName[, attrValue])
: Selects elements that contain the specified attribute. Here,attrName
can be a case-sensitive string or aRegExp
, and optionally,attrValue
can also be a case-sensitive string or aRegExp
(https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/withattribute.html)..filterVisible()
: Selects elements that do not have thedisplay: none;
orvisibility: hidden;
CSS properties and have non-zero widths and heights (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/filtervisible.html)..filterHidden()
: Selects elements that have thedisplay: none;
orvisibility: hidden;
CSS properties, or zero widths or heights (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/filterhidden.html)..filter(cssSelector)
: Selects elements that match the CSS selector (the CSS selector should be a string used to filter child elements). Also, instead of thecssSelector
argument, you could providefilterFn
(a function predicate used to filter the elements) and, optionally,dependencies
(an object with functions, variables, or objects passed to thefilterFn
function) (https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/filter.html).
When a selector is executed, TestCafe will be waiting for the target node to appear on the page until the selector timeout expires. You can specify the timeout (in milliseconds) in the following cases:
- Before test launch: It can be specified for all elements with the
selectorTimeout
config option in the.testcaferc.json
configuration file (https://devexpress.github.io/testcafe/documentation/reference/configuration-file.html). - During test launch: It can be set for all elements with the
--selector-timeout
command-line option (https://devexpress.github.io/testcafe/documentation/reference/command-line-interface.html#--selector-timeout-ms. - In test code: It can be set as an additional option inside
Selector
(https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/constructor.html#optionstimeout) to set the timeout for any particular element.
During the timeout, the selector is rerun until it returns a DOM element or the timeout is surpassed. If TestCafe can't find the corresponding node in the DOM, the test is marked as failed.
Actions
The TestCafe API provides a set of action methods to interact with the page (such as click, type, select text, hover, and so on). You can call them one after another in a chained fashion. All of these methods should be prepended with t
as they are the methods of the test controller object (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/). Also, selector
can be a string, selector, DOM node, function, or Promise; and optionally, you can use options
, which is an object with a set of options containing supplementary parameters for the action (unless otherwise specified). Here are all the main action methods:
.click(selector[, options])
: Clicks on an element on a page (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/click.html)..doubleClick(selector[, options])
: Double-clicks on an element on a page (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/doubleclick.html)..rightClick(selector[, options])
: Right-clicks on an element on a page (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/rightclick.html)..pressKey(keys[, options])
: Presses the specified keyboard keys. Here,keys
is a sequence of keys and key combinations to be pressed (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/presskey.html)..navigateTo(url)
: Navigates to the specified URL. Here,url
is a string with the URL to navigate to (which can be absolute or relative to the current page) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/navigateto.html)..typeText(selector, text[, options])
: Types the specified text into an input element. Here,text
is a string of the text to be typed into the specified web page element (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/typetext.html)..selectText(selector[, startPos][, endPos][, options])
: Selects text in input elements of various types. Here,startPos
is the number (zero-based integer, 0 by default) of the start position of the selection. Optionally,endPos
is the number (zero-based integer; by default, it is equal to the length of the visible text content) of the end position of the selection (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/selecttext.html)..hover(selector[, options])
: Hovers the mouse pointer over a web page element (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/hover.html)..drag(selector, dragOffsetX, dragOffsetY[, options])
: Drags an element to a specified offset. Here,dragOffsetX
is the number of pixels for the X offset (horizontal) of the drop coordinates from the original position of the mouse pointer, anddragOffsetY
is the number of pixels for the Y offset (vertical) of the drop coordinates from the original position of the mouse pointer (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/drag.html)..dragToElement(selector, destinationSelector[, options])
: Drags an element onto another web page element. Here,destinationSelector
should identify the web page element that will be the drop location (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/dragtoelement.html)..setFilesToUpload(selector, filePath)
: Adds file paths to the specified file upload input. Here,filePath
is a string or an array with the path to the uploaded file (or several paths, in the case of an array). Relative paths are resolved against the folder with the test file (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/setfilestoupload.html)..clearUpload(selector)
: Deletes all the file paths from the specified file upload input (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/clearupload.html)..takeScreenshot([options])
: Takes a screenshot of the entire page. The optionaloptions
object can include the following properties: thepath
string with the screenshot file's relative path and name or afullPage
boolean (false by default) that specifies if the full page should be captured, including content that is not visible due to overflow (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/takescreenshot.html)..takeElementScreenshot(selector[, path][, options])
: Takes a screenshot of the specified web page element. Here,path
(an optional argument) is a string with the screenshot file's relative path and name (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/takeelementscreenshot.html)..switchToIframe(selector)
: Switches the browsing context of the test to the specified<iframe>
(https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/switchtoiframe.html)..switchToMainWindow()
: Switches the browsing context of the test from an<iframe>
back to the main window (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/switchtomainwindow.html)..setNativeDialogHandler(fn(type, text, url)[, options])
: Specifies a handler function to deal with native dialogs triggered during the test run. Here,fn(type, text, url)
can be a function or a client function that will be invoked whenever a native dialog is triggered (null
to delete the native dialog handler). The handler function can utilize three arguments:type
, which is a string with the type of the native dialog (confirm
,alert
,prompt
, orbeforeunload
);text
, which is a string with the dialog message text; andurl
, which is a string with the URL of the page that triggered the dialog (used to check whether the dialog was called from the main window or an<iframe>
) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/setnativedialoghandler.html)..getNativeDialogHistory()
: Provides a history of the native dialogs that were triggered (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/getnativedialoghistory.html)..resizeWindow(width, height)
: Resizes a window to fit the provided width and height, wherewidth
is the value of the new width (in pixels) andheight
is the value of the new height (in pixels) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/resizewindow.html)..resizeWindowToFitDevice(deviceName[, options])
: Resizes the window to fit the screen of the specified mobile device, wheredeviceName
is a string with the device name (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/resizewindowtofitdevice.html)..maximizeWindow()
: Maximizes the browser window (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/maximizewindow.html)..wait(timeout)
: Pauses a test execution for a specified period of time. Here,timeout
is the length of the pause duration (in milliseconds) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/wait.html).
Assertions
TestCafe allows you to verify elements, page properties, and parameters (equals, contains, greater, match, and so on). To write assertions, use the test controller's t.expect
method, followed by an assertion method that accepts an expected value and optional arguments; message
is the assertion message string that shows up in the report if the test fails and options
is an object with a set of options containing supplementary parameters for the assertion. Here are all the assertion methods available in TestCafe out of the box:
.expect(actual).eql(expected[, message][, options])
: Verifies that theactual
value is equal to theexpected
value. Here,actual
is any type of comparison value andexpected
is any type of expected value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/eql.html)..expect(actual).notEql(expected[, message][, options])
: Verifies that theactual
value does not equal theexpected
value. Here,actual
is any type of comparison value andexpected
is any type of value that is expected not to be equal toactual
(https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/noteql.html)..expect(actual).ok([message][, options])
: Verifies that theactual
value istrue
. Here,actual
is any type of value tested in the assertion (the assertion will pass if the actual value istrue
) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/ok.html)..expect(actual).notOk([message][, options])
: Verifies that theactual
value isfalse
. Here,actual
is any type of value tested in the assertion (the assertion will pass if the actual value isfalse
) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/notok.html)..expect(actual).contains(expected[, message][, options])
: Verifies that theactual
value contains theexpected
value. Here,actual
is any type of comparison value andexpected
is any type of expected value (the assertion will pass if the actual value contains the expected value) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/contains.html)..expect(actual).notContains(expected[, message][, options])
: Verifies that theactual
value contains theexpected
value. Here,actual
is any type of comparison value, andexpected
is any type of expected value (the assertion will pass if the actual value does not contain the expected value) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/notcontains.html)..expect(actual).typeOf(typeName[, message][, options])
: Asserts that theactual
value type istypeName
. Here,actual
is any type of comparison value andtypeName
is a string of the expected type of an actual value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/typeof.html)..expect(actual).notTypeOf(typeName[, message][, options])
: Asserts that theactual
value type is nottypeName
. Here,actual
is any type of comparison value andtypeName
is a string of the type of the actual value that causes an assertion to fail (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/nottypeof.html)..expect(actual).gt(expected[, message][, options])
: Verifies that theactual
value is greater than theexpected
value. Here,actual
is the number tested in the assertion (the assertion will pass if the actual value is greater than the expected value) andexpected
is any type of expected value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/gt.html)..expect(actual).gte(expected[, message][, options])
: Verifies that theactual
value is greater than or equal to theexpected
value. Here,actual
is a number tested in the assertion (the assertion will pass if the actual value is greater than or equal to the expected value), andexpected
is any type of expected value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/gte.html)..expect(actual).lt(expected[, message][, options])
: Verifies that theactual
value is less than theexpected
value. Here,actual
is the number tested in the assertion (the assertion will pass if the actual value is less than the expected value) andexpected
is any type of expected value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/lt.html)..expect(actual).lte(expected[, message][, options])
: Verifies that theactual
value is less than or equal to theexpected
value. Here,actual
is the number tested in the assertion (the assertion will pass if the actual value is less than or equal to the expected value) andexpected
is any type of expected value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/lte.html)..expect(actual).within(start, finish[, message][, options])
: Verifies that theactual
value is within a specified range from start to finish (bounds are inclusive). Here,actual
is a number,start
is the number for the lower range (inclusive), andfinish
is the number for the upper range (inclusive) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/within.html)..expect(actual).notWithin(start, finish[, message][, options])
: Verifies that theactual
value is not within the specified range from start to finish (bounds are inclusive). Here,actual
is a number,start
is the number for the lower range (inclusive), andfinish
is the number for the upper range (inclusive) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/notwithin.html)..expect(actual).match(re[, message][, options])
: Verifies that theactual
value matches there
regular expression. Here,actual
is any type of comparison value andre
is a regular expression that is expected to match the actual value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/match.html)..expect(actual).notMatch(re[, message][, options])
: Verifies that theactual
value does not match there
regular expression. Here,actual
is any type of comparison value andre
is a regular expression that is expected not to match the actual value (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/notmatch.html).
User roles
TestCafe has a built-in user role mechanism that emulates user actions for logging in to a website. It also saves the logged-in state of each user in a separate role that can be reused later on in any part of your tests to switch between user accounts. This approach gives access to some unique features:
- Login actions are not duplicated upon switching to a previously used role during the same session. So, for example, if you activate a role in the
beforeEach
hook, the login actions will run only once before the first test. All further tests will just reuse the existing authentication data. - When you switch roles, the browser automatically navigates back to the page where the switch happened, so there is no need to additionally open any URLs for a new role (this behavior can be disabled if required).
- If during a test you log in to several websites, authentication data from cookies and browser storage is saved in the active role. When switching back to this role in the same test, you will be logged in to all the websites automatically.
- An anonymous built-in role that logs you out of all accounts.
Let's have a look at a practical example of creating and using roles.
To create and initialize a role, we will need to use a Role
constructor. Then, the login page URL and actions needed to log in should be passed to Role
. This is shown in the following code block:
const { Role, Selector } = require('testcafe');const regularUser = Role('https://test-site.com/login', async (t) => { await t.typeText('.login', 'TestUser') .typeText('.password', 'testuserpass') .click('#log-in');});const admin = Role('https://test-site.com/login', async (t) => { await t.typeText('.login', 'TestAdmin') .typeText('.password', 'testadminpass') .click('#log-in');});const linkLoggedInUser = Selector('.link-logged-in-user');const linkLoggedInAdmin = Selector('.link-logged-in-admin');fixture('My first test Fixture').page('https://test-site.com');test('Test login with three users', async (t) => { await t.useRole(regularUser) .expect(linkLoggedInUser.exists).ok() .useRole(admin) .expect(linkLoggedInUser.exists).notOk() .expect(linkLoggedInAdmin.exists).ok() .useRole(Role.anonymous()) .expect(linkLoggedInUser.exists).notOk() .expect(linkLoggedInAdmin.exists).notOk();});
After you create all the required roles, you can switch between them anytime; roles are shared across tests and fixtures. Roles can even be created in a separate file and then used in any test fixture that references (requires or imports) this file.
To sum up, in this section, we reviewed the TestCafe API and the main methods that it provides. We also learned how to select elements, conduct assertions, and utilize user roles to switch between different accounts. Now, let's take a look at how custom client-side code can be executed in TestCafe to give us even more control over the browser.