I was wondering how to use page object variable on .each() function.
The scenario is every I click delete link, the sweet alert confirmation will be shown, and I must confirm that dialog to delete the data.
Here is my page object:
'use strict';
// page object name
var Data = function()
{
// all delete links
this.delete_links = element.all(by.css('div[ng-click="delete(Data.id)"]'));
// confirm button
this.btn_confirm = element(by.css('.confirm'));
// delete function
this.delete = function()
{
// delete all links with confirmation
this.delete_links.each(function(element, index)
{
// click delete link
element.click().then(function()
{
browser.sleep(1000);
});
// click yes
this.btn_confirm.click().then(function()
{
browser.sleep(1000);
});
});
};
};
module.exports = Data;
this inside the "each" function/callback does not refer to the page object itself. To fix it, define a variable and set it to this.btn_confirm:
this.delete = function()
{
// delete all links with confirmation
this.delete_links.each(function(element, index)
{
var confirmButton = this.btn_confirm;
// click delete link
element.click().then(function()
{
browser.sleep(1000);
});
// click yes
confirmButton.click().then(function()
{
browser.sleep(1000);
});
});
};
Related
I am trying to extract all the comments of an article found on the yahoo news page using casperjs.
What I do first is to click on the "ver reacciones" button, then click on the response button of the first comments loaded to show the response comments.
So far, get the first comment and your answer, but I can not find the way to get all the answers of all the comments, in a few words, all the comments and the response comments found on the page.
Any algorithm that will help me get it?.
link: https://espanol.yahoo.com/noticias/el-fallo-de-orbita-del-supercohete-que-lanzaron-con-un-tesla-al-espacio-y-pasara-de-largo-marte-191841996.html
//click button "ver reacciones"
casper.waitForSelector('a.comments-title', function () {
this.click('.comments-title');
});
//click button "ver mas"
casper.waitForSelector('button.showMore', function () {
this.click('.showMore');
}
, function onWaitTimeout() {
//button does not exist
});
//click on "answer" buttons
var buttons;
casper.waitForSelector('ul.comments-list', function getLinks() {
buttons = this.evaluate(function ()
{
var buttons = document.getElementsByClassName('replies-button');
buttons = Array.prototype.map.call(buttons, function (button) {
button.click();
casper.waitForSelector('ul.comments-list', function () {
});
return button.getAttribute('class');
});
return buttons;
});
}
, function onWaitTimeout() {
});
casper.waitForSelector('ul.comments-list > li', function () {
var x = this.getHTML('ul.comments-list');
this.echo(x);
}
, function onWaitTimeout() {
});
casper.run();
I am working in Marionette and have an accordion which we have set up so that the individual panels are templates that are called in and created by
var AccorionView = require(“../folder/AccordionView”);
var expandButtons = require(“../folder/expandButtons”);
var MainPage = Marionette.View.extend({
regions: {
region: “.region”,
button: “.buttons”
},
this.newAccordion = new AccordionView({
header: “header goes here”,
childView: new panelView(),
});
this.showChildView(‘region’, this.newAccordion);”
I am going to pull in another view with the actual Expand/Collapse All button in it, which will expand and collapse all of the accordion panels on this page. The JavaScript that would be used on this page would be
expandAll: function() {
this.newAccordion.expand();
},
However, this function will be put into the new JavaScript view of the buttons. I am going to send the names of the accordion panels to the button view when calling it into this page, but how do I get the function on that view to influence the accordion panels on this main page?
I would use Backbone.Radio in this case:
const Radio = require('backbone.radio');
const accorionChannel = Radio.channel('accorion');
const MainPage = Marionette.View.extend({
// ...
initialize() {
accorionChannel.on('expand', function() {
this.newAccordion.expand();
});
accorionChannel.on('unexpand', function() {
this.newAccordion.unexpand();
});
}
// ...
});
const WhateverView = Marionette.View.extend({
someEventHandler() {
accorionChannel.trigger('expand');
// OR
accorionChannel.trigger('unexpand');
}
});
Radio channel is singleton, you can create a new one every time but it will refer to the same channel. This saves you from passing the channel variable around or having a global variable.
You can do this one of two ways
1) With triggers/childViewEvents
// in expandButtons
expandButtons = Marionette.View.extend(
triggers: {
'click #ui.expandAll': 'expandAll'
}
);
// in MainPage
MainPage = Marionette.View.extend({
childViewEvents: {
'expandAll': 'expandAll'
},
expandAll: function(child) {
this.newAccordion.expand();
// OR
this.getChildView('region').expand();
}
})
OR
2) With Backbone.Radio
// in expandButtons
var Radio = require('Backbone.Radio');
var expandChannel = Radio.channel('expand');
var expandButtons = Marionette.View.extend({
events: {
'click #ui.expandAll': 'expandAll'
},
expandAll: function(e) {
expandChannel.trigger('expand:all');
}
});
// in AccordionView
var AccordionView = Marionette.View.extend({
channelName: 'expand',
radioEvents: {
'expand:all': 'expand' // triggers this.expand();
}
});
In this case, it might be even easier to do #2 but instead of adding the radio listener to the AccordionView, attach the listeners to the PanelView (AccordionView's childView). This is because AccordionView's expand function will likely have to iterate each of its children like:
this.children.each(function(childView) {
childView.expand();
});
Writing a test to test my delete functionality for my app. I created a mock delete $modal to simulate cancelling/confirming deletion.
var modalInstanceMock=
{
result: {
then: function(confirmCallback, cancelCallback) {
//Store the callbacks for later when the user clicks on the OK or Cancel button of the dialog
this.confirmCallBack = confirmCallback;
this.cancelCallback = cancelCallback;
}
},
confirmCallBack: function(item){
return true;
},
cancelCallback: function(type){
return false;
},
close: function( item ) {
//The user clicked OK on the modal dialog, call the stored confirm callback with the selected item
this.result.confirmCallBack( item );
},
dismiss: function( type ) {
//The user clicked cancel on the modal dialog, call the stored cancel callback
this.result.cancelCallback( type );
}
};
I do this before each test:
beforeEach(inject(function($modal) {
spyOn($modal, 'open').andReturn(modalInstanceMock);
}));
This works perfectly:
var newRes = scope.deleteCar(car);
scope.modalInstance.close("ok");
However when I try this:
var newRes = scope.deleteCar(car);
scope.modalInstance.dismiss("ok");
I get a Type:error undefined is not a function at Object.modalInstanceMock.dismiss.
Can't understand what is going wrong when close works fine.
Initilize in beforeEach,
modalInstance = {
close: jasmine.createSpy('modalInstance.close'),
dismiss: jasmine.createSpy('modalInstance.dismiss')
},
and then expect.
I have one form for saving and editing records. On clicking on a record, the form should be filled with the data. After filling, I want to do some UI actions (call jQuery Plugin etc.).
The pre-filling works, but when I'm trying to access the values, it works only at the second click. On the first click, the values are empty or the ones from the record clicked before.
This action is stored in the controller:
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
$('input[type="text"]').each(function() {
console.log(this.value)
});
});
},
I need a generic way to check if the input field is empty, because I want to include this nice UI effect: http://codepen.io/aaronbarker/pen/tIprm
Update
I tried to implement this in a View, but now I get always the values from the record clicked before and not from the current clicked element:
View
Docket.OrganizationCustomersView = Ember.View.extend({
didInsertElement: function() {
$('input[type="text"]').each(function() {
console.log(this.value)
});
}.observes('controller.editedRecordID')
});
Controller
Docket.OrganizationCustomersController = Ember.ArrayController.extend({
/* ... */
isEditing: false,
editedRecordID: null,
actions: {
/* ... */
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
});
},
/* ... */
});
Update 2
OK, I think I misunderstood some things.
At first, my expected console output should be:
1.
2.
3.
but is:
1.
3.
2.
Secondly: I can use any name, even foobar, for the observed method in my view. Why?
Controller
edit: function(id) {
var _this = this;
// prefill form for editing
var customer = this.store.find('customer', id).then(function(data) {
_this.set('name',data.get('name'));
_this.set('number',data.get('number'));
_this.set('initial',data.get('initial'));
_this.set('description',data.get('description'));
_this.set('archived',data.get('archived'));
console.log('1.')
// store user for save action
_this.set('editedRecordID',id);
_this.set('isEditing',true);
console.log('2.')
});
},
View
Docket.OrganizationCustomersView = Ember.View.extend({
foobar: function() {
console.log('3.')
$('input[type="text"]').each(function() {
console.log(this.value)
});
}.observes('controller.editedRecordID')
});
Update 3
I think I "figured it out" (but I don't know why):
Docket.OrganizationCustomersView = Ember.View.extend({
movePlaceholder: function() {
$('input[type="text"], textarea').bind("checkval",function() {
var $obj = $(this);
setTimeout(function(){
console.log($obj.val());
},0);
}.observes('controller.editedRecordID')
});
setTimeout(function(){ ... }, 0); does the trick. But why?!
You can convert use that jquery code in a component, this is the best way to create a reusable view, without putting ui logic in controllers, routers etc.
Template
<script type="text/x-handlebars" data-template-name="components/float-label">
<div class="field--wrapper">
<label >{{title}}</label>
{{input type="text" placeholder=placeholder value=value}}
</div>
</script>
FloatLabelComponent
App.FloatLabelComponent = Ember.Component.extend({
onClass: 'on',
showClass: 'show',
checkval: function() {
var label = this.label();
if(this.value !== ""){
label.addClass(this.showClass);
} else {
label.removeClass(this.showClass);
}
},
label: function() {
return this.$('input').prev("label");
},
keyUp: function() {
this.checkval();
},
focusIn: function() {
this.label().addClass(this.onClass);
},
focusOut: function() {
this.label().removeClass(this.onClass);
}
});
Give a look in that jsbin http://emberjs.jsbin.com/ILuveKIv/3/edit
function getData(url) {
$.getJSON(url, function(result) {
$('#formNav ul').append('<ul/>')
$.each(result, function() {
var list = $('#formNav li'),
listItem = $('<li/>'),
html = listItem.append($('<h5/>').text(this.name));
$.each(this.items, function() {
listItem.append($('<a />').attr('href', this.id).text(this.name))
});
list.append(html)
});
});
};
$(function(){
var Menu = {
$menu: $('.config-nav #formNav'),
$trades : $(".config-nav select#tradesmanList"),
$skills : $(".config-nav select#jobList"),
init: function(){
var $menu = Menu.$menu;
// Set menu up
$menu.children("li").addClass('closed');
$menu.find(".js-reveal").hide();
Menu.$skills.attr("disabled", "disabled");
Menu.$trades.on("change", function($skills){
Menu.$skills.removeAttr("disabled");
});
// bind to click on the item...
$menu.on("click", "h4", this.toggle);
},
toggle: function() {
// Toggle the hide show of the drill down menu
var $this = $(this),
$category = $this.parent();
console.log($this.parent().index());
var data = getData("test.json");
$category.addClass("loading").toggleClass("open");
$this.next(".reveal").delay(100).toggle(0, function(){
$category.Data;
$category.removeClass("loading");
});
}
};
Menu.init();
});
I have a function that returns json data , i then call this function in the Menu function to display the data however every time i click the button the data just keeps being generated instead i want it to display the data and then once it is clicked again hide the data? if anyone has any advice that would be great.
Thanks.
When ever you call the toggle function You seem to get the data using
var data = getData("test.json"); and then use it to populate the list..
Why don't you move the getData method to outside the toggle function and instead move it to to init function .. Look's like it should be fine then..