Functional tests helper for Rendr-powered sites in BusterJS. Adds helper functions to simulate basic user interactions.
Requires: BusterJS as peer dependency.
npm install buster-rendr-functional-tests --save-dev
Add following snippet to your project's package.json
to allow buster-server
support
proxying without session prefixes.
"scripts":
{
"postinstall": "test ! -d ./node_modules/buster-rendr-functional-tests || ./node_modules/buster-rendr-functional-tests/bin/install_wrapper"
},
You might need to run npm run-script postinstall
for the first time.
Just include buster-rendr-functional-tests
as extension,
list proxied paths as resources, see Proxy resources.
'Functional tests':
{
environment: 'browser',
tests:
[
'tests/functional/**/*.js'
],
resources:
[
// provide proxy paths, "/" is occupied by buster itself
// so provide some alternative, like "/index"
{path: '/index', backend: 'http://localhost:3030/'},
{path: '/js', backend: 'http://localhost:3030/js'},
{path: '/css', backend: 'http://localhost:3030/css'},
{path: '/api', backend: 'http://localhost:3030/api'},
],
extensions:
[
require('buster-rendr-functional-tests')
],
'buster-rendr-functional-tests':
{
timeout: 120 // seconds
}
}
Loads requested page into iframe and waits for the App
object to be resolved.
Note: When page is loaded, references to it's window
, document
, $
and App
are attached to the test's context.
setUp: function(done)
{
// load new app's homepage for each test
this.load('/index').wait('postPageRender', done);
}
Cleans up iframe and proxy-cookie, leaving stage clean for the next test.
tearDown: function(done)
{
this.unload(done);
}
Simulates browser events related to touch, in the right order and with proper pauses.
'Change search type to for-rent': function(done)
{
// initially loads as For Sale search type
assert.className(this.$('#searchTypeTabs>[data-tab=for_sale]')[0], 'backgroundLowlight');
// change tab
this.touch('#searchTypeTabs>[data-tab=for_rent]', function()
{
// for rent tab should be highlighted
assert.className(this.$('#searchTypeTabs>[data-tab=for_rent]')[0], 'backgroundLowlight');
// and for sale tab should not
refute.className(this.$('#searchTypeTabs>[data-tab=for_sale]')[0], 'backgroundLowlight');
done();
});
}
Simulates user's typing, with all related events and proper timing.
'Get autocomplete suggestions': function(done)
{
this.type('[data-action=searchForm]', 'Palo Al', function()
{
// Check first line of autosuggest
// should contain "Palo Alto, CA"
assert.contains(this.$('[data-role=autosuggest_list]>li:first-child').text(), 'Palo Alto, CA');
done();
});
}
Makes sure focus event is triggered on the provided element.
Note: .type
uses it internally. It's a synchronous function.
'Test': function(done)
{
this.focus('[data-action=searchForm]');
}
Makes sure blur event is triggered on the provided element.
Note: It's a synchronous function.
'Test': function(done)
{
this.blur('[data-action=searchForm]');
}
Waits for the App level events, if no window.App
is available,
puts callbacks into queue and continues to check for the App
object
every once in a while.
'Go to the property page': function(done)
{
// no property page yet
refute.contains(this.$('[data-action=pdpDesc]').text(), 'Home Details');
// tap on the property
this.touch('[data-action=property] .propertyPhoto');
// wait for the new page to activate
this.wait('currentViewActive', function()
{
// property page is here
assert.contains(this.$('[data-action=pdpDesc]').text(), 'Home Details');
done();
});
}
Waits for CSS Transition to finish, triggers provided callback after that.
'Open side menu': function(done)
{
// define target element
var target = this.$('body>[data-view*=side_nav]');
// when menu closed it has position absolute
assert.equals(target.css('position'), 'absolute');
// Toggle menu
this.touch('#navToggle');
// there is animation delay for sliding in, wait till animation is done
this.waitForCss(target, function()
{
// when open, side menu has position fixed
assert.equals(target.css('position'), 'fixed');
done();
});
}
Waits for element to show up on the page and contain provided text. Better to use with selectors rather than elements, to allow it to catch newly created elements.
Another autocomplete example, this time much closer to the real life.
'Get autocomplete suggestions': function(done)
{
this.type('[data-action=searchForm]', 'Palo Al', function()
{
// need to wait for all the autocomplete requests to be resolved
this.waitForText('[data-role=autosuggest_list]>li:first-child b', 'Palo Al', function()
{
// Check first line of autosuggest
// should contain "Palo Alto, CA"
assert.contains(this.$('[data-role=autosuggest_list]>li:first-child').text(), 'Palo Alto, CA');
done();
});
});
}
Provides means to enhance testable environment by enhancing in-iframe window object.
Note: Enhance handler function will called bound to the test object.
setUp: function(done)
{
// load new app's homepage for each test
this.load('/index').wait('postPageRender', done);
this.enhance(function(iframeWindow)
{
// channel App's console.log output into test reporting
iframeWindow.console.log = console.log.bind(console);
});
}
At the moment PhantomJS uses older version of webkit
that doesn't support Function.prototype.bind
.
As workaround you can add es5-shim
to the list of libraries:
'Functional tests':
{
environment: 'browser',
libs:
[
// PhantomJS - https://github.com/ariya/phantomjs/issues/10522
'assets/js/vendor/es5-shim.js'
],
tests:
[
'tests/functional/**/*.js'
]
}
And add bind
to the test page loaded in iframe:
setUp: function(done)
{
// load new app's homepage for each test
this.load('/index').wait('postPageRender', done);
this.enhance(function(iframeWindow)
{
iframeWindow.Function.prototype.bind = Function.prototype.bind;
});
}
- Add support for desktop events.
- Integrate resource proxy changes into
buster-server
. - Improve documentation.
MIT