CypressError: Timed out retrying: cy.its() errored because the property: 'store' does not exist on your subject - reactjs

I am getting the following error on cypress when testing my application:
CypressError: Timed out retrying: cy.its() errored because the property: 'store' does not exist on your subject.
Here is the test line which is broken:
cy.window().its('store').invoke('getState').then((state) => {
expect(state.token).to.equal(tokenResponseMock.token);
});
In my code everything is working fine, i got all data on the store, no issues, but do not pass on the test due to the 'store' not found. I wonder why I am getting store error if it's working as expected. I have no clue what is going on. Does anybody can give me a light on how to solve this error?
Login.js - the function which dispatch to the store
async function handleClick() {
const { dispatchToken } = props;
const tokenInfo = await fetchToken();
localStorage.setItem('token', JSON.stringify(tokenInfo.token));
dispatchToken(tokenInfo.token);
history.push('/game');
}

Just found out what was going on.
When using cypress for testing, you need to add the following code to the store file in order to work:
if (window.Cypress) {
window.store = store;
}

Don't mix up localStorage and React store, they are two different things.
localStorage
cy.get(somthing).click()
.should(() => {
const token = localStorage.getItem('token')
expect(token).to.equal(tokenResponseMock.token)
})
React store
In React app, add store to Cypress object
// init store
if (window.Cypress) {
window.Cypress.store = store // add to Cypress not window in case of conflict
}
In test
cy.get(somthing).click()
.should(() => {
Cypress.store.getState().then(state => {
expect(state.token).to.equal(tokenResponseMock.token)
})
})

Related

How do I separate api / async request logic from react components when using recoil

So at the moment I am having to put my request / api logic directly into my components because what I need to do a lot of the time is set state based on the response I get from the back end.
Below is a function that I have on my settings page that I use to save the settings to recoil after the user hits save on the form:
const setUserConfig = useSetRecoilState(userAtoms.userConfig);
const submitSettings = async (values: UserConfigInterface) => {
try {
const { data: {data} } = await updateUser(values);
setUserConfig({
...data
});
} catch (error) {
console.log('settings form error: ', error);
}
}
This works perfectly...I just dont want the function in my component as most of my components are getting way bigger than they need to be.
I have tried making a separate file to do this but I can only use the recoil hooks (in this instance useSetRecoilState) inside of components and it just complains when I try and do this outside of a react component.
I have tried implementing this with recoils selector and selectorFamily functions but it gets kind of complicated. Here is how I have tried it inside of a file that has atoms / selectors only:
export const languageProgress = atom<LanguageProgress>({
key: "LanguageProgress",
default: {
level: 1,
xp: 0,
max_xp: 0
}
})
export const languageProgressUpdate = selectorFamily<LanguageProgress>({
key: "LanguageProgress",
get: () => async () => {
try {
const { data: { data } } = await getLanguageProgress();
return data;
} catch (error) {
console.log('get language progress error');
}
},
set: (params:object) => async ({set}) => {
try {
const { data: { data } } = await updateLanguageProgress(params);
set(languageProgress, {
level: data.level,
xp: data.xp,
max_xp: data.max_xp
});
} catch (error) {
console.log('language progress update error: ', error);
}
}
});
What I want to do here is get the values I need from the back end and display it in the front which I can do in the selector function get but now I have 2 points of truth for this...my languageProgress atom will initially be incorrect as its not getting anything from the database so I have to use useGetRevoilValue on the languageProgressUpdate selector I have made but then when I want to update I am updating the atom and not the actual value.
I cannot find a good example anywhere that does what I am trying to here (very suprisingly as I would have thought it is quite a common way to do things...get data from back end and set it in state.) and I can't figure out a way to do it without doing it in the component (as in the first example). Ideally I would like something like the first example but outside of a component because that solution is super simple and works for me.
So I dont know if this is the best answer but it does work and ultimately what I wanted to do was seperate the logic from the screen component.
The answer in my situation is a bit long winded but this is what I used to solve the problem: https://medium.com/geekculture/crud-with-recoiljs-and-remote-api-e36581b77168
Essentially the answer is to put all the logic into a hook and get state from the api and set it there.
get data from back end and set it in state
You may be looking for useRecoilValueLoadable:
"This hook is intended to be used for reading the value of asynchronous selectors. This hook will subscribe the component to the given state."
Here's a quick demonstration of how I've previously used it. To quickly summarise: you pass useRecoilValueLoadable a selector (that you've defined somewhere outside the logic of the component), that selector grabs the data from your API, and that all gets fed back via useRecoilValueLoadable as an array of 1) the current state of the value returned, and 2) the content of that API call.
Note: in this example I'm passing an array of values to the selector each of which makes a separate API call.
App.js
const { state, contents } = useRecoilValueLoadable(myQuery(arr));
if (state.hasValue && contents.length) {
// `map` over the contents
}
selector.js
import { selectorFamily } from 'recoil';
export const myQuery = selectorFamily({
key: 'myQuery',
get: arr => async () => {
const promises = arr.map(async item => {
try {
const response = await fetch(`/endpoint/${item.id}`);
if (response.ok) return response.json();
throw Error('API request not fulfilled');
} catch (err) {
console.log(err);
}
});
const items = await Promise.all(promises);
return items;
}
});

Geting TypeError: index.saveObject is not a function for algolia search

const searchClient: any = algoliasearch(
'', //Application ID
'' //API Key
);
const index: any = searchClient.initIndex('hackernews_search');
const [newsData, setNewsData] = useState<News[]>([]);
const [currentPage, setCurrentPage] = useState<number>(0);
useEffect(() => {
const getCurrentPageData = async () => {
let currentNewsData : News[] = [];
const response = await axios.get(`https://hn.algolia.com/api/v1/search_by_date?tags=story&hitsPerPage=30&page=${currentPage}`)
response.data.hits.map((result:any) => {
let newsObject :News = {
title: result.title,
url: result.url,
points: result.points,
}
currentNewsData.push(newsObject)
})
setNewsData(currentNewsData);
index.saveObjects(currentNewsData);
}
getCurrentPageData();
},[currentPage])
I am fetching the data from an api and then saving it in algolia in order to use the Instant Search Component provided by algolia.
For the above code, I am getting this error.
TypeError: index.saveObject is not a function
at getCurrentPageData (Home.tsx:37:1)
I am not sure what is going wrong, as this is the method followed in the docs - https://www.algolia.com/doc/api-reference/api-methods/save-objects/#examples.
Any suggestion would be appreciated
I was able to solve this error, it was simple import error
Solution
I was getting this error as I had imported algoliasearch from the algoliasearch/lite package which only provides search methods, that is why I was getting an error when using the saveObjects method as it was a write method. Importing the entire package i.e. import algoliasearch from algoliasearch solved the problem
If you log index before that saveObjects call, is it uninitialized or can you see the methods in the console? This may help you track down what exactly the index variable is and why that method doesn't exist.
console.log(index)
One thing to keep in mind if you want to save objects, you'll need a write key. I only mention this because it looks like you are using React and your write keys would be exposed in your application if anyone did some digging. If it's a non-public app it should be okay but just wanted to bring this up.
Also, if you are just looking to browse that Index, you could always use it directly using the public credentials:
App Id: UJ5WYC0L7X
API Key: 8ece23f8eb07cd25d40262a1764599b1
Index Name: Item_production

React & Sanity - Fetch Error: invalid JSON response body

I have been following a tutorial on youtube to build a twitter-clone website. However, when trying to fetch tweets from Sanity I am getting this error. I even git cloned the repo of the person that made the tutorial and I'm still getting the same error. This leads me to believe it is an issue with my VS code and not the code itself, if anyone has any suggestions that would be great thank you.
// fetchTweets.ts
export const fetchTweets = async () => {
const res = await fetch(`http://localhost:3001/api/getTweets`)
const data = await res?.json()
const tweets: Tweet[] = data.tweets
console.log('fetching', tweets)
return tweets
}
// index.tsx
export const getServerSideProps: GetServerSideProps = async (context) => {
const tweets: Tweet[] = await fetchTweets()
return {
props: {
tweets,
},
}
}
That error is typically caused by trying to render HTML as JSONā€”and particularly, when JSON is expected but instead an API returns an error page. Is your server definitely running on port 3001? Fetching from a non-existent server is likely consistent with this error.

TypeError: Cannot read property 'useGETForQueries' of undefined - GraphQL Hooks

I'm using graphQL hooks for my project, I'm running into an issue with testing, it renders this error:
TypeError: Cannot read property 'useGETForQueries' of undefined
It fails in this line of code that belongs to a search component:
const [searchManual, { loading: searchLoading, data, error }] = useManualQuery(SEARCH_QUERY);
By default useGETForQuereies is false, but not sure why this error comes up when running the test. The project runs ok, it's just the test case. I have explicitly added the boolean for that variable in the GraphQlClient
graphql interceptor JS file
const gqlAxios = axios.create()
gqlAxios.interceptors.response.use(
function (response) {
if(response.status === 401 || response.status === 403 )
window.location.href = config.api.LOGIN_URL;
return response;
},
function (error) {
console.log("responseError", error);
return Promise.reject(error);
}
)
gqlAxios.interceptors.request.use(
function (config) {
config = {
...config,
headers: {
...config.headers,
'session': getCookieValue()
}
}
return config;
}
)
const fixed = config.api.APP_GQL_HOST;
const url = fixed + 'graphql';
export const client = new GraphQLClient({ url, fetch: buildAxiosFetch(gqlAxios), useGETForQueries: false });
the client constant will be exported to index.js and used as a value in the ClientContext.Provider.
const Wrapped = (
<ClientContext.Provider value={client}>
<App />
</ClientContext.Provider>
)
ReactDOM.render(
Wrapped, document.getElementById('root')
);
Could this be an error from jsdom, this is the actual console error from running a test:
console.error node_modules/jsdom/lib/jsdom/virtual-console.js:29
Error: Uncaught [TypeError: Cannot read property 'useGETForQueries' of undefined]
If so, how can this be resolved? I'm just trying to solve pass a simple test case:
it('Expect component in the document', () => {
const { getByTestId } = render(<Search />);
const component = getByTestId("test");
expect(component).toBeInTheDocument();
});
Is there an extra configuration I need to be adding? I would like to use msw , mock service worker, but I'm stuck with this error and can not move forward. Has anyone experience this error with graphql hooks? Also what's your experience with this library? The pro is that is lightweight but it seems to be not user friendly when it comes to testing. Any comparison with Apollo. I appreciate your insights with this issue.

Why is my AsyncStorage.getItem().then undefined in React Native?

I am using AsyncStorage to get information. I previously stored but for some strange reason it is saying Cannot read property 'then' of undefined even though I use this exact same AsyncStorage function in the function below and it works just fine. Does anyone know why this isn't working here?
AsyncStorage.getItem(STORAGE_KEY_PRODUCT_SEARCH_CACHE).then((results) => {
const searchCache = JSON.parse(results);
let containedMatches = [];
if (searchCache) {
containedMatches = searchCache.filter((searchCacheItem, i) => {
return searchCacheItem.includes(searchTerm);
});
}
dispatch({
type: types.HANDLE_LOAD_PRODUCTS_SUCCESS,
containedMatches
}
);
});
Here is a video I made of this. Sorry you can hear my co-workers in the background so you'll have to mute it.
https://youtu.be/qwhywbD74l8
Use await to wait the async operation to finish like this:
async setData() {
var resultCache= await AsyncStorage.getItem(STORAGE_KEY_PRODUCT_SEARCH_CACHE);
}
you can do your further functional work using this code.

Resources