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 includes https://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:
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, the index
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 (the text
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 (the text
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 a RegExp
, and optionally, attrValue
can also be a case-sensitive string or a RegExp
(https://devexpress.github.io/testcafe/documentation/reference/test-api/selector/withattribute.html).
.filterVisible()
: Selects elements that do not have the display: none;
or visibility: 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 the display: none;
or visibility: 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 the cssSelector
argument, you could provide filterFn
(a function predicate used to filter the elements) and, optionally, dependencies
(an object with functions, variables, or objects passed to the filterFn
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:
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, and dragOffsetY
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 optional options
object can include the following properties: the path
string with the screenshot file's relative path and name or a fullPage
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
, or beforeunload
); text
, which is a string with the dialog message text; and url
, 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, where width
is the value of the new width (in pixels) and height
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, where deviceName
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 the actual
value is equal to the expected
value. Here, actual
is any type of comparison value and expected
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 the actual
value does not equal the expected
value. Here, actual
is any type of comparison value and expected
is any type of value that is expected not to be equal to actual
(https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/noteql.html).
.expect(actual).ok([message][, options])
: Verifies that the actual
value is true
. Here, actual
is any type of value tested in the assertion (the assertion will pass if the actual value is true
) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/ok.html).
.expect(actual).notOk([message][, options])
: Verifies that the actual
value is false
. Here, actual
is any type of value tested in the assertion (the assertion will pass if the actual value is false
) (https://devexpress.github.io/testcafe/documentation/reference/test-api/testcontroller/expect/notok.html).
.expect(actual).contains(expected[, message][, options])
: Verifies that the actual
value contains the expected
value. Here, actual
is any type of comparison value and expected
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 the actual
value contains the expected
value. Here, actual
is any type of comparison value, and expected
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 the actual
value type is typeName
. Here, actual
is any type of comparison value and typeName
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 the actual
value type is not typeName
. Here, actual
is any type of comparison value and typeName
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 the actual
value is greater than the expected
value. Here, actual
is the number tested in the assertion (the assertion will pass if the actual value is greater than the expected value) and expected
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 the actual
value is greater than or equal to the expected
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), and expected
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 the actual
value is less than the expected
value. Here, actual
is the number tested in the assertion (the assertion will pass if the actual value is less than the expected value) and expected
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 the actual
value is less than or equal to the expected
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) and expected
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 the actual
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), and finish
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 the actual
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), and finish
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 the actual
value matches the re
regular expression. Here, actual
is any type of comparison value and re
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 the actual
value does not match the re
regular expression. Here, actual
is any type of comparison value and re
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.