I have an input with a dynamically-generated name that I would like to select with react-testing-library. RTL doesn't detect the input, even though the CLI error output contains an element with the correct name and role.
Here is an excerpt of the test code:
const attributeInput = await getByRole(container, 'textbox', {name: 'attributes[0].value'});
await waitFor(() => {
expect(attributeInput).toBeTruthy();
});
The error message of the test is as follows:
TestingLibraryElementError: Unable to find an accessible element with the role "textbox" and name "attributes[0].value"
Here are the accessible roles:
... (list of accessible roles and inputs of different types with various names, omitted for brevity) ...
--------------------------------------------------
textbox:
Name "":
<input
aria-invalid="false"
class="MuiInputBase-input MuiOutlinedInput-input"
name="attributes[0].value"
rows="1"
type="text"
value=""
/>
Clearly, the element is in the container, is of role "textbox," and has a name property with the value 'attributes[0].value' associated with it. Why then does RTL not register that this component of the correct type and name, is the one I'm looking for? Why the erroneous
Name: ""
output in the command line?
Possibly relevant: I am using material-ui and this HTML is generated from a TextField wrapped in a react-hook-form controller. Moreover, this assertion is arrived at after some previous react-testing-library actions that programmatically navigate to this page and expect to see this input. None of the previous 'gets' 'acts' or 'waitFor/expects' fail, and a number of the other inputs have names recognized by react-testing-library (although the names assigned to them by RTL appear to be their InputLabel texts and not what is assigned to the 'name' attribute on the generated HTML).
For any Async operation it is recommended to not use getBy instead please try with findByRole/findAllByRole method.
Reference:
[
{ name: 'Chocolate', imagePath: '/images/chocolate.png' },
{ name: 'Vanilla', imagePath: '/images/vanilla.png' },
]
test("display each impage of scoop as response api", async () => {
render(<Options optionType="scoops" />);
// Find images
const scoopImages = await screen.findAllByRole('img', {
name: /scoop$/i
});
expect(scoopImages).toHaveLength(2);
const altText = scoopImages.map((elem) => elem.alt);
expect(altText).toEqual('Chcolate scoop', 'Vanilla scoop');
})
Related
I built a custom Filter component for my List View and Im having trouble populating a Select Input of ALL available options for a property. for instance
<Form onSubmit={onSubmit} initialValues={filterValues} >
{({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<ReferenceInput label="Ring Id" source="ringid" reference="candidates">
<SelectInput optionText="ringid" />
</ReferenceInput>
</form>
)}
</Form>
Without building a "getMany" dataProvider Im told that I can access all of the (2,000+ ids) "ringid"s pulled in from the "getList" provider and list every ID into the SelectInput field and search in my custom Filter component.
Issues presented:
I have to hard code amount of results I can have (Default 25)
When I submit the form to Search through the filter component "Associated reference no longer appears to be available." appears and the search fails.
The "getMany" component is only half way built but it seems that ReferenceInput only wants to use "getMany"(Im told that building the backend and building code to use getMany is not an priority to build so I cant build it myself)
25 Populated IDs Screenshot
Form Error when Filter is submitted ScreenShot
So I would like some help in the right direction to populate a SelectInput of all available ids in the getList dataProvider and be sure that I can even use this input in my Filter form component. Thank you in advance for any feedback.
1: Yes, i think there's no option to add pagination to ReferenceInput, you must hardcode it, but, if your backend already supports text search, you can use an AutocompleteInput as child, allowing users to filter results:
<ReferenceInput
label="Ring Id"
source="ringid"
reference="candidates"
filterToQuery={searchText => ({ paramNameThatYourBackendExpects: searchText })}
>
<AutocompleteInput optionText="ringid" />
</ReferenceInput>
2 & 3: 2 happens because of 3. ReferenceInput only "wants" to use getMany because it also support SelectManyInput as child, for such case, it's better to get all selected options at once, than calling one by one, so, to make code simpler, ReferenceInput always use getMany. If you can't implement backend part of getMany, but can add code to your dataProvider, you can implement getMany by making multiple getOne calls:
Assuming a v3 dataProvider:
this.getMany = async (resource, params) => {
const response = {data: []}
for (const id of params.id) {
response.data.push(await this.getOne(resource, {id}))
}
return response
}
v2 is implementation-dependant, just follow the same principle.
If you can't change the dataProvider, e.g, a third-party available dataProvider, you can wrap it:
v3
const fakeGetManyDataProvider = dataProvider => ({
...dataProvider,
getMany: async (resource, params) => {
const response = {data: []}
for (const id of params.id) {
response.data.push(await dataProvider.getOne(resource, {id}))
}
return response
}
})
v2
import { GET_MANY, GET_ONE } from 'react-admin'
const fakeGetManyDataProvider = dataProvider => async (verb, resource, params) => {
if (verb === GET_MANY) {
const response = {data: []}
for (const id of params.id) {
response.data.push(await dataProvider(GET_ONE, resource, {id}))
}
return response
}
return dataProvider(verb, resource, params)
}
Please note that error handling is omitted for simplicity, react admin expects rejecteds promise instead of unhandled expections, so you must handle errors.
I have a custom module where there is an email field. Now i want to stop the user if the email is already in the database.
I want to stop the user on save button and show the error. Like when a required field goes empty.
I tried to get some help but was not able to understand it.
Note: I realized after posting this that you are using suitecrm which this answer will not be applicable toward but I will leave it in case anyone using Sugar has this question.
There are a couple of ways to accomplish this so I'll do my best to walk through them in the order I would recommend. This would apply if you are using a version of Sugar post 7.0.0.
1) The first route is to manually create an email address relationship. This approach would use the out of box features which will ensure your system only keeps track of a single email address. If that would work for your needs, you can review this cookbook article and let me know if you have any questions:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_the_Email_Field_to_a_Bean/
2) The second approach, where you are using a custom field, is to use field validation. Documentation on field validation can be found here:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/index.html
The code example I would focus on is:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/#Method_1_Extending_the_RecordView_and_CreateView_Controllers
For your example, I would imagine you would do something like this:
Create a language key for your error message:
./custom/Extension/application/Ext/Language/en_us.error_email_exists_message.php
<?php
$app_strings['ERROR_EMAIL_EXISTS_MESSAGE'] = 'This email already exists.';
Create a custom controller for the record creation (you may also want to do this in your record.js):
./custom/modules//clients/base/views/create/create.js
({
extendsFrom: 'RecordView',
initialize: function (options) {
this._super('initialize', [options]);
//reference your language key here
app.error.errorName2Keys['email_exists'] = 'ERROR_EMAIL_EXISTS_MESSAGE';
//add validation tasks
this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
},
_doValidateEmail: function(fields, errors, callback) {
var emailAddress = this.model.get('your_email_field');
//this may take some time so lets give the user an alert message
app.alert.show('email-check', {
level: 'process',
title: 'Checking for existing email address...'
});
//make an api call to a custom (or stock) endpoint of your choosing to see if the email exists
app.api.call('read', app.api.buildURL("your_custom_endpoint/"+emailAddress), {}, {
success: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//analyze your response here
if (response == '<email exists>') {
errors['your_email_field'] = errors['your_email_field'] || {};
errors['your_email_field'].email_exists = true;
}
callback(null, fields, errors);
}, this),
error: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//throw an error alert
app.alert.show('email-check-error', {
level: 'error',
messages: "There was an error!",
autoClose: false
});
callback(null, fields, errors);
})
});
},
})
Obviously, this isn't a fully working example but it should get you most of the way there. Hope this helps!
I am currently having a list with some entries in my database. Now i want the users to be able to edit one of those entries.
When the "Edit" Button is clicked, it should load the original form and prefill all the fields with the values already stored in the database.
I used a mustache template for my form, looking like this:
<!DOCTYPE html>
<html>
<head>
<title>{{ title }}</title>
<link rel='stylesheet' href='/stylesheets/style.css' />
</head>
<body>
<h1>Issue Form</h1>
<p>{{ errors }}</p>
<form method="post">
<label>Title: <br><input type="text" name="title">{{ value }}</label><br>
<label>Description: <br><textarea type="text" name="description">{{ value }}
</textarea></label><br>
<label>Priority: <br>
<select name="priority">
<option>high</option>
<option>medium</option>
<option>low</option>
</select>
</label><br>
<input type="submit" value="Save Entry">
</form>
</body>
</html>
This is my mongoose schema:
var issueSchema = new mongoose.Schema({
title: String,
description: String,
priority: String
});
I of course did a lot of researche on how to fill my fields. I read about the mongoose "populate()" function, but when i tried to use it, there was always some errors telling me that the function itself is undefined.
Another option was using a JSON file to store the data values, but i cannot do this in this example since the values should always be stored in my MongoDB data folder.
Another version i found was creating an object via the toObject() function. But anytime i tried that out:
router.get('/edit/:id', (req, res) => {
var objectForEditing = issueModel.findOne(req.params.id).toObject;
console.log(objectForEditing);
res.render('issueFormEdit');
});
The console.log part shows me the object as undefined.
Using JQuery like i did in any other javascript file before didn't work either, even when including the modules.
I simply need a method to connect my javascript code with my hjs file. But i simply am not able to do so, my knowledge is not enough for this. I really tried out a lot and have invested hours so far. But i simply can't get to the bottom on how to connect these two files.
This is my first time ever working with this combination of Mustache.JS and MongoDB/Mongoose/Express. Please be gentle :(
If any more code is needed, please just let me know.
Your code has the following list of issues:
1) Model.prototype.findOne() method is asynchronous, so you either need to use async/await or use promises before calling toObject() on it.
2) You are querying mongoose in the wrong way. You need to use findOneById(id) or findOne({ _id: id }).
3) toObject is a function, so it has to be called.
4) objectForEditing needs to be passed to res.render function as the second argument which represents locals, which basically is:
an object whose properties define local variables for the view
Try this code (async/await):
router.get('/edit/:id', async (req, res) => {
let objectForEditing = await issueModel.findOneById(req.params.id);
objectForEditing = objectForEditing.toObject();
res.render('issueFormEdit', objectForEditing);
});
Using Promises:
router.get('/edit/:id', (req, res) => {
issueModel.findOneById(req.params.id)
.then(issue => {
const objectForEditing = issue.toObject();
res.render('issueFormEdit', objectForEditing);
});
});
I am working on ionic-framework and i'm using stanza.io library to implement chatting with xmpp server, I want to add some custom attributes while sending message, for that i have followed the steps for creating plugin. my code is as follow...
sendMsg() {
console.log("Sending message");
function customMessage(client, stanzas) {
const NS = 'http://www.w3.org/2005/Atom';
var types = stanzas.utils;
const messageAttribute = stanzas.define({
name: 'messageAttribute',
element: 'messageAttribute',
namespace: NS,
fields: {
title: types.textSub(NS, 'title'),
summary: types.textSub(NS, 'summary'),
published: types.textSub(NS, 'published'),
updated: types.textSub(NS, 'updated'),
cont: types.textSub(NS, 'cont')
}
});
stanzas.withMessage((Message) => {
stanzas.extend(Message, messageAttribute);
});
}
this.client.use(customMessage);
this.client.sendMessage({
to: this.recep,
body: "",
messageAttribute: {
'title': "some title",
'summary': "message",
'published': "time stamp here",
'updated': "time stamp here",
'cont': "cht"
}
});
console.log("Message sent " + this.sMsg);
}
but doing this way message are not being stored in Archive table on server. that will create a problem to get the history from the server. if we use the simple code then that messages being stored on Archive table on server. simple code as follow..
this.client.sendMessage({
to: this.recep,
body: this.sMsg
});
in simple code we can only send message as a string inside body. can anyone help me to solve this?
My server is only archiving messages which contain a body element with text, which is a pretty common archiving configuration. One trick would be trying to include a dummy body text to trigger message archiving, but you'd have to check if the server is storing and returning the full stanza or just extracting and saving the body text.
done everything correctly with extending Stanza to include the additional fields, but need to adjust server to get what i want. confirmed from here.
You need to add an extra param store in message stanza which makes the message to store in Archive table by default.
const store = stanzas.define({
name: 'store',
element: 'store',
namespace: 'urn:xmpp:hints'
});
stanzas.withMessage(Message => {
stanzas.extend(Message, store);
});
Sent store attribute as true in message stanza
this.client.sendMessage({
to: this.recep,
body: this.sMsg,
store: true
});
You should see store inside message stanza like
<store xmlns='urn:xmpp:hints'/>
I just started using SugarCRM CE for the first time (Version 6.5.15 (Build 1083)). I'm quite impressed with the ease of use when adding new fields or modules, but there's one quite indispensable thing that seems to be missing: Validation of user input.
I would for example like to check a lot of things:
Check if a emailadres has a valid format, using some regular expression
Check if a postalcode exists (maybe do a webswervice call to validate it)
Do a calculation to see if a citizen service number is valid
etc.
The only thing I seem to be able to do in studio is make a field required or not, there doesn't seem to be any standard way to execute a validation on a field.
All I can find when I google on it is lots of ways to hack into the source code, like this one: http://phpbugs.wordpress.com/2010/01/22/sugarcrm-adding-javascript-validation-on-form-submit/ And even then I don't find any examples that actually do a validation.
Am I just missing something? Or is editing source code the only way to add this?
I don't think the "standard" validations are available in the CE edition.
What surprises me is that you can't define a validation somewhere and attach it to a field. I kind of expected this, since the rest of the system is very well structured (modules, packages, etc..)
I now for instance created a 11-check, this is a very specific check for a dutch bank account number. to get this to work, I did the following (based upon examples I found googling around):
I added the bank account to contacts in studio and after that edited \custom\modules\Contacts\metadata\editviewdefs.php
I added the following snippets:
'includes'=> array(
array('file'=>'custom/modules/Contacts/customJavascript.js')),
array (
0 =>
array(
'customCode' =>
'<input title="Save [Alt+S]" accessKey="S" onclick="this.form.action.value=\'Save\'; return check_custom_data();" type="submit" name="button" value="'.$GLOBALS['app_strings']['LBL_SAVE_BUTTON_LABEL']>',
),
1 =>
array(
'customCode' =>
'<input title="Cancel [Alt+X]" accessKey="X" onclick="this.form.action.value=\'index\'; this.form.module.value=\''.$module_name.'\'; this.form.record.value=\'\';" type="submit" name="button" value="'.$GLOBALS['app_strings']['LBL_CANCEL_BUTTON_LABEL'].'">'
)
),
And in customJavascript.js i placed this code:
function check_custom_data()
{
if (!eleven_check(document.getElementById("bankaccount_c").value)){
alert ('Bank account not valid');
return false;
} else {
return check_form('EditView');
}
function eleven_check(bankaccount) {
bankaccount=bankaccount.replace(/\D/, "");
charcount=bankaccount.length;
var som=0;
for (i=1; i<10; i++) {
getal=bankaccount.charAt(i-1);
som+=getal*(10-i);
}
if (som % 11==0 && charcount==9) {
return true
} else {
return false
}
}
}
This check now works the way I want it to work, but I'm wondering if this is the best way to add a validation. this way of adding a validation doesn't however accommodate PHP validations, for instance, if I want to validate against some data in the database for one or another reason, I would have to use ajax calls to get that done.
Email validation is in the pro edition, I had assumed it was in CE as well but I'm not 100% sure.
The other 2 are a lot more specific - postcode validation would depend upon your country so would be difficult to roll out. For these you will need to write your own custom validation.
I know its late, but maybe still someone needs this.
You can just add your custom javascript validation as a callback in your vardefs like this:
'validation' =>
array (
'type' => 'callback',
'callback' => 'function(formname,nameIndex){if($("#" + nameIndex).val()!=999){add_error_style(formname,nameIndex,"Only 999 is allowed!"); return false;}; return true;}',
),
I documented it here as its not well documented elsewhere:
https://gunnicom.wordpress.com/2015/09/21/suitecrm-sugarcrm-6-5-add-custom-javascript-field-validation/
You can add custom validation code to the following file: ./custom/modules/.../clients/base/views/record/record.js
There you can add validation code. In this example, I will validate if the phone_number is not empty when an accounts has a customer-type:
EXAMPLE CODE IN RECORD.JS:
({
extendsFrom: 'RecordView',
initialize: function (options) {
app.view.invokeParent(this, {type: 'view', name: 'record', method: 'initialize', args:[options]});
//add validation
this.model.addValidationTask('check_account_type', _.bind(this._doValidateCheckType, this));
},
_doValidateCheckType: function(fields, errors, callback) {
//validate requirements
if (this.model.get('account_type') == 'Customer' && _.isEmpty(this.model.get('phone_office')))
{
errors['phone_office'] = errors['phone_office'] || {};
errors['phone_office'].required = true;
}
callback(null, fields, errors);
}
})
Don't forget to repair en rebuild!
The full documentation can be found here