Method “simulate” is meant to be run on 1 node. 0 found instead. jest/enzyme - jestjs

I even tried it by giving classnames and removing div and adding it back. Can somene help?
//Page1.js
<div className="email" >
<MyInput className="input1" name="email" type="email" onChange={formikProps.handleChange}/>
</div>
//Page1.test.js
describe("testing email",()=>{
test("email check",()=>{
let wrapper = shallow(<Page1/>)
wrapper.find('MyInput[name="email"]').simulate('onChange', {target: {
name: 'email',
value: 'ale#gmail.com'
}})
expect(wrapper.state('email')).toEqual('ale#gmail.com')
})
})

The first argument to simulate has to be the event name to be simulated not the name of the event handler.
wrapper.find('MyInput[name="email"]').simulate('change', {target: {
name: 'email', value: 'ale#gmail.com' }})

Shallow doesn't render child components. It will render just the react wrapper. For testing events and interactions, use mount. Also you can use console.log(wrapper.debug()) to see the content of wrapper. It will help you to identify the problem

Related

form.item doesn't seem to get the validated class when entering a value in a jest test

I have a jest test to test a custom component containing the form.item from antd. I've trimmed everything down to clean out-of-the-box code to ensure this is not in my customizations.
My test file contains a defined form like this:
const TestForm = () => {
return (
<Form id='frm_test' name='frm_test'>
<Form.Item data-testid="test" label='email' name='email' rules={[{ type: 'email', message: 'test' }]}>
<Input />
</Form.Item>
</Form>
);
};
Next I have a test like this:
it('Should should validate email input', () => {
const { getByLabelText, getByTestId } = render(<TestForm />);
screen.debug(getByTestId('test'))
userEvent.type(getByLabelText('email'), 'T');
screen.debug(getByTestId('test'));
});
When I now run the test, I expect that the generated form.item will have the error message bellow the input field if you look at the debug information. This happens when you manually type something in such a situation, so I expect it to happen in the test as well. However this doesn't happen as you can see in the output from the test debug.
What I do see is that the form item is having the class ant-form-item-is-validating. I expect this to be ant-form-item-has-error (or something like that). It's as if the validation 'hangs' somewhere.
The debug information is:
<div
class="ant-row ant-form-item ant-form-item-is-validating"
data-testid="test"
>
<div
class="ant-col ant-form-item-label"
>
<label
class=""
for="frm_test_email"
title="email"
>
email
</label>
</div>
<div
class="ant-col ant-form-item-control"
>
<div
class="ant-form-item-control-input"
>
<div
class="ant-form-item-control-input-content"
>
<input
class="ant-input ant-input-sm"
id="frm_test_email"
type="text"
value="T"
/>
</div>
</div>
</div>
</div>
Okay so I saw that the validation was actually also doing a log in the console. With some messing around, I can actually do a test to see if the message appears using spy. Right now I did it with this code :
const oSpy = jest.spyOn(console, 'warn').mockImplementation();
userEvent.type(getByLabelText('email'), 'T');
expect(oSpy).toHaveBeenCalledTimes(1);
Now the above example won't reflect my message, but will work. I adjusted my custom component to contain some console.warn('some message') and then the spy can get it.
Not exactly what I wanted though. Also, I'll end up having console... in my code. I certainly don't want that when I build it. So I'll remove that with uglyfyjs.
Spend 2 days on this but i found an answer.
You can wait for the validation to end by accessing the validateFields method as follows:
const form = wrapper.find(Form);
form.props().form.setFieldsValue({
email: 'invlid email'
});
return form.props().form.validateFields().catch(err => {
expect(!!err.errorFields).toBe(true);
})

Cast to ObjectId failed for value "req.params.id" at path "_id" for model "Project"

I am trying to create a clone of employee management system. I want to implement a feature that when admin opens a details page for any project he will get list of available employees as checkboxes and when select them and submit then those employees will be added to the given project.
I am developing it using Node.js and MongoDB
My project schema is -
const ProjectSchema = new mongoose.Schema({
name: String,
deadline: Date,
description: String,
manager:{
id: {
type: mongoose.Schema.Types.ObjectId,
ref: "User"
},
},
employees: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "User"
}
],
created_on: {
type: Date,
default: Date.now
}
});
Form -
<form action="/admin/project/<%= project._id %>/addmembers" method="POST">
<div class="row">
<% employees.forEach((employee) => { %>
<% if(!employee.projects.includes(project._id)){ %>
<div class="col-12 col-sm-6 col-md-4 col-xl-3">
<input type="checkbox" value="<%= employee._id %>" name="newMembers"> <%= employee.name %> </input>
</div>
<% } %>
<% }); %>
</div>
<hr class="my-2">
<p class="lead">
<button class="btn btn-blue btn-lg">Add Members</button>
</p>
</form>
Code for handling the request -
router.post("/project/:id/addmembers", (req, res) => {
console.log("add member post route");
Project.findById(req.params.id, (err, foundProject) => {
if(err){
console.log(err);
req.flash("error", err.message);
res.redirect("back");
} else {
req.body.newMembers.forEach((employee) => {
foundProject.employees.push(employee);
})
foundProject.save()
console.log(foundProject);
req.flash("success", "Successfully added new members");
res.redirect("/admin/project/req.params.id");
}
})
});
Now whenever I submit form users are added but I always get an error Cast to ObjectId failed for value "req.params.id" at path "_id" for model "Project" instead of success message now I am new to mongo so I googled it but can't solve my problem can anyone explain me why this error is coming and how can I fix it?
Also I know that if if only one checkbox is selected then req.body.newMembers will not be array. If you can you provide better method to do it, it will be very helpful?
Update -
I also tried findOne(), find(), findByIdAndUpdate(), and id = mongoose.Types.ObjectId(req.params.id) but still get the same error message
It looks like what is happening is you aren't properly handling ids and _id. Given that you haven't mentioned anything, I assume you see this error only once. However, given that the error has to do with the project model/schema, and you use almost the same code for both manager and employees, the error should have to do with a difference between the two. The main difference I see is manager has id: {*id stuff*}, but employee has {*id stuff*} with no id:. However, I don't think this is the actual error, but just something to keep in mind. Try also changing the id type to something like number just to see if this is related to the issue.
What I think that actual error is is the fact that you use req.params.id in a findById(). This searches your database using the _id. First, having id and _id both is a bit redundant (if you have a reason, that's fine, but _id is something MongoDB needs, so it might be better to just stick with it as it is precreated and handled). Second, you are using a string/int as an id search. To test whether this is the issue, try changing findById() to something like findOne() or find(). Just to clarify, findById() doesn't explicitly need an ObjectId, so the type may not be the issue, but findById() does handle the search differently than find({_id: id}).
Another thing to check is that req.params.id actually gives the right value. It should, but this is something to verify. To do so, just console.log() it. If this is the problem, try moving :id to the end of the path.
In response to your edit:
Make sure that the problem doesn't have to do with not having id: in the employee (as stated above) and that it doesn't have to do with the id being a ObjectId rather than a number. If you change the id to a number, make sure to change the search so that it finds the object by id not _id. Just to let you know, every MongoDB object has an _id (even subobjects like employee and manager), so using id is somewhat redundant.
To give you more things to try: make sure you console.log() req.params.id to make sure it has the right value. Then, also try commenting sections of code until you find which line it is on. Try hardcoding some input until it works.

Vuetify v-form post does not send data

Forgive me for the English of the Translator :)
I created a basic form to see if I get data in my API using vuetify however, when submitting the data the v-select data is not sent and I can not understand the reason, since in general the examples of these forms do not really make a request POST, follows snippets of the code I'm using:
<v-form method="post" action="http://127.0.0.1:3000/produtos">
<v-text-field name="escola" v-model="name" required :rules="nameRules"></v-text-field>
<v-select
v-model="selectPessoa"
:items="pessoas"
:rules="[v => !!v || 'Item is required']"
item-value="id"
item-text="nome"
label="itens"
required
name="pessoa"
return-object
value="id"
></v-select>
<v-btn color="warning" type="submit">Submit</v-btn>
</v-form>
Excerpt from javascript code:
data(){
return { pessoas: [{ id: 1, nome: "sandro" },
{ id: 2, nome: "haiden" }],
name: '',
selectPessoa: null,
}
}
The information I type in the v-text-field I get in the API node, but the one in the v-select does not:
Form screen:
API log screen:
On the<v-select> component you have defined the return-object and item-value="id" props. Using the return-object is overriding the item-value by returning the entire object from the v-select component instead of just the id. In this case you could just remove the return-object prop from the <v-select> component and that will fix your issue.
<v-select
v-model="selectPessoa"
:items="pessoas"
:rules="[v => !!v || 'Item is required']"
item-value="id"
item-text="nome"
label="itens"
required
name="pessoa"
return-object <------REMOVE THIS!!!
value="id"
></v-select>
Vuetify v-select docs: https://vuetifyjs.com/en/components/selects
Another option instead of removing the return-object prop could be to modify your API endpoint to expect an object rather than an int.
Also, I would not recommend using the "method" and "action" attributes on the <v-form> component. Instead, put a click event handler on the submit button of the form that calls a method. The method should then grab the data and send it to the API endpoint via an AJAX call.
On the Form Component
Before: <v-form method="post" action="http://127.0.0.1:3000/produtos">
After: <form #submit.prevent>
On the Button Component
Before: <v-btn color="warning" type="submit">Submit</v-btn>
After: <v-btn color="warning" #click="submit">Submit</v-btn>
In the methods have a function do something like this (used axios in my example, not sure what your project is using):
methods: {
submit () {
let data = { name: this.name, selectPessoa: this.selectPessoa }
axios.post('http://127.0.0.1:3000/produtos', data)
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
}
}

Debouncing ember search input

I want to add debounce to my search input in ember app.
I'm having problems with using debounce method.
Here's how my search looks like:
{{input type="search"
placeholder="Search by XYZ"
value=search
id="search"
}}
My route file:
export default Ember.Route.extend({
queryParams: {
search:{refreshModel: true}
}
My controller file:
export default Ember.Controller.extend({
search: "",
Implementing your desired behaviour debouncing with existing input helper is quite difficult since it will update value immediately.
So I will encourage you to use normal input html element and use keyUp event to listen and manually set value inside debounce method.
Inside controller,
import Ember from 'ember';
export default Ember.Controller.extend({
queryParams:['easyFilter'],
easyFilter:'',
setEasyFilterLazily(value){
this.set('easyFilter',value);
},
actions:{
handleEasyFilterEntry(value){
Ember.run.debounce(this, this.setEasyFilterLazily,value, 500);
}
}
});
Created twiddle for demonstration.
One more interesting options are using ember-concurrency addon.
Need to use perform helper instead of action helper,
<input type="text" value={{filterValue}} oninput={{perform triggerToUpdateFilter value='target.value'}}>
and
triggerToUpdateFilter: task(function*(value) {
yield timeout(1000); //here it will wait for 1000 ms before setting
this.set('easyFilter',value);
}).restartable(),
As this task is restartable so it will restart when you type fast within 1000 ms.
search: computed('', {
set(key, value) {
debounce(this, this.setValue, value, 500);
}
})
This worked for me, looks like this overrides the default setter for search.

How to put a delay on AngularJS instant search?

I have a performance issue that I can't seem to address. I have an instant search but it's somewhat laggy, since it starts searching on each keyup().
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:searchText">
<span>{{entry.content}}</span>
</div>
The JSON data isn't even that large, 300KB only, I think what I need to accomplish is to put a delay of ~1 sec on the search to wait for the user to finish typing, instead of performing the action on each keystroke. AngularJS does this internally, and after reading docs and other topics on here I couldn't find a specific answer.
I would appreciate any pointers on how I can delay the instant search.
UPDATE
Now it's easier than ever (Angular 1.3), just add a debounce option on the model.
<input type="text" ng-model="searchStr" ng-model-options="{debounce: 1000}">
Updated plunker:
http://plnkr.co/edit/4V13gK
Documentation on ngModelOptions:
https://docs.angularjs.org/api/ng/directive/ngModelOptions
Old method:
Here's another method with no dependencies beyond angular itself.
You need set a timeout and compare your current string with the past version, if both are the same then it performs the search.
$scope.$watch('searchStr', function (tmpStr)
{
if (!tmpStr || tmpStr.length == 0)
return 0;
$timeout(function() {
// if searchStr is still the same..
// go ahead and retrieve the data
if (tmpStr === $scope.searchStr)
{
$http.get('//echo.jsontest.com/res/'+ tmpStr).success(function(data) {
// update the textarea
$scope.responseData = data.res;
});
}
}, 1000);
});
and this goes into your view:
<input type="text" data-ng-model="searchStr">
<textarea> {{responseData}} </textarea>
The mandatory plunker:
http://plnkr.co/dAPmwf
(See answer below for a Angular 1.3 solution.)
The issue here is that the search will execute every time the model changes, which is every keyup action on an input.
There would be cleaner ways to do this, but probably the easiest way would be to switch the binding so that you have a $scope property defined inside your Controller on which your filter operates. That way you can control how frequently that $scope variable is updated. Something like this:
JS:
var App = angular.module('App', []);
App.controller('DisplayController', function($scope, $http, $timeout) {
$http.get('data.json').then(function(result){
$scope.entries = result.data;
});
// This is what you will bind the filter to
$scope.filterText = '';
// Instantiate these variables outside the watch
var tempFilterText = '',
filterTextTimeout;
$scope.$watch('searchText', function (val) {
if (filterTextTimeout) $timeout.cancel(filterTextTimeout);
tempFilterText = val;
filterTextTimeout = $timeout(function() {
$scope.filterText = tempFilterText;
}, 250); // delay 250 ms
})
});
HTML:
<input id="searchText" type="search" placeholder="live search..." ng-model="searchText" />
<div class="entry" ng-repeat="entry in entries | filter:filterText">
<span>{{entry.content}}</span>
</div>
In Angular 1.3 I would do this:
HTML:
<input ng-model="msg" ng-model-options="{debounce: 1000}">
Controller:
$scope.$watch('variableName', function(nVal, oVal) {
if (nVal !== oVal) {
myDebouncedFunction();
}
});
Basically you're telling angular to run myDebouncedFunction(), when the the msg scope variable changes. The attribute ng-model-options="{debounce: 1000}" makes sure that msg can only update once a second.
<input type="text"
ng-model ="criteria.searchtext""
ng-model-options="{debounce: {'default': 1000, 'blur': 0}}"
class="form-control"
placeholder="Search" >
Now we can set ng-model-options debounce with time and when blur, model need to be changed immediately otherwise on save it will have older value if delay is not completed.
For those who uses keyup/keydown in the HTML markup.
This doesn't uses watch.
JS
app.controller('SearchCtrl', function ($scope, $http, $timeout) {
var promise = '';
$scope.search = function() {
if(promise){
$timeout.cancel(promise);
}
promise = $timeout(function() {
//ajax call goes here..
},2000);
};
});
HTML
<input type="search" autocomplete="off" ng-model="keywords" ng-keyup="search()" placeholder="Search...">
Debounced / throttled model updates for angularjs : http://jsfiddle.net/lgersman/vPsGb/3/
In your case there is nothing more to do than using the directive in the jsfiddle code like this:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-ampere-debounce
/>
Its basically a small piece of code consisting of a single angular directive named "ng-ampere-debounce" utilizing http://benalman.com/projects/jquery-throttle-debounce-plugin/ which can be attached to any dom element. The directive reorders the attached event handlers so that it can control when to throttle events.
You can use it for throttling/debouncing
* model angular updates
* angular event handler ng-[event]
* jquery event handlers
Have a look : http://jsfiddle.net/lgersman/vPsGb/3/
The directive will be part of the Orangevolt Ampere framework (https://github.com/lgersman/jquery.orangevolt-ampere).
Just for users redirected here:
As introduced in Angular 1.3 you can use ng-model-options attribute:
<input
id="searchText"
type="search"
placeholder="live search..."
ng-model="searchText"
ng-model-options="{ debounce: 250 }"
/>
I believe that the best way to solve this problem is by using Ben Alman's plugin jQuery throttle / debounce. In my opinion there is no need to delay the events of every single field in your form.
Just wrap your $scope.$watch handling function in $.debounce like this:
$scope.$watch("searchText", $.debounce(1000, function() {
console.log($scope.searchText);
}), true);
Another solution is to add a delay functionality to model update. The simple directive seems to do a trick:
app.directive('delayedModel', function() {
return {
scope: {
model: '=delayedModel'
},
link: function(scope, element, attrs) {
element.val(scope.model);
scope.$watch('model', function(newVal, oldVal) {
if (newVal !== oldVal) {
element.val(scope.model);
}
});
var timeout;
element.on('keyup paste search', function() {
clearTimeout(timeout);
timeout = setTimeout(function() {
scope.model = element[0].value;
element.val(scope.model);
scope.$apply();
}, attrs.delay || 500);
});
}
};
});
Usage:
<input delayed-model="searchText" data-delay="500" id="searchText" type="search" placeholder="live search..." />
So you just use delayed-model in place of ng-model and define desired data-delay.
Demo: http://plnkr.co/edit/OmB4C3jtUD2Wjq5kzTSU?p=preview
I solved this problem with a directive that basicly what it does is to bind the real ng-model on a special attribute which I watch in the directive, then using a debounce service I update my directive attribute, so the user watch on the variable that he bind to debounce-model instead of ng-model.
.directive('debounceDelay', function ($compile, $debounce) {
return {
replace: false,
scope: {
debounceModel: '='
},
link: function (scope, element, attr) {
var delay= attr.debounceDelay;
var applyFunc = function () {
scope.debounceModel = scope.model;
}
scope.model = scope.debounceModel;
scope.$watch('model', function(){
$debounce(applyFunc, delay);
});
attr.$set('ngModel', 'model');
element.removeAttr('debounce-delay'); // so the next $compile won't run it again!
$compile(element)(scope);
}
};
});
Usage:
<input type="text" debounce-delay="1000" debounce-model="search"></input>
And in the controller :
$scope.search = "";
$scope.$watch('search', function (newVal, oldVal) {
if(newVal === oldVal){
return;
}else{ //do something meaningful }
Demo in jsfiddle: http://jsfiddle.net/6K7Kd/37/
the $debounce service can be found here: http://jsfiddle.net/Warspawn/6K7Kd/
Inspired by eventuallyBind directive http://jsfiddle.net/fctZH/12/
Angular 1.3 will have ng-model-options debounce, but until then, you have to use a timer like Josue Ibarra said. However, in his code he launches a timer on every key press. Also, he is using setTimeout, when in Angular one has to use $timeout or use $apply at the end of setTimeout.
Why does everyone wants to use watch? You could also use a function:
var tempArticleSearchTerm;
$scope.lookupArticle = function (val) {
tempArticleSearchTerm = val;
$timeout(function () {
if (val == tempArticleSearchTerm) {
//function you want to execute after 250ms, if the value as changed
}
}, 250);
};
I think the easiest way here is to preload the json or load it once on$dirty and then the filter search will take care of the rest. This'll save you the extra http calls and its much faster with preloaded data. Memory will hurt, but its worth it.

Resources