Migrate Tests from Protractor to TestCafe
Protractor is an end-to-end testing tool for Angular and AngularJS applications. The Angular team announced plans to end support for Protractor by 2022 and recommended that users migrate to an alternative testing solution. For more information, refer to the following post: Future of Angular E2E & Plans for Protractor.
This section describes how to adapt Protractor tests to TestCafe. The code samples below illustrate the difference between tests in Protractor and TestCafe API.
Protractor
describe('To do test', () => {
it('Correct new items', () => {
browser.get('http://todo.org/')
element(by.css('.todo-new-text')).sendKeys('Start migration to Testcafe');
element(by.cssContainingText('button', 'Add')).click();
expect(element(by.css('.todo-list')).all(by.css('.todo-item')).count()).toEqual(1);
element(by.css('.todo-new-text')).sendKeys('Finish migration to Testcafe');
element(by.cssContainingText('button', 'Add')).click();
expect(element(by.css('.todo-list')).all(by.css('.todo-item')).count()).toEqual(2);
expect(element(by.css('.todo-list')).all(by.css('li')).get(0).getText()).toEqual('Start migration to Testcafe');
expect(element(by.css('.todo-list')).all(by.css('li')).get(1).getText()).toEqual('Finish migration to Testcafe');
})
})
TestCafe
fixture('To do test')
.page('http://todo.org/');
test('Correct new items', async t => {
await t
.typeText('.todo-new-text', 'Start migration to Testcafe')
.click(Selector('button').withText('Add'))
.expect(Selector('.todo-list').childElementCount).eql(1)
.typeText('.todo-new-text', 'Finish migration to Testcafe')
.click(Selector('button').withText('Add'))
.expect(Selector('.todo-list').childElementCount).eql(2)
.expect(Selector('.todo-list').child(0).innerText).eql('Start migration to Testcafe')
.expect(Selector('.todo-list').child(1).innerText).eql('Finish migration to Testcafe')
});
Install and Setup TestCafe
Install TestCafe in your project directory. The
--save-dev
option adds TestCafe to the development dependencies.npm install --save-dev testcafe
Install a custom Angular builder for TestCafe. This command serves an Angular application and runs TestCafe tests.
npm install --save-dev angular-testcafe
Optional. TestCafe supports standard CSS selectors, Selector objects, and framework-specific Selector objects.
Install the testcafe-angular-selectors plugin to search for DOM elements in Angular applications.
npm install --save-dev testcafe-angular-selectors
Configure the angular.json file. The devServerTarget option specifies a project against which to run tests. If this option is not specified, run the required project manually before a test run.
Refer to the TestCafe configuration file topic and the schema.json file for the full list of options.
{ "projects": { "your_application_name": { "architect": { "e2e": { "builder": "angular-testcafe:testcafe", "options": { "devServerTarget": "your_application_name:serve", "browsers": [ //browsers array "chrome --no-sandbox" ], "src": ["e2e/*.e2e-spec.ts"] // path to tests } } } } } }
Test Structure
TestCafe uses the following main objects to create tests:
- Fixture and Test objects.
- The TestController object.
- Selector objects.
// Protractor
describe('Test my website', () => {
it('Test first page', () => {
browser.get('http://mypage.com/');
element(by.cssContainingText('button', 'Add')).click();
// your code
})
})
// TestCafe
fixture('Test my website')
.page('http://mypage.com/');
test ('Test first page', async t => {
await t
.click('#signin')
// your code
});
The TestController object exposes test API methods. Use the test controller (as t
) to call test actions, handle browser dialogs, use the wait function, or execute assertions. Use the async/await
construction with the test controller to wait for called actions to complete. All test controller methods are chainable and asynchronous.
test ('My first test', async t => {
await t
.click('#signin')
.typeTest('#login','login')
.typeTest('#password','password')
// your code
});
TestCafe supports standard CSS selectors and has its own Selector object to identify a target page element. TestCafe Selector objects have more extended API compared to standard CSS selectors. TestCafe selectors support a built-in automatic wait mechanism and do not require dedicated API to wait for redirects or page elements to appear.
TestCafe automatically converts a standard CSS selector within test controller methods into a TestCafe Selector object when a test runs.
import { Selector } from 'testcafe';
fixture `Getting Started`
.page `http://devexpress.github.io/testcafe/example`;
test('My first test', async t => {
await t
// Standard CSS selector.
// TestCafe will automatically convert this CSS selector to the TestCafe Selector object.
.click('div')
// TestCafe Selector
.typeText(Selector('div').withText('some text'))
});
The Selector object does not save its value. The value is re-evaluated for each element on a page. You can create the Selector object at runtime or save its value to a variable.
fixture `Getting Started`
.page `http://devexpress.github.io/testcafe/example`;
// Save selector
const sel = Selector('div').child();
test('My first test', async t => {
await t
.expect(Selector('#elementId').innerText).eql('text', 'check element text');
});
You can use a single Selector function or chain them to traverse through a DOM tree.
fixture `Getting Started`
.page `http://devexpress.github.io/testcafe/example`;
const label = Selector('#tried-section').child('label');\
test('My first test', async t => {
await t
// your code
});
Get DOM Elements
Get an element with a specified tag name.
// Protractor element(by.tagName('body'); // TestCafe Selector('body');
Get an element with a specified id attribute.
// Protractor element(by.id('id')); // TestCafe Selector('#id');
Find an element by a specified CSS selector.
// Protractor element(by.css('.class')); // TestCafe Selector('.class');
Find an element with a specified input name attribute.
// Protractor element(by.name('name')); // TestCafe Selector (input[name="name"]);
Find an element with a specified text in the CSS class name.
// Protractor element(by.cssContainingText('.class', 'text')); // TestCafe Selector('.class').withText('text');
Find the first link with the specified text.
// Protractor element(by.linkText('text')); // TestCafe Selector('link').withText('text');
Find an element with a specified ng-model expression (for AngularJS).
// Protractor element(by.model('name')); // TestCafe AngularJSSelector.byModel('name');
Find an element with specified text binding (for AngularJS).
// Protractor element(by.binding('bindingname')); // TestCafe AngularJSSelector.byBinding('bindingname');
Find an element with specified options (for AngularJS).
// Protractor element(by.options('options')); // TestCafe AngularJSSelector.byOptions('options');
Find an element with a specified ng-options expression (for AngularJS).
// Protractor element(by.repeater('repeater'); // TestCafe AngularJSSelector.byRepeater('repeater');
Find all elements with a specified condition.
TestCafe’s Selector object automatically finds all elements that match the specified condition. Use an index notation to access elements in the array (.nth(index)).
// Protractor element.all(by.css('.class')); // TestCafe Selector('.class');
TestCafe uses the first element in the array if you pass this array to assertions.
See Also: Select Page Elements
Interact with DOM Elements
TestCafe requires that you use the TestController to interact with DOM elements.
Actions
TestCafe’s TestController object includes actions as its methods.
-
// Protractor element(by.css('.class')).click(); // TestCafe Selector('.class').click();
-
// Protractor element(by.css('.class')).sendKeys(protractor.Key.SPACE); // TestCafe Selector('.class').pressKey('space');
-
// Protractor browser.get('https://github.com/DevExpress/testcafe'); // TestCafe .navigateTo('https://github.com/DevExpress/testcafe');
-
// Protractor element(by.css('.login')).sendKeys('login'); // TestCafe .typeText('#login','login');
-
// Protractor browser.actions().mouseMove(element(by.css('.popover'))).perform(); // TestCafe .hover('.popover');
-
// Protractor browser.switchTo().frame(element(by.css('.demo-frame')).getWebElement()); // TestCafe .switchToIframe('.demo-frame');
-
// Protractor browser.wait(condition, 1000); // TestCafe .wait(1000);
See Also: All TestCafe actions
Assertions
TestCafe has a set of predefined assertions and supports third-party assertion libraries as npm dependencies.
TestCafe uses the built-in wait mechanism and recalculates the actual value until it matches the expected value or an assertion timeout expires. To execute an assertion, call the t.expect method followed by an assertion method with parameters.
Asserts that the actual value equals the expected value (the eql assertion).
// Protractor expect({ a: 'bar' }).toEqual({ a: 'bar' }); // TestCafe await t .expect({ a: 'bar' }).eql({ a: 'bar' })
Asserts that the actual value is true (the ok assertion).
// Protractor expect('ok').toBeTruthy(); // TestCafe await t .expect('ok').ok()
Asserts that the actual value contains the expected value (the contains assertion).
// Protractor expect('foo bar').toContain('bar'); // TestCafe await t .expect('foo bar').contains('bar')
Asserts that the actual type is the expected type (the typeOf assertion).
// Protractor expect(typeof { a: 'bar' }).toBe('object'); // TestCafe await t .expect({ a: 'bar' }).typeOf('object')
See Also: All TestCafe Assertions
Hooks
TestCafe can run reusable code sequences before or after tests and fixtures: Test hooks and Fixture hooks. Define the “before” and “after” hooks globally in the configuration file or in an individual test or fixture.
// Protractor
describe('hooks', function() {
before(function() {
// runs once before the first test in this block
});
after(function() {
// runs once after the last test in this block
});
beforeEach(function() {
// runs before each test in this block
});
afterEach(function() {
// runs after each test in this block
});
// test cases
});
// TestCafe
fixture`Hooks`
.before(() => {
// runs once before the first test in this fixture
})
.after(() => {
// runs once after the last test in this fixture
})
.beforeEach(() => {
// runs before each test in this fixture
})
.afterEach(() => {
// runs after each test in this fixture
});
test
.before(() => {
// runs before each test
})
.after(() => {
// runs after each test
})
('test', () => {
// test cases
})
See Also:
More Capabilities
TestCafe documentation helps you get more details about the API, FAQ, common concepts, step-by-step guides and best practices. These resources allow you to migrate Protractor tests to TestCafe as easily as possible.
Run Tests
To run TestCafe tests, execute the following Angular CLI command. The command below starts a new browser window with a clean profile (for example, without extensions or profile settings). In this case, TestCafe ignores custom settings and runs more stable tests.
ng e2e
You can also use TestCafe CLI to run TestCafe tests with different settings. For example, to run tests in parallel, use the ‘testcafe’ command with the -c
flag, specify a browser and which number of browser instances to invoke.
testcafe -c 3 chrome tests/sample-fixture.js
See Also: Run Tests Concurrently
Debug Tests
You can pause tests to examine a web page and troubleshoot errors.
The --speed flag specifies the test execution speed.
testcafe chrome ./my-tests --speed 0.1
Use any of the following test controller methods and CLI flags to switch a test to debug mode:
--debug-mode - Pauses test execution before the first action or assertion. So that, you can invoke the developer tools and then debug.
--debug-on-fail - Enters debug mode when a test fails.
t.debug - Pauses the test and allows you to use the browser’s developer tools.
fixture `Debugger example`
.page `http://devexpress.github.io/testcafe/example/`;
test('Debugger', async t => {
await t
.debug();
});