How do we do e2e testing with polymer and selenium? - testing

Angular has protractor to listen to lifecycle events in Angular.
eg: ptor.waitForAngular();
Is their a way to get selenium tests to wait for the various life-cycle events in polymer?
Currently we can have simple e2e tests running mocha, to do this is embed the tests directly in the html like how they do it in the source code.
You can just run your tests like they do here.
<script>
document.addEventListener('polymer-ready', function() {
mocha.run();
});
</script>
Eg to check for correct updates in a change watcher you can do this:
Polymer('x-test', {
bar: '',
ready: function() {
this.bar = 'bar';
setTimeout(function() {
this.zonk = 'zonk';
}.bind(this));
},
barChanged: function() {
chai.assert.equal(this.bar, 'bar', 'change in ready calls *Changed');
checkDone();
},
zonkChanged: function() {
chai.assert.equal(this.zonk, 'zonk', 'change calls *Changed without prototype value')
checkDone();
}
});
and for eg if you wanted to check a computer property is correct after the ready event you can do this:
<x-foo foo="mee" bar="too" count=3></x-foo>
<polymer-element name="x-foo" attributes="foo bar count">
<template>{{ fooBar }}:{{ fooBarCounted }}</template>
<script>
Polymer('x-foo', {
computed: {
fooBarCounted: 'repeat(fooBar, count)',
fooBar: "foo + '-' + bar"
},
repeat: function(str, count) {
var retval = '';
for (var i = 0; i < count; i++) {
retval += (i ? ' ' : '') + str + '(' + i + ')';
}
return retval;
},
ready: function() {
chai.assert.equal(this.shadowRoot.innerHTML, 'mee-too:mee-too(0) mee-too(1) mee-too(2)');
done();
}
})
</script>
</polymer-element>

Actually selenium (and other libs based on on it) can wait elements to appear on the page.
E.g. browser.waitForExist('#selector')
See API documentation here http://webdriver.io/api/utility/waitForExist.html
Hope it might be useful for you.

Related

Videojs duration displaying as 0 for entire video

I'm using videojs. For some reason the duration of videos is displaying as 0, even when fully loaded.
At line 2487 of the video.js file I've made sure this section...
ControlBar.prototype.options_ = {
children: ['playToggle', 'volumeMenuButton', 'currentTimeDisplay', 'timeDivider', 'durationDisplay', 'progressControl', 'liveDisplay', 'remainingTimeDisplay', 'customControlSpacer', 'playbackRateMenuButton', 'chaptersButton', 'descriptionsButton', 'subtitlesButton', 'captionsButton', 'audioTrackButton', 'fullscreenToggle']
};
...includes the 'durationDisplay' property, so does anyone know why the duration is displaying as 0?
The videos are mp4 and are loaded inside an AngularJS directive:
app.directive('engVideo',['$timeout', '$http', function($timeout, $http) {
return {
restrict: 'A',
priority: 100,
replace: true,
templateUrl: 'components/video.html',
link: function(scope, element, attrs) {
....
function VideoJSPlayerInit(window, videojs) {
var player = videojs(scope.component.video.id, {
html5: {
nativeTextTracks: false
}
});
player.pause();
}
From a suggestion in the comments, I've also tried listening for the 'loadedmetadata' event, when the videojs element is created, like this:
function VideoJSPlayerInit(window, videojs) {
var player = videojs(scope.component.video.id, {
html5: {
nativeTextTracks: false
}
}, function() {
this.on('loadedmetadata', function(){
console.log("video metadata loaded");
});
}
);
But nothing gets output to console - so I'm guessing there's no metadata loaded(?) I have also changed it to listen for the 'loadeddata' event and that DOES gets consoled.
Could this be a video encoding issue? I've been looking for how to export from Premiere with the duration metadata included, but as far as I can tell, it's there.
Any clues, much appreciated.
OK, I've finally figured it out: It was not to do with metadata; The version of video.js we're using for some reason was hardcoding the duration value as '0:00'. If it's useful to anyone else, here's what I added (to the video.js file from line 5241) to get the duration to display correctly:
DurationDisplay.prototype.createEl = function createEl() {
var el = _Component.prototype.createEl.call(this, 'div', {
className: 'vjs-duration vjs-time-control vjs-control'
});
// following three lines are new...
var intSeconds = parseInt(this.player_.duration());
var intMinutes = parseInt(intSeconds / 60);
intSeconds = intSeconds - (60 * intMinutes);
this.contentEl_ = Dom.createEl('div', {
className: 'vjs-duration-display',
// label the duration time for screen reader users
//innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span> 0:00' // - old line
innerHTML: '<span class="vjs-control-text">' + this.localize('Duration Time') + '</span>' + intMinutes + ':' + intSeconds
}, {
// tell screen readers not to automatically read the time as it changes
'aria-live': 'off'
});
el.appendChild(this.contentEl_);
return el;
};

Shopify Slate - prevent go to cart page on add to cart

I'm building a theme with Slate and I have been researching how to prevent the default function of going to the cart page after you click add to cart on a product page.
All the answers I have gotten thus far have lead to dead ends. I also tried to load Cart.js onto the theme and it didn't let me because there's some liquid code mixed in with the initialize script.
Really looking for help to prevent a theme built with Slate from automatically going to the cart page once you click add to cart. Thanks!
The redirect is probably based on a form submission, so you just need to use jQuery's preventDefault method when the form is submitted.
$('form[action^="/cart/add"]').on('submit', function(evt) {
evt.preventDefault();
//add custom cart code here
});
Found a solution using Ajaxify cart (https://help.shopify.com/themes/customization/products/add-to-cart/stay-on-product-page-when-items-added-to-cart)
To get this to work with Slate, you need to follow the instructions and make a few changes once they are all followed. Here's what I did.
I had to take all the jQuery in the script tags and place it in a new file under scripts > vendor > vendor.js
/**
* Module to ajaxify all add to cart forms on the page.
*
* Copyright (c) 2015 Caroline Schnapp (11heavens.com)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
Shopify.AjaxifyCart = (function($) {
// Some configuration options.
// I have separated what you will never need to change from what
// you might change.
var _config = {
// What you might want to change
addToCartBtnLabel: 'Add to cart',
addedToCartBtnLabel: 'Thank you!',
addingToCartBtnLabel: 'Adding...',
soldOutBtnLabel: 'Sold Out',
howLongTillBtnReturnsToNormal: 1000, // in milliseconds.
cartCountSelector: '.cart-count, #cart-count a:first, #gocart p a, #cart .checkout em, .item-count',
cartTotalSelector: '#cart-price',
// 'aboveForm' for top of add to cart form,
// 'belowForm' for below the add to cart form, and
// 'nextButton' for next to add to cart button.
feedbackPosition: 'nextButton',
// What you will never need to change
addToCartBtnSelector: '[type="submit"]',
addToCartFormSelector: 'form[action="/cart/add"]',
shopifyAjaxAddURL: '/cart/add.js',
shopifyAjaxCartURL: '/cart.js'
};
// We need some feedback when adding an item to the cart.
// Here it is.
var _showFeedback = function(success, html, $addToCartForm) {
$('.ajaxified-cart-feedback').remove();
var feedback = '<p class="ajaxified-cart-feedback ' + success + '">' + html + '</p>';
switch (_config.feedbackPosition) {
case 'aboveForm':
$addToCartForm.before(feedback);
break;
case 'belowForm':
$addToCartForm.after(feedback);
break;
case 'nextButton':
default:
$addToCartForm.find(_config.addToCartBtnSelector).after(feedback);
break;
}
// If you use animate.css
// $('.ajaxified-cart-feedback').addClass('animated bounceInDown');
$('.ajaxified-cart-feedback').slideDown();
};
var _setText = function($button, label) {
if ($button.children().length) {
$button.children().each(function() {
if ($.trim($(this).text()) !== '') {
$(this).text(label);
}
});
}
else {
$button.val(label).text(label);
}
};
var _init = function() {
$(document).ready(function() {
$(_config.addToCartFormSelector).submit(function(e) {
e.preventDefault();
var $addToCartForm = $(this);
var $addToCartBtn = $addToCartForm.find(_config.addToCartBtnSelector);
_setText($addToCartBtn, _config.addingToCartBtnLabel);
$addToCartBtn.addClass('disabled').prop('disabled', true);
// Add to cart.
$.ajax({
url: _config.shopifyAjaxAddURL,
dataType: 'json',
type: 'post',
data: $addToCartForm.serialize(),
success: function(itemData) {
// Re-enable add to cart button.
$addToCartBtn.addClass('inverted');
_setText($addToCartBtn, _config.addedToCartBtnLabel);
_showFeedback('success','<i class="fa fa-check"></i> Added to cart! View cart or continue shopping.',$addToCartForm);
window.setTimeout(function(){
$addToCartBtn.prop('disabled', false).removeClass('disabled').removeClass('inverted');
_setText($addToCartBtn,_config.addToCartBtnLabel);
}, _config.howLongTillBtnReturnsToNormal);
// Update cart count and show cart link.
$.getJSON(_config.shopifyAjaxCartURL, function(cart) {
if (_config.cartCountSelector && $(_config.cartCountSelector).size()) {
var value = $(_config.cartCountSelector).html() || '0';
$(_config.cartCountSelector).html(value.replace(/[0-9]+/,cart.item_count)).removeClass('hidden-count');
}
if (_config.cartTotalSelector && $(_config.cartTotalSelector).size()) {
if (typeof Currency !== 'undefined' && typeof Currency.moneyFormats !== 'undefined') {
var newCurrency = '';
if ($('[name="currencies"]').size()) {
newCurrency = $('[name="currencies"]').val();
}
else if ($('#currencies span.selected').size()) {
newCurrency = $('#currencies span.selected').attr('data-currency');
}
if (newCurrency) {
$(_config.cartTotalSelector).html('<span class=money>' + Shopify.formatMoney(Currency.convert(cart.total_price, "{{ shop.currency }}", newCurrency), Currency.money_format[newCurrency]) + '</span>');
}
else {
$(_config.cartTotalSelector).html(Shopify.formatMoney(cart.total_price, "{{ shop.money_format | remove: "'" | remove: '"' }}"));
}
}
else {
$(_config.cartTotalSelector).html(Shopify.formatMoney(cart.total_price, "{{ shop.money_format | remove: "'" | remove: '"' }}"));
}
};
});
},
error: function(XMLHttpRequest) {
var response = eval('(' + XMLHttpRequest.responseText + ')');
response = response.description;
if (response.slice(0,4) === 'All ') {
_showFeedback('error', response.replace('All 1 ', 'All '), $addToCartForm);
$addToCartBtn.prop('disabled', false);
_setText($addToCartBtn, _config.soldOutBtnLabel);
$addToCartBtn.prop('disabled',true);
}
else {
_showFeedback('error', '<i class="fa fa-warning"></i> ' + response, $addToCartForm);
$addToCartBtn.prop('disabled', false).removeClass('disabled');
_setText($addToCartBtn, _config.addToCartBtnLabel);
}
}
});
return false;
});
});
};
return {
init: function(params) {
// Configuration
params = params || {};
// Merging with defaults.
$.extend(_config, params);
// Action
$(function() {
_init();
});
},
getConfig: function() {
return _config;
}
}
})(jQuery);
Shopify.AjaxifyCart.init();
Next I made sure that this file was being called to the main vendor.js file using this code in scripts > vendor.js
/*!
* ajaxify-cart.js
*/
// =require vendor/ajaxify-cart.js
Last thing I had to do was edit out the liquid markup that was in the ajaxify-cart.js file. Since it is a .js file the liquid markup was making it malfunction. Here's the lines where I replaced liquid markup:
if (newCurrency) {
$(_config.cartTotalSelector).html('<span class=money>' + Shopify.formatMoney(Currency.convert(cart.total_price, "CAD", newCurrency), Currency.money_format[newCurrency]) + '</span>');
}
else {
$(_config.cartTotalSelector).html(Shopify.formatMoney(cart.total_price));
}
}
else {
$(_config.cartTotalSelector).html(Shopify.formatMoney(cart.total_price));
}
It's a bit hacky but so far it is working with my slate theme.
I'm open to suggestions for improvement. Thanks.

Karma Chai How to access DOM element

I am writing test cases using Karma Mocha.
Following is my function:
fun : function()
{
if(a == 1)
$("#test").hide();
}
We set the DOM element property based on some condition.
While writing its test:
it('fun', function (){
var a = 1;
// how do I test the DOM element.
// Is it possible to access the DOM element of the source file in the test file.
})
I tried using chai-jquery but it accesses only body and not the other elements.I guess it works on DOM elements of test file.
Can anyone please help.?
I assume you have your jQuery loaded upon testing then you would select you element with $('#test') and then do you tests.
Like so:
describe('obj.fun', function (){
before(function() {
$('<div id="test"></div>').appendTo(document.body);
});
after(function() {
$('#test').remove();
});
it('should hide the element when a is 1', function() {
var $test = $('#test');
expect( $test.is(':hidden') ).to.be.false;
obj.a = 1;
obj.fun();
expect( $test.is(':hidden') ).to.be.true;
});
});

How can i test ember shortcuts with qunit

I am trying to test my ember application. There are shortcuts defined at my routes and everything works fine.
But i have no idea how to test these scenarios with qunit?
App.BaseRoute = Ember.Route.extend({
shortcuts: {
'alt+a': 'gotoArticles',
'alt+p': 'gotoPhotos',
'alt+t': 'gotoTests'
},
actions: {
gotoArticles: function() {
console.log('alt + a was pressed, --> articles');
this.get('controller').transitionToRoute('articles');
},
gotoPhotos: function() {
console.log('alt + p was pressed --> photos');
this.get('controller').transitionToRoute('photos');
},
gotoTests: function() {
console.log('alt + t was pressed --> tests');
this.get('controller').transitionToRoute('testability');
}
}
});
The question is how can i test the defined shortcuts with qunit?
You'll need to do an integration test where you insert the view that's backed by BaseRoute then you can trigger an event on the element.
var e = jQuery.Event("keydown");
e.which = 50; // # Some key code value
$("myelementintheview").trigger(e);

How to test AngularJS Directive with scrolling

I have an infinite scroll directive that I am trying to unit test. Currently I have this:
describe('Infinite Scroll', function(){
var $compile, $scope;
beforeEach(module('nag.infiniteScroll'));
beforeEach(inject(function($injector) {
$scope = $injector.get('$rootScope');
$compile = $injector.get('$compile');
$scope.scrolled = false;
$scope.test = function() {
$scope.scrolled = true;
};
}));
var setupElement = function(scope) {
var element = $compile('<div><div id="test" style="height:50px; width: 50px;overflow: auto" nag-infinite-scroll="test()">a<br><br><br>c<br><br><br><br>e<br><br>v<br><br>f<br><br>g<br><br>m<br>f<br><br>y<br></div></div>')(scope);
scope.$digest();
return element;
}
it('should have proper initial structure', function() {
var element = setupElement($scope);
element.find('#test').scrollTop(10000);
expect($scope.scrolled).toBe(true);
});
});
However the .scrollTop(10000); does not seem to trigger anything.
Is there anyway to unit test this type of functionality (I am able to unit test other directives with similar interactions like clicking on elements)?
In case it is relative, this is the infinite scroll code:
angular.module('nag.infiniteScroll', [])
.directive('nagInfiniteScroll', [
function() {
return function(scope, elm, attr) {
var raw = elm[0];
elm.bind('scroll', function() {
if (raw.scrollTop + raw.offsetHeight >= raw.scrollHeight) {
scope.$apply(attr.nagInfiniteScroll);
}
});
};
}
]);
You have to trigger the scroll event on your element manually in your test:
element.find('#test').scrollTop(10000);
element.find('#test').triggerHandler('scroll');
Had the same issue recently. For the scrolling to work, you will need to set some dimensions on the body tag, so the window can be scrolled.
var scrollEvent = document.createEvent( 'CustomEvent' ); // MUST be 'CustomEvent'
scrollEvent.initCustomEvent( 'scroll', false, false, null );
var expectedLeft = 123;
var expectedTop = 456;
mockWindow.document.body.style.minHeight = '9000px';
mockWindow.document.body.style.minWidth = '9000px';
mockWindow.scrollTo( expectedLeft, expectedTop );
mockWindow.dispatchEvent( scrollEvent );
Unfortunately this does not work in PhantomJS.
If you are running your tests on Travis CI, you can also use Chrome by adding the following to your .travis.yml
before_install:
- export CHROME_BIN=chromium-browser
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
And a custom Chrome launcher in your karma config:
module.exports = function(config) {
var configuration = {
// ... your default content
// This is the new content for your travis-ci configuration test
// Custom launcher for Travis-CI
customLaunchers: {
Chrome_travis_ci: {
base: 'Chrome',
flags: ['--no-sandbox']
}
},
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: true
};
if(process.env.TRAVIS){
configuration.browsers = ['Chrome_travis_ci'];
}
config.set( configuration );
};