I've been trying to write a bot to autocomplete some forms on a website, by copy+pasting the script into the Chrome console. (Nothing illegal.) However, the problem is that this website is written in React, which means that the controlled components they use for forms interfere with simple form.value changes. If I try to fill a form using something akin to form.value = answer, I still need to do a manual keypress on the form to have it work, which isn't suited for my needs of automation.
What I have tried so far:
- Filling in the form.value and firing a keypress/keydown/keyup afterwards.
- Filling in the form.value minus one letter and firing a keypress afterwards, corresponding to the letter that was missed out.
For some strange reason afterwards, as well, the enter key doesn't work to submit until I do the manual keypress.
Can anyone help me out? Thanks!
Better dirty way for filling form fields
I use this when doing dirty browser testing of forms
Adapted from Here
(()=>{
const inputTypes = [
window.HTMLInputElement,
window.HTMLSelectElement,
window.HTMLTextAreaElement
];
const triggerInputChange = (selector,value)=>{
const node = document.querySelector(selector);
// only process the change on elements we know have a value setter in their constructor
if (inputTypes.indexOf(node.__proto__.constructor) > -1) {
const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
let event = new Event('input',{
bubbles: true
});
if(node.__proto__.constructor === window.HTMLSelectElement){
event = new Event('change',{
bubbles: true
});
}
setValue.call(node, value);
node.dispatchEvent(event);
}
}
const formFields = [
['company', 'Shorts & Company'],
['first_name', 'McFirsty'],
['last_name', 'McLasty'],
['address1', '123 N. In The Woods'],
['city', 'Trunkborns'],
['state', 'WA'],
['zip', '55555']
];
formFields.forEach(field=>triggerInputChange(field[0], field[1]));
}
)()
Addressing the specific question
document.querySelector('input').focus();
document.execCommand('insertText', false, 'Some Text For the Input');
Or if you want to replace the text every time
document.querySelector('input').select();
document.execCommand('insertText', false, 'Some Text For the Input');
I have a chrome script dev tools -> sources -> scripts that I use when doing dirty tests of forms
(()=>{
const fillText = (selector, value) => {
document.querySelector(selector).select();
document.execCommand('insertText', false, value);
}
const formFields = [
['[data-ref-name="company"]', 'My Company'],
['[data-ref-name="first_name"]', 'Styks']
]
formFields.forEach(field => fillText(field[0], field[1]));
}
)()
Related
I have implemented a form that has a FormArray. In the FormArray, one control needs to have a unique value throughout the array. The unique() validator from RxWeb is working fine normally but the issue is working when I am pushing values in the FormArray manually it is not triggering the unique() validator. An example method mentioned below which is called on a button click:
abc() {
let skillsArray = <FormArray>this.jobFormGroup.controls.skills;
skillsArray.removeAt(0);
const arr = [
{ skillName: 'A' },{ skillName: 'A' },
];
arr.forEach(item => skillsArray.push(this.formBuilder.group(item)))
}
None of the field is highlighed as red.
Highlighted in red when I enter data from UI.
You can use this Stackblitz example for playing around with the issue. Please click on the "Trigger Issue" button to create the issue shown in Image 1.
Because your function abc() does not add the controls with the unique validator
const arr = [
{ skillName:['A',RxwebValidators.unique()] },{ skillName:
['A',RxwebValidators.unique()] },
];
I have a form where you can add/remove groups and input items, I was able to get the groups of input field working, but I'm having trouble with the items input within the respected groups:
I created a code sand box here: https://codesandbox.io/s/twilight-cache-4ipv6?file=/src/Form.jsx
If you click on Add Items + button, and type in the item fields, the value duplicates to all the fields.
Also, sometimes I feel like the x button doesn't work, and will only remove the last item or something, I believe this is "controlled component?"
In addition, I want to ask if there's a better method on what I'm doing? It seems like there's a lot of complexities in the code I'm trying to write up. I feel like I'm writing too much of the set state hooks.
I think we don't need that fields state.
And we can update Add Handlers like this
const handleAddGroup = i => {
const newGroup = [...group];
newGroup.push({
id: null,
cat: "",
items: [
{
name: "",
value: ""
}
]
});
setGroups(newGroup);
};
const handleAddField = i => {
setGroups(state => {
const stateCopy = JSON.parse(JSON.stringify(state));
stateCopy[i].items.push({
name: "",
value: ""
});
return stateCopy;
});
};
https://codesandbox.io/s/cool-frog-2yrlt
I have a method called populateProvidedValuesForNewMandate that looks like this
exports.populateProvidedValuesForNewMandate = (team, assignee, disputeValue, lawField,
subjectOfDispute, party, fileReference, costUnit, clientUnit, sideEffect, comment) => {
const teamInput = element(by.css('div#team input'));
const assigneeInput = element(by.css('div#assignee input'));
const disputeValueInput = element(by.id('dispute_value'));
const lawFieldInput = element(by.css('div#law_field input'));
const subjectOfDisputeInput = element(by.id('subject_of_dispute'));
const partyInput = element(by.id('party'));
const fileReferenceInput = element(by.id('file_reference'));
const costUnitInput = element(by.css('div#cost_unit input'));
const clientUnitInput = element(by.id('client_unit'));
const sideEffectInput = element(by.css('div#side_effect input'));
const mandateComment = element(by.id('mandate_comment'));
// TODO: Figure out how to choose these dynamically as well
// relevantCase, risReportRelevant, economicRelevance, activePassive
const relevantCaseInput = element(by.css(".relevant_case input[value='no']"));
const riskReportRelevantInput = element(by.css(".risk_report_relevant input[value='no']"));
const economicRelevanceInput = element(by.css("label[for='economic_relevance']"));
const activePassiveInput = element(by.css(".active_passive input[value='passive']"));
teamInput.sendKeys(team);
assigneeInput.sendKeys(assignee);
disputeValueInput.sendKeys(disputeValue);
lawFieldInput.sendKeys(lawField);
subjectOfDisputeInput.sendKeys(subjectOfDispute);
partyInput.sendKeys(party);
fileReferenceInput.sendKeys(fileReference);
costUnitInput.sendKeys(costUnit);
clientUnitInput.sendKeys(clientUnit);
sideEffectInput.sendKeys(sideEffect);
mandateComment.sendKeys(comment);
// TODO: Figure out how to choose these dynamically as well
// relevantCase, risReportRelevant, economicRelevance, activePassive
browser.actions().mouseMove(relevantCaseInput).doubleClick().perform();
browser.actions().mouseMove(riskReportRelevantInput).click().perform();
browser.actions().mouseMove(economicRelevanceInput).click().perform();
browser.actions().mouseMove(activePassiveInput).click().perform();
};
and here is an example of its use case
values.populateProvidedValuesForNewMandate(texts.DISPUTE_VALUE, texts.PARTY, texts.CLIENT_UNIT,
texts.SIDE_EFFECT, texts.COMMENT);
The method fills out the specified values that lie within a file called texts.js into the appropriate fields. The problem is that I get the error message: 'each key must be a number of string; got undefined' meaning that this method doesn't work because I have to send the keys for each specified variable in the method.
I really want to avoid sending empty strings for this method (especially because it won't work, I've tried it out -> I get a error from the app itself, not protractor/selenium).
How can I turn this method into one that only considers the specified variables in the test cases.
Also as you can see from my comment, I am trying to figure out how to do this for the checkbox and radio buttons as well. If anyone has a hint, I'd really appreciate it
Honestly, only you can answer the question. Because there are hundreds of ways to fo this, and some may work better than another. So to us it's silly to make guesses which way is the best for you. So I'll give one example and hopefully you can take it from here
One way is to make the method accept an object and check if a property has been passed
function fillForm(obj) {
if (obj.hasOwnProperty('team')) teamInput.sendKeys(team);
if (obj.hasOwnProperty('assignee')) assigneeInput.sendKeys(assignee);
if (obj.hasOwnProperty('disputeValue')) disputeValueInput.sendKeys(disputeValue);
// ...
}
and then call it
fillForm({
assignee: texts.ASIGNEE,
disputeValue: texts.DISPUTE_VALUE
})
so it will skip sending keys to team field
Update: scroll to see my solution, can it be improved?
So I have this issue, I am building a word translator thats translates english to 'doggo', I have built this in vanilla JS but would like to do it React.
My object comes from firebase like this
dictionary = [
0: {
name: "paws",
paws: ["stumps", "toes beans"]
}
1: {
name: "fur",
fur: ["floof"]
}
2: {
name: "what"
what: ["wut"]
}
]
I then convert it to this format for easier access:
dictionary = {
what : ["wut"],
paws : ["stumps", "toe beans"],
fur : ["floof"]
}
Then, I have two text-area inputs one of which takes input and I would like the other one to output the corresponding translation. Currently I am just logging it to the console.
This works fine to output the array of the corresponding word, next I have another variable which I call 'levelOfDerp' which is basically a number between 0 - 2 (set to 0 by default) which I can throw on the end of the console.log() as follows to correspond to the word within the array that gets output.
dictionary.map(item => {
console.log(item[evt.target.value][levelOfDerp]);
});
When I do this I get a "TypeError: Cannot read property '0' of undefined". I am trying to figure out how to get past this error and perform the translation in real-time as the user types.
Here is the code from the vanilla js which performs the translation on a click event and everything at once. Not what I am trying to achieve here but I added it for clarity.
function convertText(event) {
event.preventDefault();
let text = inputForm.value.toLowerCase().trim();
let array = text.split(/,?\s+/);
array.forEach(word => {
if (dictionary[word] === undefined) {
outputForm.innerHTML += `${word} `;
noTranslationArr.push(word);
} else {
let output = dictionary[word][levelOfDerp];
if (output === undefined) {
output = dictionary[word][1];
if (output === undefined) {
output = dictionary[word][0];
}
}
outputForm.innerHTML += `${output} `;
hashtagArr.push(output);
}
});
addData(noTranslationArr);
}
Also here is a link to the translator in vanilla js to get a better idea of the project https://darrencarlin.github.io/DoggoSpk/
Solution, but could be better..
I found a solution but I just feel this code is going against the reason to use react in the first place.. My main concern is that I am declaring variables to store strings inside of an array within the function (on every keystroke) which I haven't really done in React, I feel this is going against best practice?
translate = evt => {
// Converting the firebase object
const dict = this.state.dictionary;
let dictCopy = Object.assign(
{},
...dict.map(item => ({ [item["name"]]: item }))
);
let text = evt.target.value.toLowerCase().trim();
let textArr = text.split(/,?\s+/);
let translation = "";
textArr.forEach(word => {
if (dictCopy[word] === undefined) {
translation += `${word} `;
} else {
translation += dictCopy[word][word][this.state.derpLvl];
}
});
this.setState({ translation });
};
levelOfDerp is not defined, try to use 'levelOfDerp' as string with quotes.
let output = dictionary[word]['levelOfDerp' ];
The problem happens because setState() is asynchronous, so by the time it's executed your evt.target.value reference might not be there anymore. The solution is, as you stated, to store that reference into a variable.
Maybe consider writing another function that handles the object conversion and store it in a variable, because as is, you're doing the conversion everytime the user inputs something.
I am working on a vue app that can convert from fiat currency to crypto-currency and vise versa. A demo of the app can be found here: Demo.
You will note that the calculation is done and shown in the opposite input box automatically as text is entered. The issue is that currently the app just watches for changes in the variables bound to the inputs.
I also need this calculation to take place when the user chooses another selection from the selection box.
I have struggled thinking of a way to make this happen without causing an infinite loop.
Here was my naive attempt: codepen.
Currently my understanding of why this does not work is because:
...input1 changes -> watch input1 called -> watch input1 modifies
input2 -> watch input2 called -> watch input2 modifies input1....
Which is an infinite loop. There must be a part of Vue I'm not familiar with enough yet to handle this exact issue.
Thank you,
Use the #input event listener. When you change the value in one input that changed will update the value of the other input.
<template>
<div>
<input v-model="value1" #input="update2">
to
<input v-model="value2" #input="update1">
</div>
</template>
<script>
const mul = 1.5;
export default {
data: () => ({
value1: null,
value2: null
}),
methods: {
update1() {
this.value1 = this.value2 / mul;
},
update2() {
this.value2 = this.value1 * mul;
}
}
};
</script>
You can make this work with watch, but then you'll need to prevent the update loop with an additional check:
watch: {
value1(val) {
const tmp = val * mul;
if (tmp !== this.value2) {
this.value2 = tmp;
}
},
value2(val) {
const tmp = val / mul;
if (tmp !== this.value1) {
this.value1 = tmp;
}
}
},
But this code assumes that multiplying by x and then dividing by x results in the exact same number, and while that mathematically true, with floats (javascript's number type) there are exceptions you'll need to guard against (NaN, rounding) that will make the code complex.
I therefor recommend the input event-listener approach.