Skip to content

Commit b81eb86

Browse files
mxschmitttaion
authored andcommitted
tests: increase test coverage on various components (react-bootstrap#3893)
1 parent 715364f commit b81eb86

File tree

5 files changed

+142
-45
lines changed

5 files changed

+142
-45
lines changed

src/Nav.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,4 @@ Nav.defaultProps = defaultProps;
145145
Nav.Item = NavItem;
146146
Nav.Link = NavLink;
147147

148-
Nav._Nav = Nav; // for Testing until enzyme is working with context
149-
150148
export default Nav;

src/Toast.js

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useEffect } from 'react';
1+
import React, { useEffect, useRef } from 'react';
22
import PropTypes from 'prop-types';
33
import classNames from 'classnames';
44

@@ -69,19 +69,28 @@ const Toast = ({
6969
innerRef,
7070
...props
7171
}) => {
72+
const delayRef = useRef(delay);
73+
const onCloseRef = useRef(onClose);
74+
75+
// We use refs for these, because we don't want to restart the autohide
76+
// timer in case these values change.
77+
delayRef.current = delay;
78+
onCloseRef.current = onClose;
79+
7280
useEffect(() => {
73-
if (autohide && show) {
74-
const timer = setTimeout(() => {
75-
onClose();
76-
}, delay);
77-
return () => {
78-
clearTimeout(timer);
79-
};
81+
if (!(autohide && show)) {
82+
return undefined;
8083
}
81-
return () => null;
82-
// also clear the Timeout of the delay has changed
83-
// eslint-disable-next-line react-hooks/exhaustive-deps
84-
}, [autohide, onClose, show]);
84+
85+
const autohideHandle = setTimeout(() => {
86+
onCloseRef.current();
87+
}, delayRef.current);
88+
89+
return () => {
90+
clearTimeout(autohideHandle);
91+
};
92+
}, [autohide, show]);
93+
8594
const useAnimation = Transition && animation;
8695
const toast = (
8796
<div

test/CarouselSpec.js

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ describe('<Carousel>', () => {
99
<Carousel.Item key={2}>Item 2 content</Carousel.Item>,
1010
];
1111

12-
it('Should show the correct item', () => {
12+
it('should show the correct item', () => {
1313
const wrapper = mount(<Carousel defaultActiveIndex={1}>{items}</Carousel>);
1414

1515
const carouselItems = wrapper.find('CarouselItem');
@@ -18,7 +18,7 @@ describe('<Carousel>', () => {
1818
assert.equal(carouselItems.at(1).is('.active'), true);
1919
});
2020

21-
it('Should show the correct item with defaultActiveIndex', () => {
21+
it('should show the correct item with defaultActiveIndex', () => {
2222
const wrapper = mount(<Carousel defaultActiveIndex={1}>{items}</Carousel>);
2323

2424
const carouselItems = wrapper.find('CarouselItem');
@@ -29,7 +29,7 @@ describe('<Carousel>', () => {
2929
wrapper.find('.carousel-indicators > li').length.should.equal(2);
3030
});
3131

32-
it('Should handle null children', () => {
32+
it('should handle null children', () => {
3333
const wrapper = mount(
3434
<Carousel defaultActiveIndex={1}>
3535
<Carousel.Item>Item 1 content</Carousel.Item>
@@ -47,7 +47,7 @@ describe('<Carousel>', () => {
4747
wrapper.find('.carousel-indicators > li').length.should.equal(2);
4848
});
4949

50-
it('Should call onSelect when indicator selected', done => {
50+
it('should call onSelect when indicator selected', done => {
5151
function onSelect(index) {
5252
expect(index).to.equal(0);
5353

@@ -66,7 +66,7 @@ describe('<Carousel>', () => {
6666
.simulate('click');
6767
});
6868

69-
it('Should call onSelect with direction', done => {
69+
it('should call onSelect with direction', done => {
7070
function onSelect(index, direction, event) {
7171
expect(index).to.equal(0);
7272
expect(direction).to.equal('prev');
@@ -87,7 +87,7 @@ describe('<Carousel>', () => {
8787
.simulate('click');
8888
});
8989

90-
it('Should show back button control on the first image if wrap is true', () => {
90+
it('should show back button control on the first image if wrap is true', () => {
9191
const wrapper = mount(
9292
<Carousel defaultActiveIndex={0} controls wrap>
9393
{items}
@@ -97,7 +97,7 @@ describe('<Carousel>', () => {
9797
wrapper.assertSingle('a.carousel-control-prev');
9898
});
9999

100-
it('Should show next button control on the last image if wrap is true', () => {
100+
it('should show next button control on the last image if wrap is true', () => {
101101
const wrapper = mount(
102102
<Carousel defaultActiveIndex={1} controls wrap>
103103
{items}
@@ -107,23 +107,23 @@ describe('<Carousel>', () => {
107107
wrapper.assertSingle('a.carousel-control-next');
108108
});
109109

110-
it('Should not show the prev button on the first image if wrap is false', () => {
110+
it('should not show the prev button on the first image if wrap is false', () => {
111111
mount(
112112
<Carousel defaultActiveIndex={0} controls wrap={false}>
113113
{items}
114114
</Carousel>,
115115
).assertNone('a.carousel-control-prev');
116116
});
117117

118-
it('Should not show the next button on the last image if wrap is false', () => {
118+
it('should not show the next button on the last image if wrap is false', () => {
119119
mount(
120120
<Carousel defaultActiveIndex={1} controls wrap={false}>
121121
{items}
122122
</Carousel>,
123123
).assertNone('a.carousel-control-next');
124124
});
125125

126-
it('Should allow user to specify a previous and next icon', () => {
126+
it('should allow user to specify a previous and next icon', () => {
127127
const wrapper = mount(
128128
<Carousel
129129
controls
@@ -142,7 +142,7 @@ describe('<Carousel>', () => {
142142
wrapper.assertSingle('.ficon-right');
143143
});
144144

145-
it('Should allow user to specify a previous and next SR label', () => {
145+
it('should allow user to specify a previous and next SR label', () => {
146146
const wrapper = mount(
147147
<Carousel
148148
controls
@@ -164,7 +164,7 @@ describe('<Carousel>', () => {
164164
assert.equal(labels.at(1).text(), 'Next awesomeness');
165165
});
166166

167-
it('Should not render labels when values are falsy', () => {
167+
it('should not render labels when values are falsy', () => {
168168
[null, ''].forEach(falsyValue => {
169169
const wrapper = mount(
170170
<Carousel
@@ -188,7 +188,7 @@ describe('<Carousel>', () => {
188188
});
189189
});
190190

191-
it('Should transition properly when slide animation is disabled', done => {
191+
it('should transition properly when slide animation is disabled', done => {
192192
const spy = sinon.spy();
193193
const wrapper = mount(
194194
<Carousel defaultActiveIndex={0} slide={false} onSelect={spy}>
@@ -211,7 +211,7 @@ describe('<Carousel>', () => {
211211
}, 150);
212212
});
213213

214-
it('Should render on update, active item > new child length', () => {
214+
it('should render on update, active item > new child length', () => {
215215
// default active is the 2nd item, which will be removed on
216216
// subsequent render
217217
let wrapper = mount(<Carousel defaultActiveIndex={1}>{items}</Carousel>);
@@ -234,8 +234,57 @@ describe('<Carousel>', () => {
234234
wrapper.find('div.carousel-item').length.should.equal(1);
235235
});
236236

237-
it('Should have div as default component', () => {
237+
it('should have div as default component', () => {
238238
const wrapper = mount(<Carousel>{items}</Carousel>);
239239
wrapper.find('div').length.should.equal(4);
240240
});
241+
242+
it('should go through the items after given seconds', () => {
243+
const clock = sinon.useFakeTimers();
244+
245+
try {
246+
const onSelectSpy = sinon.spy();
247+
const interval = 500;
248+
mount(
249+
<Carousel interval={interval} onSelect={onSelectSpy}>
250+
{items}
251+
</Carousel>,
252+
);
253+
clock.tick(interval * 2);
254+
expect(onSelectSpy).to.have.been.calledOnce;
255+
} finally {
256+
clock.restore();
257+
}
258+
});
259+
260+
it('should handle Keyboard events', () => {
261+
const clock = sinon.useFakeTimers();
262+
263+
try {
264+
const onSelectSpy = sinon.spy();
265+
const wrapper = mount(
266+
<Carousel interval={0} onSelect={onSelectSpy}>
267+
{items}
268+
</Carousel>,
269+
);
270+
271+
wrapper.simulate('keyDown', {
272+
key: 'ArrowRight',
273+
});
274+
clock.tick(50);
275+
expect(onSelectSpy).to.have.been.calledOnce;
276+
expect(onSelectSpy.getCall(0).args[0]).to.equal(1);
277+
278+
clock.tick(150);
279+
280+
wrapper.simulate('keyDown', {
281+
key: 'ArrowLeft',
282+
});
283+
clock.tick(50);
284+
expect(onSelectSpy).to.have.been.calledTwice;
285+
expect(onSelectSpy.getCall(1).args[0]).to.equal(0);
286+
} finally {
287+
clock.restore();
288+
}
289+
});
241290
});

test/ListGroupItemSpec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,22 @@ describe('<ListGroupItem>', () => {
4040
);
4141
});
4242
});
43+
44+
describe('onClick', () => {
45+
it('Should call on click', () => {
46+
const listGroupItemOnClick = sinon.spy();
47+
const wrapper = mount(<ListGroupItem onClick={listGroupItemOnClick} />);
48+
wrapper.find('div.list-group-item').simulate('click');
49+
expect(listGroupItemOnClick).to.be.calledOnce;
50+
});
51+
52+
it('Should not call if disabled', () => {
53+
const listGroupItemOnClick = sinon.spy();
54+
const wrapper = mount(
55+
<ListGroupItem onClick={listGroupItemOnClick} disabled />,
56+
);
57+
wrapper.find('div.list-group-item').simulate('click');
58+
expect(listGroupItemOnClick).not.to.have.been.called;
59+
});
60+
});
4361
});

test/ToastSpec.js

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { mount } from 'enzyme';
33

44
import Toast from '../src/Toast';
55

6-
describe('Toasts', () => {
7-
it('will render an entire toast', () => {
6+
describe('<Toast>', () => {
7+
it('should render an entire toast', () => {
88
mount(
99
<Toast>
1010
<Toast.Header />
@@ -16,7 +16,8 @@ describe('Toasts', () => {
1616
});
1717

1818
it('should trigger the onClose event after clicking on the close button', () => {
19-
let onCloseSpy = sinon.spy();
19+
const onCloseSpy = sinon.spy();
20+
2021
mount(
2122
<Toast onClose={onCloseSpy}>
2223
<Toast.Header>header-content</Toast.Header>
@@ -28,21 +29,43 @@ describe('Toasts', () => {
2829
.find('button')
2930
.simulate('click');
3031

31-
expect(onCloseSpy).to.be.calledOnce;
32+
expect(onCloseSpy).to.have.been.calledOnce;
3233
});
3334

3435
it('should trigger the onClose event after the autohide delay', () => {
35-
const clock = sinon.useFakeTimers({
36-
toFake: ['setTimeout'],
37-
});
38-
const onCloseSpy = sinon.spy();
39-
mount(
40-
<Toast onClose={onCloseSpy} delay={500} show autohide>
41-
<Toast.Header>header-content</Toast.Header>
42-
<Toast.Body>body-content</Toast.Body>
43-
</Toast>,
44-
);
45-
clock.tick(500);
46-
expect(onCloseSpy).to.be.calledOnce;
36+
const clock = sinon.useFakeTimers();
37+
38+
try {
39+
const onCloseSpy = sinon.spy();
40+
mount(
41+
<Toast onClose={onCloseSpy} delay={500} show autohide>
42+
<Toast.Header>header-content</Toast.Header>
43+
<Toast.Body>body-content</Toast.Body>
44+
</Toast>,
45+
);
46+
clock.tick(1000);
47+
expect(onCloseSpy).to.have.been.calledOnce;
48+
} finally {
49+
clock.restore();
50+
}
51+
});
52+
53+
it('should clearTimeout after unmount', () => {
54+
const clock = sinon.useFakeTimers();
55+
56+
try {
57+
const onCloseSpy = sinon.spy();
58+
const wrapper = mount(
59+
<Toast delay={500} onClose={onCloseSpy} show autohide>
60+
<Toast.Header>header-content</Toast.Header>
61+
<Toast.Body>body-content</Toast.Body>
62+
</Toast>,
63+
);
64+
wrapper.unmount();
65+
clock.tick(1000);
66+
expect(onCloseSpy).not.to.have.been.called;
67+
} finally {
68+
clock.restore();
69+
}
4770
});
4871
});

0 commit comments

Comments
 (0)