Using asynchronous Nightwatch After Hook with client interaction does not work - nightwatch.js

As far as I can tell, using promises or callbacks in After hook prevents Command Queue from executing when using promises / callbacks. I'm trying to figure out why, any help or suggestions are appreciated. Closest issue I could find on github is: https://github.com/nightwatchjs/nightwatch/issues/341
which states: finding that trying to make browser calls in the after hook is too late; it appears that the session is closed before after is run. (exactly my problem). But there is no solution provided. I need to run cleanup steps after my scenarios run, and those cleanup steps need to be able to interact with browser.
https://github.com/nightwatchjs/nightwatch/wiki/Understanding-the-Command-Queue
In the snippet below, bar is never outputted. Just foo.
const { After } = require('cucumber');
const { client } = require('nightwatch-cucumber');
After(() => new Promise((resolve) => {
console.log('foo')
client.perform(() => {
console.log('bar')
});
}));
I also tried using callback approach
After((browser, done) => {
console.log('foo');
client.perform(() => {
console.log('bar');
done();
});
});
But similar to 1st example, bar is never outputted, just foo
You can instead use something like:
const moreWork = async () => {
console.log('bar');
await new Promise((resolve) => {
setTimeout(resolve, 10000);
})
}
After(() => client.perform(async () => {
console.log('foo');
moreWork();
}));
But the asynchronous nature of moreWork means that the client terminates before my work is finished, so this isn't really workin for me. You can't use an await in the perform since they are in different execution contexts.
Basically the only way to get client commands to execute in after hook is my third example, but it prevents me from using async.
The 1st and 2nd examples would be great if the command queue didn't freeze and prevent execution.
edit: I'm finding more issues on github that state the browser is not available in before / after hooks: https://github.com/nightwatchjs/nightwatch/issues/575
What are you supposed to do if you want to clean up using the browser after all features have run?

Try the following
After(async () => {
await client.perform(() => {
...
});
await moreWork();
})

Related

How do you wait on multiple XHR in Cypress that match the same intercept

In my app after login in from a clean state, there are a series of sync queries that are being fired to make sure that the local data is updated. There is a loading screen while this is happening. I just need to cypress to wait for all these calls to finish before performing the test.
cy.intercept() is identifying the call, but cy.wait() only waits for the first one to be finished.
Is there a way to create the alias dynamilcally or have the application wait for the spinner to disapper?
describe('Navigation', function () {
beforeEach(function () {
// Programmatically login via Amazon Cognito API
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'), Cypress.env('cognito_password'));
cy.wait(['#dataStore']);
});
it('shows logged in', function () {
cy.get('[data-test=logo]').should('be.visible');
});
You can repeat wait on a single intercept, so count up the number of orange dataStore tags (looks like 11) and wait that amount of times
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'), Cypress.env('cognito_password'));
Cypress._.times(11, () => {
cy.wait('#dataStore')
})
Or it might be 10 - looking at the route defn. In any case, experiment. The app should be consistent in the calls it makes.
I had a similar case. What I do is store an array of objects in a different file and each object represents a specific test scenario. That way you can iterate through your test cases an assign an alias dynamically.
So you could do something like this:
beforeEach(function () {
yourArray.forEach((testcase) => {
cy.intercept('POST', '**/graphql').as(`${testcase.testname}datastore`);
cy.loginByCognitoApi(Cypress.env('cognito_username'),
Cypress.env('cognito_password'));
cy.wait(`#${testcase.testname}datastore`);
}
});
If the number of requests aren't consistent, something I've done is the following (I've since put this in a command to use in multiple places):
cy.intercept('POST', '**/graphql').as('dataStore');
cy.loginByCognitoApi(Cypress.env('cognito_username'),Cypress.env('cognito_password'));
cy.get('#dataStore.all').then(xhrs => cy.wait(Array(xhrs.length).fill('#dataStore')));
Doing a wait on the alias with "all" returns all of the calls made to aliased route that Cypress has seen since the alias was made.
#user16695029 is a great solution.
If you run into the issue of API calls not being predictable (kicked off by a timer async etc), then keeping track of API call count might be useful:
at the start of your test code
let responseCounter = 0;
cy.intercept({ method: 'POST', url: '/save', middleware: true }, req => {
req.on('response', (res) => {
responseCounter++;
})
}).as('save')
then later
let expectedSaveCount = 12;
Cypress._.times(expectedSaveCount - responseCounter, () => {
cy.wait('#save')
})
cy.get('#save.all').should('have.length', expectedSaveCount)

Expecting a Promise *not* to complete, in Jest

I have the following need to test whether something does not happen.
While testing something like that may be worth a discussion (how long wait is long enough?), I hope there would exist a better way in Jest to integrate with test timeouts. So far, I haven't found one, but let's begin with the test.
test ('User information is not distributed to a project where the user is not a member', async () => {
// Write in 'userInfo' -> should NOT turn up in project 1.
//
await collection("userInfo").doc("xyz").set({ displayName: "blah", photoURL: "https://no-such.png" });
// (firebase-jest-testing 0.0.3-beta.3)
await expect( eventually("projects/1/userInfo/xyz", o => !!o, 800 /*ms*/) ).resolves.toBeUndefined();
// ideally:
//await expect(prom).not.toComplete; // ..but with cancelling such a promise
}, 9999 /*ms*/ );
The eventually returns a Promise and I'd like to check that:
within the test's normal timeout...
such a Promise does not complete (resolve or reject)
Jest provides .resolves and .rejects but nothing that would combine the two.
Can I create the anticipated .not.toComplete using some Jest extension mechanism?
Can I create a "run just before the test would time out" (with ability to make the test pass or fail) trigger?
I think the 2. suggestion might turn handy, and can create a feature request for such, but let's see what comments this gets..
Edit: There's a further complexity in that JS Promises cannot be cancelled from outside (but they can time out, from within).
I eventually solved this with a custom matcher:
/*
* test-fns/matchers/timesOut.js
*
* Usage:
* <<
* expect(prom).timesOut(500);
* <<
*/
import { expect } from '#jest/globals'
expect.extend({
async timesOut(prom, ms) { // (Promise of any, number) => { message: () => string, pass: boolean }
// Wait for either 'prom' to complete, or a timeout.
//
const [resolved,error] = await Promise.race([ prom, timeoutMs(ms) ])
.then(x => [x])
.catch(err => [undefined,err] );
const pass = (resolved === TIMED_OUT);
return pass ? {
message: () => `expected not to time out in ${ms}ms`,
pass: true
} : {
message: () => `expected to time out in ${ms}ms, but ${ error ? `rejected with ${error}`:`resolved with ${resolved}` }`,
pass: false
}
}
})
const timeoutMs = (ms) => new Promise((resolve) => { setTimeout(resolve, ms); })
.then( _ => TIMED_OUT);
const TIMED_OUT = Symbol()
source
The good side is, this can be added to any Jest project.
The down side is, one needs to separately mention the delay (and guarantee Jest's time out does not happen before).
Makes the question's code become:
await expect( eventually("projects/1/userInfo/xyz") ).timesOut(300)
Note for Firebase users:
Jest does not exit to OS level if Firestore JS SDK client listeners are still active. You can prevent it by unsubscribing to them in afterAll - but this means keeping track of which listeners are alive and which not. The firebase-jest-testing library does this for you, under the hood. Also, this will eventually ;) get fixed by Firebase.

How to listen global events with Cypress?

We have an application that polls the server periodically until a task is completed. We fire a global event so that Cypress can catch and find out if the task is finished but we had trouble using document.addEventListener on Cypress. Here's what we're doing:
document.addEventListener('queryEnd', () => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})
However; when we use it in a spec, it doesn't work expected and we're not able to catch it. Also, Cypress doesn't wait for the test and runs afterEach without waiting for the callback to run.
The reason why your code isn't working like you expect is because in Cypress, your tests run in a separate frame than your application under test (AUT). The event you're waiting for will never fire inside of Cypress's document.
To get the document of the AUT, use cy.document() like this:
cy.document()
.then($document => {
// now $document is a reference to the AUT Document
$document.addEventListener(...)
})
To make Cypress wait for your event before continuing, you can just wrap it in a Cypress.Promise. The Cypress docs have an example about waiting for a Promise to complete. For your queryEnd event, it would look something like this:
cy.document() // get a handle for the document
.then($document => {
return new Cypress.Promise(resolve => { // Cypress will wait for this Promise to resolve
const onQueryEnd = () => {
$document.removeEventListener('queryEnd', onQueryEnd) // cleanup
resolve() // resolve and allow Cypress to continue
}
$document.addEventListener('queryEnd', onQueryEnd)
})
})
.then(() => {
cy.get('.chart').should('be.visible')
cy.get('.table').should('be.visible')
})

How fast / efficient is Bluebird's timeout?

The following example times out in most cases (outputs timed out):
Promise = require('bluebird');
new Promise(resolve => {
setTimeout(resolve, 1000);
})
.timeout(1001)
.then(() => {
console.log('finished');
})
.catch(error => {
if (error instanceof Promise.TimeoutError) {
console.log('timed out');
} else {
console.log('other error');
}
});
Does this mean that the Bluebird's promise overhead takes longer than 1ms?
I see it often time out even if I use .timeout(1002).
The main reason for asking - I'm trying to figure what the safe threshold is, which gets more important with smaller timeouts.
Using Bluebird 3.5.0, under Node.js 8.1.2
I have traced your bug in Bluebird's code. Consider this:
const p = new Promise(resolve => setTimeout(resolve, 1000));
const q = p.timeout(1001); // Bluebird spawns setTimeout(fn, 1001) deep inside
That looks rather innocent, yeah? Though, not in this case. Internally, Bluebird implemented it something like (not actually valid JS; timeout clearing logic is omitted):
Promise.prototype.timeout = function(ms) {
const original = this;
let result = original.then(); // Looks like noop
setTimeout(() => {
if result.isPending() {
make result rejected with TimeoutError; // Pseudocode
}
}, ms);
return result;
}
Bug was presence of line ret.isPending(). It resulted in brief time when original.isPending() === false and ret.isPending() === true because "resolved" status didn't propagate yet from original to children. Your code hit that extremely short period and BOOM, you had race condition.
I think what's going on here is that there's a race between the time the rest of the promise chain takes and the timer from the .timeout(). Since they are both so close in timing, sometimes one wins and sometimes the other wins - they are racy. When I run this code that logs the sequence of events, I get different ordering on different runs. The exact output order is unpredictable (e.g. racy).
const Promise = require('bluebird');
let buffer = [];
function log(x) {
buffer.push(x);
}
new Promise(resolve => {
setTimeout(() => {
log("hit my timeout");
resolve();
}, 1000);
}).timeout(1001).then(() => {
log('finished');
}).catch(error => {
if (error instanceof Promise.TimeoutError) {
log('timed out');
} else {
log('other error');
}
});
setTimeout(() => {
console.log(buffer.join("\n"));
}, 2000);
Sometimes this outputs:
hit my timeout
finished
And, sometimes it outputs:
hit my timeout
timed out
As has been mentioned in the comments, if .then() was always executed via microtask (which should precede any macrotasks), then one would think that the .then() would precede the setTimeout() from the .timeout(), but things are apparently not that simple.
Since the details of promise .then() scheduling is not mandated by specification (only that the stack is clear of application code) a code design should not assume a specific scheduling algorithm. Thus, a timeout this close to the execution of the async operation it is following can be racy and thus unpredictable.
If you could explain exactly what problem you're trying to solve, we could probably offer more concrete advice about what to do. No timers in Javascript are precise to the ms because JS is single threaded and all timer events have to go through the event queue and they only call their callbacks when their event gets serviced (not exactly when the timer fired). That said, timer events will always be served in order so a setTimeout(..., 1000) will always come before setTimeout(..., 1001), even though there may not be exactly 1ms delta between the executing of the two callbacks.

How listen for events in Hyperledger Fabric Composer?

I haven't received any event notifications and am wondering if I am missing something.
I followed the instructions from the Fabric Composer website to define the BasicEvent in my cto model and added the code for creating and emitting events in a transaction and updated the network. I created a separate eventListener.js program that subscribes to the events using the businessNetworkConnection using the code sample from the website.
After I start my eventListener.js app it seems to be listening (after receiving the connected status message in console, nothing else happens...it doesn't go back to the normal prompt line.)
I then execute the transaction that should emit the event and it runs successfully but no event is received in the other terminal window where the eventlistener.js is running.
Here is the key part of the eventListener.js program:
businessNetworkConnection.connect(connectionProfile, businessNetworkIdentifier, participantId, participantPwd)
.then((result) => {
businessNetworkDefinition = result;
console.log('Connected: BusinessNetworkDefinition obtained=' + businessNetworkDefinition.getIdentifier());
});
businessNetworkConnection.on('event', (event) => {
// event: { "$class": "org.namespace.BasicEvent", "eventId": "0000-0000-0000-000000#0" }
console.log(event);
});
Is the businessNetworkConnection.on('event', (event) ... command supposed to cause the program to appear to hang while its listening?
If so, is there something else that could be done to troubleshoot where the problem is?
I'm using v0.6 HLF on local Docker.
I think your issue is that you don't wait for the businessNetworkDefinition to be connected before you register your listener. Remember that the then block is executed asynchronously and your following businessNetworkConnection.on code will execute immediately.
You should add a second then block to register your listener after the businessNetworkDefinition has been connected.
e.g.
return adminConnection.connect('hlfv1', 'admin', 'adminpw')
.then(() => {
return BusinessNetworkDefinition.fromDirectory(path.resolve(__dirname, '..'));
})
.then((businessNetworkDefinition) => {
return adminConnection.deploy(businessNetworkDefinition);
// return true;
})
.then(() => {
businessNetworkConnection = new BusinessNetworkConnection();
return businessNetworkConnection.connect('hlfv1', 'my-network', 'admin', 'adminpw');
})
.then(() => {
businessNetworkConnection.on('event', (event) => {
console.log( '****** received the event ' + JSON.stringify(businessNetworkConnection.getBusinessNetwork().getSerializer().toJSON(event)));
});
});
I wrote a sample code for a basic sample application developed by Hyperledger team. You can check out about from this code snippet: SampleEventListener

Resources