I'm writing a simple test in Cypress which clicks a button and starts a download. The API's response contains the unformatted contents of the csv file, so I can use that to assert what I want - but the problem is that when you're using Chrome or Firefox to test with, the download still occurs. There is no dialogue prompt - the download happens on click - but I was wondering if maybe there was a way I could prevent it from happening, maybe with cy.window() or cy.stub()?
it('Export list to CSV', () => {
//Click button and assert on the API
cy.findByTestId('btnExport_SystemUsers')
.should('be.visible')
.click()
.wait('#exportToCsv')
.then((xhr) => {
//Assert on some keywords from the response such as Role names, user names, and site group name (that won't change)
expect(xhr.status).to.eq(200)
expect(xhr.response.body.csvFileData).to.contain('CY.User')
expect(xhr.response.body.csvFileData).to.contain('CY.Admin')
expect(xhr.response.body.csvFileData).to.contain('unlockUserAccount')
expect(xhr.response.body.csvFileData).to.contain('CY Administrator')
expect(xhr.response.body.csvFileData).to.contain('Site Group 1')
})
})
Thanks
If your server sends specific disposition headers which cause a browser to prompt for download, you can figure out what URL this request is made to, and use cy.request() to hit that directly. Then you can test that the server send the right response headers.
You can then intercept the response so it doesn't download via cy.intercept. See: https://docs.cypress.io/api/commands/intercept#Intercepted-responses
Reference: https://docs.cypress.io/faq/questions/using-cypress-faq#Is-there-a-way-to-test-that-a-file-got-downloaded-I-want-to-test-that-a-button-click-triggers-a-download
Related
I have a website where users message each other through another website. My problem is that everytime user click on message item this action opens a new tab.
Having a lot of conversations makes this quite annoying, especially when using on a smartphone.
Is there any way to check if the website for texting is open?
If you are using window.open() you can add the target parameter.
A string, without whitespace, specifying the name of the browsing context the resource is being loaded into. If the name doesn't identify an existing context, a new context is created and given the specified name. The special target keywords, _self, _blank, _parent, and _top, can also be used.
example
window.open("https://www.mozilla.org/", "mozillaTab");
You are looking for postMessage. Synapsis:
postMessage(message, targetOrigin, transfer)
message is the actual message you want to send
targetOrigin specifies which domain is the target
transfer is a sequence of transferrable objects that are transmitted with the message
So, what you really want is to have this conversation:
Page1: are you there?
Page2: yes
or
Page1: are you there?
... (timeout)
I will call Page2 the page whose existence we wonder about and Page1 the page which wonders about Page2's existence.
First of all, you need a message handler at both Page1 and Page2. Synapsis:
window.addEventListener("message", (event) => {
if (event.origin !== "http://example.org:8080")
return;
//check event.data and see what happens
}, false);
On Page 2, your message handler will need to check whether the message asks about its existence and calls postMessage('Yes, I am here', event.origin); if so. Page1, on the other hand initiates the messaging by calling postMessage('Are you there?', theurl); (where you need to replace theurl with your value). Now, Page1 expects a message. If the message arrives, then Page2 exists. If not, then after a timeout you need to handle the nonexistence of Page2. So, you will have something like this at Page1:
var wasFound = false;
postMessage('Are you there', theurl);
setTimeout(function() {
if (!wasFound) {
//The page needs to be opened
}
}, 500);
Naturally, you will need to set wasFound to true when you receive a message from Page2 and you will need to make sure that the message handler sees the same wasFound variable that your timeout checks for.
I have put some push notifications in my code, in order to someone to be notified when an action is made. I made the back end with lib.net.webpush 3.1.0 with .net 4.5.2 in C#.
So far, the notification system is working very well, but there is something I can't succeed :
Within my service worker file (sw.js), using Angular 9, i want, when someone received the notification, that when a user click on it, he is redirect to the page from which it was sent.
first i made it like it (as i read in the doc):
self.addEventListener('notificationclick', function (event) {
const urlToOpen = new URL(event.notification.data, self.location.origin).href;
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen));
});
But i was always redirect to the "localhost://4200" and not to the complete url.
In order to test, i made this :
self.addEventListener('notificationclick', function (event) {
const urlToOpen = new URL(self.location.origin + "/#/rdc/rapports/2015");
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen));
});
and it worked. But i can't get the dynamic URL.
Does anyone knows how to get a dynamic URL within this file? Or how to be redirect to the page that sent the notification?
I also tried something like this here : notificationclick event service worker
But i really dont understand.
Thanks.
I just succeed in doing it.
So, for anyone in the same case as I was : within the service worker, you can't access the DOM, i wasn't able to get any ID or any path I was trying to aim in my code.
The solution was to, in my C# code, to add a "URL" property and parameter to my "SendNotification" funtion. Then, when i got a user, i can target his ID because it's already here.
Then, in my Angular code (within the service worker file), I just had to to this (i am storing my url in "data" here) :
self.addEventListener('notificationclick', function (event) { console.log("notif click event", event);
const urlToOpen = new URL(self.location.origin + event.notification.data);
event.notification.close();
event.waitUntil(clients.openWindow(urlToOpen)); });
An then, when i click on the notification, I am redirected to the desired URL.
I have a button Submit when clicked performs some operation calling an API. Post click, the button is disabled or basically the initial state of the button and the operation is changed.
I have two or multiple browser tabs which shows same screen of Submit. If in any one of the tab, the Submit operation is performed, the other tabs should show the updated version. The other tabs should show the disabled version and should not show the initial state.
How do I achieve this? I am using React, JS
#1 Data duplication MUST be restricted at the server side.
I would recommend some cache like node-cache. Node-cache will having scalability issues, so better to go with redis. (The logic should be smt. like: If the form has submited with the user_id, or with form_id, you can create a cache for that, and if you proceed it, store it in the db, other case throws an error. On the other browser you must validate before the submit if the unique_id neither in the cache nor in the db. If exists, you can throws an error in the nr.2 browser.
#2 If you want to disable the button, you have to use websockets
If you're looking for a client-only solution, here is a great article about sharing state between browser tabs. The limitation is that it won't work on different browsers/machines.
The best way to handle this from a UI/UX perspective is to use validation. If User A clicks submit, then User B clicks submit from a different browser or tab, an error should be displayed to User B indicating that "This action has already taken place".
That being said, what you are trying to achieve is possible.
One way is by using a WebSocket. A WebSocket is a persistent connection between the client and server, that allows bi-directional communication.
The page with the submit button in your React app would be a "subscriber" to some websocket channel. When the submit button is clicked for the first time(it doesn't matter from where), a message can be "published" from a WebSocket server to ALL subscribers, regardless of the browser or tab being used.
Basically, you would add an onMessage handler in your React app where you can disable the submit button when a specific message is received.
I don't know what tech you are using on the server side, but for a WebSocket server, there are many options out there. For the React app, there is react-websocket which is straight-forward to use.
you can do it in client-side
const Form = () => {
const [buttonDisabled, setButtonDisable] = useState(false);
// first tab fire
const onSubmit = () => {
.
.
.
localStorage.setItem("formSubmited", "true");
};
useEffect(() => {
const disableButton = (e) => {
if (e.storageArea.formSubmited) {
setButtonDisable(true);
}
};
// second tab fire
window.addEventListener("storage", disableButton);
return () => {
window.removeEventListener("storage", disableButton);
};
}, []);
.
.
.
};
In my Cypress code, I want to decide action based upon the response from the backend. Please note, I am not mocking a server, rather wanting to hit & get response from API server & base next commands off the response.
cy.visit("http://localhost:3000/login");
cy.get(".cookieConsent button").click();
cy.get("#email-input").type(userLoginDetail.email);
cy.get("#password-input").type(userLoginDetail.password);
cy.get("form").submit();
cy.wait(3000);
// logic being it user is redirected to homepage, then login was successful. Not happy with this O(
if (cy.url() === "http://localhost:3000/") {
console.log("eeeeeeeeeee");
} else {
console.log(
"aaaaaaaaaaaaaaaaaaa ");
How can I check the response from the form submission & use if condition based off that.
I think the problem is, that you want to do conditional testing. Using cy.wait() is bad practice and is even mentioned in the docs itself.
Anti-Pattern
You almost never need to wait for an arbitrary period of
time. There are always better ways to express this in Cypress.
You should not execute different actions upon response. In cypress testing it is expected, that you know the result of your action beforehand.
If user enters invalid email, you might expect form submisson to fail and an error to be displayed.
cy.get("#email-input").type('invalid_email.com');
cy.get("#password-input").type(userLoginDetail.password);
cy.get("form").submit();
cy.get(".email-error").should('be.visible');
In this case cypress will check them DOM until it finds the error or hits the defaultCommandTimeout from cypress config.
On the other hand, if you want to check if user is redirected correclty, you can simply check for a div on the success/redirected page.
cy.get("#email-input").type('valid#email.com');
cy.get("#password-input").type(userLoginDetail.password);
cy.get("form").submit();
cy.get(".success-page").should('be.visible');
However, if you still decide to execute an action depending on the servers response, cypress enables you to declaratively cy.wait() for requests and their responses.
cy.server()
cy.route({
method: 'POST',
url: '/myApi',
}).as('apiCheck')
cy.visit('/')
cy.wait('#apiCheck').then((xhr) => {
assert.isNotNull(xhr.response.body.data, 'api call successfull')
})
I am using Paypals Adaptive Payments and Embedded flow feature to provide checkout via a minibrowser. Everything seems to be working correctly in the sandbox environment except that when the payment is completed successfully, the user is never redirected to my returnUrl set in the PAY API request. Same goes for my cancelUrl.
After the payment is complete, the user is shown an order overview in the minibrowser and a button labelled "close". If a user clicks this button, the minibrowser is closed.
If a user clicks cancel at any time, the minibrowser is closed.
There doesn't seem to be a way to have my page aware of the change besides setting up some polling or something which doesn't make sense, my returnUrl and cancelUrl should be used somewhere, right?
this is my code to get the redirect url (using adaptive payments gem):
pay_request = PaypalAdaptive::Request.new
data = {
'requestEnvelope' => {'errorLanguage' => 'en_US'},
'currencyCode' => 'USD',
'receiverList' =>
{ 'receiver' => [
{'email' => '...', 'amount'=> 10.00}
]},
'actionType' => 'PAY',
'returnUrl' => 'http://www.example.com/paid',
'cancelUrl' => 'http://www.example.com/cancelled',
'ipnNotificationUrl' => 'http://www.example.com/ipn'
}
pay_response = pay_request.pay(data)
redirect_to pay_response.approve_paypal_payment_url "mini"
And here is how I am setting up the paypal js:
var dg = new PAYPAL.apps.DGFlowMini({ trigger: "buyit", expType: "mini" });
It all seems pretty straight forward, not sure what I am missing.
Well - seems to be a bug on our side - just tried it myself and confirmed with our integration teams. :-(
Unfortunately the other short term fix I can think of other than what you've mentioned (checking for the existence of the popup window) is to call the PaymentDetails API from your server side to check the status of the Payment. I've opened the bug on our side but don't have an ETA.
Edit 10/18: Sorry I'm wrong. This is working - it's just that our developer guide is not providing all the required information. In case of the mini-browser flow, you would need to provide a 'callbackFunction' and also name your dgFlow variable as 'dgFlowMini'. (the latter is important - as apdg.js is expecting the 'dgFlowMini' variable to be defined) Here is the code that works:
var returnFromPayPal = function(){
alert("Returned from PayPal");
// Here you would need to pass on the payKey to your server side handle to call the PaymentDetails API to make sure Payment has been successful or not
// based on the payment status- redirect to your success or cancel/failed urls
}
var dgFlowMini = new PAYPAL.apps.DGFlowMini({trigger: 'em_authz_button', expType: 'mini', callbackFunction: 'returnFromPayPal'});
I have a working sample here: https://pp-ap-sample.appspot.com/adaptivesample?action=pay (make sure you select mini as the Experience Type)
We will get our docs updated and also cleanup apdg.js to remove the dependency on the JS variable name.
Looks like the PayPal experience for embedded flows has gotten worse. Now you'll receive an error message after invoking mini or lightbox that says "Payment can't be completed. This feature is currently unavailable."