Enzyme sub element has no 'find()' - enzyme

I have a simple test where I am searching on '.shop' elements, returning two of them, and then I want to get the first element and look at an embedded element : '.shop__title'.
See code :
describe('Shop Page', () => {
let wrapper
let store = createStore(reducers, mockStoreData);
beforeEach(() => {
wrapper = mount(
<Provider store={store}>
<Dashboard />
</Provider>,
);
});
afterEach(() => {
wrapper.unmount();
});
it('test ', () => {
let elems = wrapper.find('.shop');
expect(elems.length).toBe(2); //yes, i have two shops!
let e = elems.get(0);
e.find('.shop__title') //find function does not exist!!!
})
});
I am not quite sure what this 'e' element is (the first element in the array that I am looking at), but my IDE shows me this :
What sort of object is this? How do I get the 'find' and 'simulate' functions working on this element?

You need to use at instead of get
let e = elems.at(0);
e.find('.shop__title') //find function does not exist!!!
The problem is get returns you the ReactElement but at returns a ShallowWrapper which has the find method.

Related

Unit testing sessionStorage value in emberJS

I'm new to ember and trying to figure out how to unit test, using sinon, the sessionStorage based on url parameters when that page is visited. I've tried a few things but still can't get the desired result. It passes even if I change the 'sessionValue' without editing the query param.
Thank you in advance.
ember component
beforeModel(transition) {
//transition will contain an object containing a query parameter. '?userid=1234' and is set in the sessionStorage.
if(transition.queryparam.hasOwnProperty('userid')){
sessionStorage.setItem('user:id', transition.queryparam)
}
}
Ember test
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let store = {};
const mockLocalStorage = {
getItem: (key) => {
return key in store ? store[key] : null;
},
setItem: (key, value) => {
store[key] = `${value}`;
},
clear: () => {
store = {};
}
};
asserts.expect(1);
let spy = sinon.spy(sessionStorage, "setItem");
spy.calledWith(mockLocalStorage.setItem);
let stub = sinon.stub(sessionStorage, "getItem");
stub.calledWith(mockLocalStorage.getItem);
stub.returns(sessionValue);
await visit('/page?userid=1234');
mockLocalStorage.setItem(sessionKey, sessionValue);
assert.equal(mockLocalStorage.getItem(sessionKey), sessionValue, 'storage contains value');
})
Welcome to Ember!
There are many ways to test, and the below suggestion is one way (how I would approach interacting with the SessionStorage).
Instead of re-creating the SessionStorage API in your test, how do you feel about using a pre-made proxy around the Session Storage? (ie: "Don't mock what you don't own")
Using: https://github.com/CrowdStrike/ember-browser-services/#sessionstorage
Your app code would look like:
#service('browser/session-storage') sessionStorage;
beforeModel(transition) {
// ... details omitted ...
// note the addition of `this` -- the apis are entirely the same
// as SessionStorage
this.sessionStorage.setItem('user:id', ...)
}
then in your test:
module('Scenario Name', function (hooks) {
setupApplicationTest(hooks);
setupBrowserFakes(hooks, { sessionStorage: true });
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let sessionStorage = this.owner.lookup('browser/session-storage');
await visit('/page?userid=1234');
assert.equal(sessionStorage.getItem(sessionKey), '1234', 'storage contains value');
});
})
With this approach, sinon isn't even needed :)

How to write unit test with Higher Order Function with Jest in React JS

Hello i'm new to React and i'm trying to write a unit test on a Higher Order Functions with Jest and i don't know how to do it please can someone help me ? This is the code of my HIGHER ORDER FUNCTION below :
const updateSearchTopStoriesState = (hits, page) => (prevState) => {
const { searchKey, results } = prevState
const oldHits = results && results[searchKey]
? results[searchKey].hits
: []
const updatedHits = [
...oldHits,
...hits
]
// returning our previous state
return {
results: {
...results,
[searchKey]: { hits: updatedHits, page }
},
isLoading: false
}
}
export default updateSearchTopStoriesState
Without knowing WHAT you are trying to test, or WHAT the shape of any of the parameters are, this is near impossible to answer accurately. Here are a couple of unit tests I would write:
describe("updateSearchTopStoriesState", () => {
it("should return a function", () => {
expect(typeof updateSearchTopStoriesState()).toBe("function");
});
it("should return default return value", () => {
const { results, isLoading } = updateSearchTopStoriesState()({
searchKey: "test"
});
expect(results).toEqual({ test: { hits: [] } });
expect(isLoading).toBe(false);
});
});
In the sandbox I've started a third test that currently fails (admittedly likely due to my lack of context on the parameters, but should be passing based upon an internal implementation comment you left in the function code).
This should assist you in starting a unit test file for this function, but please comment if anything is unclear or this isn't quite what you are asking about.

Shorting list by name - Angular 5 + Firebase

I have created a service where I get all the elements of my database:
Service
getElements() {
return (this.eleList= this.firebase.list("elements"));
}
Component
eleList: Element[];
getBets() {
return this.databaseService
.getElements()
.snapshotChanges()
.subscribe(item => {
this.eleList= [];
item.forEach(element => {
let x = element.payload.toJSON();
x["$key"] = element.key;
this.eleList.push(x as Element);
});
});
}
With these two methods what I do is to store all my elements in this.eleList.
I would like to create a new method, named filterByName(name), where I would update this.eleList to an array which contains only the ones that contain namein the object, for example, this.eleList[1].name
I do not know if Firebase provides a way to short it, or I need to use Javascript/Typescript for it.
Firebase takes full advantage of the observables and async pipes.
You should take advantage of that :
eleList$ = new Subject();
getElements() {
this.this.firebase.list("elements")
.pipe(take(1))
.subscribe(list => this.eleList$.next(list));
}
getBets() {
this.databaseService
.getElements()
.snapshotChanges()
.pipe(
map(item => items.map(element => ({
...element.payload.toJSON(),
'$key': element.key
})))
)
.subscribe(elements => this.eleList$.next(list));
}
Now for a sorted list :
sortedList$ = this.eleList$.pipe(
map(elements => elements.filter(element => !!element.name))
);

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');

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

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!