How to give two targets in chai expect? - chai

I have following code in my Protractor test ,
async function canSeeLoanPage(): Promise<void> {
const header1 = element(by.tagName('h1')).getText();
const header2 = element(by.css('section h1')).getText();
try {
await expect(header1).to.eventually.equal('Your Name');
} catch (e) {
await expect(header2).to.eventually.equal('Your Name');
}
}
rather checking in a try catch, what is the correct way to give two targets in expect like this?
await expect(header2,header2).to.eventually.equal('Your Name');

I don't know of a better way than simply using two lines of code:
await expect(header1).to.eventually.equal('Your Name');
await expect(header2).to.eventually.equal('Your Name');
Why is it you need the try catch block?

Related

null is not an object (using async storage)

I'm trying to implement secure store (like async storage) into my testproject and it works. The only thing that doesn't work appears to be my load() useEffect that runs every time I start a new session.
So every time I start the session "null is not an object" appears and throws errors at every part where I'm looking for my list on my home-screen (my state is called goals).
but if I temporarily turn it off, add a goal to my list, and then turn it on - it works fine every time I restart the app
I feel like I need to write a condition in my load() statement but I can't figure out what, I think the app runs the load() and gets stuck at the statements where I use goals before I've added any. Can someone help me here? I've tried if (goals !== null) but it doesn't work.
const [goals, setGoals] = useState([])
const load = async() => {
if (goals !== null) {
try {
const goalsValue = await SecureStore.getItemAsync('Goals');
setGoals(JSON.parse(goalsValue))
} catch (err) {
alert(err)
}
}
}
useEffect(()=> {
load()
},[])
So what is happening is that there's nothing inside goalsValue so when you try parsing it you get an error. To avoid that you should add an if statement that checks if it's empty or not
const [goals, setGoals] = useState([])
const load = async() => {
if (goals !== null) {
try {
const goalsValue = await SecureStore.getItemAsync('Goals');
if(goalsValue) setGoals(JSON.parse(goalsValue))
}catch (err) {alert(err)}
}}
useEffect(()=> {
load()
},[])
Try this and let me know if it works ☺️

Simplest way to merge two useState values

I have a react native hook where trips must be updated every time a createdTrips is added to the state:
const [trips, setTrips] = useState([]);
function fetchCreatedTrips() {
try {
API.graphql(graphqlOperation(onCreateTrip)).subscribe({
next: (result) => {
console.log(result);
const updatedTrips = [...trips, result.value.data.onCreateTrip]
setTrips(updatedTrips)
}
})
} catch (err) { console.log(err) }
}
Now, when i first open the screen, it renders all the trips items of the list.
However with the current code, after i create a trip and go back to that screen, it doesn't currently return all the trips + the newly created one, but only one, that is the newly created one. How can i return all the items of the list? Sorry in advance, i'm a beginner.
While setting the new state on your effect you should include the previous values of the trips. You can use the spread operator to do the same.
const createdTrips = [result.value.data.onCreateTrip];
setTrips([...createdTrips, ...trips]);
or better merge the newly created trip in the updateTrips variable and then set it as the state value,
const udpatedTrips = [...trips,result.value.data.onCreateTrip];
setTrips(udpatedTrips);
The problem in your code is you are firing those two methods at the same time and there can be race conditions, so you see random updates on the screen.
Ideally, you need to bring in some consistency in the API calls and the state update. So first fetchTrips()->then->fetchCreatedTrips(). Try the below code wherein I don't update the state immediately in fetchTrips() but rather pass on the results to fetchCreatedTrips() which completes the API call and updates the state together.
const [trips, setTrips] = useState([]);
useEffect(() => {
fetchTrips();
}, [])
async function fetchTrips() {
try {
const tripData = await API.graphql(graphqlOperation(listTrips));
const trips = tripData.data.listTrips.items
fetchCreatedTrips(trips);
} catch (err) { console.log(err) }
}
function fetchCreatedTrips(fetchedTrips) {
try {
API.graphql(graphqlOperation(onCreateTrip)).subscribe({
next: (result) => {
console.log(result);
const updatedTrips = [...fetchedTrips,...updatedTrips];
setTrips(updatedTrips)
}
})
} catch (err) { console.log(err) }
}
PS: Please handle exceptions correctly.

AsyncStorage functions just producing garbage in react native

So I implemented this code in a file from the react native docs.
class Storage {
//store data in 'key'
store = async (key, data) => {
try {
await AsyncStorage.setItem(key, data);
} catch (error) {
// Error saving data
console.log(error.message);
}
};
retrieve = async (key) => {
try {
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
}
} catch (error) {
// Error retrieving data
console.log(error.message);
}
};
}
And this in other I want to use to actually store and retrieve the variables:
strg.store('test', 'testing');
testing = strg.retrieve('test');
I kept getting an error but then looking it up here I figured out my storage output was an object and not a string as I expected. So I used JSON.stringify(***) and this gibberish came out instead of a "testing".
{"_40":0, "_65":0, "_55":null,"_72":null}
edit: I figure out how to use the console to debug and I found out the 'testing' was inside the promise object that comes out of my function. I read a little about async functions and now I want to know how do I extract the values from the promises?
This happened because you are using AsyncStorage - an asynchronous storage system. You have to wait until it done retrieve data from storage to get the proper data.
I think there are two correct ways to get data from your implementation:
Use async with your container function name & await with your function called
async function getData() {
....
let data = await strg.retrieve('test');
console.log("data", data);
}
or simple use .then():
strg.retrieve('test').then((data) => {
console.log("data", data);
// Handle retrieved data
});
Hope that help. :)
This is how i did it and it works like a charm
import { AsyncStorage } from 'react-native';
module.exports = {
retrieve: async (value) => {
try {
let data = await AsyncStorage.getItem(value);
return data;
} catch (err) {
return err;
}
},
store: async (key, value) => {
try {
// stringify the value since value can only be string.
if (typeof (value) === 'object')
value = JSON.stringify(value)
return await AsyncStorage.setItem(key, value);
} catch (err) {
console.log(err)
return err;
}
}
}
Your store and retrieve functions are asyn so you have to use await until the actual task is complete.
So the code should be like below.
await strg.store('test', 'testing');
const testing = await strg.retrieve('test');
The garbage value is a promise so it will be something like the object you got.
If you return value like this you will retrieve it from outside.
const value = await AsyncStorage.getItem(key);
if (value !== null) {
// We have data!!
console.log(value);
return value;
}

How to Catch Errors from Apollo/GraphQL

I use graphCool as a backend, but there is a bug which won't be fixed any time soon.
Making a delete mutation on File schema will always throw this error (not a big deal):
"GraphQL error: Whoops. Looks like an internal server error. Please contact us from the Console (https://console.graph.cool) or via email (support#graph.cool) and include your Request ID:"
The call works, but I need to access the update function after the call and I can't because of the error.
Two solutions:
1. Catch the graphql error so I can reach update: function, how can I do this?
2. Update the store without using the update: function, how can I do this?
Here is my code:
_deleteImageFile = async () => {
// THIS WORKS, BUT GET AN ERROR
const socialPostId = this.props.socialPost.id
// Tried to wrap the following in try catch without success
await this.props.deleteImageFileMutation({
variables: {
id: this.props.socialPost.image.id
}, //Error hits here "internal service error..."
update: (store) => { //This is never run because of error
console.log('this text will never log')
const userId = localStorage.getItem(GC_USER_ID)
const data = store.readQuery({query: ALL_SOCIAL_POSTS_QUERY,
variables: {
id: userId
}})
// Need to change the store here
store.writeQuery({query: ALL_SOCIAL_POSTS_QUERY, data,
variables: {
id: userId
}}).catch(res => { const errors = res.graphQLErrors; console.log(errors)})
//The catch was an attempt to fix it
}
})
}
Reference Bug: https://github.com/graphcool/framework/issues/434
You could try to pass down another dummy mutation and do that after the deleteImageFileMutation throws.
try {
await this.props.deleteImageFileMutation ...
} catch (e) {
// ...
} finally {
await this.props.dummyMutation ...
}

How to parse data from AsyncStorage to <Text>

I am getting the following error when I get data from AsyncStorage:
Objects are not valid as a React child (found: object with keys {_40, _65, _55, _72})
async function setItem(key, value) {
try {
await AsyncStorage.setItem(key, value);
console.log(key, value);
return value;
} catch (error) {
// Handle errors here
}
}
async function getItem(item) {
try {
const value = await AsyncStorage.getItem(item);;
console.log(value);
return value;
} catch (error) {
// Handle errors here
}
}
setVar = setItem('username', 'Name');
getVar = getItem('username');
I am getting the error when outputting the result to:
<Text>{getVar}</Text>
When I check console I get the required output/everything is correct but I cannot render the text to the screen.
Any help or suggestions would be greatly appreciated.
Thanks.
Cross posting from https://forums.expo.io/t/how-to-parse-data-from-asyncstorage-to-text/3417/4
Ah, your function getItem is giving back a Promise, remember that async keyword automatically makes the function return Promise. Hence the error message, can’t put the Promise object in
Suggest you:
Put the string you’ll use in state:
state = {showThisText:''}
in render: {this.state.showThisText}
Then in async componentDidMount, get the data and do a this.setState({showThisText:YOUR_PULLED_STRING})
try it like this it will work,
setVar = await setItem('username', 'Name');
getVar = await getItem('username');
make both of your calling functions async too.