Skip to content

Latest commit

 

History

History
854 lines (608 loc) · 37.1 KB

CHANGELOG.md

File metadata and controls

854 lines (608 loc) · 37.1 KB

Changelog

v0.13.0 (2017-2-16)

IDE plugins, fixture hooks, speed option for test actions, a couple of API enhancements and lots of bug fixes.

Enhancements

⚙️ IDE Plugins

With this release, we have prepared test runner plugins for VSCode and SublimeText. These plugins allow you to

  • Run a particular test, fixture, all tests in a file or directory via the context menu or built-in commands,
  • Automatically detect browsers installed on the local machine,
  • Repeat last test run,
  • Debug tests,
  • View test results in the Debug Console panel.

⚙️ Fixture hooks (#903)

You can now specify fixture hooks that will be executed before the first test in a fixture is started and after the last test is finished.

fixture `My fixture`
    .page `http://example.com`
    .before( async ctx => {
        /* fixture initialization code */
    })
    .after( async ctx => {
        /* fixture finalization code */
    });

Unlike test hooks, fixture hooks are executed between test runs and do not have access to the tested page. Use them to perform server-side operations like preparing the server that hosts the tested app.

Sharing variables between fixture hooks and test code

Use the ctx parameter passed to fixture.before and fixture.after methods (fixture context) to share values and objects with test code. You can assign to ctx parameter's properties or add new properties.

In test code, use the t.fixtureCtx property to access the fixture context.

fixture `Fixture1`
    .before(async ctx  => {
        ctx.someProp = 123;
    })
    .after(async ctx  => {
        console.log(ctx.newProp); // > abc
    });

test('Test1', async t => {
    console.log(t.fixtureCtx.someProp); // > 123
});

test('Test2', async t => {
    t.fixtureCtx.newProp = 'abc';
});

⚙️ Speed option for test actions (#865)

You can now specify speed for individual test actions using the speed option.

import { Selector } from 'testcafe';

const nameInput = Selector('#developer-name');

fixture `My Fixture`
    .page `http://devexpress.github.io/testcafe/example/`

test('My Test', async t => {
    await t
        .typeText(nameInput, 'Peter')
        .typeText(nameInput, ' Parker', { speed: 0.1 });
});

If speed is also specified for the whole test, the action speed setting overrides test speed.

⚙️ Setting test speed from test code (#865)

You can now specify test speed from code using the t.setTestSpeed method.

import { Selector } from 'testcafe';

fixture `Test Speed`
    .page `http://devexpress.github.io/testcafe/example/`;

const nameInput = Selector('#developer-name');

test(`Test Speed`, async t => {
    await t
        .typeText(nameInput, 'Peter')
        .setTestSpeed(0.1)
        .typeText(nameInput, ' Parker');
});

⚙️ Using test controller outside of test code (#1166)

You may sometimes need to call test API from outside of test code. For instance, your page model can contain methods that perform common operations used in many tests, like authentication.

import { Selector } from 'testcafe';

export default class Page {
    constructor () {
        this.loginInput    = Selector('#login');
        this.passwordInput = Selector('#password');
        this.signInButton  = Selector('#sign-in-button');
    }
    async login (t) {
        await t
            .typeText(this.loginInput, 'MyLogin')
            .typeText(this.passwordInput, 'Pa$$word')
            .click(this.signInButton);
    }
}

In this instance, you need to access the test controller from the page model's login method.

TestCafe allows you to avoid passing the test controller to the method explicitly. Instead, you can simply import t to the page model file.

import { Selector, t } from 'testcafe';

export default class Page {
    constructor () {
        this.loginInput    = Selector('#login');
        this.passwordInput = Selector('#password');
        this.signInButton  = Selector('#sign-in-button');
    }
    async login () {
        await t
            .typeText(this.loginInput, 'MyLogin')
            .typeText(this.passwordInput, 'Pa$$word')
            .click(this.signInButton);
    }
}

TestCafe will implicitly resolve test context and provide the right test controller.

⚙️ Inserting text with one keystroke with t.typeText action (by @ericyd) (#1230)

The new paste option allows you to insert a portion of text with one keystroke, similar to the paste operation.

import { Selector } from 'testcafe';

fixture `My fixture`
    .page `http://devexpress.github.io/testcafe/example/`;

const nameInput = Selector('#developer-name');

test(`My test`, async t => {
    await t
        .typeText(nameInput, 'Peter')
        .typeText(nameInput, ' Parker', { paste: true });
});

⚙️ prevSibling and nextSibling selector's DOM search methods (#1218)

The new prevSibling and nextSibling methods allow you to search among sibling elements that reside before and after the selector's matching elements in the DOM tree.

Selector('li .active').prevSibling(2);
Selector('li').nextSibling('.checked');

⚙️ Deprecated functionality removed (#1167)

The following deprecated members have been removed from the API.

  • t.select method - use Selector instead:
const id = await t.select('.someClass').id;

// can be replaced with

const id = await Selector('.someClass').id;

Bug Fixes

  • Fixed a bug where tests failed with a script error (#1188)
  • Text can now be typed to an input field with type "email" in Firefox (#1187)
  • npm install no longer displays warnings (#769)
  • Dev Tools can now be opened with a keyboard shortcut or right click on macOS (#1193)
  • A warning no longer appears when using ClientFunction with dependencies (#1168)
  • Tests can now run against React Storybook (#1147)
  • Script error is no longer thrown in iOS webviews (Firefox, Chrome of iOS) (#1189)
  • XhrSandbox.createNativeXHR now works correctly (testcafe-hammerhead/#1042)
  • Window.prototype is no longer used for NativeMethods initialization (testcafe-hammerhead/#1040)
  • Functions from the 'vm' module are now overridden on the client (testcafe-hammerhead/#1029)
  • Input type is now changed while setting the selection range in Firefox (testcafe-hammerhead/#1025)
  • An iframe with the about:blank src can now send postMessage (testcafe-hammerhead/#1026)
  • The formaction attribute is now overridden correctly after it is appended in DOM (testcafe-hammerhead/#1021)
  • Fixed a bug where the Authorization Header was wrongly removed (testcafe-hammerhead/#1016)
  • The file:// protocol is now supported (testcafe-hammerhead/#908)

v0.12.1 (2017-1-20)

🏎️ A recovery release following v0.12.0 with an important fix. 🏎️

Bug Fixes

  • Fixed a bug when the cursor was not visible while running tests (#1156).

v0.12.0 (2017-1-19)

HTTP authentication support, a CI-friendly way to start and stop the tested app and lots of API enhancements.

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)
  • ShadowUI root is now hidden for elementFromPoint in an iframe in IE (#1029)
  • Preventing the form submit event no longer leads to additional delay between actions (#1115)
  • TestCafe no longer hangs when a cursor is moved out of a reloading iframe (#1140)
  • Onclick event handler is now executed correctly during click automation in specific cases (#1138)
  • The application/pdf mime type is no longer recognized as a page (testcafe-hammerhead#1014)
  • Limited support for the frameset tag is implemented (testcafe-hammerhead#1009)
  • Function.prototype.toString is now proxied correctly when it is overriden in a user script (testcafe-hammerhead#999)
  • Script processing no longer hangs on chained assignments (testcafe-hammerhead#866)
  • formaction attribute is now processed (testcafe-hammerhead#988)
  • document.styleSheets is now overrided (testcafe-hammerhead#1000)
  • href attribute is now processed correctly in an iframe without src when it is set from the main window (testcafe-hammerhead#620)
  • Cookies without a key are now set correctly (testcafe-hammerhead#899)
  • The noscript tag is now processed correctly when it was added via innerHTML (testcafe-hammerhead#987)
  • Element.insertAdjacentHTML function is now overrided in IE (testcafe-hammerhead#954)
  • Browser behaviour is now emulated correctly when the cookie size is bigger than the browser limit (testcafe-hammerhead#767)

v0.11.1 (2016-12-8)

🏎️ A quick follow-up for the v0.11.0 with important fix for Firefox users. 🏎️

Bug Fixes

  • Firefox now launches successfully if TestCafe installation directory contains whitespaces (#1042).

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:

  1. finds the second parent (parent of parent) of .container elements;
  2. peeks the first element in the matching set;
  3. in that element, finds elements that match the .content selector;
  4. filters them by text yo!;
  5. 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
const id = await t.select('.someClass').id;

// can be replaced with

const id = await Selector('.someClass').id;

⚙️ 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...');
});

⚙️ 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 and event.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 and t.clearUpload methods with the hidden inputs (#963).
  • Now test not hangs if object toString method uses this.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 (testcafe-hammerhead#917).
  • Fixed corruption of <template> content (testcafe-hammerhead#912).
  • Fixed querySelector for href attribute if value contains URL hash (testcafe-hammerhead#922).
  • HTTP responses with Brotli encoding now processed correctly (testcafe-hammerhead#900).
  • Element.attributes now behaves as a live collection (testcafe-hammerhead#924).
  • TestCafe doesn't fail with Error: Can't set headers after they are sent. error on network errors (testcafe-hammerhead#937).
  • Element property value setters now return correct value (testcafe-hammerhead#907).
  • window.fetch without parameters now returns rejected promise as expected (testcafe-hammerhead#939).
  • Hyperlinks created in iframe and added to the top window now have correct URL (testcafe-hammerhead#564).
  • autocomplete attribute now not forced on all elements (testcafe-hammerhead#955).
  • Cookies set via XHR response now available from client code (testcafe-hammerhead#905).
  • Fixed client rendering problems caused by incorrect DOM element determination (testcafe-hammerhead#953).

v0.10.0 (2016-11-8)

Enhancements

⚙️ Snapshot API shorthands. (#771)

Previously, if you needed to use a single property from the snapshot, you had to introduce two assignments

const snapshot = await selector();
const nodeType = snapshot.nodeType;

or additional parentheses.

const nodeType = (await selector()).nodeType;

Now snapshot methods and property getters are exposed by selectors (and selector promises as well) so that you can write more compact code.

const nodeType = await selector.nodeType;

// or

const nodeType = await selector('someParam').nodeType;

However, shorthand properties do not allow you to omit parentheses when working with dictionary properties like style, attributes or boundingClientRect.

const width = (await selector.style)['width'];

That is why we have also introduced shorthand methods for these dictionaries: getStyleProperty, getAttribute and getBoundingClientRectProperty.

const width = await selector.getStyleProperty('width');
const id    = await selector.getAttribute('id');
const left  = await selector.getBoundingClientRectProperty('left');

Finally, we have added the hasClass method.

if (await selector.hasClass('foo')) {
    //...
}

See Snapshot API Shorthands.

⚙️ Improved automatic wait mechanism. (#245)

We got rid of unnecessary waiting so that tests now run almost two times faster.

Tests running in v0.10.0 vs v0.9.0

⚙️ Test execution speed control. (#938)

We have introduced an option that allows you to specify how fast tests run.

By default, tests run at the maximum speed. However, if you need to watch a test running to understand what happens in it, this speed may seem too fast. In this instance, use the new speed option to slow the test down.

This option is available from the command line

testcafe chrome my-tests --speed 0.1

and from the API.

await runner.run({
    speed: 0.1
})

You can use factor values between 1 (the fastest, used by default) and 0.01 (the slowest).

⚙️ t.maximizeWindow test action. (#812)

We have added a test action that maximizes the browser window.

import { expect } from 'chai';
import { Selector } from 'testcafe';

const menu = Selector('#side-menu');

fixture `My fixture`
    .page `http://www.example.com/`;

test('Side menu is displayed in full screen', async t => {
    await t.maximizeWindow();

    expect(await menu.visible).to.be.ok;
});

Bug Fixes

  • The t.resizeWindow and t.resizeWindowToFitDevice actions now work correctly on macOS (#816)
  • Browser aliases are now case insensitive in the command line (#890)
  • Tests no longer hang if target scrolling coordinates are fractional (#882)
  • The 'Element is not visible' error is no longer raised when scrolling a document in Quirks mode (#883)
  • <table> child elements are now focused correctly (#889)
  • The page is no longer scrolled to the parent element when focusing on a non-focusable child during click automation (#913)
  • Browser auto-detection now works with all the Linux distributions (#104, #915)

v0.9.0 (2016-10-18)

🎉 Initial release 🎉