September 10, 2015
NATIONAL SWAP
IDEAS DAY
Introduction
My name is Tomas Miliauskas
Main hobbies...
Wix Hotels and Guild activities in Lithuania
JavaScript, Node and Stuff like that
Responsibilities...
Reach me out on...
tomasm@wix.com
Skype: tqmukas
email/hangouts:
Simplify Protractor Tests
Wix Hotels & TDD
The numbers...
1 year 6 month
~2050 client side tests
85-90% Coverage
(about half are e2e)
Issues with tests code
-
Doesn't look nice
-
Not self explaining
-
Lots of code duplication
-
Hard to maintain
We started to use...
Page Objects
Tests Code (only)
Page Object
(selectors & helpers)
Protractor
Eventually we created...
hotels-world-statics-e2e-utils
...library
-
Page
-
Elem
-
BI
-
Browser (coming soon)
Library Structure
Page reference
-
.extend(constructor, props)
-
.experiments(obj)
-
.modify(what, how)
-
.addMock(name, mock)
-
.addMockOnce(name, mock)
-
.addMocks(mocks)
-
.removeMock(name)
-
.open(url, query, fragment);
-
.refresh()
-
.clearLocalStorage()
-
.setLocalStorageItem(key, value)
-
.closeNewWindow()
Elem reference
-
.elem(locator, properties)
-
.elem.many(locator, properties)
Elem instance
var elem = require('hotels-world-statics-e2e-utils').elem
var field = elem('.shiny-field');
var country = elem(by.model('ctrl.country'));
var slides = elem.many('.blury-slide');
slides.get(index);
slides.first();
slides.last();
slides(index);
field.enter(text);
field.obtain('width');
field.obtain('class');
country.optionByValue(value);
country.optionByLabel(label);
country.obtain('selectedValue');
Example 1
Testing a feature toggle
Regular way
'use strict';
describe('ICal scenarios', function() {
it('should not show iCal url field when experiment is disabled', function () {
browser.addMockModule('experimentsMock', function() {
angular.module('experimentsMock', []).value('experiments', {ICalField: false});
});
browser.get('/ical');
expect(element(by.model('ctrl.icalLink')).isPresent()).toBe(false);
});
it('should show iCal url field when experiment is enabled', function () {
browser.addMockModule('experimentsMock', function() {
angular.module('experimentsMock', []).value('experiments', {ICalField: true});
});
browser.get('/ical');
expect(element(by.model('ctrl.icalLink')).isDisplayed()).toBe(true);
});
});
Creating a page object
'use strict';
var Page = require('hotels-world-statics-e2e-utils').page;
function ICalPage() {
Page.call(this, '/ical');
}
module.exports = Page.extend(ICalPage, {
back: elem('.go-back'),
discard: elem('.btn-cancel'),
save: elem('.btn-save'),
room: elem('.room', {
errors: elem('errors')
}),
url: elem(by.model('ctrl.icalLink')),
hint: elem('.hint', {
link: elem('.info a')
})
});
Writing tests
'use strict';
describe('ICal scenarios', function() {
var page = new (require('./pages/ICalPage'))();
it('should not show iCal url field when experiment is disabled', function () {
page.experiments({ICalField: false}).open();
expect(page.url().isPresent()).toBe(false);
});
it('should show iCal url field when experiment is enabled', function () {
page.experiments({ICalField: true}).open();
expect(page.url().isDisplayed()).toBe(true);
});
});
Example 2
Testing values
Regular way
'use strict';
describe('Rooms scenarios', function() {
it('should render room list with appropriate data', function () {
browser.get('/rooms/list');
expect(element.all(by.bind('room.name')).get(0).getText()).toBe('Room1');
expect(element.all(by.bind('room.name')).get(1).getText()).toBe('Room3');
expect(element.all(by.bind('room.name')).get(2).getText()).toBe('Room2');
expect(element.all('.item-big img').get(0).isDisplayed()).toBe(true);
expect(element.all('.item-big img').get(1).isDisplayed()).toBe(true);
expect(element.all('.item-big img').get(2).isDisplayed()).toBe(true);
});
});
Creating a page object
'use strict';
var Page = require('hotels-world-statics-e2e-utils').page;
function RoomsPage() {
Page.call(this, '/rooms');
}
module.exports = Page.extend(RoomsPage, {
list: elem.many(by.repeater('room in rooms'), {
img: elem('.left-part img'),
title: elem(by.binding('::room.name')),
amount: elem(by.binding('::room.noOfRooms')),
edit: elem('.actions-bar .btn-icon[ui-sref*=edit]'),
copy: elem('.actions-bar .btn-icon[ui-sref*=copy]'),
delete: elem('.actions-bar .btn-icon[ng-click="deleteRoom(room.roomId)"]'),
tooltip: elem('.tooltip'),
handle: elem('.btn-drag')
}),
});
Writing tests
'use strict';
describe('Rooms scenarios', function() {
var rooms = new (require('./pages/RoomsPage'))();
it('should render room list with appropriate data', function () {
page.open();
expect(rooms.list(0).title().obtain('text')).toBe('Room1');
expect(rooms.list(1).title().obtain('text')).toBe('Room3');
expect(rooms.list(2).title().obtain('text')).toBe('Room2');
expect(rooms.list(0).img().isDisplayed()).toBe(true);
expect(rooms.list(1).img().isDisplayed()).toBe(true);
expect(rooms.list(2).img().isDisplayed()).toBe(true);
});
});
Questions?
Cheers!
Simplify Protractor Tests
By Tomas Miliauskas
Simplify Protractor Tests
- 1,346