Releases: DevExpress/testcafe
v0.12.0 (2017-1-19)
Enhancements
⚙️ HTTP authentication support (#955, #1109)
TestCafe now supports testing webpages protected with HTTP Basic and NTLM authentication.
Use the httpAuth function in fixture or test declaration to specify the credentials.
fixture `My fixture`
.page `http://example.com`
.httpAuth({
username: 'username',
password: 'Pa$$word',
// Optional parameters, can be required for the NTLM authentication.
domain: 'CORP-DOMAIN',
workstation: 'machine-win10'
});
test('Test1', async t => {}); // Logs in as username
test // Logs in as differentUserName
.httpAuth({
username: 'differentUserName',
password: 'differentPa$$word'
})
('Test2', async t => {});
⚙️ Built-in CI-friendly way to start and stop the tested web app (#1047)
When launching tests, you can now specify a command that starts the tested application.
TestCafe will automatically execute this command before running tests and stop the process when tests are finished.
testcafe chrome tests/ --app "node server.js"
runner
.startApp('node server.js')
.run();
You can also specify how long TestCafe should wait until the tested application initializes (the default is 1 sec).
testcafe chrome tests/ --app "node server.js" --app-init-delay 4000
runner
.startApp('node server.js', 4000)
.run();
⚙️ Screenshot and window resize actions now work on Linux (#1117)
The t.takeScreenshot
, t.resizeWindow
, t.resizeWindowToFitDevice
and t.maximizeWindow
actions can now be executed on Linux machines.
⚙️ Adding custom properties to the element state (#749)
The state of webpage elements can now be extended with custom properties.
We have added the addCustomDOMProperties method to the selector, so that you can add properties to the element state like in the following example.
import { Selector } from 'testcafe'
fixture `My fixture`
.page `https://devexpress.github.io/testcafe/example/`;
test('Check Label HTML', async t => {
const label = Selector('label').addCustomDOMProperties({
innerHTML: el => el.innerHTML
});
await t.expect(label.innerHTML).contains('input type="checkbox" name="remote"');
});
⚙️ Skipping tests (#246)
TestCafe now allows you to specify that a particular test or fixture should be skipped when running tests.
Use the fixture.skip
and test.skip
methods for this.
fixture.skip `Fixture1`; // All tests in this fixture will be skipped
test('Fixture1Test1', () => {});
test('Fixture1Test2', () => {});
fixture `Fixture2`;
test('Fixture2Test1', () => {});
test.skip('Fixture2Test2', () => {}); // This test will be skipped
test('Fixture2Test3', () => {});
You can also use the only
method to specify that only a particular test or fixture should run while all others should be skipped.
fixture.only `Fixture1`;
test('Fixture1Test1', () => {});
test('Fixture1Test2', () => {});
fixture `Fixture2`;
test('Fixture2Test1', () => {});
test.only('Fixture2Test2', () => {});
test('Fixture2Test3', () => {});
// Only tests in Fixture1 and the Fixture2Test2 test will run
⚙️ Specifying the start webpage for a test (#501)
An individual test can now override the fixture's page
setting and start on a different page.
fixture `MyFixture`
.page `http://devexpress.github.io/testcafe/example`;
test('Test1', async t => {
// Starts at http://devexpress.github.io/testcafe/example
});
test
.page `http://devexpress.github.io/testcafe/blog/`
('Test2', async t => {
// Starts at http://devexpress.github.io/testcafe/blog/
});
⚙️ Initialization and finalization methods for a test (#1108)
We have added the before and after methods to the test declaration.
Use them to provide code that will be executed before a test is started and after it is finished.
test
.before( async t => {
/* test initialization code */
})
('My Test', async t => {
/* test code */
})
.after( async t => {
/* test finalization code */
});
⚙️ Sharing variables between hooks and test code (#841)
You can now share variables between fixture.beforeEach
, fixture.afterEach
, test.before
, test.after
functions and test code by using the test context object.
Test context is available through the t.ctx
property.
Instead of using a global variable, assign the object you want to share directly to t.ctx
or create a property like in the following example.
fixture `Fixture1`
.beforeEach(async t => {
t.ctx.someProp = 123;
});
test
('Test1', async t => {
console.log(t.ctx.someProp); // > 123
})
.after(async t => {
console.log(t.ctx.someProp); // > 123
});
⚙️ Assertion methods to check for regexp match (#1038)
We have added match
and notMatch
methods to check if a string matches a particular regular expression.
await t.expect('foobar').match(/^f/, 'this assertion passes');
await t.expect('foobar').notMatch(/^b/, 'this assertion passes');
⚙️ Improved filtering by predicates in selectors (#1025 and #1065)
Selector's filter predicates now receive more information about the current node, which enables you to implement more advanced filtering logic.
The filter
, find
, parent
, child
and sibling
methods now pass the node's index to the predicate.
The find
, parent
, child
and sibling
methods now also pass a node from the preceding selector.
Selector('ul').find((node, idx, originNode) => {
// node === the <ul>'s descendant node
// idx === index of the current <ul>'s descendant node
// originNode === the <ul> element
});
In addition, all these methods now allow you to pass objects to the predicate's scope on the client. To this end, we have added an optional dependencies
parameter.
const isNodeOk = ClientFunction(node => { /*...*/ });
const flag = getFlag();
Selector('ul').child(node => {
return isNodeOk(node) && flag;
}, { isNodeOk, flag });
⚙️ Filtering by negative index in selectors (#738)
You can now pass negative index
values to selector methods. In this instance, index is counted from the end of the matching set.
const lastChild = Selector('.someClass').child(-1);
⚙️ Improved cursor positioning in test actions (#981)
In action options, X and Y offsets that define the point where action is performed can now be negative.
In this instance, the cursor position is calculated from the bottom-right corner of the target element.
await t.click('#element', { offsetX: -10, offsetY: -30 });
⚙️ Client functions as an assertion's actual value (#1009)
You can now pass client functions to assertion's expect
method. In this instance, the Smart Assertion Query Mechanism will run this client function and use the return value as the assertion's actual value.
import { ClientFunction } from 'testcafe';
const windowLocation = ClientFunction(() => window.location.toString());
fixture `My Fixture`
.page `http://www.example.com`;
test('My Test', async t => {
await t.expect(windowLocation()).eql('http://www.example.com');
});
⚙️ Automatic waiting for scripts added during a test action (#1072)
If a test action adds scripts on a page, TestCafe now automatically waits for them to finish before proceeding to the next test action.
⚙️ New ESLint plugin (#1083)
We have prepared an ESLint plugin.
Get it to ensure that ESLint does not fail on TestCafe test code.
Bug Fixes
- Remote browser connection timeout has been increased (#1078)
- You can now run tests located in directories with a large number of files (#1090)
- Key identifiers for all keys are now passed to key events (#1079)
- Touch events are no longer emulated for touch monitors (#984)
- v8 flags can now be passed to Node.js when using TestCafe from the command line (#1006)
- ShadowU...
0.12.0-alpha6
Bump version. Release candidate (#1150)
v0.12.0-alpha5
Update hammerhead and browser-tools versions, bump testcafe version t…
v0.12.0-alpha4
Update hammerhead, bump version to 0.12.0-alpha4 (#1114)
v0.12.0-alpha3
Bump version to 0.12.0-alpha3 (#1113)
v0.12.0-alpha2
Bump version (#1084)
v0.12.0-alpha1
Bump version (#1077)
v0.12.0-alpha
Bump version. Set 'alpha' tag (#1050)
v0.11.1 (2016-12-8)
v0.11.0 (2016-12-8)
Enhancements
⚙️ Redesigned selector system. (#798)
New selector methods
Multiple filtering and hierarchical methods were introduced for selectors.
Now you can build flexible, lazily-evaluated functional-style selector chains.
Here are some examples:
Selector('ul').find('label').parent('div.someClass')
Finds all ul
elements on page. Then, in each found ul
element finds label
elements.
Then, for each label
element finds a parent that matches the div.someClass
selector.
Like in jQuery, if you request a property of the matching set or try evaluate
a snapshot, the selector returns values for the first element in the set.
// Returns id of the first element in the set
const id = await Selector('ul').find('label').parent('div.someClass').id;
// Returns snapshot for the first element in the set
const snapshot = await Selector('ul').find('label').parent('div.someClass')();
However, you can obtain data for any element in the set by using nth
filter.
// Returns id of the third element in the set
const id = await Selector('ul').find('label').parent('div.someClass').nth(2).id;
// Returns snapshot for the fourth element in the set
const snapshot = await Selector('ul').find('label').parent('div.someClass').nth(4)();
Note that you can add text and index filters in the selector chain.
Selector('.container').parent(1).nth(0).find('.content').withText('yo!').child('span');
In this example the selector:
- finds the second parent (parent of parent) of
.container
elements; - peeks the first element in the matching set;
- in that element, finds elements that match the
.content
selector; - filters them by text
yo!
; - in each filtered element, searches for a child with tag name
span
.
Getting selector matching set length
Also, now you can get selector matching set length and check matching elements existence by using selector count
and exists
properties.
Unawaited parametrized selector calls now allowed outside test context
Previously selector call outside of text context thrown an error:
const selector = Selector(arg => /* selector code */);
selector('someArg'); // <-- throws
test ('Some test', async t=> {
...
});
Now it's not a case if selector is not awaited. It's useful when you need to build a page model outside the test context:
const selector = Selector(arg => /* selector code */);
const selector2 = selector('someArg').find('span'); // <-- doesn't throw anymore
test ('Some test', async t=> {
...
});
However, once you'll try to obtain element property outside of test context it will throw:
const selector = Selector(arg => /* selector code */);
async getId() {
return await selector('someArg').id; // throws
}
getId();
test ('Some test', async t=> {
...
});
Index filter is not ignored anymore if selector returns single node
Previously if selector returned single node index
was ignored:
Selector('#someId', { index: 2 } ); // index is ignored and selector returns element with id `someId`
however it's not a case now:
Selector('#someId').nth(2); // returns `null`, since there is only one element in matching set with id `someId`
Deprecated API
t.select
method - useSelector
instead:
const id = await t.select('.someClass').id;
// can be replaced with
const id = await Selector('.someClass').id;
- selectorOptions.index - use selector.nth() instead.
- selectorOptions.text - use selector.withText() instead.
- selectorOptions.dependencies - use filtering and hierarchical methods to build combined selectors instead.
⚙️ Built-in assertions. (#998)
TestCafe now ships with numerous built-in BDD-style assertions.
If the TestCafe assertion receives a Selector's property as an actual value, TestCafe uses the smart assertion query mechanism:
if an assertion did not passed, the test does not fail immediately. The assertion retries to pass multiple times and each time it re-requests the actual shorthand value. The test fails if the assertion could not complete successfully within a timeout.
This approach allows you to create stable tests that lack random errors and decrease the amount of time required to run all your tests due to the lack of extra waitings.
Example page markup:
<div id="btn"></div>
<script>
var btn = document.getElementById('btn');
btn.addEventListener(function() {
window.setTimeout(function() {
btn.innerText = 'Loading...';
}, 100);
});
</script>
Example test code:
test('Button click', async t => {
const btn = Selector('#btn');
await t
.click(btn)
// Regular assertion will fail immediately, but TestCafe retries to run DOM state
// assertions many times until this assertion pass successfully within the timeout.
// The default timeout is 3000 ms.
.expect(btn.textContent).contains('Loading...');
});
⚙️ Added selected
and selectedIndex
DOM node state properties. (#951)
⚙️ It's now possible to start browser with arguments. (#905)
If you need to pass arguments for the specified browser, write them right after browser alias. Surround the browser call and its arguments with quotation marks:
testcafe "chrome --start-fullscreen",firefox tests/test.js
See Starting browser with arguments.
Bug Fixes
- Action keyboard events now have
event.key
andevent.keyIdentifier
properties set (#993). document.body.nextSibling
, that was broken is some cases previously, now operates properly (#958).- Now it's possible to use
t.setFilesToUpload
andt.clearUpload
methods with the hidden inputs (#963). - Now test not hangs if object
toString
method usesthis.location
getter (#953). - Touch events now correctly dispatched in latest Chrome versions with touch monitor (#944).
- Now test compilation doesn't fail if imported helper contains module re-export (e.g.
export * from './mod'
) (#969). - Actions now scroll to element to make it completely visible (there possible) (#987, #973).
- Dispatched key events now successfully pass
instanceof
check (#964). - Ember elements doesn't throw
Uncaught TypeError: e.getAttribute is not a function
anymore (#966). - First run wizards now automatically disabled in Chrome in Firefox (testcafe-browser-tools#102).
<td>
now correctly focused on actions (testcafe-hammerhead#901).document.baseURI
now returns correct value (testcafe-hammerhead#920).Function.constructor
now returns correct value (testcafe-hammerhead#913).- Setting
location
to the URL hash value doesn't lead to JavaScript error anymore ([testcaf...