Reputation: 157
I have a simple button component in React
TypeScript
, I using fromEvent
method to translate click event to Observable
wrapper of RxJs
it is defiened fromEvent(el:HTMLElement, eventType:string)
in RxJS
type NullableObservarbel = Observable<any> | null
type NUllabe = HTMLElement | null
export const makeObservable = (el:NUllabe, eventType:string):NullableObservarbel => el ? fromEvent(el, eventType) : null;
type Props = {
interval?: number // timee interval in millisecond
label?:string
}
const Button:SFC<Props> = ({label}:Props) => {
const btn = useRef(null)
useEffect(() => {
if(btn.current !== null){
const observable = makeObservable(btn.current, 'click')
}
}, [])
return <button ref={btn}>{label}</button>
}
As you can see makeObservable
method,
HTMLElement
and string as an argument it should return Observable
But none of this test is not working as expected.
This is the test first one always pass even their is no el passed, the second one always fails.
import React from 'react'
import { shallow, mount } from 'enzyme'
import Button, {makeObservable} from './Button'
import {Observable} from 'rxjs'
describe('Observable', () => {
it('should create observable', () => {
const wrapper = shallow(<Button />)
const el = wrapper.find('button')
const observable = makeObservable(el, 'click') // here i have the issue
expect(observable instanceof Observable).toBe(true)
})
it('should create observable', () => {
const wrapper = shallow(<Button />)
const el = wrapper.find('button')
const observable = makeObservable(el, 'click')
let _six = 0;
if(observable){
observable
.pipe(map(e => 6))
.subscribe(s => {
_six = s
})
}
el.simulate('click')
expect(_six).toEqual(6) // fails always
})
})
The biggest problem is I can't get HTMLElement
type from wrapper
so i just use find
button and pass the result as an element, But
expect(observable instanceof Observable).toBe(true)
this line is always passing event if I pass null
as argument for el
to makeObservable
.
Please help me to test properly these scenarios.
Upvotes: 1
Views: 1228
Reputation: 3548
You can try with testing-library/react
instead of using enzyme
shallow
, I have added data-testid="btn"
attribute to your component then from the test I use
testing-library/react
to render your button then get your HTMLElement
of your button I used this testid
attribute.
It worked as expected in test and browser, pls check the source code.
Button.tsx
import React, { SFC, useRef, useEffect, useState, RefObject} from 'react'
import {fromEvent, Observable} from 'rxjs'
import {map, debounceTime} from 'rxjs/operators'
type NullableObservarbel = Observable<any> | null;
type NUllabe = HTMLButtonElement | null; // more precise type
export const makeObservable = (el:NUllabe, eventType:string):NullableObservarbel => el ? fromEvent(el, eventType) : null;
type Props = {
interval?: number // timee interval in millisecond
label?:string
}
export type Result = [RefObject<HTMLButtonElement>, number]
// decoupled it from Button function body because you can test this later.
export const useEls = ():Result => {
const btn: RefObject<HTMLButtonElement> = useRef(null)
const [count, updateCount] = useState<number>(0)
useEffect(() => {
const el = btn ? btn.current : null
if(el){
updateCount(1)
let _count = count
const observerble = makeObservable(el, 'click');
if(observerble){
observerble.pipe(
map(e => _count++),
//debounceTime(400)
).subscribe(c => updateCount(c))
}
}
}, [])
return [btn, count]
}
const Button:SFC<Props> = (props:Props) => {
const [btn, count] = useEls()
return <button data-testid="btn" ref={btn}>Hello {count}</button>
}
export default Button
Button.test.ts
import React from 'react'
import Button, {makeObservable} from './Button'
import {Observable} from 'rxjs'
import {map, debounceTime} from 'rxjs/operators'
import {render, fireEvent} from '@testing-library/react'
describe('Observable', () => {
it('should create observable', () => {
const {getByTestId} = render(<Button/>)
const el = getByTestId('btn') as HTMLButtonElement
const observable = makeObservable(el, 'click')
expect(observable instanceof Observable).toBe(true)
})
it('should return false', () => {
const observable = makeObservable(null, 'click')
expect(observable instanceof Observable).toBe(false)
})
it('Should subscribe observable', (done) => {
const {getByTestId} = render(<Button/>)
const el = getByTestId('btn') as HTMLButtonElement
const observerble = makeObservable(el, 'click');
if(observerble){
let count = 1
observerble
.pipe(
map(e => count++),
debounceTime(400) // delay clicks if you want
)
.subscribe(s => {
expect(s).toEqual(6)// test just inside the subscription
done();
})
fireEvent.click(el)
fireEvent.click(el)
fireEvent.click(el)
fireEvent.click(el)
fireEvent.click(el)
fireEvent.click(el)
}
})
})
This test is running as expected with simulated clicks and delays.
Upvotes: 3