cypress response time action for page load and functions - time

I am trying to implement the response time for the cypress test such us time that took something to happen after clicking, page loads etc. I have seen one copy of this and if we set up something like this can we assert it for the "time limit" and then have like assertion for specifictime that test will run this reaction and fails if he overdues on time on it ?
Example something like this:
let t1 = Date.now();
let t2 = null;
var start = 0;
cy.then(() => {
start = performance.now();
});
cy.get('[data-cy="login"]').click()
// .then(() => {
// cy.log(`duration: ${performance.now() - start} ms`);
// });
.then(() => t2 = new Date());
cy.get('[data-cy="login"]', { timeout: 20000 })
.should(elements => {
expect(elements.length).to.eq(0);
t2 = new Date();
})
.then(() => cy.log(`duration: ${(t2-t1)/1000} seconds`))
})

Related

response from await browser.tabs.sendMessage is set in chrome, but not in firefox

I have successfully used await browser.tabs.sendMessage in chrome to get response from the listener, but the same code in firefox does not work. await browser.tabs.sendMessage return immediately and sets response to undefined. In content script inject.js, sendResponse should be called after 1000ms timeout.
I attached a minimalistic example. Any idea why await browser.tabs.sendMessage
returns what sendResponse set only in chrome, but not in firefox?
//inject.js
(async () => {
if (typeof browser === "undefined") {
var browser = chrome;
}
browser.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log(msg);
setTimeout(function(){
let pageObject = {a:1};
sendResponse(pageObject);
},1000)
return true;
});
})();
//background.js
(async () => {
if (typeof browser === "undefined") {
var browser = chrome;
}
//**code for injecting content scripts on extension reload**
browser.runtime.onInstalled.addListener(async () => {
let manifest = browser.runtime.getManifest();
for (const cs of manifest.content_scripts) {
for (const tab of await browser.tabs.query({ url: cs.matches })) {
browser.scripting.executeScript({
target: { tabId: tab.id },
files: cs.js,
});
}
}
});
async function SendMessageToFront(message) {
let resolve;
const promise = new Promise(r => resolve = r);
browser.tabs.query({}, async function (tabs) {
for (let index = 0; index < tabs.length; index++) {
const tab = tabs[index];
if (tab.url) {
let url = new URL(tab.url)
if (url.hostname.includes("tragetdomain.com")) {
var startTime = performance.now()
let response = await browser.tabs.sendMessage(tab.id, { message: message });
var endTime = performance.now()
console.log(`Call to doSomething took ${endTime - startTime} milliseconds`) // this takes 0ms
console.log("got response");
console.log(response); // this is undefined
console.log(browser.runtime.lastError); // this is empty
resolve(response);
break;
}
}
}
});
return promise;
}
await SendMessageToFront();
})();
I guess for the tests in firefox you do the reload of the background script (F5 or the specific button in devtools)
Just as you have coded the background you have little hope of getting an answer because every time you reload the background you break the wire with all content scripts injected into the page(s).
Move the browser check inside the "SendMessageToFront" function. Move the "SendMessageToFront" function (async is not needed) to the main thread and run that function in the main thread.
/*async*/ function SendMessageToFront(message) {
if (typeof browser === "undefined")
var browser = chrome;
let resolve;
const promise = new Promise(r => resolve = r);
browser.tabs.query({}, async function(tabs) {
for (let index = 0; index < tabs.length; index++) {
const tab = tabs[index];
if (tab.url) {
let url = new URL(tab.url);
if (url.hostname.includes("tragetdomain.com")) {
var startTime = performance.now()
let response = await browser.tabs.sendMessage(tab.id, {'message': message});
var endTime = performance.now()
console.log(`Call to doSomething took ${endTime - startTime} milliseconds`) // this takes 0ms
console.log("got response");
console.log(response); // this is undefined
console.log(browser.runtime.lastError); // this is empty
resolve(response);
break
}
}
}
});
return promise
}
(async _ => {
await SendMessageToFront()
})();
in this way you will get an error message as soon as the background is ready which tells you that the content script on the other side does not exists or it's not ready yet, but now, when the content script will be ready, you should just re-launch the function from the background script devtools
(async _ => {
await SendMessageToFront()
})();
this time you will get the correct answer {a: 1}

NextJS API Route Returns Before Data Received?

I'm not sure what's going on here. I have set up an API route in NextJS that returns before the data has been loaded. Can anyone point out any error here please?
I have this function that calls the data from makeRequest():
export async function getVendors() {
const vendors = await makeRequest(`Vendor.json`);
console.log({ vendors });
return vendors;
}
Then the route: /api/vendors.js
export default async (req, res) => {
const response = await getVendors();
return res.json(response);
};
And this is the makeRequest function:
const makeRequest = async (url) => {
// Get Auth Header
const axiosConfig = await getHeader();
// Intercept Rate Limited API Errors & Retry
api.interceptors.response.use(
function (response) {
return response;
},
async function (error) {
await new Promise(function (res) {
setTimeout(function () {
res();
}, 2000);
});
const originalRequest = error.config;
if (error.response.status === 401 && !originalRequest._retry) {
token[n] = null;
originalRequest._retry = true;
const refreshedHeader = await getHeader();
api.defaults.headers = refreshedHeader;
originalRequest.headers = refreshedHeader;
return Promise.resolve(api(originalRequest));
}
return Promise.reject(error);
}
);
// Call paginated API and return number of requests needed.
const getQueryCount = await api.get(url, axiosConfig).catch((error) => {
throw error;
});
const totalItems = parseInt(getQueryCount.data['#attributes'].count);
const queriesNeeded = Math.ceil(totalItems / 100);
// Loop through paginated API and push data to dataToReturn
const dataToReturn = [];
for (let i = 0; i < queriesNeeded; i++) {
setTimeout(async () => {
try {
const res = await api.get(`${url}?offset=${i * 100}`, axiosConfig);
console.log(`adding items ${i * 100} through ${(i + 1) * 100}`);
const { data } = res;
const arrayName = Object.keys(data)[1];
const selectedData = await data[arrayName];
selectedData.map((item) => {
dataToReturn.push(item);
});
if (i + 1 === queriesNeeded) {
console.log(dataToReturn);
return dataToReturn;
}
} catch (error) {
console.error(error);
}
}, 3000 * i);
}
};
The issue that I'm having is that getVendors() is returned before makeRequest() has finished getting the data.
Looks like your issue stems from your use of setTimeout. You're trying to return the data from inside the setTimeout call, and this won't work for a few reasons. So in this answer, I'll go over why I think it's not working as well as a potential solution for you.
setTimeout and the event loop
Take a look at this code snippet, what do you think will happen?
console.log('start')
setTimeout(() => console.log('timeout'), 1000)
console.log('end')
When you use setTimeout, the inner code is pulled out of the current event loop to run later. That's why end is logged before the timeout.
So when you use setTimeout to return the data, the function has already ended before the code inside the timeout even starts.
If you're new to the event loop, here's a really great talk: https://youtu.be/cCOL7MC4Pl0
returning inside setTimeout
However, there's another fundamental problem here. And it's that data returned inside of the setTimeout is the return value of the setTimeout function, not your parent function. Try running this, what do you think will happen?
const foo = () => {
setTimeout(() => {
return 'foo timeout'
}, 1000)
}
const bar = () => {
setTimeout(() => {
return 'bar timeout'
}, 1000)
return 'bar'
}
console.log(foo())
console.log(bar())
This is a result of a) the event loop mentioned above, and b) inside of the setTimeout, you're creating a new function with a new scope.
The solution
If you really need the setTimeout at the end, use a Promise. With a Promise, you can use the resolve parameter to resolve the outer promise from within the setTimeout.
const foo = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('foo'), 1000)
})
}
const wrapper = async () => {
const returnedValue = await foo()
console.log(returnedValue)
}
wrapper()
Quick note
Since you're calling the setTimeout inside of an async function, you will likely want to move the setTimeout into it's own function. Otherwise, you are returning a nested promise.
// don't do this
const foo = async () => {
return new Promise((resolve) => resolve(true))
}
// because then the result is a promise
const result = await foo()
const trueResult = await result()

How to throw error after subscription timeout?

I'm simply trying to display in the view ( when for example user's connection is too slow and results won't come from subscription) after 30 seconds.
I'm surely missing something because even when results all arrive, the console timeout error message is still appearing.
This is what I tried:
searchInfo() {
this.isLoading = true;
this.info= [];
const source = this.infoService.searchInfo('help');
source.pipe(
timeout(30000),
takeUntil(this.onDestroy$)
).subscribe((infoTable: infoTableContent) => {
this.info.push(infoTable);
this.isLoading = false;
}),
(err) => {
this.isLoading = false;
console.log(err, 'Sorry, ...') //not working
}
I want to be able to display a message like "Sorry, took too long to retrieve data" after 30 seconds of trying to subscribe.
Thank you all in advance
You have an extra ) right after your next callback in the subscribe.
Therefore, your error callback is ignored.
.subscribe((infoTable: infoTableContent) => {
this.info.push(infoTable);
this.isLoading = false;
}), // <-- This paranthesis closes the subscribe method
The following works as expected
.subscribe(
(infoTable: infoTableContent) => {
this.info.push(infoTable);
this.isLoading = false;
},
(err) => {
this.isLoading = false;
console.log(err, 'Sorry, ...');
}
);
When I run this with Mocked Services it runs just fine for me:
const mockService = {
searchInfo: msg => NEVER.pipe(startWith({key: msg}))
};
const onDestroy$ = timer(5000);
let isLoading = true;
const info = [];
mockService.searchInfo('help').pipe(
timeout(1000),
takeUntil(onDestroy$)
).subscribe({
next: (mockTable: any) => {
info.push(mockTable);
isLoading = false;
},
error: err => {
isLoading = false;
console.log('Sorry, ...', err);
}
});
I suspect your bug might be due to miss-formatting (brackets in the wrong place etc)

Execute function if and only if a certain amount of time has elapsed since subscription with RxJS

I am making an API call through an Observable. If this API call takes more than 200ms, I would like to show a loading screen (by assigning 'true' to my 'loading' variable), otherwise I don't want to show anything, in order to avoid a blink on screen.
Is there an RxJS operator capable of doing this ?
this.apiService.get(`/api/someEndpoint`)
// I hope for something like
.triggerIfAtLeastThisAmountOfTimeHasElapsed(200, () => {
this.loading = true;
})
.subscribe(response => {
// Process the response
this.loading = false;
});
There are many ways to do this so you can use for example this:
const api = this.apiService.get(`/api/someEndpoint`);
const loading = Observable
.timer(1000)
.do(() => loading = true) // show loading
.ignoreElements(); // or `filter(() => false)
Observable.merge(api, loading)
.take(1)
.subscribe(() => loading = false);
Along the same lines of Martin's response, this is an example that should simulate your context
const obs1 = Observable.timer(200).take(1);
const apiSubject = new Subject<string>();
const apiObs = apiSubject.asObservable();
const apiExecutionElapsed = 1000;
const obs3 = Observable.merge(obs1, apiObs);
let loading = undefined;
obs3.subscribe(
data => {
console.log(data);
if (loading === undefined && data === 0) {
loading = true;
} else {
loading = false;
}
console.log('loading', loading);
},
console.error,
() => {
loading = false;
console.log('loading', loading);
}
)
setTimeout(() => {
apiSubject.next('I am the result of the API');
apiSubject.complete()}, apiExecutionElapsed)
If the execution of the api (apiExecutionElapsed) takes longer than the configured timer (200 ms in this case) you see the loading flag to become first true and then false. Otherwise it remains always false.

Promise for-loop with Ajax requests

I'm creating a native JavaScript application which does loads of Ajax calls in the same process at a certain time. Instead of going through a normal for loop and do them all at once, I thought I'd wait for the Ajax call to complete and then do the next one.
With the help of Stackoverflow I've managed to do this like the following:
function ajaxCall(index) {
return new Promise(function(resolve) {
// Ajax request {
resolve();
// }
});
}
Promise.resolve(0).then(function loop(i) {
if (i < length) {
return ajaxCall(i).thenReturn(i + 1).then(loop);
}
}).then(function() {
// for loop complete
});
Promise.prototype.thenReturn = function(value) {
return this.then(function() {
return value;
});
};
However, this is too slow for me. I want to be able to keep a var which keeps track of how many Ajax calls are currently in the process so that I can limit the amount.
I've tried multiple things, but I keep running into ininite loops or not getting to the desired result.
How can I do multiple, limited by a specific number, async Ajax calls using the Promise for loop?
Sounds like you want a version of Promise.all that takes an array of asynchronous functions rather than promises, and a ceiling on the number of simultaneous operations. Something like:
Promise.allFuncs = (funcs, n) => {
n = Math.min(n, funcs.length);
var results = [];
var doFunc = i => funcs[i]().then(result => {
results[i] = result; // store result at the correct offset
if (n < funcs.length) {
return doFunc(n++);
}
});
// start only n simultaneous chains
return Promise.all(funcs.slice(0, n).map((p, i) => doFunc(i)))
.then(() => results); // final result
};
// --- Example: ---
var log = msg => div.innerHTML += msg + "<br>";
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var example = ['a','b','c','d','e','f'].map(name =>
() => (log("started "+name),wait(2000).then(() => (log("ended "+ name), name))));
Promise.allFuncs(example, 2)
.then(results => log("Results: "+ results))
.catch(log);
<div id="div"></div>

Resources