I'm trying to optimize my E2E tests to incorporate the usage of entities.
Our tests are basically filling data into a form on a webpage. Our tests are using the PageObject method where our PageObject stores our elements in variables and we also have variables containing the interactions with the elements stored in the PO file.
Our spec file is what calls the PO file and inputs the data into each element similar to(this is just examples of what we are doing):
PO File:
this.firstNameField = by.model('firstName');
this.lastNameField = by.model('lastName');
this.setFirstNameField = function(firstname) {
element(this.firstNameField).sendKeys(firstname);
};
this.setLastNameField = function(lastname) {
element(this.lastNameField).sendKeys(lastname);
};
Spec file:
pageObject.setFirstNameField('TestName');
pageObject.setLastNameField('TestLastName');
In our spec file, we have roughly 100 lines of this code which is not very effecient from what I can tell. I want to remove this style and use Entity's instead, however I'm not sure exactly how i would go about this, hence why I'm coming here.
A friend of mine gave me a hint of how i'd go about this and here is what he has provided me:
spec file:
var nameEntity = {
firstName: 'TestName',
lastName: 'TestLastName'
};
pageObject.PopulateUIWithNameEntity(nameEntity);
Now i know i can switch the nameEntity to be stored in the pageObejct file, however i'm not exactly sure how the PopulateUIWIthNameEntity should be created.
I've tried the following but I can't seem to get it to input the values from the nameEntity into the element itself.
pageObject File:
this.PopulateUIWithNameEntity = function(nameEntity) {
element(this.setFirstNameField).sendKeys(nameEntity);
};
You were close... just needed a little refactor.
Adding your test data to an object (hash) is definitely a good idea. Then you just need to extract the elements from it in your method. You also already have individual methods for each individual action... so you just needed to use them.
spec...
var nameEntity = {
firstName: 'TestName',
lastName: 'TestLastName'
};
pageObject.populateUIWithNameEntity(nameEntity);
page object...
this.populateUIWithNameEntity = function(nameEntity) {
this.setFirstNameField(nameEntity.firstName);
this.setLastNameField(nameEntity.lastName);
};
Related
i'm building an Gmail-like email browser client app prototype and i need a little help/advice structuring my React/Flux app. I decided to use pure Flux to get a better idea of how it works.
It's a simple email client with a list of letters, grouped by folders and tags and an ability to add letters to favorites.
So, i have a LettersStore containing an array of letters. The single letter data object looks something like this
{
id: 0,
new: true, //unread
checked: false,
starred: false,
folder: "inbox", //could be 'sent', 'spam', 'drafts'
sender: "Sender Name",
subject: "Re:",
snippet: "Hello there, how are you...",
body: "Hello there, how are you doing, Mike?",
date: "02.19.2016 16:30",
tags:["personal", "urgent"]
}
So what i'm trying to achieve is to let users navigate through folders (inbox, sent, drafts, spam) and filters (starred, tag, etc.)
In both folders and filters there has to be a way to select (check) some/all letters. The view state depends on how many letters are selected (the Select-all checkbox update, just like on Gmail). When the user selects a letter, the Flux action is being triggered and the state of the app updates.
The controller-view on top of the app does all the calls to the LettersStore public methods and passes the data down as props, but i'm not sure, what public methods the LettersStore should have. Currently it has:
emitChange()
addChangeListener()
removeChangeListener()
getAll() //returns array
areSomeLettersInFolderChecked(folderName) //returns bool
areAllLettersInFolderChecked(folderName) //returns bool
countNewLettersInAllFolders() //returns object
This works ok with folders, but when it comes to filters, it doesn't make sense anymore, since a starred letter is in some folder, and i feel like it's not the right thing to add specific methods like areSomeLettersInFilterChecked(filterType) etc.
Also, just like in Gmail, there has to be a way to select letter in the "Starred" filter, which belongs to the "Inbox" folder, then navigate to "Inbox" folder and keep that letter selected.
Maybe i should move the areSomeLettersInFolderChecked-like stuff to the component level?
I'm sure here has to be a proper way of doing it. Thanks in advance!
Rather than trying to encapsulate all the possible states and filters into your letter objects, keep it dumb. Normalize it and use supporting data structures to represent the other characteristics.
I'd strip it down to just the following properties:
{
id: 0,
sender: "Sender Name",
subject: "Re:",
snippet: "Hello there, how are you...",
body: "Hello there, how are you doing, Mike?",
date: "02.19.2016 16:30",
tags:["personal", "urgent"]
}
Your LetterStore can stay the same, or alternatively you could use an object or map to store letters against their id's for quick lookups later.
Now we need to represent the properties we removed from the message.
We can use individual sets to determine whether a message belongs to the new, checked and starred categories.
For instance, to star a message, just add it's id to the starred set.
var starred = new Set();
starred.add(message.id);
You can easily check whether a message is starred later on.
function isStarred(message) {
return starred.has(message.id);
}
The pattern would be the same for checked and unread.
To represent folders you probably want to use a combination of objects and sets.
var folders = {
inbox: new Set(),
sent: new Set(),
spam: new Set(),
drafts: new Set()
}
Simplifying your structures into these sets makes designing queries quite easy. Here are some examples of the methods you talked about implemented with sets.
function checkAll() {
messages.forEach(function(message) {
checked.add(message.id);
});
return checked;
}
function isChecked(message) {
return checked.has(message.id);
}
function inFolder(name, message) {
return folders[name].has(message.id);
}
// is message checked and in inbox
if(isChecked(message) && inFolder('inbox', message)) {
// do something
}
It becomes easy to construct complex queries, simply by checking whether messages belong to multiple sets.
NOTE: the following code and demo are extracted from a larger Meteor + Angular project.
I have the following functions to select and delete objects:
DEMO: http://plnkr.co/edit/Qi8nIPEd2aeXOzmVR6By?p=preview
$scope.selectParty = function(party) {
$scope.party = party;
$scope.type = party.type;
$scope.date = party.date;
}
$scope.deletParty = function(party) {
$scope.parties.remove(party);
}
$scope.selectOrganizer = function(organizer) {
$scope.organizer = organizer;
$scope.name = organizer.name;
$scope.title = organizer.title;
}
$scope.deletOrganizer = function(organizer) {
$scope.party.organizers.remove(organizer);
}
The Select action works on both Parties and Organizers as you can see in the demo, displaying the data in the table underneath.
The Delete action doesn't work. Although, let me point out that in my app, the one I have on my machine and currently working on in Meteor, the Delete action works splendidly on Parties, meaning the syntax "$scope.parties.remove(party)" works. But it doesn't work on the plnkr demo for some reason :(
My question is really about the Organizers Delete action, where I'm targeting an object (organizer) inside an array inside the selected object (party)… that one doesn't work. I'm wondering why, and what is the right syntax.
NOTE 2: I'm aware of Angular's splice and index but I can't use them here as I'm not simply working with Angular arrays but with database data in Meteor.
Thanks!
The organizer is a part of the party object and not a collection on it's own. So what you would need to do is remove the party from the object and then save the party object.
Note2 is incorrect. Unless you wrote your question and plunker wrong.
This is sort of a three part question. I have a JSON file. A few of the values in the JSON file are arrays. Keeping that in mind:
1) On any given page, I'd only want one set of values coming out of the JSON file. For example (as you'll see in code below) my JSON file is a list of attorneys. On any given bio page, I'd obviously only want one attorney's information. I'm currently, successfully, doing this by pulling back the entire JSON and then using ng-show. But this is causing some other issues that I'll explain in later points, so I'm wondering if there's something to put in the app.factory itself to only bring back the one set in the first place.
2) As mentioned, some of the values are arrays. This comes into play two ways in this situation. One of the ways is that there is an array of quotes about the attorney that I'll need to drop into a JS array so that my JS function can loop through them. Currently, I'm hardcoding the quotes for the one test attorney but I'm really trying to figure out how to make this dynamic. This is one reason I'm trying to figure out how to bring back only one attorney's information so I can then, somehow, say his quotes go into this array.
3) Another array value is a list of his specialty areas. I have another, hardcoded, JS object, associating the short terms with the display names. I realized though, that this has two issues.
a) The JS renders after the Angular, so I can't reference that JS in the Angular code
b) I have no way , anyway, to display the JS dynamically inside the Angular code.
My solution to that aspect was to create a second JSON file holding the area hash but besides being a little cumbersome, I'm also not sure how to dynamically display just the ones I want. e.g: If my attorney only specializes in securities and litigation, how would I tell the code to only display {{areas.securities}} and {{areas.litigation}}? So,I'm open to thoughts there as well.
Here is the current, relevant code. If you need more, just ask.
Thanks.
attorneys.json (irrelevant lines removed)
{"attorneys":
[
{
"id":1,
"name":"Bob Smith",
"quotes":
[
{
"id": 1,
"quote": "Wonderful guy!",
"person": "Dovie"
},
{
"id": 2,
"quote": "If ye be wanting a haggis like no other, Bob be yer man!",
"person": "Angus McLoed"
},
{
"id": 3,
"quote": "Wotta Hottie!",
"person": "Bob's wife"
}
],
"areas": ["altdispute", "litigation", "securities"],
}
]
}
...and the relevant current JS object that I'm not sure what to do with:
var practiceareas = {
altdispute: "Alternative Dispute Resolution",
businesscorp: "Businesses & Corporations",
estateplanning: "Estate Planning",
futures: "Futures & Derivatives",
litigation: "Litigation",
productliability: "Product Liability",
realestate: "Real Estate",
securities: "Securities"
}
script.js (relevant function)
var idno = 0;
/* This is what I want replaced by the Angular pull */
var quotelist = ["\"Wonderful guy!\"<br/>-Dovie", "\"If ye be wanting a haggis like no other, Bob be yer man!\"<br/>-Angus McLoed", "\"Hubba, Hubba! What a hottie!\"<br/>-Bob's wife"];
$("#bio_quotes").html(quotelist[0]);
function quoteflip(id, total){
var src1 = quotelist[idno];
$("#bio_quotes").fadeOut(500, function(){
$("#bio_quotes").html(src1).fadeIn(500);
});
idno = (id + 1) % total;
window.setTimeout(function(){quoteflip(idno, quotelist.length);}, 5000);
}
window.setTimeout(function(){quoteflip(idno, quotelist.length);}, 500);
By the way, as far as the quotes, I'm even happy to turn the JSON into a more condensed version by removing the id and consolidating the quote and author - making it an array of strings instead of mini-objects - if that makes it easier. In fact, it might be easier as far as the function anyway.
Can definitely filter things out at the service / factory using Array.filter. If you want to filter it server side, you have to have the code at server side that will do that.
Not sure what your backend store is but definitely doable.
Again, you can do this pretty easily with Array.map which let you pull specific values into a new Array. If you just want the name and quotes' quote and person name, you can definitely do this using Array .filter and .map and bind the new array to your viewmodel / scope.
Hmm.. again, I'd disagree, this look like the same issue with JavaScript array manipulation. You can definitely as part of the transformation in point 1 and 2, include this so it will transfer area to the long practice area names. The easiest way to show the relevant practice area is to map it to the long name during the transformation in the service layer.
//get matching attorney from the store by id
var matches = data.attorneys.filter(function(a) {
return a.id === id;
});
//If match found,
if (matches.length === 1) {
var result = matches[0];
//map the long name for practicing area
//use the matching attorney's area name as key
//and overwrite the result areas with the map
result.areas = result.areas.map(function(a) {
return practiceareas[a];
});
return result;
}
See this solution: http://embed.plnkr.co/xBPju7/preview
As for the fade in and fade out, I'll let you figure it out...
I want to create some layers with names taken from database. "Trees" in database becomes a "Layer_Trees" openlayers layer.
I tried many things with eval function, yet with no success. Seems like its completely uncapable of defining new variables.
function addLayer_ImageWMS(SourceName,SourceLayerName) {
LayerName="Layer_" + SourceLayerName;
eval(LayerName) = new ol.layer.Image({
title: LayerName,
source: new ol.source.ImageWMS({
url: SourceName,
params: {
'LAYERS': SourceLayerName,
'TRANSPARENT': 'true'
}
})
})
LayersArray.push(LayerName);
}
If I remove "eval()" everything is working, but layers are inaccessible from outside.
Openlayers 3. I have to adress this layers from outside of this function, because they are turned on and off through a menu.
Is there any simple way to do this?
I was planning to turn them on and off with such code:
SourceName = "Layer_" + $(layer).children("#SourceName").val();
IsChecked = $(layer).children(".Style_LayerList_Radiobutton").prop("checked");
eval(SourceName).setVisible(IsChecked);
You don't need eval for this. Simply use an object like this:
var layers = {};
layers['Layer_' + SourceLayerName] = new ...;
Later you can access the layer with:
layers['Layer_Trees'].setVisible(true);
I'm working with ASP.NET MVC 3, and wondering how to return the contents of a file, as an array to a jtable in a view. Each line of the array contains a comma separated list. (The contents of the file came from either a .csv file or excel spreadsheet)
The first column contains the field headers, and the number of field headers in the file can vary, so I guess you could say the contents of the jtable would be dynamic?
I've written jtables with explicitly named fields, but that's when I know how many fields to expect. In this case, it could be anywhere from 1 to 11 fields, and since the field names are in the first line of the array, i'm not quite sure how to setup the jtable to recognize them as column headers for the jtable.
For starters, here's a couple of examples of an array I would expect to bind to the table.
array[0] = "phone,first,last";
array[1] = "1111111111,firstname,lastname";
OR
array[0] = "first,last,email";
array[1] = "firstname,lastname,emailaddress#email.com";
For the first example, I would not need to display the 'email' field, and for the second I don't need the phone field. The actual contents would probably contain many lines, which is why I want to bind it to jtable with paging enabled.
In my controller method, I have the contents of the file as a string array, one line with comma separated fields per array item. Where do I go from there is the question.
Thanks for any help,
Carrie
Actually, I've solved my first problem by using the Expando object, to create a list of dynamic objects . . . problem now is that jtable seems to need to bind to the results returned from a post. The json object i now have has already been returned, so I don't need to do a post to get it. So, the question now is, is it possible to bind a json result to a jtable without making jtable do the post?
Again, thanks.
Working on a similar question; I am returning json and need to bind to jtable. Here is the code from within my ajax call. The only jtable method I found is the addRecord.
Hope this helps and I will edit as I improve and get this fully functional. Yeh, I know that I should only include well formed answers but since you have had no help, we can start with this and let it be a work in progress.
if (data.Result == "OK") {
for (var i = 0; i < data.Records.length; i++) {
$("#SearchResultsAddContact").jtable('addRecord', {
record: {
FirstName: data.Records[i].FirstName,
LastName: data.Records[i].LastName,
NumGroups: data.Records[i].NumGroups,
NumPhones: data.Records[i].NumPhones,
NumTexts: data.Records[i].NumTexts,
NumTags: data.Records[i].NumTags,
NumTDDs: data.Records[i].NumTDDs,
NumEmails: data.Records[i].NumEmails
}
});
}
$("#SearchResultsAddContact").jtable('load');
Make sure that you edit your jquery.jtable.js file as follows: (lines 1297-1308)
addRecord: function (options) {
var self = this;
options = $.extend({
**clientOnly: true,**
animationsEnabled: self.options.animationsEnabled,
url: self.options.actions.createAction,
success: function () { },
error: function () { }
}, options);
Change the clientOnly: to true