I am using cypress framework to test node app. I want to check variable is set to 'SUCCESS'. Intially it is set to 'FAILURE' but from cypress exec it is set to 'SUCCESS'. But when I invoke the function it is still 'FAILURE'. Can someone help me understand where I made it wrong.
cypress\support\returnStatus.js
module.exports = function() {
this.returnStatus = "FAILURE";
this.SuccessMsg = function() {
cy.exec('echo hello', { log: true, failOnNonZeroExit: false }).then((output) => {
this.returnValue = "SUCCESS";
});
}
}
cypress\integration\checkReturnValue.spec.js
let Status = require('../support/returnStatus');
let helper = new Status();
describe("Check return value", function(){
it("should check latest value", function(){
let reply = helper.SuccessMsg();
console.log(reply);//still it prints /*FAILURE*/
})
});
There's some async stuff going on here, since cy.exec spawns a child process.
Normally you would await the async call, but Cypress commands only return chainers (they don't really follow the promise pattern), so you will need to add a Promise into the mix and await it.
Helper
module.exports = function() {
this.returnStatus = "FAILURE";
this.SuccessMsg = function() {
return new Cypress.Promise(resolve => {
cy.exec('echo hello', { log: true, failOnNonZeroExit: false }).then((output) => {
this.returnValue = "SUCCESS";
resolve(this.returnValue)
});
})
}
}
Test
let Status = require('../support/returnStatus');
let helper = new Status();
describe("Check return value", function(){
it("should check latest value", async function(){ // NOTE async test
let reply = await helper.SuccessMsg();
console.log('reply', reply); // now it prints /*SUCCESS*/
})
});
If you turned your helper function into a custom command, likely the test will not need to be made async since Cypress automatically waits for promises to resolve.
Related
Im trying to set a variable from within event handler/listener/function
export async function mis() {
let result; // <--------- LOCAL VARIABLE IM TRYING TO CHANGE (currently undefined)
const m = await spawn(`/cmd`);
m.stdout.on('data', function () {
result = true; // <---------- HERE IS WHERE IM TRING TO CHANGE LOCAL VARIABLE (set to true)
});
return result; // -------- RETURNING undefined instead of true
}
thanks in advance
As is currently written the code will executes sequentially and the function will not await the data and will reach the return statement before.
Using a Promise should work. Or you can also use util.promisify()
(Im not handling errors)
export async function mis() {
let result; // undefined
const m = await spawn(`/cmd`);
const onData = new Promise((resolve) => {
let data;
m.stdout.on('data', function () {
data = true;
resolve();
});
m.stdout.on('end', function() {
resolve(data); // resolve when the events end
})
})
const res = await onData();
return res;
}
I am a complete beginner with Node.JS and Mocha and was tasked to write unit test's for a group project. My problem is that i do not even know where to start since the return value is a promise.
Watching a lot of guides i have learned how to check return values for common functions but it wouldn't help me with a real world example.
If any expierienced developer could help me with a guide and code example specific to the function i listed i could hack and understand it and apply to other functions as well.
Here is a code example that get's statistics from a CSV File
function getStatistics() {
return new Promise((resolve, reject)=>{
try {
let readStatistics = [];
const statisticsReadStream = fs.createReadStream(statisticsFileName);
csv.fromStream(statisticsReadStream, {headers: true})
.transform(function (data) {
data.avgDuration = parseFloat(data.avgDuration);
data.avgPassed = parseFloat(data.avgPassed);
data.avgReachedPoints = parseFloat(data.avgReachedPoints);
data.minReachedPoints = parseInt(data.minReachedPoints);
data.maxReachedPoints = parseInt(data.maxReachedPoints);
return data;
})
.on("data", function (data) {
readStatistics.push(data);
})
.on("end", function () {
resolve(readStatistics);
statisticsReadStream.close();
});
}catch(err){
reject();
}
});
}
In mocha, you can return the promise from the test function to indicate that the test (it) has completed.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().then(readStatistics => {
// Assert whatever you want here
});
});
});
Any error that gets thrown from your getStatistics function or any assertion error will cause the test to fail.
If you are specifically looking to see if something throws an error, you can catch the error (reject()) in a test too.
describe('My Test', function () {
it('should do something cool', function () {
return getStatistics().catch(error => {
// Assert whatever you want here about the error
});
});
});
https://mochajs.org/#asynchronous-code
Here is the code:
`describe('Statistic', function(){
it('should transform data into strings', function () {
return statGet().then(readStatistics => {
let maybe = statGet();
var csv = maybe.csv;
let dat = function (data) {
data.avgDuration = "1,2";
data.avgPassed = "2,3";
data.avgReachedPoints ="3,4";
data.minReachedPoints = "4";
data.maxReachedPoints = "5";
return data;
}
assert.typeOf(csv.transform(dat, 'string'));
});
});
});`
on the other hand, i have liuttle idea of what i shold be testing in the first place.
I feel hopelessly lost. I want to go back to hello world =(
I have a function I want to execute in the page using chrome.tabs.executeScript, running from a browser action popup. The permissions are set up correctly and it works fine with a synchronous callback:
chrome.tabs.executeScript(
tab.id,
{ code: `(function() {
// Do lots of things
return true;
})()` },
r => console.log(r[0])); // Logs true
The problem is that the function I want to call goes through several callbacks, so I want to use async and await:
chrome.tabs.executeScript(
tab.id,
{ code: `(async function() {
// Do lots of things with await
return true;
})()` },
async r => {
console.log(r); // Logs array with single value [Object]
console.log(await r[0]); // Logs empty Object {}
});
The problem is that the callback result r. It should be an array of script results, so I expect r[0] to be a promise that resolves when the script finishes.
Promise syntax (using .then()) doesn't work either.
If I execute the exact same function in the page it returns a promise as expected and can be awaited.
Any idea what I'm doing wrong and is there any way around it?
The problem is that events and native objects are not directly available between the page and the extension. Essentially you get a serialised copy, something like you will if you do JSON.parse(JSON.stringify(obj)).
This means some native objects (for instance new Error or new Promise) will be emptied (become {}), events are lost and no implementation of promise can work across the boundary.
The solution is to use chrome.runtime.sendMessage to return the message in the script, and chrome.runtime.onMessage.addListener in popup.js to listen for it:
chrome.tabs.executeScript(
tab.id,
{ code: `(async function() {
// Do lots of things with await
let result = true;
chrome.runtime.sendMessage(result, function (response) {
console.log(response); // Logs 'true'
});
})()` },
async emptyPromise => {
// Create a promise that resolves when chrome.runtime.onMessage fires
const message = new Promise(resolve => {
const listener = request => {
chrome.runtime.onMessage.removeListener(listener);
resolve(request);
};
chrome.runtime.onMessage.addListener(listener);
});
const result = await message;
console.log(result); // Logs true
});
I've extended this into a function chrome.tabs.executeAsyncFunction (as part of chrome-extension-async, which 'promisifies' the whole API):
function setupDetails(action, id) {
// Wrap the async function in an await and a runtime.sendMessage with the result
// This should always call runtime.sendMessage, even if an error is thrown
const wrapAsyncSendMessage = action =>
`(async function () {
const result = { asyncFuncID: '${id}' };
try {
result.content = await (${action})();
}
catch(x) {
// Make an explicit copy of the Error properties
result.error = {
message: x.message,
arguments: x.arguments,
type: x.type,
name: x.name,
stack: x.stack
};
}
finally {
// Always call sendMessage, as without it this might loop forever
chrome.runtime.sendMessage(result);
}
})()`;
// Apply this wrapper to the code passed
let execArgs = {};
if (typeof action === 'function' || typeof action === 'string')
// Passed a function or string, wrap it directly
execArgs.code = wrapAsyncSendMessage(action);
else if (action.code) {
// Passed details object https://developer.chrome.com/extensions/tabs#method-executeScript
execArgs = action;
execArgs.code = wrapAsyncSendMessage(action.code);
}
else if (action.file)
throw new Error(`Cannot execute ${action.file}. File based execute scripts are not supported.`);
else
throw new Error(`Cannot execute ${JSON.stringify(action)}, it must be a function, string, or have a code property.`);
return execArgs;
}
function promisifyRuntimeMessage(id) {
// We don't have a reject because the finally in the script wrapper should ensure this always gets called.
return new Promise(resolve => {
const listener = request => {
// Check that the message sent is intended for this listener
if (request && request.asyncFuncID === id) {
// Remove this listener
chrome.runtime.onMessage.removeListener(listener);
resolve(request);
}
// Return false as we don't want to keep this channel open https://developer.chrome.com/extensions/runtime#event-onMessage
return false;
};
chrome.runtime.onMessage.addListener(listener);
});
}
chrome.tabs.executeAsyncFunction = async function (tab, action) {
// Generate a random 4-char key to avoid clashes if called multiple times
const id = Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
const details = setupDetails(action, id);
const message = promisifyRuntimeMessage(id);
// This will return a serialised promise, which will be broken
await chrome.tabs.executeScript(tab, details);
// Wait until we have the result message
const { content, error } = await message;
if (error)
throw new Error(`Error thrown in execution script: ${error.message}.
Stack: ${error.stack}`)
return content;
}
This executeAsyncFunction can then be called like this:
const result = await chrome.tabs.executeAsyncFunction(
tab.id,
// Async function to execute in the page
async function() {
// Do lots of things with await
return true;
});
This wraps the chrome.tabs.executeScript and chrome.runtime.onMessage.addListener, and wraps the script in a try-finally before calling chrome.runtime.sendMessage to resolve the promise.
Passing promises from page to content script doesn't work, the solution is to use chrome.runtime.sendMessage and to send only simple data between two worlds eg.:
function doSomethingOnPage(data) {
fetch(data.url).then(...).then(result => chrome.runtime.sendMessage(result));
}
let data = JSON.stringify(someHash);
chrome.tabs.executeScript(tab.id, { code: `(${doSomethingOnPage})(${data})` }, () => {
new Promise(resolve => {
chrome.runtime.onMessage.addListener(function listener(result) {
chrome.runtime.onMessage.removeListener(listener);
resolve(result);
});
}).then(result => {
// we have received result here.
// note: async/await are possible but not mandatory for this to work
logger.error(result);
}
});
For anyone who is reading this but using the new manifest version 3 (MV3), note that this should now be supported.
chrome.tabs.executeScript has been replaced by chrome.scripting.executeScript, and the docs explicitly state that "If the [injected] script evaluates to a promise, the browser will wait for the promise to settle and return the resulting value."
I have a function as shown below:
function test(parms) {
var self = this;
return this.test2(parms)
.then(function (data) {
if (data) {
return ;
}
else {
return Bluebird.delay(1000)
.then(self.test.bind(self, parms));
}
}.bind(self));
};
I am trying to write unit tests for this function. I am using sinon.stub to mock the functionality of the function test2.
I wrote a test case where test2 returns true and therefore the test function successfully completes execution. However I want a test case where on the first instance test2 returns false, it waits for delay and next time test2 returns true. For that I wrote my test case as below:
var clock;
var result;
var test2stub;
var count = 0;
before(function () {
clock = sinon.useFakeTimers();
//object is defined before
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
if (count === 0) {
return (Bluebird.resolve(false));
}
else if (count === 1) {
return (Bluebird.resolve(true));
}
});
clock.tick(1000);
object.test("xyz")
.then(function (data) {
result = data;
});
clock.tick(1000);
count = count + 1;
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
});
it("result should be undefined. Check if test2 returned false first & true next",
function () {
expect(result).to.be.undefined;
});
In the logs it shows that count has value 0 only.
The code of test is actually incorrect. It never returns data on success. It returns undefined. The function should return data on success otherwise you won't be able to use it as parameter for next .then handler
.then(function (data) {
if (data) {
return data;
}
Next you make wrong assumptions about function test. It will NEVER return undefined. The function is rather dangerous and will call itself forever in an endless chain of promises until it squeezes out any not-null data from test2.
One shouldn't launch test code in before or beforeEach section. before and after are meant to prepare the environment like faking timers and then restoring them.
One reason for calling tested code in the it handler is because promises should be handled differently. The handler should accept a parameter which indicates that the test will be asynchronous and the the test engine gives it a timeout (usually 10 secs) to complete. The test is expected to either call done() to indicate test is successful or call done(error) if it failed and there is an error object (or expect threw an exception).
Also you should move the fake timer after the async operation started. In your code actually the first clock.tick is useless.
There is a trick with using fakeTimers. You can move time manually however it doesn't move on its own. For the first tick it works well. The promise is executed. However upon returning .delay(1000) promise, there will be no command to move the time forward. So, to finish the test correctly (not to modify tested code) you have also to stub Bluebird.delay
I would change the stubs implementation and do something like this
describe("test2", function(){
beforeEach(function(){
clock = sinon.useFakeTimers();
test2stub = sinon.stub(object,"test2", function () {
console.log("Count is: " + count);
return (Bluebird.resolve((count++) > 0));
});
var _delay = Bluebird.delay.bind(Bluebird);
bluebirdDelayStub = sinon.stub(Bluebird,"delay", function (delay) {
var promise = _delay(delay);
clock.tick(1000);
return promise;
});
})
it("should eventually return true", function (done) {
object.test("xyz")
.then(function (data) {
expect(data).to.be.true;
expect(count).to.equal(2);
done();
})
.catch(function(err){
done(err);
});
clock.tick(1000);
});
after(function () {
test2stub.restore();
clock.restore();
bluebirdDelayStub.restore();
});
})
PS I verified this code under Node.js 0.10.35 and Bluebird 2.9.34
I've been using Bluebird a lot recently on a HAPI API development. I've just run into my first real problem, that perhaps my understanding or naivety has me stumped.
The following code is an example of what I am facing:-
var Promise = require('bluebird'),
stuff = require('../stuff');
module.exports = {
getSomething: function(request, reply) {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p = p.then(function(resultFromPromise) {
//problems begin here
var data = stuff.doSomeReallyLongAndBoringFunction(resultFromPromise);
return data;
});
p.then(function(data) {
//no data here.
});
};
};
I've commented where the problems usually begin. the stuff.doSomeReallyLongAndBoringFunction() returns an object (using more promises concidently) and it's this object I want to access, but //no data here always fires before data returns. stuff.doSomeReallyLongAndBoringFunction() continues to run regardless and completes successfully, but after the code goes async, I don't know how to promise that function's return value back.
Can anyone offer any guidance? Please accept my apologies for any naivety in the question!
Help as always, is appreciated
NB just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself. Although, I did try return new Promise(reject, resolve) { }); manual wrap. It is simply a function that uses promises itself (successfully) to get data.
Update 1
stuff.doSomeReallyLongAndBoringFunction() is too big to post directly, but it does something like this:-
var Promise = require('bluebird'),
rp = require('request-promise');
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
var p = Promise.resolve();
p = p.then(function() {
return db.find() //etc. etc.
});
p.then(function() {
rp(options).then(function(response){
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
db.something.create({
Field: 'something'
}).exec(function(err, saved) {
return marshalledData;
});
});
}).catch(function(err) {
});
};
};
Update 2
Thank you Justin for your help. Here is the actual code, perhaps this may help?
Promise.resolve()
.then(function() {
if(typeof utils.intTryParse(place) !== 'number') {
return foursquare.createPlaceFromFoursquare(sso, place, request, reply);
} else {
return { Place: { PlaceId: place }};
}
}).then(function(placeObj) {
console.log('Place set as', placeObj); //always returns undefined, despite function actually completing after async op...
});
If your doSomeReallyLongAndBoringFunction is really running asynchronously, then it doesn't make sense to run it the way you have setup.
Edit - Here's a simple explanation of the way your code looks to be running vs a refactored version. It's been simplified , so you'll need to fill in the relevant sections with your actual implementation.
var Promise = require('bluebird');
function myAsync() {
setTimeout(function(){
return 'done sleeping';
}, 2000);
};
//The way your code is running
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return myAsync(); //your error is here
})
.then(function(done){
console.log(done);
});
//refactored
Promise.resolve()
.then(function(){
return 'hello';
})
.then(function(done){
console.log(done);
return new Promise(function(resolve) {
setTimeout(function(){
resolve('done sleeping');
}, 2000);
});
})
.then(function(done){
console.log(done);
});
just for clarity, stuff.doSomeReallyLongAndBoringFunction() does not return a Promise itself.
And that's your problem. As it does something asynchronous and you want to get its result, it should return a promise. In fact, that's the case for every asynchronous function, especially then callbacks! It should be something like
module.exports = {
doSomeReallyLongAndBoringFunction: function() {
return db.find()
// ^^^^^^
.then(function() {
return rp(options).then(function(response){
// ^^^^^^
//get some data from remote location
}).then(function(dataFromService) {
//do some jiggery pokery with said data
var marshalledData = dataFromService;
return db.something.create({
// ^^^^^^
Field: 'something'
}).execAsyc();
});
}).catch(function(err) {
});
}
};
Your getSomething method has the same issues, and should look like this:
var createPlace = Promise.promisify(foursquare.createPlaceFromFoursquare);
module.exports = {
getSomething: function(request) {
var p;
if (typeof utils.intTryParse(place) !== 'number')
p = createPlace(sso, place, request); // this returns a promise!
else
p = Promise.resolve({Place: {PlaceId: place}});
return p.then(function(placeObj) {
// ^^^^^^
console.log('Place set as', placeObj);
});
}
};
See also these generic rules for promise development.
doSomeReallyLongAndBoringFunction needs to look like this:
doSomeReallyLongAndBoringFunction: function(param) {
var resolver = Promise.defer();
/*
* do some asynchronous task and when you are finished
* in the callback, do this:
*/
resolver.resolve(resultFromAsyncTask);
/*
*
*
*/
return resolver.promise;
}