In our tool we create a url which quiet a few parameters with values. And I want Cypress to check the contents of this url.
The example url is:
http://someUrl.com/sap/?action=create&type=sw¬ifno=70432&repby=TRL&repres=ABC&geo=017&startloc=12345¬iftp=2021-06-15T08:06:42.379Z&scen=1.0&refno=1234567&awsrt=-&vrst=&sbst=&objtp=art&objtxt=&objfc=&tel=084123456&prio=4 Niet urgent&priost=&prioen=&wbi=&facts=&bgeb=AB-CD&bequi=
I have stored the url in 'href' variable but how i can now check all the attr and their values? I really don't have a clue.
I'd parse it into an object and then use .wrap(), .its(), and .should() commands:
const url = "http://someUrl.com/sap/?action=create&type=sw¬ifno=70432&repby=TRL&repres=ABC&geo=017&startloc=12345¬iftp=2021-06-15T08:06:42.379Z&scen=1.0&refno=1234567&awsrt=-&vrst=&sbst=&objtp=art&objtxt=&objfc=&tel=084123456&prio=4 Niet urgent&priost=&prioen=&wbi=&facts=&bgeb=AB-CD&bequi=";
const arr = url.split('/?')[1].split('&');
const paramObj = {};
arr.forEach(param => {
const [ key, value ] = param.split('=');
paramObj[key] = value;
});
cy
.wrap(paramObj)
.its('tel')
.should('eq', '084123456');
or if you want to assert more properties:
cy
.wrap(paramObj)
.then(obj => {
expect(obj.notifno).to.eq('70432');
expect(obj.tel).to.eq('084123456');
});
My colleague came with this solution, now the Cucumber line included:
Given('I expect the parameter {string} of the SAP-link on dossier {string} to equal {string}',(parameter:string, dossier:string, value:string) => {
cy.get('selector').each(ele => {
if(ele.text().trim().indexOf(dossier) == 0) {
cy.get('selector')
.parents('selector')
.find('selector').should('have.attr', 'href').then((sapUrl: JQuery<HTMLElement>) => {
cy.log(sapUrl.toString());
const queryParamString: string = sapUrl.toString().split('?')[1];
cy.log(queryParamString);
const queryParamArray: string[] = queryParamString.split('&');
var params: {} = {};
queryParamArray.forEach((keyValueString: string) => {
const currentParamArray: string[] = keyValueString.split('=');
params[currentParamArray[0]] = currentParamArray[1];
});
// Actual param check
expect(params[parameter]).to.equal(value);
});
}
});
});
Related
I've got 3 commands i am calling from the front end, 2 of them work perfectly, the third does not.
The issue lies with the function tournament_search
main.rs:
fn main() {
tauri::Builder::default()
.manage(ApiKey {key: Default::default()})
.invoke_handler(tauri::generate_handler![set_api_key, check_connection, tournament_search])
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
#[tauri::command]
fn set_api_key(key: String , state: State<ApiKey>){
let mut api_key = state.key.lock().unwrap();
*api_key = key;
}
#[tauri::command]
async fn check_connection(api_key: State<'_, ApiKey>) -> Result<bool, ()> {
let key = api_key.key.lock().unwrap().clone();
let res = Client::new().get(API_URL).bearer_auth(key).send().await.unwrap().text().await.unwrap();
let json: Value = serde_json::from_str(res.as_str()).unwrap();
match json["success"].as_bool() {
Some(_x) => Ok(false),
None => Ok(true)
}
}
#[tauri::command]
async fn tournament_search(search_string: String, api_key: State<'_, ApiKey>) -> Result<&str, ()> {
println!("test: {}", search_string);
let key = api_key.key.lock().unwrap().clone();
let mut query: String = String::new();
query.push_str("query($name:String){tournaments(query:{filter:{name:$name}}){nodes{name,slug,id}}},{$name:");
query.push_str(search_string.as_str());
query.push_str("}");
let res = Client::new().get(API_URL).bearer_auth(key).body(query).send().await.unwrap().text().await.unwrap();
println!("{}", res);
Ok("")
}
index.js:
const { invoke } = window.__TAURI__.tauri
window.addEventListener("load", (ev) => {
let test = document.getElementById("test");
let apiKey = document.getElementById("apiKey");
let tournamentSearch = document.getElementById("tournamentSearch");
let tourneyList = document.getElementById("tourneyList");
apiKey.addEventListener("input", (ev) => {
invoke("set_api_key", {key: apiKey.value});
invoke("check_connection").then((res) => {
if(res){
tournamentSearch.disabled = false;
}else{
tournamentSearch.disabled = true;
}
});
});
tournamentSearch.addEventListener("input", (ev) => {
test.innerText = "e";
invoke('tournament_search', {search_string: tournamentSearch.value}).then((res) => {
test.innerText = res;
});
});
});
Already looked for zero width characters, whether the event get's called in js etc. The issue is just that the function is not called.
You'd only see an error message by adding a .catch() to the invoke call.
Anyawy, the issue here is that Tauri converts command arguments to camelCase on the rust side (to match the JS default) so it would be { searchString: tournamentSearch.value } instead.
If you'd prefer snake_case instead, you can tell Tauri to use that for arguments by changing the command like this:
#[tauri::command(rename_all = "snake_case")]
Let's say I have an object that contains the values:
const pathParams = { schoolId :'12', classroomId: 'j3'}
and I have a path: school/:schoolId/classroom/:classroomId
I would like to extract: 'schoolId' and 'classroomId' from the path so that I can later replace them with their corresponding values from the pathParams object. Rather than iterating from the pathParam object keys, I want to do the other way around, check what keys the path needs and then check their values in the object.
I currently have this:
function addPathParams(path, paramsMap) {
const pathParamsRegex = /(:[a-zA-Z]+)/g;
const params = path.match(pathParamsRegex); // eg. school/:schoolId/classroom/:classroomId -> [':schoolId', ':classroomId']
console.log('--params--', params)
let url = path;
params.forEach((param) => {
const paramKey = param.substring(1); // remove ':'
url = addPathParam(url, param, paramsMap[paramKey]);
});
return url;
}
function addPathParam(path, param, value) {
return path.replace(`${param}`, value);
}
Does this look robust enough to you? Is there any other case I should be considering?
Here's a couple of tests I did:
Result:
I am unable to pass any object or arrays to IPCRenderer.
I am getting error when passing an object or array through ipcs, I have even tried to send by converting to string using JSON.stringify but it converts it into empty object string.
I have tried passing a fileList, an array of object & even an object nothing passes. only string or handwritten objects are working.
I've read that it uses Structured Clone Algorithm and fileList & Array is allowed by this algorithm
ERROR:
electron/js2c/renderer_init.js:74 Uncaught Error: An object could not be cloned.
at EventEmitter.i.send.i.send (electron/js2c/renderer_init.js:74)
at HTMLButtonElement.compressNow (ImageHandling.js:190)
I have tried many possible solutions but nothing worked
code:
const compressNow = () => {
ipcRenderer.send("image:compress", filess). ///This is the error.
// filess is a variable containing an array of selected files from an HTML input.
}
Now i have tried to send filess as JSON.stringify, i tried to send it as an object but nothing works unless i manually write a dummy object or string.
Here's My Github Repo for this project
Files With ErrorJ:-
ImageHandling.js
const fs = window.require('fs');
const {ipcRenderer} = require("electron")
const SELECT = (target) => document.querySelector(`${target}`)
var filess = []
const imgUploadInput = SELECT("#imgUploadInput")
const warning = SELECT("#warning")
const setImgBase64 = (imgEl, file) => {
const ReadAbleFile = fs.readFileSync(file.path).toString('base64')
let src = "data:image/png;base64," + ReadAbleFile
imgEl.setAttribute("src", src)
// El.src=src
// console.log(`FIXED IMAGE # ${imgEl} `,ReadAbleFile)
}
const renderImages = () => {
const files = filess && Array.from(filess)
const defaultImg = SELECT("#defaultImg")
const addImgBtn = SELECT("#addImgBtn")
imgUploadInput.disabled = true;
let numOfFiles = files.length
if (numOfFiles < 1) {
SELECT("#compressContainer").style.visibility = "hidden"
} else {
SELECT("#compressContainer").style.visibility = "visible"
}
if (numOfFiles > 49) {
warning.innerHTML = `<b style="font-weight:bold; color:red;">WARNING:</b><br/>
<span style="padding:10px;text-align:left">
Your processor/computer may not be able to process ${numOfFiles} Images at once, We recommend selecting less than 50 Images at once for better performance.
</span>
`;
}
addImgBtn.innerHTML = `LOADING.....`
if (defaultImg && numOfFiles > 0)
defaultImg.remove();
setTimeout(() => {
if (files && numOfFiles > 0) {
let displayImages = SELECT("#displayImages")
displayImages.innerHTML = ""
files ?. forEach((file, i) => {
let divEl = document.createElement("div")
let imgEl = document.createElement("img")
imgEl.src = file.path
imgEl.id = `PNG_${i}_${
btoa(file.name)
}`
divEl.className = "displayedImg"
imgEl.setAttribute("onclick", `document.getElementById('ImageView').src=this.src`)
const a = document.createElement("a")
a.appendChild(imgEl)
a.setAttribute("href", `#ViewImage`)
a.className = "perfundo__link"
divEl.appendChild(a)
divEl.className = "displayedImg perfundo"
displayImages.appendChild(divEl)
if (i == files.length - 1) {
warning.innerHTML = "";
updateNumOfImages();
}
imgEl.onerror = () => setImgBase64(imgEl, file) // converting to base64 only on error, this make performance better and help us avoid freezes. (before this i was converting all images to base64 wither errored or not that was making computer freez)
})
addImgBtn.innerHTML = "+ Add MORE"
imgUploadInput.disabled = false
findDuplicate()
}
}, 0);
}
const hasDuplicate=()=>{
let FileNames = [... filess.map(f => f.name)]
let duplicateFiles = filess.filter((file, i) => FileNames.indexOf(file.name) !== i)
return {FileNames,duplicateFiles,FilesLength:duplicateFiles.length}
}
const findDuplicate = (forceAlert = false) => {
if (filess && filess.length) {
let {FileNames} = hasDuplicate()
let {duplicateFiles} = hasDuplicate()
if (duplicateFiles.length) { // alert(``)
let countFiles = duplicateFiles.length
let fileStr = countFiles > 1 ? "files" : "file"
console.log("result from removeDup=> ", filess, " \n dupfilename=> ", FileNames, " \n dupfiles=> ", duplicateFiles)
let shouldNotAsk = localStorage.getItem("NeverAsk")
let msg = `You've selected ${
countFiles > 1 ? countFiles : "a"
} duplicate ${fileStr}`
let duplInner = `<span style='color:red'>
<b>WARNING</b>
<p style="margin:0px;line-height:1"> ${msg} . <button onClick="findDuplicate(true)" type="button" class="btn btn-danger btn-rounded btn-sm">REMOVE DUPLICATE</button></p>
</span>`
if (! shouldNotAsk || forceAlert) {
swal("DUPLICATE FILES DETECTED", `${msg} , Would you like to un-select duplicate ${fileStr} having same name?`, {
icon: 'warning',
dangerMode: true,
buttons: {
cancel: true,
...forceAlert ? {} : {
never: "Never Ask"
},
confirm: "Yes !"
}
}).then((Yes) => {
if (Yes == "never") {
localStorage.setItem("NeverAsk", true)
warning.innerHTML=duplInner
} else if (Yes) {
removeDuplicates()
}
})
} else {
warning.innerHTML=duplInner
}
}
}
}
const removeDuplicates = (showAlert=true) => {
let {FileNames} = hasDuplicate()
let {duplicateFiles} = hasDuplicate()
let duplicateFileNames = duplicateFiles.map(f => f.name)
let uniqueFiles = filess.filter((file) => ! duplicateFileNames.includes(file.name))
filess = [
... uniqueFiles,
... duplicateFiles
]
console.log("result from removeDup=> ", filess, " \n filename=> ", FileNames, " \n dupfiles=> ", duplicateFiles, "\n unique fil=> ", uniqueFiles)
renderImages()
if(showAlert){
swal("DONE", "Removed Duplicate Files ", {icon: 'success'}).then(() =>{
renderImages()
setTimeout(() => {
let hasDuplicateFiles = hasDuplicate().FilesLength
if(hasDuplicate){//Re-check if any duplicate files left after the current removal process.
removeDuplicates(false) //Re-run the function to remove remaining. false will make sure that this alert does not show and the loop does not continue.
}
renderImages()
}, 10);
})
}
}
const updateNumOfImages = () => {
warning.innerHTML = `
<span style="text-align:left; color:green">
Selected ${
filess.length
} Image(s)
</span>
`;
}
const compressNow = () => {
ipcRenderer.send("image:compress", filess)
// alert("WOW")
}
CompressBtn.addEventListener("click", compressNow)
imgUploadInput.addEventListener("change", (e) => {
let SelectedFiles = e.target.files
if (SelectedFiles && SelectedFiles.length) {
filess = [
... filess,
... SelectedFiles
]
renderImages()
}
})
// SELECT("#imgUploadInput").addEventListener("drop",(e)=>console.log("DROP=> ",e))
UPDATE:-
I REPLACED THIS:
const compressNow = () => {
ipcRenderer.send("image:compress",filess)
}
INTO THIS:-
const compressNow = () => {
filess.forEach(file => {
ipcRenderer.send("image:compress",file.path )
});
}
Now here i am sending the files one by one via forEach, actually its sending string "file path" so thats how its working i am still confused why do i have to do this? why can't i send whole fileList i assume that this loop method is a bad practice because it will consume more CPU its one additional loop however it won't be necessary if i am able to send the whole array.
See Behavior Changed: Sending non-JS objects over IPC now throws an exception. DOM objects etc. are not serializable. Electron 9.0 (and newer) throws "object could not be cloned" error when unserializable objects are sent.
In your code, File and FileList are DOM objects.
If you want to avoid using forEach, try this code:
const compressNow = () => {
const paths = filess.map(f => f.path);
ipcRenderer.send("image:compress", paths);
}
Can refer to electron github issue tracker for this issue (already closed)
Error: An object could not be cloned #26338
Docs for ipcRenderer.send(channel, ...args)
This issue mainly comes when we have non-cloneable values like function within an object in data we are sending via IPC, to avoid that we can use JSON.stringify() before sending and JSON.parse() later on receiving end, but doing so will cause to lose some of the values eg:
const obj = {
x :10,
foo : ()=>{
console.log('This is non-cloneable value')
}
}
console.log(JSON.stringify(obj))
output:{"x":10}
Instead of sending the images save them in fs and send the path
The simplest thing that could possibly work is to use lodash cloneDeep()
ipcMain.handle('stuffgetList', async () => {
return _.cloneDeep(await stuffStore.getList())
})
in the windows JSON.stringify()
in the main.js JSON.parse()
Remove :compress from. .send method and try
I have a map of routes with react router path as keys, eg:
const routes = [
{
page: "mySettings",
label: "pages.mySettings",
path: "/professionels/mes-reglages.html",
exact: true
},
{
page: "viewUser",
label: "pages.viewUser",
path: "/users/:id/view.html",
exact: true
}
];
I want from a location retrieved with useHistory().location.pathname, to match all the path that match the key in react-router terms, eg:
(pathname) => get(routesMap, "/professionels/mes-reglages.html") => should match routesMap.get('/professionels/mes-reglages.html')
(pathname) => get(routesMap, "/users/11/view.html") => should match routesMap.get('/users/:id/view.html')
and all react-router paths so this should work too:
(pathname) => get(routesMap, "/users/11/settings/10/items/24/view.html") => should match routesMap.get('/users/:userId/settings/:settingId/items/:id/view.html')
I have started here, any idea how I can do that with a regexp?
https://codesandbox.io/s/youthful-wing-fjgm1
Based on your comments i adjusted the code a bit and wrote a rapper function for your lookup.
The following rules you have to watch out for when creating the urls:
The last id always gets replaced by {id}
All other ids get replaced by url part to id without plural and "Id" attached ("/users/111" -> "/users/{userId}")
This would be the function:
const getRouteFromPath = (map, url) => {
if (url.match(/\/\d+\//g).length > 1) {
let allowedUrlPart = getAllowedIdQualifier(map);
let urlParts = url.match(/(?<=\/)\w+(?=\/\d+\/)/g);
urlParts.forEach(val => {
if (!allowedUrlPart.includes(val)) {
urlParts = urlParts.slice(urlParts.indexOf(val), 1);
}
});
urlParts.forEach((val, key, arr) => {
if (key === arr.length - 1) {
let regex = new RegExp("(?<=/" + val + "/)\\d+", "g");
let replacement = ":id";
url = url.replace(regex, replacement);
} else {
let regex = new RegExp("(?<=/" + val + "/)\\d+", "g");
let replacement = ":" + val.slice(0, -1) + "Id";
url = url.replace(regex, replacement);
}
});
return map.get(url);
} else {
url = url.replace(/\/\d+\//g, "/:id/");
return map.get(url);
}
};
const getAllowedIdQualifier = map => {
let allowedQualifiers = [];
map.forEach(val => {
let allowed = val.path.match(/(?<=\/)\w+(?=\/:)/g);
allowed.forEach(e => {
if (!allowedQualifiers.includes(e)) {
allowedQualifiers.push(e);
}
});
});
return allowedQualifiers;
};
export default getRouteFromPath;
As parameter you pass in the url to match as first parameter and the map of routes as the second paramter and call the function getRoute() instead of the direct map.get() call you where using before.
Here is the example with the urls adjusted to follow the rules, since you need some rules to be able to apply RegEx.
EDIT:
I adjusted the script, so that it reads the map first and determines the allowed paths which accept a id and then check the possible ids from an actual url against it.
https://codesandbox.io/s/kind-moon-9oyj9?fontsize=14&hidenavigation=1&theme=dark
I've been talking to my dev duck for the past few hours and cannot for the life of me rubber ducky debug this code. Basically, it returns [object Object] for a sub object in JSON. The juicy part is that if I copy and paste the logged raw JSON text before its parsed, and then parse it, it parses fine.
Heres the aforementioned code, with the values being fed in:
/*
We are feeding in:
{
'src' : './template.html',
'format' : 'plain',
'input' : {
'noun' : 'World'
},
'replace' : 'templateExampleSkeleton'
}
*/
// Locals.
let tmpScripts:Array<string> = [];
let tmpScriptIds:Array<string> = [];
let tmpStrings:Array<string> = [];
let tmpStringIds:Array<string> = [];
// Replace scripts with placeholder IDs, and store their contents in a temporary location.
// They will be restored later, because they would cause issues with the JSON parser.
// This isn't used in this case but is used in general.
args = args.replace(/js{{(.|\s)*}}/g, (substring:string) => {
let tmpScriptId:string = this.#utils.genRandomId(false, tmpScriptIds);
tmpScripts.push(substring.replace('js{{','').replace('}}',''));
return `%%{{${tmpScriptId}}}%%`;
})
// Replace 's with "s.
.replace(/'/gm, '"')
// Replace whitespace.
.replace(/(\s|\n|\t|\r)*/gm, '')
// Restore the strings using their IDs.
.replace(/##{{.{32}}}##/gm, (substring:string) => {
let tmpStringValue:string = '';
tmpStringIds.forEach((id:string, i:number) => {
if (substring.includes(id)) tmpStringValue = tmpStrings[i];
});
return tmpStringValue;
});
// Add curly brackets so that the JSON parser doesn't yell.
args = '{' + args + '}';
console.log(args); // ==> {"src":"./template.html","format":"plain","input":{"noun":"World"},"replace":"templateExampleSkeleton"}
// Parse the arguments as JSON.
let argsJson = JSON.parse(args);
// Using the new object, iterate through its keys in order to
// restore the scripts that were removed for parsing as JSON.
// This isn't(?) used in this case but is used in general.
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
// Log the object for debug.
console.log(argsJson); // ==> Object { src: "./template.html", format: "plain", input: "[object Object]", replace: "templateExampleSkeleton" }
Any help is very appreciated :^)
To close this question with an answer, as pointed out by #LexWebb:
Object.keys(argsJson).forEach((argKey, i) => {
argsJson[argKey] = argsJson[argKey].toString().replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
});
Should be:
Object.keys(argsJson).forEach((argKey, i) => {
if (typeof argsJson[argKey] === 'string') {
argsJson[argKey] = argsJson[argKey].replace(/%%{{.*}}%%/gm, (substring:string) => {
substring = substring.replace(/%%{{/, '').replace(/}}%%/, '');
let tmpScriptValue:string = '';
tmpScriptIds.forEach((id:string, i:number) => {
if (id === substring) tmpScriptValue = tmpScripts[i];
});
return tmpScriptValue;
});
}
});