With Protractor, and using Mocha framework, I am comparing two arrays of values, one from a bar chart, one from text fields.
The code looks like this:
it("Each bar should have a value equal to its percentage", () => {
var identicalValue: boolean = false;
helper.getFirstValues(textLocator).then((textValue) => {
helper.getBarValues(barLocator).then((barValue) => {
identicalValue = helper.compareArray(textValue, barValue);
//compareArray returns a boolean, true if the arrays have the same values
expect(identicalValue).to.equal(true);
});
});
});
the functions are coded this way:
public getFirstValues(factsheetTab: protractor.ElementFinder): webdriver.promise.Promise<{}> {
var deferred = protractor.promise.defer();
factsheetTab.all(by.tagName("tr")).map((ele, index) => {
return {
index: index,
text: ele.all(by.tagName("td")).first().getText()
}
}).then((topValue) => {
deferred.fulfill(topValue);
},
(reason) => { deferred.reject(reason) });
return deferred.promise;
};
public getBarValues(factsheetTab: protractor.ElementFinder): webdriver.promise.Promise<{}> {
var deferred = protractor.promise.defer();
factsheetTab.all(by.tagName("tr")).map((ele, index) => {
return {
index: index,
text: ele.all(by.tagName("td")).last().element(by.tagName("div")).getAttribute("data-value")
}
}).then((barValue) => {
deferred.fulfill(barValue);
},
(reason) => { deferred.reject(reason) });
return deferred.promise;
};
My problem is that when the comparison returns false, so when the two arrays have differences, the test is blocked. It doesn't fail, the browser remains opened on that step, and the process stops, ignoring the remaining tasks.
Note: the function helper.compareArray returns a correct value. I could also write "expect(false).to.equal(true)" and get blocked too.
Am I doing something wrong in this test? Do you see a reason why this test is not finished?
edit: I found a workaround, to not get stuck in case of the test failing:
it("Each bar should have a value equal to its percentage", () => {
var identicalValue: boolean = false;
var textValue = null;
helper.getFirstValues(textLocator).then((value) => {
textValue = value;
});
helper.getBarValues(barLocator).then((barValue) => {
chai.assert.deepEqual(barValue, textValue);
});
});
(using #Brine's suggestion, for the deepEqual)
This seems to work, the other tests are ran if this one fails.
I'm still curious to know what was wrong with the first version though.
Not sure if this is a bug in your helper.compareArray or Mocha... but why not compare the arrays directly? I don't use Mocha but it seems something like this would work:
expect(textValue).deepEqual(barValue);
Related
in my case localesCollection within compareValue is having an old (not updated) value. State is not real-time. I assumed bind was here to save my day.
// Redux
let localesCollectionValues = useSelector((state: IStore) => state.localesStoreSlice.localesCollectionValues);
const compareValue = (fieldValue: any): void => {
console.log('compareValue', fieldValue, localesCollectionValues);
}
const attachListeners = () => {
console.log('attachListeners');
Object.keys(sdk.entry.fields).forEach((field: string) => {
const fieldRef: any = sdk.entry.fields[field];
fieldRef.locales.forEach((localeKey: string) => {
fieldRef.getForLocale(localeKey).onValueChanged(compareValue.bind(this));
});
});
};
edit:
is was coming from having the arrow function directly in the callback with the same issue as described above
edit2:
the value of 'localesCollectionValues' is the value on the moment that the code get initialised and the handler gets set. After that its not updated and it just keeps hold of n old reference. Typical memory pointer / scope issue, in need of a direction to solve this.
fieldRef.getForLocale(localeKey).onValueChanged(() => {
console.log(localesCollectionValues) // <- not real time,
// contains old value
});
In hindsight I just had to follow React Hooks directions for State Management, makes perfect sense:
const [changedLocale, setChangedLocale] = useState('');
useEffect(() => {
console.log('changedLocale', changedLocale, localesCollection);
// accurate, real-time
}, [changedLocale, localesCollection]);
const attachListeners = () => {
Object.keys(sdk.entry.fields).forEach((field: string) => {
sdk.entry.fields[field].locales.forEach((localeKey: string) => {
const fieldRef: any = sdk.entry.fields[field];
fieldRef.getForLocale(localeKey).onValueChanged(() => {
setChangedLocale(localeKey);
});
});
});
};
What is the recommended way to map the data to an object and return it as a promise/observable while being able to add dynamic/conditional parameters to the query.
In getCompanies2 I can dynamically add parameters to the query but I can't figure out how to map the data returned to my object and return it as a promise/observable.
In getCompanies everything works as I want it but I have to duplicate the code (as below) if I have dynamic query parameters to add.
Note: convertDocTimeStampsToDate just does what it says. I have excluded it to reduce the size of the code section.
getCompanies(searchText: string, useWhereActive: boolean): Observable<Company[]> {
if (useWhereActive) {
return this.db.collection('companies', ref => ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff')
.where('active', '==', true)
)
.snapshotChanges()
.pipe(
map(snaps => convertSnaps<Company>(snaps)),
first()
);
} else {
return this.db.collection('companies', ref => ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff')
)
.snapshotChanges()
.pipe(
map(snaps => convertSnaps<Company>(snaps)),
first()
);
}
}
getCompanies2(searchText: string, useWhereActive: boolean) {
let query = this.db.collection('companies').ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff');
if (useWhereActive) {
query.where('active', '==', true);
}
query.get().then(querySnapshot => {
const results = this.convertDocuments<Company>(querySnapshot.docs);
console.log(results);
});
}
convertDocuments<T>(docs) {
return <T[]>docs.map(doc => {
return {
id: doc.id,
...doc.data()
};
});
}
export function convertSnaps<T>(snaps) {
return <T[]>snaps.map(snap => {
const data = convertDocTimeStampsToDate(snap.payload.doc.data());
return {
id: snap.payload.doc.id,
...data
};
});
}
I got it to work like below, I guess I am still getting my head around promises.
Any better solutions will be accepted though as I am still learning and don't know if this is the best method.
getCompanies2(searchText: string, useWhereActive: boolean) {
let query = this.db.collection('companies').ref
.orderBy('name').startAt(searchText).endAt(searchText + '\uf8ff');
if (useWhereActive) {
query.where('active', '==', true);
}
return query.get().then(querySnapshot => {
return this.convertDocuments<Company>(querySnapshot.docs);
});
}
Someone please help me not kill my Server.. Here's my MVC controller action:
(Don't worry about names, I'm mid-refactoring)
public async Task<ActionResult> AllByLead(int leadId)
{
try
{
var lead = await _researchService.GetLeadWithContacts(leadId);
var contactViewModels = Mapper.Map<Lead, List<ContactViewModel>>(lead);
contactViewModels.Each(contact => PopulateContactOptions(contact));
var listViewModel = new ContactListViewModel {Results = contactViewModels};
return PartialView(listViewModel);
}
catch
{
return Json(string.Format(Resources.BasicErrorMessageFormat, "Error retrieving Lead Contacts"),
JsonRequestBehavior.AllowGet);
}
}
Service:
public async Task<Lead> GetLeadWithContacts(int leadId)
{
return await _repository.GetLeadWithContacts(leadId).ConfigureAwait(false);
}
Repo:
public async Task<Lead> GetLeadWithContacts(int leadId)
{
var leadEntity = await _context.Leads
.Where(lead => lead.LeadID == leadId)
//.Include(lead => lead.LeadContactMaps.Select(map => map.Contact.Addresses))
//.Include(lead => lead.LeadContactMaps.Select(map => map.Contact.PhoneNumbers))
//.Include(lead => lead.Organizations.Select(business => business.Addresses))
//.Include(lead => lead.Organizations.Select(business => business.PhoneNumbers))
.FirstOrDefaultAsync();
return leadEntity;
}
EDIT
DbContext Module
internal class DbContextModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.Register(ctx => new CRTechEntities()).InstancePerLifetimeScope();
}
}
JS Ajax Call:
function populateContactList() {
var leadId = $("#LeadId").val();
$.ajax({
url: url + "/Contact/AllByLead/",
type: "GET",
data: { 'leadId': leadId },
success: function(data) {
$("#contactContainer").html(data);
},
error: function(data) {
}
});
}
Bonus points on if you can school me on my includes, they may very well be terrible.. It's pretty slow just grabbing the lead right now. Hence the async change. I'd like to see if it will be easier on the system with more users. (I can do my own profiling/tests on whether explicit loading will be better here, just saying..)
Anyway, I hit this, the server is completely borked when the await FirstOrDefaultAsync() gets hit.
EDIT 2: I've updated the controller action to show exactly what I'm doing here. I only included the code that was being hit originally.
Um, are you returning anything in your controller? That would cause it to hang.
Try
public async Task<JsonResult> AllByLead(int leadId)
{
var lead = await _researchService.GetLeadWithContacts(leadId);
return Json(lead);
}
Is it possible to locate sub-elements in protractor's .map?
I'm trying to modify example code from here to parse ToDo items into a data structure.
describe('angularjs homepage todo list', function() {
it('should add a todo', function() {
browser.get('https://angularjs.org');
element(by.model('todoList.todoText')).sendKeys('write first protractor test');
element(by.css('[value="add"]')).click();
var todoList = element.all(by.repeater('todo in todoList.todos'));
var todoStruct = todoList.map(function(el) {
return {
checkbox: el.element(by.model('todo.done')),
label: el.element(by.tagName('span')).getText()
};
});
todoStruct.then(function(resolvedTodoStruct) {
expect(todoStruct.length).toEqual(3);
expect(todoStruct[2].label).toEqual('write first protractor test');
});
});
});
From what I've read, map is the right choice for this kind of task. But for some reason, as soon as I use .element inside .map, protractor freezes. :(
Why would it happen? Did I miss anything?
I found a couple of work-arounds for this, though, while not pretty, seem work fine.
For the getText(), I moved the process of getting the text out of the JSON and then save the variable in the returned JSON.
For the element, the workaround is to wrap the element in a function:
var todoList = element.all(by.repeater('todo in todoList.todos'));
var spanText;
var todoStruct = todoList.map(function(el, index) {
var spanText = el.element(by.tagName('span')).getText().then(function(t){
return t;
});
return {
checkbox: function(){
return todoList.get(this.index).element(by.model('todo.done'));
},
label: spanText
};
});
Then when you want to access the element at a later time, you access as a function:
todoStruct.checkbox().click();
Yes, we can get sub elements from a map in the following way
getRows() {
return this.element.all(by.css('.ag-row')).map((row, index) => {
return {
index: index,
title: row.element(by.css("[colid=\"title\"]")).getText(),
operationStatus: row.element(by.css("[colid=\"operation_status_enum\"]")).getText()
};
});
}
// You can print this in the following way
this.getRows().then((rows) => {
for (var i=0; i<rows.length; i++) {
console.log(rows[i].title);
console.log(rows[i].operationStatus);
}
});
This is my code
/******************************************************/
import Ember from "ember";
var TodosController = Ember.ArrayController.extend({
actions: {
createTodo: function(){
// Get the todo title by the "New Todo" input
var title = this.get('newTitle');
if(!title.trim()){ return; }
// Create the new Todo model
var todo = this.store.createRecord('todo', {
title: title,
isCompleted: false
});
// Clear the 'New Todo' input field
this.set('newTitle', '');
// Save the new model
todo.save();
},
clearCompleted: function(){
var completed = this.filterBy('isCompleted', true);
completed.invoke('deleteRecord');
completed.invoke('save');
}
},
remaining: function() {
return this.filterBy('isCompleted', false).get('length');
}.property('#each.isCompleted'),
inflection: function() {
var remaining = this.get('remaining');
return remaining === 1 ? 'todo' : 'todos';
}.property('remaining'),
hasCompleted: function(){
return this.get('completed') > 0;
}.property('completed'),
completed: function(){
return this.filterBy('isCompleted', true).get('length');
}.property('#each.isCompleted'),
allAreDone: function(key, value) {
if(value === undefined){
return !!this.get('length') && this.everyProperty('isCompleted', true);
} else {
this.setEach('isCompleted', value);
this.invoke('save');
return value;
}
}.property('#each.isCompleted')
});
export default TodosController;
/*******************************************************/
In terminal not showing any error when i run this command
$ ember server
but in browser not showing any thing and console showing this error
Uncaught Error: Assertion Failed: ArrayProxy expects an Array or
Ember.ArrayProxy, but you passed object
Please suggest me what i m doing wrong, the code is also on github : https://github.com/narayand4/emberjs
thanks in advance.
The most likely reason for this is that you have a controller which extends from Ember.ArrayController while you only return a plain object in the corresponding model.
I had the same issue and changed my controller to extend Ember.Controller instead.
In the related route for this controller, your model method doesn't return an array, as you've indicated by extending an arrayController.