How to return value AND promise in Protractor framework - return

let us suggest the function _headerSave() save to DB some Text header. We need to know both the Header and operate the correspond promise.
How could we do it?
Wrong code example:
header = headerCreate(); //header is the promise but real header needed
function headerCreate(){
var header_name = "Random Header Created";
return _headerSave(header_name); //return promise
}
function _headerSave(header_name){
element(by.css('button[ng-click="HeaderEditCtrl.saveHeader(true)"]')).click().then(function() {
element.all(by.repeater('button in modal.buttons')).get(1).click(); //press Save
browser.driver.wait( function(){
return element(by.css('table[class="grid-table"] span[title="'+header_name+'"]')).isPresent();
}, 5000);
});
return element(by.css('table[class="grid-table"] span[title="'+header_name+'"]'));
}

Promises are objects.
Objects in JavaScript can have properties. You can add those properties dynamically.
function _headerSave(header_name){
// ...
var p = element(by.css('table[class="grid-table"] span[title="'+header_name+'"]'));
p.header_name = header_name;
return p;
}
Usage:
_headerSave(header_name).header_name;

The easiest way to fix the problem is make the code simpler.
Instead of
headerCreate().then(function(HeaderPromise){
element(by.css('div')).then(function(){
console.log(headerPromise.header_name);
});
});
better to write:
headerPromise=headerCreate();
element(by.css('div'));
console.log(headerPromise.header_name);
theControlFlow object allows to do it in this way.

Related

Mapping a Stream<List> to another type is returning a Stream<Null>

I'm trying to transform a Stream of a list of one type into a Stream of a list of another type, and having an issue with this.
I have this list of Habits that I'm streaming from Firebase, and I want to accept that stream in a function, and return a new stream that is a list of ViewModels of another type from it. But my function is returning a stream of the wrong type.
Here is my code:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) => completion.date
.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
I am getting a compile error because the result variable is showing as type Stream<Null> when I hover over it, where I would expect it to be Stream<List<HabitCompletionViewModel>>. Any idea what I'm doing wrong?
Your outer .map call does not have a return statement which is why you are getting a Stream<Null>.
So add a return statement like so:
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.map((habitsList) {
// added return statement here
return habitsList.map(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}
However the above code still has an error because it is now returning a Stream<List<Future<HabitCompletionViewModel>>> instead of the desired Stream<List<HabitCompletionViewModel>>. To solve this you can use .asyncMap instead of .map.
Stream<List<HabitCompletionViewModel>> _getTodaysHabits(
Stream<List<Habit>> habitsStream) {
var result = habitsStream.asyncMap((habitsList) {
return Stream.fromIterable(habitsList).asyncMap(
(habit) async {
await _getHabitCompletionsCurrent(habit);
HabitCompletion completion = habit.completions!.firstWhere(
(completion) =>
completion.date.dayEqualityCheck(DateTime.now().startOfDate()));
return HabitCompletionViewModel(completion: completion, habit: habit);
},
).toList();
});
return result;
}

Dart streams error with .listen().onError().onDone()

I have an issue with some code that looks like this. In this form I have an error
The expression here has a type of 'void', and therefore can't be used.
Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void.dart(use_of_void_result).
If I remove the .onDone() the error goes away. Why? ELI5 please :-)
I was looking at https://api.dart.dev/stable/2.7.0/dart-async/Stream/listen.html but seem to still be misundertanding something.
I also read https://api.dart.dev/stable/2.7.0/dart-async/StreamSubscription/onDone.html
serviceName.UploadThing(uploadRequest).listen((response) {
uploadMessageOutput = response.message;
if (response.uploadResult) {
showSuccess();
} else {
showError();
}
getUploadFileList(event);
isSaveInProgress = false;
}).onError((error) {
isSaveInProgress = false;
_handleFileUploadError(uploadRequest, error);
}).onDone(() {
isSaveInProgress = false;
});
Your code is almost right, but will only require a simple change to work correctly.
You would be seeing the same error if you swapped the ordering of onError and onDone, so the issue has nothing to do with your stream usage. However, you're attempting to chain together calls to onError and then onDone which won't work since both of these methods return void.
What you're looking for is cascade notation (..), which will allow for you to chain calls to the StreamSubscription returned by listen(). This is what your code should look like:
serviceName.UploadThing(uploadRequest).listen((response) {
uploadMessageOutput = response.message;
if (response.uploadResult) {
showSuccess();
} else {
showError();
}
getUploadFileList(event);
isSaveInProgress = false;
})..onError((error) { // Cascade
isSaveInProgress = false;
_handleFileUploadError(uploadRequest, error);
})..onDone(() { // Cascade
isSaveInProgress = false;
});

Adding key value pair to object inside forEach loop

I'm trying to add a thenable result to an object using a forEach loop. I can see the entries when I console.log the results, but when I try to use them for other parts of my code, I get an empty object.
I was getting an error previously telling me that the object's name (results) was not defined. I moved the object outside of the function and now I just get an empty object returned when I try to return the values of the object.
I tried this first:
let results = {};
// Check for all videos in cache (returns [])
const findAllVidsInCache = (videoArray) => {
videoArray.forEach(video => {
check(video).then(res => {
// resultsArray.push(res);
results[video] = res;
return results;
});
});
return results;
}
Then I tried this:
let results = {};
// Check for all videos in cache (returns [])
const findAllVidsInCache = (videoArray) => {
videoArray.forEach(video => {
check(video).then(res => {
// resultsArray.push(res);
results[video] = res;
return results;
});
});
let values = Object.values(results);
return values;
}
But I still keep getting an empty object when the function is called (I'm using devTools to call the function so nothing else should be interfering with it).
What I'm looking for, and what I can see in the console when I log it to the console, is an object that appears like so:
'video1': false,
'video2': false,
'video3': false,
'video4': true,
'video5': false,
...
Up to 12 videos.
Any ideas what I'm doing wrong here?
Try reading about promise handling. return results; returns a promise.

How to create a "loading" spinner in Breeze?

I'm trying to create a loading spinner that will be displayed when breeze is communicating with the server. Is there some property in Breeze that is 'true' only when breeze is sending data to the server, receiving data, or waiting for a response (e.g. after an async call has been made but no response yet)? I thought of binding this data to a knockout observable and binding the spinner to this observable,
Thanks,
Elior
Use spin.js
http://fgnass.github.io/spin.js/
Its so simple..make it visible before you execute the query and disable it after the query succeeds or fails.
I don't see any property that is set or observable while Breeze is querying, but if you are using a datacontext, or some JavaScript module for your data calls, this is what you can do -
EDIT
Taking John's comments into account, I added a token'd way of tracking each query.
var activeQueries = ko.observableArray();
var isQuerying = ko.computed(function () {
return activeQueries().length !== 0;
});
var toggleQuery = function (token) {
if (activeQueries.indexOf(token) === -1)
{ activeQueries.push(token); }
else { activeQueries.remove(token); }
};
var getProducts = function (productsObservable, forceRemote) {
// Don't toggle if you aren't getting it remotely since this is synchronous
if (!forceRemote) {
var p = getLocal('Products', 'Product','product_id');
if (p.length > 0) {
productsObservable(p);
return Q.resolve();
}
}
// Create a token and toggle it
var token = 'products' + new Date().getTime();
toggleQuery(token);
var query = breeze.EntityQuery
.from("Products");
return manager.executeQuery(query).then(querySucceeded).fail(queryFailed);
function querySucceeded(data) {
var s = data.results;
log('Retrieved [Products] from remote data source', s, true);
// Toggle it off
toggleQuery(token);
return productsObservable(s);
}
};
You will need to make sure all of your fail logic toggles the query as well.
Then in your view where you want to place the spinner
var spinnerState = ko.computed(function () {
datacontext.isQuerying();
};

Executing 3 asynchronous operations (where the 2nd one depends of a condition)

I am working on a durandal / breeze project.
I need to perform several things in my activate function. Thanks to promises I am able to chain asynchronous operations. Please note that I'm still a beginner with all these things so don't hesitate to correct my code.
I have the following activate function inside my viewModel:
var activate = function (routeData) {
initLookups()
var idTran = parseInt(routeData.idTran);
var idItin = parseInt(routeData.idItin);
return datacontext.getTransportById(idTran, transport)
.then(function () { return datacontext.getItineraryById(idItin, itinerary); });
}
So in the return statement:
I fill the transport observable thanks to getTransportById
then I fill the itinerary observable thanks to getItineraryById
-
So far so good. This code works and do his job as expected (maybe not optimised). Now I need to insert a condition between these 2 operations for the case of creating a new entity (if idItin==-1). In this case, a new entity is created. I try with the code below but it doesn't work: I mean the promise doesn't seems to do his job here and thus the view is displayed without waiting for the asynchronous operations to complete.
var activate = function (routeData) {
initLookups()
var idTran = parseInt(routeData.idTran);
var idItin = parseInt(routeData.idItin);
return datacontext.getTransportById(idTran, transport)
.then(function () { if (idItin == -1) return datacontext.createItineraryDetailTransport(idTran); })
.then(function () { return datacontext.getItineraryById(idItin, itinerary); });
}
And below is the createItineraryDetailTransport function of the datacontext:
var createItineraryDetailTransport = function (idTransport) {
var initialValues = ({
transportId: idTransport
});
var newItinerary = manager.createEntity(entityNames.itinerary, initialValues);
return manager.addEntity(newItinerary);
}
So does someone have an idea how to code this thing?
I think that the rigth way is:
return datacontext.getTransportById(idTran, transport).then(function () {
return datacontext.getItineraryById(idItin, itinerary).then(function(){
if (idItin == -1) return datacontext.createItineraryDetailTransport(idTran);
//You have to return something different tu null,false,undefined... or a promise
return true;
);
});
Also, if there is not needed to do this calls sequently, you can do in parallel using Q library (https://github.com/kriskowal/q). The solution could be:
if(idItin ==-1){
//CreateItineraryDetailTransport don't return a promise.
//You can call that like a normal methond.
//It is not a asyncronous methond
datacontext.createItineraryDetailTransport(idTran);
}
return Q.all([datacontext.getTransportById(idTran, transport),datacontext.getItineraryById(idItin, itinerary)]);
-----EDIT-----
Solution doing the creation between the two other calls:
return datacontext.getTransportById(idTran, transport).then(function () {
if (idItin == -1) return datacontext.createItineraryDetailTransport(idTran).then(function(){
return datacontext.getItineraryById(idItin, itinerary);
});
return datacontext.getItineraryById(idItin, itinerary)
});
// I am using Q to do this method return a promise
var createItineraryDetailTransport = function (idTransport) {
var deferred = Q.defer();
var initialValues = ({
transportId: idTransport
});
var newItinerary = manager.createEntity(entityNames.itinerary, initialValues);
manager.addEntity(newItinerary);
deferred.resolve(true);
return deferred.promise;
}
Also you can use Q.fcall:
return Q.fcall(function(){ datacontext.createItineraryDetailTransport(idTran);};
I think that maybe, you don't need to do createItineraryDetailTransport return a promise. Maybe this solution is also possible:
return datacontext.getTransportById(idTran, transport).then(function () {
if (idItin == -1) datacontext.createItineraryDetailTransport(idTran);
return datacontext.getItineraryById(idItin, itinerary);
});

Resources