I have a use case where I need to ensure that after a filter is changed on a page, there are only 2 API calls - no more, no less.
How can I achieve this using cypress ?
Gleb Bahmutov has a blog post about using intercepts, and one of his concepts is counting the number of times a request is matched. You could use that along with a very broad matcher, something like...
const getAliasCount = (alias) => {
// implementation details, use at your own risk
const testRoutes = cy.state('routes')
const aliasRoute = Cypress._.find(testRoutes, { alias })
if (!aliasRoute) {
return
}
return Cypress._.keys(aliasRoute.requests || {}).length
}
it('confirms the number of times an intercept was called', () => {
cy.intercept({ url: /.*/}).as('myIntercept');
// test code
...
cy.wait('myIntercept').then(() => {
const interceptCount = getAliasCount('myIntercept');
cy.wrap(interceptCount).should('deep.equal', 2);
});
});
Related
I am trying to set up a simple endpoint in SvelteKit that reads the input given in a form and returns the result of an SQL query. In my first attempt I used the form actions and wrote the following code in the +page.server.js file:
export const actions = {
default: async (event) => {
let form_input = await event.request.formData();
let query = {
text: "select * from ux_return_shipments($1, $2, $3, $4)",
values: [form_input.get('beg-dep-date') || null, form_input.get('end-dep-date') || null, form_input.get('beg-arr-date') || null, form_input.get('end-arr-date') || null
}
try {
const result = await event.locals.pool.query(query);
return result.rows;
} catch (e) {
console.log(e);
}
}
};
I am now trying to set up the same process using a GET request instead of a POST one but I am having difficulties setting up the endpoint. I tried to replace the above code with this template but it looks like the endpoint is not getting activated since I see no activity server side:
export function GET({ url }) {
console.log(url);
return new Response("Test response");
};
What am I doing wrong? I see that using this code for an API endpoint (+server.js file) works correctly. I also checked the form element and the URL looks correct.
In case someone has the same problem I managed to solve it using this template:
export const load = async (event) => {
return ...
};
Using the load function I was able to set up a get endpoint and pass some data to the frontend using a return.
Currently, I am working on my react-native application using MST as a state management library.
Now I have encountered an issue where the app has a chance to fire 2 similar API calls. The 1st API responded after the 2nd one, which caused the data to be overridden by an outdated response.
In redux-saga, we can use takeLatest to make sure we get the data from the latest request. I am looking for similar function in MST to address the problem.
I have found that there's Axios' cancel token to cancel the API calls, but I want to see is there any way in a more generic async way to solve it.
As far as I know there is no build in features like that in MobX or MST, so you would need to implement it by yourself.
Generic way I usually use to cancel promises is this one (credit to https://wanago.io):
class RaceConditionGuard {
private lastPromise: PromiseLike<unknown> | null = null;
getGuardedPromise<T>(promise: PromiseLike<T>) {
this.lastPromise = promise;
return this.lastPromise.then(this.preventRaceCondition()) as Promise<T>;
}
preventRaceCondition() {
const currentPromise = this.lastPromise;
return (response: unknown) => {
if (this.lastPromise !== currentPromise) {
return new Promise(() => null);
}
return response;
};
}
cancel = () => {
this.lastPromise = null;
};
}
And the usage, assuming you have some class based store, for example:
class SomeStore {
raceConditionGuard = new RaceConditionGuard();
loadItems = () => {
// Previous call will be automatically canceled (it will never resolve actually)
this.raceConditionGuard
// Wrap your async operation
.getGuardedPromise(fetchSomething())
// Handle result somehow
.then(this.handleResult);
};
// Or you can cancel manually
cancelLoading = () => {
this.raceConditionGuard.cancel()
}
// ...
}
I'm trying to perform real-time Named Entity Recognition highlighting in a WYSIWYG editor, which requires me to make a request to my back-end in between each keystroke.
After spending about a week on ProseMirror I gave up on it and decided to try DraftJS. I have searched the repository and docs and haven't found any asynchronous examples using Decorations. (There are some examples with Entities, but they seem like a bad fit for my problem.)
Here is the stripped down Codepen of what I'd like to solve.
It boils down to me wanting to do something like this:
const handleStrategy = (contentBlock, callback, contentState) => {
const text = contentBlock.getText();
let matchArr, start;
while ((matchArr = properNouns.exec(text)) !== null) {
start = matchArr.index;
setTimeout(() => {
// THROWS ERROR: Cannot read property '0' of null
callback(start, start + matchArr[0].length);
}, 200) // to simulate API request
}
};
I expected it to asynchronously call the callback once the timeout resolved but instead matchArr is empty, which just confuses me.
Any help is appreciated!
ok, one possible solution, a example, simple version (may not be 100% solid) :
write a function take editor's string, send it to server, and resolve the data get from server, you need to figure out send the whole editor string or just one word
getServerResult = data => new Promise((resolve, reject) => {
...
fetch(link, {
method: 'POST',
headers: {
...
},
// figure what to send here
body: this.state.editorState.getCurrentContent().getPlainText(),
})
.then(res => resolve(res))
.catch(reject);
});
determine when to call the getServerResult function(i.e when to send string to server and get entity data), from what I understand from your comment, when user hit spacebar key, send the word before to server, this can done by draftjs Key Bindings or react SyntheticEvent. You will need to handle case what if user hit spacebar many times continuously.
function myKeyBindingFn(e: SyntheticKeyboardEvent): string {
if (e.keyCode === 32) {
return 'send-server';
}
return getDefaultKeyBinding(e);
}
async handleKeyCommand(command: string): DraftHandleValue {
if (command === 'send-server') {
// you need to manually add a space char to the editorState
// and get result from server
...
// entity data get from server
const result = await getServerResult()
return 'handled';
}
return 'not-handled';
}
add entity data get from server to specific word using ContentState.createEntity()
async handleKeyCommand(command: string): DraftHandleValue {
if (command === 'send-server') {
// you need to manually add a space char to the editorState
// and get result from server
...
// entity data get from server
const result = await getServerResult()
const newContentState = ContentState.createEntity(
type: 'string',
mutability: ...
data: result
)
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
// you need to figure out the selectionState, selectionState mean add
// the entity data to where
const contentStateWithEntity = Modifier.applyEntity(
newContentState,
selectionState,
entityKey
);
// create a new EditorState and use this.setState()
const newEditorState = EditorState.push(
...
contentState: contentStateWithEntity
)
this.setState({
editorState: newEditorState
})
return 'handled';
}
return 'not-handled';
}
create different decorators find words with specific entity data, and return different style or whatever you need to return
...
const compositeDecorator = new CompositeDecorator([
strategy: findSubjStrategy,
component: HandleSubjSpan,
])
function findSubjStrategy(contentBlock, callback, contentState) {
// search whole editor content find words with subj entity data
// if the word's entity data === 'Subj'
// pass the start index & end index of the word to callback
...
if(...) {
...
callback(startIndex, endIndex);
}
}
// this function handle what if findSubjStrategy() find any word with subj
// entity data
const HandleSubjSpan = (props) => {
// if the word with subj entity data, it font color become red
return <span {...props} style={{ color: 'red' }}>{props.children}</span>;
};
In my react App I have a input element. The search query should be memoized, which means that if the user has previously searched for 'John' and the API has provided me valid results for that query, then next time when the user types 'Joh', there should be suggestion for the user with the previously memoized values(in this case 'John' would be suggested).
I am new to react and am trying caching for the first time.I read a few articles but couldn't implement the desired functionality.
You don't clarify which API you're using nor which stack; the solution would vary somewhat depending on if you are using XHR requests or something over GraphQL.
For an asynchronous XHR request to some backend API, I would do something like the example below.
Query the API for the search term
_queryUserXHR = (searchTxt) => {
jQuery.ajax({
type: "GET",
url: url,
data: searchTxt,
success: (data) => {
this.setState({previousQueries: this.state.previousQueries.concat([searchTxt])
}
});
}
You would run this function whenever you want to do the check against your API. If the API can find the search string you query, then insert that data into a local state array variable (previousQueries in my example).
You can either return the data to be inserted from the database if there are unknowns to your view (e.g database id). Above I just insert the searchTxt which is what we send in to the function based on what the user typed in the input-field. The choice is yours here.
Get suggestions for previously searched terms
I would start by adding an input field that runs a function on the onKeyPress event:
<input type="text" onKeyPress={this._getSuggestions} />
then the function would be something like:
_getSuggestions = (e) => {
let inputValue = e.target.value;
let {previousQueries} = this.state;
let results = [];
previousQueries.forEach((q) => {
if (q.toString().indexOf(inputValue)>-1) {
result.push(a);
}
}
this.setState({suggestions: results});
}
Then you can output this.state.suggestions somewhere and add behavior there. Perhaps some keyboard navigation or something. There are many different ways to implement how the results are displayed and how you would select one.
Note: I haven't tested the code above
I guess you have somewhere a function that queries the server, such as
const queryServer = function(queryString) {
/* access the server */
}
The trick would be to memorize this core function only, so that your UI thinks its actually accessing the server.
In javascript it is very easy to implement your own memorization decorator, but you could use existing ones. For example, lru-memoize looks popular on npm. You use it this way:
const memoize = require('lru-memoize')
const queryServer_memoized = memoize(100)(queryServer)
This code keeps in memory the last 100 request results. Next, in your code, you call queryServer_memoized instead of queryServer.
You can create a memoization function:
const memo = (callback) => {
// We will save the key-value pairs in the following variable. It will be our cache storage
const cache = new Map();
return (...args) => {
// The key will be used to identify the different arguments combination. Same arguments means same key
const key = JSON.stringify(args);
// If the cache storage has the key we are looking for, return the previously stored value
if (cache.has(key)) return cache.get(key);
// If the key is new, call the function (in this case fetch)
const value = callback(...args);
// And save the new key-value pair to the cache
cache.set(key, value);
return value;
};
};
const memoizedFetch = memo(fetch);
This memo function will act like a key-value cache. If the params (in our case the URL) of the function (fetch) are the same, the function will not be executed. Instead, the previous result will be returned.
So you can just use this memoized version memoizedFetch in your useEffect to make sure network request are not repeated for that particular petition.
For example you can do:
// Place this outside your react element
const memoizedFetchJson = memo((...args) => fetch(...args).then(res => res.json()));
useEffect(() => {
memoizedFetchJson(`https://pokeapi.co/api/v2/pokemon/${pokemon}/`)
.then(response => {
setPokemonData(response);
})
.catch(error => {
console.error(error);
});
}, [pokemon]);
Demo integrated in React
I have 2 objects in my state and I call a common API for getting data by passing the id in the request.
Following is my fetch call for the first Section which fires receiveSectionA to update sectionA in my state.
export function fetchSection(sectionCode){
return(dispatch,getState,api)=>{
const endPoint = 'url/?sectionCode='+sectionCode
const method = 'GET'
const isAuth = true
const promise = api(endPoint,method,isAuth)
promise
.then(response =>
response.json().then(json => ({
status:response.status ,
json
})
))
.then(
({ status, json }) => {
if( status >= 200 && status < 300) {
const sectionDictionary = utils.convertSectionsToDictionary(camelizeKeys(json))
dispatch(receiveSectionA(sectionDictionary))
}
if (status >= 400 ) {
//throw error
}
},
err => {
console.log("error"+err);
}
);
}
}
Now I making a same call for sectionB which fires following :-
dispatch(receiveSectionB(sectionDictionary))
Now since the above fetch call is same is there any way I can make this generic. I feel there is too much of code repeat.
I was thinking about switch cases to dispatch different actions based on sectionCode but I have around 20 sections and I think the code will get really complicated.
Is there a better to handle this?
As I understand you are looking for a dynamic function call. You can use eval which may lead to unexpected result. So, use with caution.
Expecting you have two function as receiveSectionA and receiveSectionB;
export function fetchSection(sectionCode){
return(dispatch,getState,api)=>{
....................
))
.then(
({ status, json }) => {
if( status >= 200 && status < 300) {
const sectionDictionary = utils.convertSectionsToDictionary(camelizeKeys(json))
let funToCall = 'receiveSection' + sectionCode + '(sectionDictionary )'; // preparing function to call
dispatch(eval(funToCall)); // Calling Function
}
if (status >= 400 ) {
//throw error
}
},
err => {
console.log("error"+err);
}
);
}
}
Edit:
Read more about eval especially the Security Section-
https://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/
After pondering over this a bit, clearly eval is not a good option.
I thought I would try one of the amazing things that Javascript supports which is passing functions as parameters.
While dispatching an action I pass the receiveActionCreator as callback.
so my fetch would now be:-
export function fetchSection(callback,sectionCode){
return(dispatch,getState,api)=>{
....................
))
.then(
({ status, json }) => {
if( status >= 200 && status < 300) {
const sectionDictionary = utils.convertSectionsToDictionary(camelizeKeys(json))
dispatch(callback(sectionDictionary)); // Calling Function
}
if (status >= 400 ) {
//throw error
}
},
err => {
console.log("error"+err);
}
);
}
}
Now I can pass multiple receivers from different points when I dispatch the action from different sections by avoiding code repetition.
I cannot see much of your code from reducer, but seems to me you have too much logic around actions. Usually action is only intent to modify app state, so think twice about shape of your app state and just provide minimal information in actions that your reducer can compute the state. Also if you have to transform json and check status code for each request why not doing it in api module, if you can't do anything about errors then you should not handle them, handle in api or reflect them in app state. App business logic always live in reducers. After this your action will look better and you will not worry about code repeat. Hope this helps you.
So after searching for this I finally found something which I read in redux-docs .
I used reusing reducer logic to handle repeated actions,state. This basically wraps a parent function around my reducer function to which I pass id as parameter and then create action types from my id dynamically.
Following code should really explain it clearly. Its from the docs itself.
function createCounterWithNamedType(counterName = '') {
return function counter(state = 0, action) {
switch (action.type) {
case `INCREMENT_${counterName}`:
return state + 1;
case `DECREMENT_${counterName}`:
return state - 1;
default:
return state;
}
}
}