How to catch Cypress request timeout error? - request

Is it possible to handle request timeouts in Cypress? I don't want error to be thrown. Request for example:
cy.request('http://re6l.com/')
At the moment I suppose that's not possible and I should use another methods for network requests

To test for all of the bad links on the page, and output all bad links, I'd imagine that adding failOnStatusCode: false will get you most of the way there.
let badLinks = [];
cy.get('a') // get all anchor tags
.each(($a) => {
const href = $a.attr('href'); // get the href attribute
cy.request(href, {failOnStatusCode: false})
.then((res) => {
if (res.statusCode >= 400) { // check if the call failed
badLinks.push(href) // add the failed href to the badLinks array
}
});
})
.log(badLinks); // log the badLinks

You can globally increase the responseTimeout by adding the value in the cypress config file(cypress.json before cypress 10, cypress.config.js after cypress 10). The default is 30 seconds or 30000 ms.
If you want to increase it for just one cy.request, you can do it locally like this:
cy.request('http://re6l.com/', {timeout: 40000})
You can add failOnStatusCode: false to prevent cypress from failing the test if response codes other than 2xx and 3xx.
cy.request('http://re6l.com/', {timeout: 40000, failOnStatusCode: false})

Related

Communicating a successful workbox-background-sync replay to open clients

I'm using React 17, Workbox 5, and react-scripts 4.
I created a react app with PWA template using:
npx create-react-app my-app --template cra-template-pwa
I use BackgroundSyncPlugin from workbox-background-sync for my offline requests, so when the app is online again, request will be sent automatically.
The problem is I don't know when the request is sent in my React code, so I can update some states, and display a message to the user.
How can I communicate from the service worker to my React code that the request is sent and React should update the state?
Thanks in advance.
You can accomplish this by using a custom onSync callback when you configure BackgroundSyncPlugin. This code is then executed instead of Workbox's built-in replayRequests() logic whenever the criteria to retry the requests are met.
You can include whatever logic you'd like in this callback; this.shiftRequest() and this.unshiftRequest(entry) can be used to remove queued requests in order to retry them, and then re-add them if the retry fails. Here's an adaption of the default replayRequests() that will use postMessage() to communicate to all controlled window clients when a retry succeeds.
async function postSuccessMessage(response) {
const clients = await self.clients.matchAll();
for (const client of clients) {
// Customize this message format as you see fit.
client.postMessage({
type: 'REPLAY_SUCCESS',
url: response.url,
});
}
}
async function customReplay() {
let entry;
while ((entry = await this.shiftRequest())) {
try {
const response = await fetch(entry.request.clone());
// Optional: check response.ok and throw if it's false if you
// want to treat HTTP 4xx and 5xx responses as retriable errors.
postSuccessMessage(response);
} catch (error) {
await this.unshiftRequest(entry);
// Throwing an error tells the Background Sync API
// that a retry is needed.
throw new Error('Replaying failed.');
}
}
}
const bgSync = new BackgroundSyncPlugin('api-queue', {
onSync: customReplay,
});
// Now add bgSync to a Strategy that's associated with
// a route you want to retry:
registerRoute(
({url}) => url.pathname === '/api_endpoint',
new NetworkOnly({plugins: [bgSync]}),
'POST'
);
Within your client page, you can use navigator.seviceWorker.addEventListener('message', ...) to listen for incoming messages from the service worker and take appropriate action.

navigator.geolocation.getCurrentPosition() is not getting a response from googleapi

I am using react to get geolocation on localhost:3000. I have seen another person get the geolocation coordinates on their localhost, but I am unable to do so even with allow location access enabled on Chrome.
I have tried using both the hooks and class syntax in react. I have enabled allow access. I eventually used an ip address api to get a general location, but since the geolocation is supposed to work(at least that is what I have been told) I would at least like to see it work so I can implement it with https in the future. The error log does not even get fired, whereas the first three logs are getting fired when the component is mounted. Here is the code I have tried, I have made it as simple as possible:
const App = props => {
useEffect(() => {
console.log('hello')
console.log(navigator)
console.log(navigator.geolocation)
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition((position) => {
console.log(position)
}, (error) => {
console.log(error)
})
} else {
console.log('error')
}
}, [])
return (
<div>
<h3>Please Login.</h3>
</div>
)
}
export default App
I expect to receive a response from googleapi.
Edit:
I added the error callback and it printed:
message: "Network location provider at 'https://www.googleapis.com/' : No response received."
add the optional error callback to handle the error (if user declines location permission)
navigator.geolocation.getCurrentPosition(success[, error[, [options]])
you are checking only if it is in navigator or not !!!
if user declines location permission then error callback will handle it...
possible options are (reference taken from mdn)
{
enableHighAccuracy: true,
maximumAge : 30000,
timeout : 27000
}

Timeout on Axios Requests

Our site currently has a filter feature that fetches new data via axios depending on what is being filtered.
The issue is that the filter is done on real time and every change made via react causes an axios request.
Is there a way to put a timeout on the axios request so that I only fetch the last state?
I would suggest using debounce in this case to trigger API call after a specified millisecond of user input.
But just in case you need to add a timeout during axios call, this can be achieved like -
instance.get('/longRequest', {
timeout: 5000
});
The problem has two parts.
The first part is debouncing and is default for event listeners that can be triggered often, especially if their calls are expensive or may cause undesirable effects. HTTP requests fall into this category.
The second part is that if debounce delay is less than HTTP request duration (this is true for virtual every case), there still will be competing requests, responses will result in state changes over time, and not necessarily in correct order.
The first part is addressed with debounce function to reduce the number of competing requests, the second part uses Axios cancellation API to cancel incomplete requests when there's a new one, e.g.:
onChange = e => {
this.fetchData(e.target.value);
};
fetchData = debounce(query => {
if (this._fetchDataCancellation) {
this._fetchDataCancellation.cancel();
}
this._fetchDataCancellation = CancelToken.source();
axios.get(url, {
cancelToken: this._fetchDataCancellation.token
})
.then(({ data }) => {
this.setState({ data });
})
.catch(err => {
// request was cancelled, not a real error
if (axios.isCancel(err))
return;
console.error(err);
});
}, 200);
Here is a demo.
From this axios issue (Thanks to zhuyifan2013 for giving the solution), I've found that axios timeout is response timeout not connection timeout.
Please check this answer
You can also use as a general setting by axios.defaults for all requests:
axios.defaults.timeout = 5000

IE 11, Data from server not update

I have strange problem with IE11 only. I have form in my app, when I fill all inputs and try save, request to server from IE11 is send properly, then I try get this data and I recive empty collection from resposne (using axios). I use polyfill to promises and push. Where could be a problem ?
In application I use React, Redux.
Sample code:
getService(id) {
return this.api.get('/' + id)
.then(resp => {
console.log(resp.data.model.collect) // arr length 0 in other browser data are exist here
let Collection = [];
resp.data.model.collect.map((item, idx) => {
Collection.push(item)
});
return Collection;
})
.catch(err => {
throw err;
})
}
the problem is that the internet explorer browser uses data cache for load form. It's necessary stop to record cache. for this you need request your api puting in header the pragma parameter
header {
pragma: 'no-cache'
}

Wait for page redirection in Protractor / Webdriver

I have a test that clicks a button and redirects to a user dashboard. When this happens Webdriver returns:
javascript error: document unloaded while waiting for result.
To fix this I insert browser.sleep(2000) at the point where redirection occurs and assuming my CPU usage is low, this solves the issue. However, 2000 ms is arbitrary and slow. Is there something like browser.waitForAngular() that will wait for the angular to load on the redirected page before the expect(..)?
it('should create a new user', () => {
$signUp.click();
$email.sendKeys((new Date().getTime()) + '#.com');
$password.sendKeys('12345');
$submit.click();
browser.sleep(2000); // Need alternative to sleep...
// This doesn't do it...
// browser.sleep(1);
// browser.waitForAngular();
$body.evaluate('user')
.then((user) => {
expect(user).toBe(true);
});
});
do you think something like this could work for you? This will wait up to 10 seconds for the url to include the text 'pageTwo', or whatever you put in.
var nextPageButton = $('#nextPage');
nextPageButton.click().then(function(){
return browser.driver.wait(function() {
return browser.driver.getCurrentUrl().then(function(url) {
return /pageTwo/.test(url);
});
}, 10000);
};
Just stick in the regex of the url you are expecting.
Alternatively, you could wait for an element from the next page to appear as well:
var nextPageButton = $('#nextPage');
nextPageButton.click();
var elementFromSecondPage = $('#coolElement');
browser.wait(protractor.until.elementIsVisible(elementFromSecondPage), 5000, 'Error: Element did not display within 5 seconds');
When using .click, protractor will naturally wait for angular to finish the action attached to the click, such as changing the page. But, while the page change, you may still be needing something specific to be loaded, so the test fails before that part is available. Using this, it should wait for the click part to finish, then wait for the element to appear.
To expand on user2020347's answer:
Thanks that solved my issue. I wonder why this isn't a built in function. I'll be using this in many places to wait for browser navigation.
To make it more concise, I made a little helper:
Object.assign(global, {
waitUntilURLContains: string => {
let fn = () => {
return browser.driver.wait(() => {
return browser.driver.getCurrentUrl().then((url) => {
return url.includes(string);
});
}, waitDelay);
}
return fn.bind(null, string);
}
});
In my test:
$button.click().then(waitUntilURLContains('dashboard'));
keeping it very simple. I was also running into the same problem but was able to solve it using the following code :
page.setUsername(objectrepository.userdetails.useremail);
page.setPassword(objectrepository.userdetails.userpassword);
page.login().click();
**browser.wait(EC.visibilityOf(page.greetingMessageElement()), 5000);**
page.greetingMessageElement().getText()
.then(function (value){
expect(browser.getCurrentUrl()).toContain("#/mytickets");
});

Resources