Combine/Concat TestCafe Selector for reuse - testing

I use the page object pattern and it's often the case, that I like to address an element inside another selector. So I would just like to reuse an already defined CSS-selector. My current workaround is just to define a plain string.
Is it possible to do something like:
const list = (listName) => Selector('#list' + listName)
const item = (itemName) => Selector(list, '#item-' + itemName)
It would be even enough to get the CSS-Selector text of the Selector like:
const list = (listName) => Selector('#list' + listName)
const item = (itemName) => Selector(`${list.selectorText} #item-${itemName}`)
Or that the find-method supports a Selector:
const list = (listName) => Selector('#list' + listName)
const item = (itemName) => list.find(Selector('#item-' + itemName))

Yes, you surely may combine selector strings, since it is a general css selector string.
Note: testscafe testing api actions such as Click (https://devexpress.github.io/testcafe/documentation/test-api/actions/click.html) accept strings as arguments. If you don't need to use the Selector class methods such as withText, nth etc., you can write a test as follows:
const listSelector = (listName) => `#list${listName}`;
//somewhere in test:
await t.click(listSelector('My List Name');

Related

How to Add Google Map Places "textSearch()" api in React Native

I have a tourist App, and i have made a tab i.e ATM onPress it will display List of ATM's in nearby defined Radius.
I have tried Different modules which available in NPM website, but didn't figured it out the use case for my specific result
The problem i have facing is when reading '''textSearch()''' documentation's is uses
'''google.maps.places.PlacesService(map);''' from where this .object should i import
if any relevant sources welcome
const handleSearch = async () => {
const url = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?";
const fulllocation = `location=${location.latitude},${location.longitude}`;
const radius = `&radius=2000`;
const type = `&keyword=ATM`;
const key = "&key=xyz";
const restaurantSearchUrl = url + fulllocation + radius + type + key;
await fetch(restaurantSearchUrl)
.then((response) => response.json())
.then((result) => setPlaceData(result))
.catch((e) => console.log(e));
I have used placeSearch to get result - but it needed to click few times to get the result, on first click it shows empty array then on second click it shows result with "status" : "ZERO_RESULTS" then after few clicks it shows the results.
I have tried to use async/await to get result, but it gives empty array or ZERO_RESULTS. How can i fix that
Every array of object is a result after click

How to increase the range of an array taken from an API query

I have a function for when clicking a button increase the contents of a list.
Content is removed from an API by the following code:
const [data, setData] = useState();
const [maxRange, setMaxRange] = useState(2);
const getAPIinfo = ()=>{
GetEvents(maxRange, 0).then((response) => response.json())
.then(result_events => {
const events = result_events;
setData({events:events});
}).catch(e => setData({events:events}));
}
And my function is this:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
what I'm not able to do is update the maxRange value of the API query to increase the list...
this function should be heavily refactored:
const buttonLoadMore = ({data,type}) =>{
setMaxRange(prevRange => prevRange + 4);
data = data.slice(0,maxRange);
}
when you use maxRange here, you are setting new state, while the function itself ir running, the state is not instantly updated, buttonLoadMore is a function in a particular time. it cannot get new maxRange instantly, while running buttonLoadMore does that make sense? Also you cannot update data state just like a regular variable by assigning new variable using = operator, you should refactor this function to something like this:
const buttonLoadMore = ({data})=> {
const newMaxRange = maxRange + 4;
setMaxRange(newMaxRange);
const newData = {events: [...data.events.slice(0, newMaxRange)]};
setData({...newData})
}
also you will get bug here. since your getAPIinfo is setting data state to an object {events: events}. I took the liberty and tried refactoring it here.
There is also a bug in your getAPIinfo in line }).catch(e => setData({events:events})); the events variable you declared in .then function cannot be reached here. It is simply out of scope. unless you know that .catch resolves into data, you will get an error in this line.
take a look at this example here:
const promiseFunction = ()=>{
return new Promise<string>((resolve)=>resolve('i like coca cola'))
}
const getter = () => {
promiseFunction()
.then(response => {
const thenVariable = response;
console.log(thenVariable) // i like coca cola
})
.catch(error=>{
console.log(thenVariable) // Error:Cannot find name 'thenVariable'.
})
}
as you can see .catch() is in different scope than .then() will not be available outside so events cannot be reached by .catch function.
Usually you would use catch for error handling. Maybe show a line on screen, that error has accoured, and data cannot be fetched at this time. etc. There's a very good book that explains all these concepts in detail here: https://github.com/getify/You-Dont-Know-JS
I would strongly recommend for you to switch to typescript because your code is crawling with bugs that should be easily avoided just by type checking, and adding eslint configurations.

How to map some data to the data fetched from API before showing it on screen?

I am new to React Native. I am trying to make an app of my own to try out the different things that I learnt and also get to know new things and one such thing that I came across and is giving me a hard time is the following issue:
I have an API which gives me certain data about an item. The properties of the item are listed in the API like "sizeofitem" , "nameofitem" or "itemacategory". Now there are multiple items for different items and not all properties are present in each item. What I was trying to achieve is to somehow map these properties in the following manner:
If let's say "sizeofitem", should become "Size of Item", "nameofitem" should become "Name of Item". Now these properties are different of all the items so for example, sizeofitem might be in one item detail list but might not be in another, but I have all the properties that are can be there. Can someone help me how to do this?
Till now I have the following:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
setItemDtl(response.data.obj.itemutils);
}catch(err){
console.log(err);
}
}
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
console.log(arr);
useEffect(() => {
getItemInfo(id);
})
arr array has whatever the properties where listed for the item in the API i.e. [sizeofitem, nameofitem , etc].
I want an array to have [Size of Item, Name of Item , etc].
Basically just, to sum up, I want to rename the list of properties that can be there for when whatever property comes up is then stored in an array with the mapped string I have given, so for example if an item has 'sizeofitem : 50', I want it to be stored as "Size of item" so that I can show that on the screen. And there are like a total of 5 properties that can exist for an item so I can code it somewhere maybe like sizeofitem : 'Size Of Item' so that when sizeofitem property is top be shown on the screen I can use this and show Size of Item on the screen.
try this:
const [itemDtl , setItemDtl] = useState([]);
const getItemInfo = async (id) => {
try{
const response = await api.get(`myAPI/${id}`);
let arr = [];
for(let i in itemDtl){
arr.push(itemDtl[i].util_type);
}
setItemDtl(arr);
}catch(err){
console.log(err);
}
}
useEffect(() => {
getItemInfo(id);
})

react-admin exclude "record" from the redux-form

I have an issue when I send SimpleForm (edit) request with react-admin.
The request includes more parameters than I have in the form's fields.
For example I have form:
<Edit {...props}>
<SimpleForm>
<TextInput source="title_new" />
<TextInput source="age_new" />
</SimpleForm>
</Edit>
It includes only 2 fields but when I click "save" the request includes more fields.
I understood that those fields are coming from the GET_ONE request which fill the data from the DB.
GET_ONE:
{
title: 'title',
title_new: 'title-new',
age: 'age',
age_new: 'age-new',
}
The update request UPDATE:
{
title: 'title',
title_new: 'title-new',
age: 'age',
age_new: 'age-new',
}
I expect that the UPDATE will include only the forms fields (title_new and age_new) without the title and age fields that come from "record".
Those fields make me a lot of trouble on the API side and I want to avoid/exclude them from all the forms, basically I want to send only the form inputs with the SimpleForm inputs only.
Few solutions I have in mind:
1. "Altering the Form Values before Submitting" here
2. Manipulate the request in the restProvider
Both solutions are not good for me because I have many forms like that and the restProvider code will look bad. Also I don't want to "alter" any form I build.
PLEASE ADVICE.
This is the way react-admin works. If you want the UPDATE dataProvider verb to post only the changes fields (and probably send a PATCH rather than a POST), you have to do it in the dataProvider.
I'm not sure that the provider will look bad after the change: all you have to do is to alter the UPDATE verb. By default, it looks like (for the simple rest provider):
case UPDATE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'PUT';
options.body = JSON.stringify(params.data);
break;
You just need to update it like so:
case UPDATE:
url = `${apiUrl}/${resource}/${params.id}`;
options.method = 'PATCH';
- options.body = JSON.stringify(params.data);
+ options.body = JSON.stringify(diff(params.data, param.previousData));
break;
Where diff can be written as:
const diff = (previous, current) => lodash.pickBy(current, (v, k) => previous[k] !== v);
To selectively send just altered fields, use a diff function like this:
// in diff.js
import { transform, isEqual, isObject } from 'lodash';
/**
* Deep diff between two object, using lodash
* #param {Object} object Object compared
* #param {Object} base Object to compare with
* #return {Object} Return a new object who represent the diff
*/
const diff = (object, base) => {
return transform(object, (result, value, key) => {
if (!isEqual(value, base[key])) {
result[key] =
isObject(value) && isObject(base[key]) ? diff(value, base[key]) : value;
}
});
};
export default diff;
then:
// in dataProvider.js
update: (resource, params) =>
httpClient(`${apiUrl}/${resource}/${params.id}`, {
method: 'PATCH',
body: JSON.stringify(diff(params.data, params.previousData)),
}).then(({ json }) => ({ data: json })),

Remove / clear all entities from a contentState

I'm trying to remove all entities from a contentState.
What would be the prefered way to do that?
Not sure what the canonical way is, but I've been able to do it by using Modifier.applyEntity(): https://draftjs.org/docs/api-reference-modifier#applyentity.
You basically need to loop through all the blocks, and use that method on the entire range of text in each block. So something like this:
import {Modifier, SelectionState} from 'draft-js';
function clearEntityRanges(contentState){
contentState.getBlockMap().forEach(block => {
const blockKey = block.getKey();
const blockText = block.getText();
// You need to create a selection for entire length of text in the block
const selection = SelectionState.createEmpty(blockKey);
const updatedSelection = selection.merge({
//anchorOffset is the start of the block
anchorOffset: 0,
// focustOffset is the end
focusOffset: blockText.length
})
Modifier.applyEntity(contentState, updatedSelection, null);
});
return contentState
}