I am coding a couple of tests for my app. I want to check that after saving time, it goes to the list.
Now I got this test working by finding a text from the screen, but I want to specify it by id. I mean to check that this 00:01:00 is really in the right place, not just somewhere on the page. I hope guys understand what I mean.
How can I do this?
This is working by finding from screen:
test("Time saved", async () => {
render(<SecondTimer />);
const start = screen.getByText("START");
fireEvent.click(start);
await waitFor(() => screen.getByText(/00:01:00/i), {
timeout: 2000
});
const pause = screen.getByText("PAUSE");
fireEvent.click(pause);
const saveTime = screen.getByText("SAVE TIME");
fireEvent.click(saveTime);
const history = screen.queryByText(/00:01:00/i);
screen.debug(); // printtaa renderinnin consoliin
});
I have DIV like this:
<div data-testid={'aikalista'} className={'tulostaulu'}>
<ul>
{this.state.aikoja === false && ('No times my friend!')}
{this.state.aikoja === true && (<div>{this.state.history.map((item, index) => <li key={index}>{item}</li>)}</div>)}
</ul>
This is what I've tried with different variations, but no luck.
test("Time saved v2", async () => {
render(<SecondTimer />);
const start = screen.getByText("START");
fireEvent.click(start);
await waitFor(() => screen.getByText(/00:01:00/i), {
timeout: 2000
});
const pause = screen.getByText("PAUSE");
fireEvent.click(pause);
const saveTime = screen.getByText("SAVE TIME");
fireEvent.click(saveTime);
const aikalista = screen.getByTestId('aikalista');
expect(getByTestId('aikalista').textContent).toEqual(/00:01:00/i)
screen.debug(); // printtaa renderinnin consoliin
});
getByText has this signature.
getByText(
container: HTMLElement, // if you're using `screen`, then skip this argument
text: TextMatch,
options?: {
selector?: string = '*',
exact?: boolean = true,
ignore?: string|boolean = 'script, style',
normalizer?: NormalizerFn,
}): HTMLElement
You can you use this selector property inside options object to select the element. So you can write something like this.
screen.getByText(/00:01:00/i, {selector: "#someId"})
You can check if the element has an id you want after you get it by text also.
const element = screen.getByText(/00:01:00/i);
expect(element).toHaveAttribute('id', "someId");
Related
I'm following this tutorial and made a few changes to typescript for learning purposes but got stuck when creating a filter function from react context script.
I have a working function called getCampaigns where it maps all the object from the blockchain like below:
const getCampaigns = useCallback(async () => {
const signer = accountProvider?.getSigner();
const contractWithSigner = contract?.connect(signer);
const campaigns = await contractWithSigner?.getCampaigns();
const parsedCampaigns = campaigns.map((campaign, i) => ({
owner: campaign.owner,
title: campaign.title,
description: campaign.description,
target: ethers.utils.formatEther(campaign.target.toString()),
deadline: campaign.deadline.toNumber(),
amountCollected: ethers.utils.formatEther(
campaign.amountCollected.toString()
),
image: campaign.image,
pId: i,
}));
return parsedCampaigns;
}, [contract, accountProvider]);
This is working as it should and manage to see the content like below:
[{…}]
0:
amountCollected:"0.0"
deadline:1673049600000
description: "I want to build a Robot"
image:"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAA
owner:"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
pId:0
target:"3.0"
title:"Build a Robot"
As my new function, I wanted to filter from the getCampaigns function only to display all of the owner's post and display it on a Profile page like below:
const getUserCampaigns = async () => {
const allCampaigns = await getCampaigns();
const filteredCampaigns = allCampaigns.filter(
campaign => campaign.owner === account
);
return filteredCampaigns;
};
So when I console.log filteredCampaigns, it doesnt show any result. Is there anything that I missed here? The typeof account is string and it is working if I put it like this
const filteredCampaigns = allCampaigns.filter(
campaign => campaign.owner === "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
);
Update:
So far I have been playing around with the syntax and console.log the following:
const filteredCampaigns = allCampaigns.filter(campaign => {
console.log(campaign.owner);
return campaign.owner === account;
});
it's managed to fetch the same data and the typeof campaign.owner is in fact a string (same as typeof account). But when I run it like this
const filteredCampaigns = allCampaigns.filter(campaign => {
console.log(campaign.owner === account.toString());
return campaign.owner === account;
});
It's still come out as false
It is working if I hard coded like this
console.log(campaign.owner === "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")
filteredCampaign is empty, because the content of account doesn't match any content of campaign.owner.
Check the content of account.
allCampaign.filter(elementOfArray => condition)
return element only if condition is true.
The logic of your getUserCampaign, looks right for what you want to do.
Not sure if this is the case, but may have sense, to have a field/global var/state where you keep all your campaigns.
In this way when you want to filter, you can do something like
const filteredCampaign = (account: string) => {
return allCampaigns.filter(campaign => campaign.owner === account);
}
filteredCampaign is not anymore async call, because doesn't have to await and receive the
account
I am trying to pass a key for navigation, which specifies to show the query/page after the current query call.
useform.ts
...
...
export const useupdateSurveyForm = () => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateSurveyForm,
onSuccess: (data) => {
queryClient.invalidateQueries(["searched-public-survey"]);
},
});
};
here added "invalidateQueries(["searched-public-survey"]" directly then the code is working properly, but I want to make this dynamic. like,
queryClient.invalidateQueries(navigationqueryKey);
For that I made some changes
plans.ts
...
...
const {
mutate: updateArchiveSurveyStatus,
navigationqueryKey: "searched-public-survey",
} = useupdateSurveyForm();
...
...
pass "navigationKey: "searched-public-survey", but it shows an error
Property 'searched-public-survey' does not exist on type 'UseMutationResult<any, unknown, SurveyUpdatePayload, unknown>'.
Give me some solution to fix this problem.
I dont know if it is correct to pass the value for the query that way.
The statement from the plain.ts is just the return value. I did not found anything in the docs which would lead to putting in the navigation query key.
If I get you correctly I think what you want to do would look like:
export const useupdateSurveyForm = (key: string) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateSurveyForm,
onSuccess: (data) => {
queryClient.invalidateQueries([key]);
},
});
};
And the plans.ts would then look like:
const {
mutate: updateArchiveSurveyStatus,
} = useupdateSurveyForm("searched-public-survey");
Maybe that helps. :-)
I tried to create a interactable map following this example here: https://docs.mapbox.com/mapbox-gl-js/example/cluster/
In my componentDidMount (where I create a mapboxgl) I implemented clickable markers, when clicked on the markers a popup appears which displays various informations.
After the click I want to call a second function (fetch) to get more data on that specific marker: this.props.getData(id);
I then want to display these data in the same popup as the other information.
My problem is that this.props.testdata is empty on the first click. If I double-click on the marker, the data appear. So my guess is that my component does not notice the change of the state/prop and therefore does not update?
How do I do that or what am I missing?
Map.js
this.map.on('click', 'unclustered-point', (e) => {
const coordinates = e.features[0].geometry.coordinates.slice();
const id = e.features[0].properties.id;
const infos = e.features[0].properties.infos;
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
if (id == null) {
console.log("Missing id, cant get informations")
return;
}
this.props.getData(id);
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(
`
Id: ${id}
<br>
Infos: ${infos}
<br>
<br>
Testdata: ${this.props.testdata}
`
)
.addTo(this.map);
});
this.map.on('mouseenter', 'clusters', () => {
this.map.getCanvas().style.cursor = 'pointer';
});
this.map.on('mouseleave', 'clusters', () => {
this.map.getCanvas().style.cursor = '';
});
});
App.js (getData function):
getData = (id) => {
if (id== null) {
console.log("Missing id")
return;
}
const {mapCenter, startDate, endDate} = this.state;
const neo4j = require('neo4j-driver')
const driver = neo4j.driver('bolt://xxx', neo4j.auth.basic("xx", "xx-xx"))
const session = driver.session()
session
.run('Here goes a neo4j cypher statment',{id: id})
.then((results)=> {
const data= [];
results.records.forEach((record) => data.push([record.get("r"), record.get("n"), record.get("b")]))
this.setState({
data
});
session.close()
driver.close()
}).catch(e => {
console.log(e)
session.close();
});
};
I am not familiar with neo4j, but it is apparent that getData(id) fetches data from a server. This is going to be an asynchronous operation, so you should add a state property to maybe show a spinner while data is being fetched?
Regarding testdata not being available, I do not see the code where it is being set.
Maybe your setState code should be:
this.setState({
testdata: data
});
//If your data prop is testdata.
As per the current setState, data property of your component state would be set with server response.
Updates:
Temporary fix for async server call:
You can change following methods and try if it fixes your issue:
this.map.on('click', 'unclustered-point', async (e) => {
// ...previous code
await this.props.getData(id);
// This forces the following code to execute synchronously. Basically it should wait for your API call to be complete
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(
`
Id: ${id}
<br>
Infos: ${infos}
<br>
<br>
Testdata: ${this.props.testdata}
`
)
.addTo(this.map);
});
this.map.on('mouseenter', 'clusters', () => {
this.map.getCanvas().style.cursor = 'pointer';
});
this.map.on('mouseleave', 'clusters', () => {
this.map.getCanvas().style.cursor = '';
});
});
getData = (id) => {
//... previous code
// we return a promise to use await in the onClick handler
return session
.run('Here goes a neo4j cypher statment',{id: id})
.then((results)=> {
const data= [];
results.records.forEach((record) => data.push([record.get("r"), record.get("n"), record.get("b")]))
this.setState({
data
});
session.close()
driver.close()
}).catch(e => {
console.log(e)
session.close();
});
}
If you are still facing an issue, please create a sample app and share.
I have not yet managed to fix the original problem.
However, I have found another solution:
In my Map.js I'm calling the this.props.testdata in th UI like this:
<div className="sidebar">
info: {JSON.stringify(this.props.testdata)}
</div>
I'm trying to reset my state by calling the APi to get the latest data. The API call returns an array of object.
export const launchesState = atom<Launch[]>({
key: 'launchesStateLatest',
default: selector<Launch[]>({
key: 'launchesStateInit',
get: async () => {
const response = await getLaunches();
return response.data.map(launch => ({ ...launch, isSelected: false }));
},
}),
});
I'm using a selectorFamily to select each object when the list is rendered.
export const launchState = selectorFamily<Launch | undefined, string>({
key: 'launchState',
get:
missionName =>
({ get }) => {
return get(launchesState).find(item => item.mission_name === missionName);
},
set:
missionName =>
({ get, set }) => {
const currentState = get(launchesState);
const index = currentState.findIndex(item => item.mission_name === missionName);
set(
launchesState,
produce(currentState, draft => {
draft[index].isSelected = !currentState[index].isSelected;
}),
);
},
});
The UI contains a checkbox for each item in the array and when click it uses the set function in the selectorFamily ti update the launchesState.
I'd like to refresh the data using:
useRecoilRefresher_UNSTABLE(launchesState)
Which works ok if the data has never been altered, or is reset using useResetRecoilState(launchesState), but if I have clicked any of the checkboxes and altered the state then the state isn't refreshed.
Can someone help me understand why this is happening, is it a bug or is this happening for a reason?
Here the addToCart function. i want to call this funtion and navigate to another page.
addToCart = () => {
const {product, quantity, variation, meta_data} = this.state;
const {dispatch} = this.props;
let check = true;
// Check select variations
if (product.get('type') === productType.VARIABLE) {
const attributeProduct = product
.get('attributes')
.filter(attr => attr.get('variation'));
if (!meta_data.size || attributeProduct.size !== meta_data.size) {
check = false;
showMessage({
message: 'Please select variations',
type: 'danger',
});
}
}
if (check) {
dispatch(
addToCart(
{
product_id: product.get('id'),
quantity,
variation,
product,
meta_data,
},
() => this.setState({isAddToCart: true}),
),
);
}
};
And Render function
<FooterProduct
isAddToCart={isAddToCart}
onPressAddCart={this.addToCart}
onPressBuyNow={
this.addToCart // function one
() => navigation.navigate(homeTabs.cart) // function two
}
/>
how can I run both? actually I try to create a buy now button.
thank you.
Create a custom method that calls both the methods like below
const combinedMethod = () => {
this.addToCart();
navigation.navigate(homeTabs.cart);
}
And use the above method like
<FooterProduct
isAddToCart={isAddToCart}
onPressAddCart={this.addToCart}
onPressBuyNow={combinedMethod}
/>
Try this one:
onPressBuyNow = () => {
this.addToCart(); // function one
navigation.navigate(homeTabs.cart); // function two
}
The answer would be either this or create a wrapper function like other folks suggest.
Edit
Modified the answer after Michael Mishin's comment.