Chapter 2: Exploring TestCafe Under the Hood
The main goal of this chapter is to learn how TestCafe works under the hood and how it can be used in test automation to cover different features of websites and portals. We will get acquainted with the architecture of TestCafe, its API, and custom client-side code.
These topics will give us the ability to understand what main methods and functions of TestCafe are available out of the box and how to invoke them.
In this chapter, we're going to cover the following main topics:
- Scouting the TestCafe architecture.
- Learning about the TestCafe API.
- Executing custom client-side code.
Scouting the TestCafe architecture
From the beginning of time, end-to-end web testing frameworks have depended on external drivers to emulate user actions in real browsers. This approach, however, has a number of downsides:
- Third-party dependencies and a limited number of supported browsers: You had to download, install, configure, and update additional drivers or libraries for each test environment (and sometimes even for each test run). In addition to that, you could only use the browsers supported by each driver.
- Lack of flexibility: Old tools were unable to operate on the tested page directly. As long as the test code does not interfere with the app code, operating on the tested page directly enables the tool to execute many additional scenarios and workarounds. For example, this way it can add and remove styles or change the visibility of any elements on the tested page.
- Code duplication: Legacy testing frameworks ran with the same browser instance during the entire test run, maintaining the tested web application state from test to test (and keeping the same values in cookies and storage). As a consequence, end-to-end tests had a huge amount of duplicated code for clearing the web application state between tests to avoid interference.
However, TestCafe has a fix for each of these problems.
The core idea behind the architecture of TestCafe is that users should not need any external drivers to run end-to-end browser tests. Instead, all the test scripts that emulate user actions can be executed from the page itself. This enables a true cross-platform and cross-browser approach as tests will be able to run on any device with a modern browser!
After each test finishes its execution, TestCafe purges the browser state: it deletes cookies, clears localStorage
and sessionStorage
, and reloads the page. If you launch several tests in parallel, TestCafe executes each test run in an independent server-side context to prevent server-side collisions.
TestCafe execution can be split into two parts:
- Server-side (in the Node.js process).
- Client-side (in the browser).
Let's take a look at each of these parts.
The server side
Test code is performed in the Node.js environment on the server side. This enables TestCafe to use advantages of standalone server-side code, including the possibility of launching tested web application servers before tests and enhanced control over the testing environment and test execution.
Executing test code in Node.js provides a lot of advantages:
- Database preparation and the launching of the application can be done from within the tests.
- Tests have access to the server's filesystem, so you can read data or create files needed for testing.
- Tests can use all recent syntax features of Node.js. In addition to that, you can include and utilize any Node.js third-party packages.
- Improved stability and speed of execution due to test logic separation from automation scripts.
Since Node.js code executes on the server, it doesn't have direct access to the Document Object Model (DOM) of the page or browser API, but this is handled by custom client-side functions that have access to the DOM and are executed in the browser context.
The client side
TestCafe automation scripts are designed to imitate user actions on any tested page. Their main goal is to enable you to write high-level cross-browser tests, so element-focusing, triggering events, and processing attributes are performed in the same way as a real human would in a browser.
Scripts that emulate user activity (TestCafe internal scripts) run on the client side on the tested page in the browser. This enables TestCafe to utilize the advantages of browser scripts, including built-in smart waits, mobile testing, and user roles. For client-side code to work in the browser, TestCafe proxies the tested page on the server and injects the scripts into its code. This approach is also known as a reverse proxy. When you run TestCafe tests, the browser address bar shows a URL that is prefixed with some digits – this is because TestCafe uses its own open source URL-rewriting proxy (https://github.com/DevExpress/testcafe-hammerhead) and proxies the tested pages.
When you run tests with TestCafe, a reverse proxy is automatically launched locally on your computer. It injects automation scripts into the tested page, so neither the page code nor the resources it communicates with can tell that the page has been modified. In other words, when TestCafe proxies the tested page, it adds automation scripts and rewrites all the URLs on the tested page to point to the proxy:

Figure 2.1 – TestCafe reverse proxies between the user's browser and the web server
When the browser refers to these new, rewritten URLs, the original resources are also proxied and enhanced in the same way. TestCafe also mocks the browser API to separate automation scripts from the rest of the page code. The proxying mechanism is absolutely safe – it guarantees that the page appears to be hosted at the original URL, even to the test code.
In this section, we reviewed how TestCafe operates from the server and client sides. We also learned about the main advantages of this architecture, including the possibility to prelaunch applications before tests, extend control over testing environments, proxying and injecting scripts, which enables smart waiting, mobile testing, and user roles, which we will discuss a bit later.
TestCafe supports JavaScript – the most popular programming language for web development – which allows most users to use their existing coding skills and minimizes the learning curve for newcomers. In addition to that, its clear API makes tests easy to create, read, and maintain. So, let's see what methods TestCafe has to offer.
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:
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.
Executing custom client-side code
With TestCafe, you can create client functions that can run on the client side (in the browser) and return any serializable value. For example, you can obtain the URL of the current page, set cookies, or even manipulate any elements on the page.
In some complex scenarios, TestCafe helps you write code to be executed on the tested page. Here are several examples of tasks that can be done with custom client-side code:
- Get elements from the web page for further actions. TestCafe allows you to create selectors based on client-side code that returns DOM nodes. You can write this code in the server-side test and TestCafe will run these functions in the browser when it needs to locate an element:
const { Selector } = require('testcafe');const testElement = Selector(() => { return document.querySelector('.test-class-name');});await t.click(testElement);
- Obtain data from a client function that returns any serializable object from the client side (including any objects that can be converted to JSON). Unlike selectors, test code can access the object this client function returns. Usually, the data obtained from client functions is used to assert different page parameters. Here is an example of getting and verifying a page URL:
const { ClientFunction } = require('testcafe');const getPageUrl = ClientFunction(() => { return window.location.href;});await t.expect(getPageUrl).eql('https://test-site.com');
- Inject custom code into the tested page. Injected scripts can then be used to add helper functions or to mock browser API:
fixture('My second test Fixture') .page('https://test-site.com') .clientScripts( 'assets/jquery-latest.js', 'scripts/location-mock.js' );
Note
It is recommended that you avoid changing the DOM with custom client-side code. A rule of thumb is to use client-side code only to explore the page, find and return information to the server.
You can find more examples of client-side scripts and injections at the following links:
- https://devexpress.github.io/testcafe/documentation/guides/basic-guides/obtain-client-side-info.html.
- https://devexpress.github.io/testcafe/documentation/guides/advanced-guides/inject-client-scripts.html.
As we just discovered, TestCafe client functions are quite useful for different browser manipulations and getting additional data to verify in our tests.
Summary
In this chapter, we learned how TestCafe works under the hood. We got acquainted with the architecture of TestCafe, saw how it performs on client and server sides, and learned about the strategies for selecting elements, actions, assertions, roles, and custom client-side code.
All of this will be used in the upcoming chapters to write our own suite of end-to-end tests. In addition to that, you can always use this chapter as a reference to search for any particular method or assertion and see how it's called and what it does.
Now, let's move on from the main methods and functions of TestCafe to more practical aspects, such as setting up the testing environment for our future automated tests.