Cypress Testing Async Await Functions with useContext and Next.js - async-await

I am looking to ensure a notification appears within a Cypress integration test, with the correct message. I've created an async await function that has a try/catch block for the user to login, displaying different messages throughout their progress.
Stages of notification status attribute:
Before the try/catch block - status = "info"
At the end of try block - status = "success"
At the end of the catch block - status = "error"
Example:
async function signIn(event) {
notificationCtx.showNotification({
message: "Logging in now",
status: "info",
});
try {...
Cypress Test:
it.only("case 'The InvalidParameterException shows a suitable error'", () => {
onFormLayoutsPage.testPlainTextInput("email", "team");
onFormLayoutsPage.testPlainTextInput("password", "8s8d7fds!!");
cy.get('[data-cy="login"]').click();
cy.get('[data-cy="notification"]').should(
"have.attr",
"status",
"error"
);
});
Problem:
The notification appears, but then disappears because of setTimeout(), before the cy.get() has a chance of seeing the component. The specific error is Timed out retrying after 20000ms: Expected to find element: [data-cy="notification"], but never found it.
Component:
<Snackbar open={true} onClose={notificationCtx.hideNotification}>
<Alert
data-cy="notification"
onClose={notificationCtx.hideNotification}
severity={status}
sx={{ width: "100%" }}
>
{message}
</Alert>
</Snackbar>

You can control the setTimeout() using Cypress cy.clock() and cy.tick() commands.
it("case 'The InvalidParameterException shows a suitable error'", () => {
cy.clock() // take control of when setTimeout() finishes
onFormLayoutsPage.testPlainTextInput("email", "team");
onFormLayoutsPage.testPlainTextInput("password", "8s8d7fds!!");
cy.get('[data-cy="login"]').click();
cy.get('[data-cy="notification"]').should("have.attr", "status", "error");
cy.tick(3000) // allow time to pass & setTimeout to complete
cy.get('[data-cy="notification"]').should('not.exist')
});

Related

Inertiajs - Asynch Redirect

I have a short question.
Does inertia render asynch?
I realized, as soon I delete a DB - Entry, while I connect to new Nav-Link direct afterwards (Inertia.onStart)- which redirects me on another Page, the changes (onSuccess) wont be showed up.
Inertia.post('data-delete', {
id: this.meeeh.data[index].id,
}, {
preserveScroll: true,
onBefore: () => {
window.Toast.confirm('Delete?');
},
onStart: (visit) => {
window.Toast.load('Delete...');
},
onSuccess: (page) => {
return Promise.all([
window.Toast.success(page.props.toast),
/** Wont show after click another Link in Navbar */
])
},
onError: (errors) => {
window.Toast.error(errors);
}
});
How does it come, I have to wait until the process is Finished - otherwise my Page is not working correctly?
Not sure if I understand what you're looking for.
onSuccess runs immediately after the post request has finished AND in successful. It is completely separated from other links and it's purpose (if your returning a Promise from it) is to delay the execution of the onFinish handler.
From the docs:
It's also possible to return a promise from the onSuccess() and
onError() callbacks. This will delay the "finish" event until the
promise has resolved.
I also believe there's some problem in your code: Promise.all should receive an array os Promises and I'm pretty sure window.Toast.success(page.props.toast) isn't returning one, is it?
So... chances are that your Promise.all is never resolving.

Exit Event / Welcome Event Not Firing

We're trying to get some events/messages to post when a user exits a chatbot window (or the site) (or a welcome message), but so far the events are not firing.
I can see within Inspector tools:
Screen Shot 2020-02-18 at 3 15 39 PM
Various activities/conversations are created, the chatbot works, but no welcome/exit events are triggered.
The code we're using is nearly if not identical to documentation code here: https://github.com/microsoft/BotFramework-WebChat/blob/master/docs/WELCOME_MESSAGE.md
and here: How to handle user leaving conversation
I have a function that fires when the window is closed, as follows:
const store = window.WebChat.createStore( {}, ( { dispatch } ) => next => async action => {
return next( action );});
window.addEventListener( 'sendEventActivity', ( { data } ) => {
store.dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'user_event',
value: {
name: 'end_conversation',
value: 'user ended conversation'
},
text: 'The user has left the conversation.'
}
})
});
function exitEvent(){
const eventSendActivity = new Event( 'sendEventActivity' );
eventSendActivity.data = 'User left conversation';
window.dispatchEvent( eventSendActivity );
console.log('Exit Event Submitted (hopefully)');
}
exitEvent();
I have tried other variations, defining the store earlier, above render chat, after render chat, sending welcome messages from various locations and at various times but can't seem to get it to send.
We are using https://cdn.botframework.com/botframework-webchat/latest/webchat.js
Any idea what the issue might be? Not sure where we are going wrong or why it's not firing - copying in theory known to be working code straight into our code doesn't seem to do the trick.
Thanks in advance and please let me know if I have failed to include any necessary details- new to chatbot and do not post much on github. Many thanks,
EDIT:
I was able to marry the aforementioned code and code from here: https://github.com/microsoft/BotFramework-WebChat/issues/2120#issuecomment-516056614 in order to achieve what I wanted. I'll post below in case it helps anyone else...
const store = window.WebChat.createStore({}, ({ dispatch }) => next => action => {
if (action.type === 'DIRECT_LINE/CONNECT_FULFILLED') {
dispatch({
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/join'
}
});
}
return next(action);
});
window.addEventListener( 'sendEventActivity', ( { data } ) => {
store.dispatch( {
type: 'WEB_CHAT/SEND_EVENT',
payload: {
name: 'webchat/exit'
}
} );
} );
document.getElementById("action_menu_btn").addEventListener( 'click', function() {
const eventSendActivity = new Event( 'sendEventActivity' );
eventSendActivity.data = 'User left conversation';
window.dispatchEvent( eventSendActivity );
console.log('End Converstaion Event Fired');
});
Cheers!
I failed to mention this in the other post (I'll update it), but the reason the code works is because of the window.onbeforeunload() function. Without it, the window closes before any code can finish executing. The result being no event is created, is caught by a listener, nor is sent via the Web Chat store to the bot.
Here, using the above, refreshing the page produces the "User left conversation" activity.
Also, something to note, any function you create and pass thru like you have with exitEvent() is going to run as soon as the page loads. Take the following code which gets the user's location via the browser (placed just before the closing </script> tag). As you can see, it's loading even before Web Chat. If you are wanting a function to run according to some activity passed from the bot, then utilize either the store's actions (i.e. DIRECT_LINE/INCOMING_ACTIVITY, or some other) or via the available middleware.
let geoLoc = async () => {
await navigator.geolocation.getCurrentPosition(position => {
console.log('Latitude: ', position.coords.latitude);
console.log('Longitude: ', position.coords.longitude);
});
}
geoLoc();
Regarding a welcome message, you have two options. Either send as an activity from your bot (reference this sample) or initiate an event on your page after some initial activity is received (reference this sample).
Lastly, I would recommend getting the code working as-is before tinkering with it. This usually trips me up, so thought I'd pass it along.
Hope of help!

How to debug "Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL"?

I have a test that passes locally but it fail during Gitlab CI pipeline due to timeout error.
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Which way can I go through to debug this? I tried to increase defaultTimeoutInterval to 240000 in protractor configuratoin file, but nothing has changed.
Test
describe('Test', () => {
beforeAll(async () => {
console.log('1) start beforeAll');
await api_wrapper.generateAllLatestMeasureToPatient(patient); // it breaks here
console.log('2) API calls completed'); // it never gets here
await page.navigateTo();
console.log('3) end beforeAll');
});
it('should display map, edit fence button and toggle fence button', async () => {
console.log('4) start test');
// ...
});
});
In generateAllLatestMeasureToPatient() I do ten HTTP POST requests to API endpoint. In CI it stops at fourth, locally works fine.
Console output
1) start beforeAll
4) start test
I use 2 types of timeouts :
defaultTimeoutInterval: 120000,
also in
exports.config = {
allScriptsTimeout: 90000,
}
my test also used to timeout more in CI environment I started running them in headless mode with Browser window-size set and it really helped.
capabilities: {
'browserName': 'chrome'
},
'chromeOptions': {
'args': ["--headless", "--disable-gpu", "--window-size=1920,1080"]
},
}

Error handling using the catch block in cypress

I'm trying to handle an error in Cypress but the Cypress app is throwing error
cy.get('button[name="continue"]',{timeout: 30000})
.catch((err) => {
cy.console.log("error caught");
})
The error I get:
TypeError: cy.get(...).catch is not a function
tl;dr
Cypress has no .catch command the error message clearly states that.
Exception handling in Cypress
The documentation on error recovery clearly states:
The following code is not valid, you cannot add error handling to Cypress commands. The code is just for demonstration purposes.
cy.get('button').contains('hello')
.catch((err) => {
// oh no the button wasn't found
// (or something else failed)
cy.get('somethingElse').click()
})
They deliberately left this out and in the docs they explain it in great length why you shouldn't be able to do it.
If you really want, you can catch uncaught exceptions, just try the suggestions of the Catalog of Events on this matter:
it('is doing something very important', function (done) {
// this event will automatically be unbound when this
// test ends because it's attached to 'cy'
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('something about the error')
// using mocha's async done callback to finish
// this test so we prove that an uncaught exception
// was thrown
done()
// return false to prevent the error from
// failing this test
return false
})
// assume this causes an error
cy.get('button').click()
})

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')
})

Resources