Enzyme - How to access and set <input> value? - unit-testing

I'm confused about how to access <input> value when using mount. Here's what I've got as my test:
it('cancels changes when user presses esc', done => {
const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input');
console.log(input.render().attr('value'));
input.simulate('focus');
done();
});
The console prints out undefined. But if I slightly modify the code, it works:
it('cancels changes when user presses esc', done => {
const wrapper = render(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input');
console.log(input.val());
input.simulate('focus');
done();
});
Except, of course, the input.simulate line fails since I'm using render now. I need both to work properly. How do I fix this?
EDIT:
I should mention, <EditableText /> is not a controlled component. But when I pass defaultValue into <input />, it seems to set the value. The second code block above does print out the value, and likewise if I inspect the input element in Chrome and type $0.value in the console, it shows the expected value.

I think what you want is:
input.simulate('change', { target: { value: 'Hello' } })
Here's my source.
You shouldn't need to use render() anywhere to set the value. And just FYI, you are using two different render()'s. The one in your first code block is from Enzyme, and is a method on the wraper object mount and find give you. The second one, though it's not 100% clear, is probably the one from react-dom. If you're using Enzyme, just use shallow or mount as appropriate and there's no need for render from react-dom.

With Enzyme 3, if you need to change an input value but don't need to fire the onChange function you can just do this (node property has been removed):
wrapper.find('input').instance().value = "foo";
You can use wrapper.find('input').simulate("change", { target: { value: "foo" }}) to invoke onChange if you have a prop for that (ie, for controlled components).

Got it. (updated/improved version)
it('cancels changes when user presses esc', done => {
const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input');
input.simulate('focus');
input.simulate('change', { target: { value: 'Changed' } });
input.simulate('keyDown', {
which: 27,
target: {
blur() {
// Needed since <EditableText /> calls target.blur()
input.simulate('blur');
},
},
});
expect(input.get(0).value).to.equal('Hello');
done();
});

So lots of different opinions here. The only thing that worked for me was none of the above, it was using input.props().value. I hope that helps.

I'm using react with TypeScript and the following worked for me
wrapper.find('input').getDOMNode<HTMLInputElement>().value = 'Hello';
wrapper.find('input').simulate('change');
Setting the value directly
wrapper.find('input').instance().value = 'Hello'`
was causing me a compile warning.

I am using create-react-app which comes with jest by default and enzyme 2.7.0.
This worked for me:
const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input')[index]; // where index is the position of the input field of interest
input.node.value = 'Change';
input.simulate('change', input);
done();

None of the above worked for me. This is what worked for me on Enzyme ^3.1.1:
input.instance().props.onChange(({ target: { value: '19:00' } }));
Here is the rest of the code for context:
const fakeHandleChangeValues = jest.fn();
const fakeErrors = {
errors: [{
timePeriod: opHoursData[0].timePeriod,
values: [{
errorIndex: 2,
errorTime: '19:00',
}],
}],
state: true,
};
const wrapper = mount(<AccessibleUI
handleChangeValues={fakeHandleChangeValues}
opHoursData={opHoursData}
translations={translationsForRendering}
/>);
const input = wrapper.find('#input-2').at(0);
input.instance().props.onChange(({ target: { value: '19:00' } }));
expect(wrapper.state().error).toEqual(fakeErrors);

In case anyone is struggling, I found the following working for me
const wrapper = mount(<NewTask {...props} />); // component under test
const textField = wrapper.find(TextField);
textField.props().onChange({ target: { value: 'New Task 2' } })
textField.simulate('change');
// wrapper.update() didn't work for me, need to find element again
console.log(wrapper.find(TextField).props()); // New Task 2
It seems that you need to define what happens in the change event first and then simulate it (instead of simulating the change event with data)

This works for me using enzyme 2.4.1:
const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input');
console.log(input.node.value);

None of the solutions above worked for me because I was using Formik and I needed to mark the field "touched" along with changing the field value. Following code worked for me.
const emailField = orderPageWrapper.find('input[name="email"]')
emailField.simulate('focus')
emailField.simulate('change', { target: { value: 'test#example.com', name: 'email' } })
emailField.simulate('blur')

In my case i was using ref callbacks,
<input id="usuario" className="form-control" placeholder="Usuario"
name="usuario" type="usuario"
onKeyUp={this._validateMail.bind(this)}
onChange={()=> this._validateMail()}
ref={(val) =>{ this._username = val}}
>
To obtain the value.
So enzyme will not change the value of this._username.
So i had to:
login.node._username.value = "mario#com.com";
user.simulate('change');
expect(login.state('mailValid')).toBe(true);
To be able to set the value then call change . And then assert.

here is my code..
const input = MobileNumberComponent.find('input')
// when
input.props().onChange({target: {
id: 'mobile-no',
value: '1234567900'
}});
MobileNumberComponent.update()
const Footer = (loginComponent.find('Footer'))
expect(Footer.find('Buttons').props().disabled).equals(false)
I have update my DOM with componentname.update()
And then checking submit button validation(disable/enable) with length 10 digit.

I solved in a very simple way:
Set the value from props:
const wrapper: ShallowWrapper = shallow(<ProfileViewClass name: 'Sample Name' />);
Html code:
<input type='text' defaultValue={props.name} className='edituser-name' />
Access the attribute from wrapper.find(element).props().attribute-name:
it('should render user name', () => {
expect(wrapper.find('.edituser-name').props().defaultValue).toContain(props.name);
});
Cheers

This worked for me:
let wrapped = mount(<Component />);
expect(wrapped.find("input").get(0).props.value).toEqual("something");

I use Wrapper's setValue[https://vue-test-utils.vuejs.org/api/wrapper/#setvalue-value] method to set value.
inputA = wrapper.findAll('input').at(0)
inputA.setValue('123456')

.simulate() doesn't work for me somehow, I got it working with just accessing the node.value without needing to call .simulate(); in your case:
const wrapper = mount(<EditableText defaultValue="Hello" />);
const input = wrapper.find('input').at(0);
// Get the value
console.log(input.node.value); // Hello
// Set the value
input.node.value = 'new value';
// Get the value
console.log(input.node.value); // new value
Hope this helps for others!

Related

Dart Testing with Riverpod StateNotifierProvider and AsyncValue as state

This is my first app with Dart/Flutter/Riverpod, so any advice or comment about the code is welcome.
I'm using Hive as embedded db so the initial value for the provider's state is loaded asynchronously and using an AsyncValue of riverpod to wrapped it.
The following code works but I've got some doubts about the testing approach, so I would like to confirm if I'm using the Riverpod lib as It supposed to be used.
This is my provider with its deps (Preferences is a HiveObject to store app general config data):
final hiveProvider = FutureProvider<HiveInterface>((ref) async {
return await App.setUp();
});
final prefBoxProvider = FutureProvider<Box<Preferences>>((ref) async {
final HiveInterface hive = await ref.read(hiveProvider.future);
return hive.openBox<Preferences>("preferences");
});
class PreferencesNotifier extends StateNotifier<AsyncValue<Preferences>> {
late Box<Preferences> prefBox;
PreferencesNotifier(Future<Box<Preferences>> prefBoxFuture): super(const AsyncValue.loading()) {
prefBoxFuture.then((value) {
prefBox = value;
_loadCurrentPreferences();
});
}
void _loadCurrentPreferences() {
Preferences pref = prefBox.get(0) ?? Preferences();
state = AsyncValue.data(pref);
}
Future<void> save(Preferences prefs) async {
await prefBox.put(0, prefs);
state = AsyncValue.data(prefs);
}
Preferences? get preferences {
return state.when(data: (value) => value,
error: (_, __) => null,
loading: () => null);
}
}
final preferencesProvider = StateNotifierProvider<PreferencesNotifier, AsyncValue<Preferences>>((ref) {
return PreferencesNotifier(ref.read(prefBoxProvider.future));
});
And the following is the test case, I'm mocking the Hive box provider (prefBoxProvider):
class Listener extends Mock {
void call(dynamic previous, dynamic value);
}
Future<Box<Preferences>> prefBoxTesting() async {
final hive = await App.setUp();
Box<Preferences> box = await hive.openBox<Preferences>("testing_preferences");
await box.clear();
return box;
}
void main() {
test('Preferences value changes', () async {
final container = ProviderContainer(overrides: [
prefBoxProvider.overrideWithValue(AsyncValue.data(await prefBoxTesting()))
],);
addTearDown(() {
container.dispose();
Hive.deleteBoxFromDisk("testing_preferences");
});
final listener = Listener();
container.listen<AsyncValue<Preferences>>(
preferencesProvider,
listener,
fireImmediately: true,
);
verify(listener(null, const TypeMatcher<AsyncLoading>())).called(1);
verifyNoMoreInteractions(listener);
// Next line waits until we have a value for preferences attribute
await container.read(preferencesProvider.notifier).stream.first;
verify(listener(const TypeMatcher<AsyncLoading>(), const TypeMatcher<AsyncData>())).called(1);
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
verify(listener(const TypeMatcher<AsyncData>(), const TypeMatcher<AsyncData>())).called(1);
verifyNoMoreInteractions(listener);
final name = container.read(preferencesProvider.notifier).preferences!.currentListName;
expect(name, equals('Lista1'));
});
}
I've used as reference the official docs about testing Riverpod and the GitHub issue related with AsyncValues
Well, I found some problems to verify that the listener is called with the proper values, I used the TypeMatcher just to verify that the state instance has got the proper type and I check ("manually") the value of the wrapped object's attribute if It's the expected one. Is there a better way to achieve this ?
Finally, I didn't find too many examples with StateNotifier and AsyncValue as state type, Is there a better approach to implement providers that are initialized with deferred data ?
I didn't like too much my original approach so I created my own Matcher to compare wrapped values in AsyncValue instances:
class IsWrappedValueEquals extends Matcher {
final dynamic value;
IsWrappedValueEquals(this.value);
#override
bool matches(covariant AsyncValue actual, Map<dynamic, dynamic> matchState) =>
equals(actual.value).matches(value, matchState);
#override
Description describe(Description description) => description.add('Is wrapped value equals');
}
In the test, the final part is a bit different:
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
// the following line is the new one
verify(listener(IsWrappedValueEquals(Preferences()), IsWrappedValueEquals(preferences))).called(1);
verifyNoMoreInteractions(listener);
}
I prefer my custom Matcher to the original code, but I feel that there are too many custom code to test something, apparently, common.
If anyone can tell me a better solution for this case, It'd be great.

How to test whether component contains string?

I am trying to get my head around jest. This is my test:
test('whether contains className', () => {
let list = [
{
id: 12,
name: 'two',
completed: true
}
];
const wrapper = shallow(
<Todos todos={list}>
</Todos>
);
expect(wrap).toMatch(/strikethrough/);
});
How can I check whether a component contains a (sub)string inside the component?
You need to get the rendered text of the current render tree with .text() method.
expect(wrapper.text()).toMatch(/strikethrough/)
You can also use
expect('Something').toContain('thing');
jest-extended package has extension toInclude
expect('hello world').toInclude('ell');
expect('hello world').not.toInclude('bob');

How to simulate a key up Event from an input in an Angular -cli 4 unit test

In vanilla JS you can simulate a key up on an input by doing:
testComponent.dispatchEvent(new Event("keyup"))
However doing this in the angular-cli unit test or in the console doesn't trigger this function in my component, which responds to key events by:
#HostListener('keyup', ['$event'])
onKeyUp(event: KeyboardEvent) {
Any ideas?
You should create an event
const event = new KeyboardEvent('keyup', {
bubbles : true, cancelable : true, shiftKey : false
});
And then get the reference of the debugElement using css selector
const input = debugElement.query(By.css('#id_of_element'));
And then reference of native html element from the previous one
const inputElement = input.nativeElement;
Assign the value for the native element as , now the text field value contains 12.
inputElement.value = 12;
finally dispatch the key up event
inputElement.dispatchEvent(event);
it will trigger the function
Dont forget to add the following line in before each and make sure you define debugElement
debugElement = fixture.debugElement;
Hope it helps
I my case somehow value was empty to I had to explicitly define it:
function generateKeyUpEvent(value: string): KeyboardEvent {
const event: KeyboardEvent = new KeyboardEvent('keyup', { bubbles: true, cancelable: true });
Object.defineProperty(event, 'target', { value: { value } });
return event;
}
and then despatch in my test case:
component.input.nativeElement.dispatchEvent(generateKeyUpEvent('a'));

Unit testing an AngularJS (Smart Table) directive

I am working with "Smart Table" and will be using their example plugin where a checkbox selects a row in a table: http://lorenzofox3.github.io/smart-table-website/#section-custom
I am writing a unit test for this directive, code below, this is failing. Has anyone written a unit test for this code or could help direct me as to where I am going wrong and if I am actually testing the correct logic?
Directive:
myApp.directive('csSelect', function () {
return {
require: '^stTable',
template: '',
scope: {
row: '=csSelect'
},
link: function (scope, element, attr, ctrl) {
element.bind('change', function (evt) {
scope.$apply(function () {
ctrl.select(scope.row, 'multiple');
});
});
scope.$watch('row.isSelected', function (newValue, oldValue) {
if (newValue === true) {
element.parent().addClass('st-selected');
} else {
element.parent().removeClass('st-selected');
}
});
}
};
});
Unit test:
describe('csSelect',function(){
var scope, element, attr, ctrl;
beforeEach(module('myApp.selectorresult'));
beforeEach(inject(function($rootScope, $compile) {
elm = angular.element(
'<td cs-select="row" class="ng-isolate-scope">' +
'<input type="checkbox">' +
'</td>');
scope = $rootScope;
$compile(elm)(scope);
scope.$digest();
}));
it('should create selectable input',function(){
console.log(elm.find('input'));
var checkbox = elm.find('input');
expect(checkbox.length).toBe(1);
});
});
You need to mock out the stTableController with $controllerProvider before you set up beforeEach(inject...
Check out the test spec for the pagination directive (https://github.com/lorenzofox3/Smart-Table/blob/master/test/spec/stPagination.spec.js), which also requires 'stTable'. It's a good example of how to provide the 'stTableController' with the functions you need from it.
For anyone still having this issue. I hope this helps.
I was struggling with this for ages. I tried mocking the stTableController, I tried adding the vendor files to the karma.conf.js files among other things but could not get any tests to pass.
It seemed that when I removed the require: '^stTable' the tests would pass no problem, but with it in, all tests would fail. I couldn't remove this as this would break my code.
So what I finally found was that all I had to do was add st-table to my element in the spec.js file.
So if my element was
var element = angular.element('<my-component></my-component');
I had to make it
var element = angular.element('<my-component st-table></my-component>');
After that, all tests were passing.

How can I simulate blur when testing directives in angularjs?

The problem
I am trying to test some directives (code for both below). One of them is an "email" (called "epost" in the code(norwegian)) directive. The solution to this should work for all of them, so I am keeping it to this one for now.
Technologies: Angularjs, Jasmine, Requirejs, (grunt & karma running in Chrome)
The directive validates email addresses in two ways; on upshift and on blur. I can test the upshift without problems as you can see in the test below, but I can't figure out how to simulate a blur so the bind('blur') in the directive runs.
What I have done
I have tried to catch the compiled element like this:
elem = angular.element(html);
element = $compile(elem)($scope);
And then in the test i tried several permutations to trigger the blur with a console log just inside the bind function in the directive. None of the below works. It does not trigger.
elem.trigger('blur');
element.trigger('blur');
elem.triggerHandler('blur');
element.triggerHandler('blur');
element.blur();
elem.blur();
I based the injection and setup on this: To test a custom validation angularjs directive
The email directive in angularjs wrapped in requirejs
define(function() {
var Directive = function() {
return {
require: 'ngModel',
link: function(scope, elem, attrs, ctrl) {
var pattern = /^[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/;
elem.bind('blur', function() {
scope.$apply(function () {
if (!elem.val() || pattern.test(elem.val())) {
ctrl.$setValidity('epost', true);
} else {
ctrl.$setValidity('epost', false);
}
});
});
ctrl.$parsers.unshift(function(viewValue) {
if (pattern.test(viewValue)) {
ctrl.$setValidity('epost', true);
return viewValue;
} else {
return undefined;
}
});
}
};
};
return Directive;
});
The test (using jasmine and requirejs)
define([
'Angular',
'AngularMocks',
], function () {
describe('Directives', function () {
var $scope;
var form;
beforeEach(module('common'));
beforeEach(function () {
var html = '<form name="form">';
html += '<input type="text" id="epost" name="epost" epost="" ng-model="model.epost"/>';
html += '</form>';
inject(function ($compile, $rootScope) {
$scope = $rootScope.$new();
$scope.model = {
epost: null
};
// Compile the element, run digest cycle
var elem = angular.element(html);
$compile(elem)($scope);
$scope.$digest();
form = $scope.form;
});
});
describe('(epost) Given an input field hooked up with the email directive', function () {
var validEmail = 'a#b.no';
var invalidEmail = 'asdf#asdf';
it('should bind data to model and be valid when email is valid on upshift', function () {
form.epost.$setViewValue(validEmail);
expect($scope.model.epost).toBe(validEmail);
expect(form.epost.$valid).toBe(true);
});
});
});
});
I have been able to figure out where I went wrong after some breakpoint debugging.
The "element" item I get out using the approach described in the top of the question is not actually the directive it self. It's an object which wraps the form and the directive.
Like this
{ 0: // The form
{ 0: // The directive (input element)
{
}
}
}
To actually simulate a blur on the directive it self, I did something like this
var directiveElement = $(element[0][0]);
directiveElement.blur();
After getting the element I wanted, and wrapping it in a jQuery object (may be optional), it worked like a charm. I then used the approach like in the test in the question with $setViewValue and checked the model value like this.
form.epost.$setViewValue('a#b.no');
directiveElement.blur();
expect($scope.model.epost).toBe('a#b.no');
expect($scope.form.epost.$valid).toBeTruthy();
Hope this could be of help to others trying to figure the directive testing out.
I too ran into a similar problem and it mystified me. My solution was to use JQuery to get the input and then use angular.element(input).triggerHandler('blur') to make it work. This is odd to me because I do not have to do this with the click event.
spyOn(controller, 'setRevenueIsInvalid');
var sugarRow = $(element).find('tr#ve_id_5')[0];
var amount = $(sugarRow).find('input.amount')[0];
angular.element(amount).triggerHandler('blur');
expect(controller.setRevenueIsInvalid).toHaveBeenCalled();