Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit a4423f8

Browse files
test(drawer): Migrate drawer unit tests to Jasmine (#5536)
PiperOrigin-RevId: 292135977 Co-authored-by: Material Web Copybara Robot <[email protected]>
1 parent 8df1270 commit a4423f8

File tree

9 files changed

+790
-727
lines changed

9 files changed

+790
-727
lines changed

karma.conf.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const istanbulInstrumenterLoader = {
8585
/data-table\/.*$/,
8686
/dialog\/.*$/,
8787
/dom\/.*$/,
88+
/drawer\/.*$/,
8889
/floating-label\/.*$/,
8990
/form-field\/.*$/,
9091
/icon-button\/.*$/,
@@ -134,7 +135,7 @@ const mochaConfig = {
134135
'emitWarning': false,
135136
'thresholds': {
136137
statements: 95,
137-
branches: 92.5,
138+
branches: 92,
138139
lines: 95,
139140
functions: 95,
140141
},
@@ -184,6 +185,7 @@ const jasmineConfig = {
184185
'packages/!(mdc-data-table)/**/*',
185186
'packages/!(mdc-dialog)/**/*',
186187
'packages/!(mdc-dom)/**/*',
188+
'packages/!(mdc-drawer)/**/*',
187189
'packages/!(mdc-floating-label)/**/*',
188190
'packages/!(mdc-form-field)/**/*',
189191
'packages/!(mdc-icon-button)/**/*',
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google Inc.
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
25+
import {getFixture} from '../../../../testing/dom';
26+
import {verifyDefaultAdapter} from '../../../../testing/helpers/foundation';
27+
import {setUpFoundationTest, setUpMdcTestEnvironment} from '../../../../testing/helpers/setup';
28+
import {cssClasses, strings} from '../../constants';
29+
import {MDCDismissibleDrawerFoundation} from '../foundation';
30+
31+
describe('MDCDismissibleDrawerFoundation', () => {
32+
setUpMdcTestEnvironment();
33+
34+
const setupTest = () => {
35+
const {foundation, mockAdapter} =
36+
setUpFoundationTest(MDCDismissibleDrawerFoundation);
37+
return {foundation, mockAdapter};
38+
};
39+
40+
it('exports strings', () => {
41+
expect('strings' in MDCDismissibleDrawerFoundation).toBe(true);
42+
expect(MDCDismissibleDrawerFoundation.strings).toEqual(strings);
43+
});
44+
45+
it('exports cssClasses', () => {
46+
expect('cssClasses' in MDCDismissibleDrawerFoundation).toBe(true);
47+
expect(MDCDismissibleDrawerFoundation.cssClasses).toEqual(cssClasses);
48+
});
49+
50+
it('defaultAdapter returns a complete adapter implementation', () => {
51+
verifyDefaultAdapter(MDCDismissibleDrawerFoundation, [
52+
'hasClass',
53+
'addClass',
54+
'removeClass',
55+
'elementHasClass',
56+
'saveFocus',
57+
'restoreFocus',
58+
'focusActiveNavigationItem',
59+
'notifyClose',
60+
'notifyOpen',
61+
'trapFocus',
62+
'releaseFocus',
63+
]);
64+
});
65+
66+
it('#destroy cancels pending rAF for #open', () => {
67+
const {foundation, mockAdapter} = setupTest();
68+
69+
foundation.open();
70+
foundation.destroy();
71+
72+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(cssClasses.OPENING);
73+
});
74+
75+
it('#destroy cancels pending setTimeout for #open', () => {
76+
const {foundation, mockAdapter} = setupTest();
77+
78+
foundation.open();
79+
jasmine.clock().tick(1);
80+
foundation.destroy();
81+
82+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(cssClasses.OPENING);
83+
});
84+
85+
it('#open does nothing if drawer is already open', () => {
86+
const {foundation, mockAdapter} = setupTest();
87+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
88+
foundation.open();
89+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
90+
});
91+
92+
it('#open does nothing if drawer is already opening', () => {
93+
const {foundation, mockAdapter} = setupTest();
94+
mockAdapter.hasClass.withArgs(cssClasses.OPENING).and.returnValue(true);
95+
foundation.open();
96+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
97+
});
98+
99+
it('#open does nothing if drawer is closing', () => {
100+
const {foundation, mockAdapter} = setupTest();
101+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(true);
102+
foundation.open();
103+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
104+
});
105+
106+
it('#open adds appropriate classes and saves focus', () => {
107+
const {foundation, mockAdapter} = setupTest();
108+
109+
foundation.open();
110+
jasmine.clock().tick(50);
111+
112+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.OPEN);
113+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.ANIMATE);
114+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.OPENING);
115+
expect(mockAdapter.saveFocus).toHaveBeenCalledTimes(1);
116+
});
117+
118+
it('#close does nothing if drawer is already closed', () => {
119+
const {foundation, mockAdapter} = setupTest();
120+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(false);
121+
foundation.close();
122+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
123+
});
124+
125+
it('#close does nothing if drawer is opening', () => {
126+
const {foundation, mockAdapter} = setupTest();
127+
mockAdapter.hasClass.withArgs(cssClasses.OPENING).and.returnValue(true);
128+
foundation.close();
129+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
130+
});
131+
132+
it('#close does nothing if drawer is closing', () => {
133+
const {foundation, mockAdapter} = setupTest();
134+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(true);
135+
foundation.close();
136+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(jasmine.any(String));
137+
});
138+
139+
it('#close adds appropriate classes', () => {
140+
const {foundation, mockAdapter} = setupTest();
141+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
142+
foundation.close();
143+
144+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.CLOSING);
145+
});
146+
147+
it(`#isOpen returns true when it has ${cssClasses.OPEN} class`, () => {
148+
const {foundation, mockAdapter} = setupTest();
149+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
150+
expect(foundation.isOpen()).toBe(true);
151+
});
152+
153+
it(`#isOpen returns false when it lacks ${cssClasses.OPEN} class`, () => {
154+
const {foundation, mockAdapter} = setupTest();
155+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(false);
156+
expect(foundation.isOpen()).toBe(false);
157+
});
158+
159+
it(`#isOpening returns true when it has ${cssClasses.OPENING} class`, () => {
160+
const {foundation, mockAdapter} = setupTest();
161+
mockAdapter.hasClass.withArgs(cssClasses.ANIMATE).and.returnValue(true);
162+
mockAdapter.hasClass.withArgs(cssClasses.OPENING).and.returnValue(true);
163+
expect(foundation.isOpening()).toBe(true);
164+
});
165+
166+
it('#isOpening returns true when drawer just start animate', () => {
167+
const {foundation, mockAdapter} = setupTest();
168+
mockAdapter.hasClass.withArgs(cssClasses.ANIMATE).and.returnValue(true);
169+
mockAdapter.hasClass.withArgs(cssClasses.OPENING).and.returnValue(false);
170+
expect(foundation.isOpening()).toBe(true);
171+
});
172+
173+
it(`#isOpening returns false when it lacks ${cssClasses.OPENING} class`,
174+
() => {
175+
const {foundation, mockAdapter} = setupTest();
176+
mockAdapter.hasClass.withArgs(cssClasses.ANIMATE).and.returnValue(false);
177+
mockAdapter.hasClass.withArgs(cssClasses.OPENING).and.returnValue(false);
178+
expect(foundation.isOpening()).toBe(false);
179+
});
180+
181+
it(`#isClosing returns true when it has ${cssClasses.CLOSING} class`, () => {
182+
const {foundation, mockAdapter} = setupTest();
183+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(true);
184+
expect(foundation.isClosing()).toBe(true);
185+
});
186+
187+
it(`#isClosing returns false when it lacks ${cssClasses.CLOSING} class`,
188+
() => {
189+
const {foundation, mockAdapter} = setupTest();
190+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(false);
191+
expect(foundation.isClosing()).toBe(false);
192+
});
193+
194+
it('#handleKeydown does nothing when event key is not the escape key', () => {
195+
const {foundation, mockAdapter} = setupTest();
196+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
197+
foundation.handleKeydown({key: 'Shift'});
198+
199+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(cssClasses.CLOSING);
200+
});
201+
202+
it('#handleKeydown does nothing when event keyCode is not 27', () => {
203+
const {foundation, mockAdapter} = setupTest();
204+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
205+
foundation.handleKeydown({keyCode: 11});
206+
207+
expect(mockAdapter.addClass).not.toHaveBeenCalledWith(cssClasses.CLOSING);
208+
});
209+
210+
it('#handleKeydown calls close when event key is the escape key', () => {
211+
const {foundation, mockAdapter} = setupTest();
212+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
213+
foundation.handleKeydown({key: 'Escape'});
214+
215+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.CLOSING);
216+
});
217+
218+
it('#handleKeydown calls close when event keyCode is 27', () => {
219+
const {foundation, mockAdapter} = setupTest();
220+
mockAdapter.hasClass.withArgs(cssClasses.OPEN).and.returnValue(true);
221+
foundation.handleKeydown({keyCode: 27});
222+
223+
expect(mockAdapter.addClass).toHaveBeenCalledWith(cssClasses.CLOSING);
224+
});
225+
226+
it('#handleTransitionEnd removes all animating classes', () => {
227+
const {foundation, mockAdapter} = setupTest();
228+
const mockEventTarget = getFixture(`<div class="foo">bar</div>`);
229+
mockAdapter.elementHasClass.withArgs(mockEventTarget, cssClasses.ROOT)
230+
.and.returnValue(true);
231+
foundation.handleTransitionEnd({target: mockEventTarget});
232+
expect(mockAdapter.removeClass).toHaveBeenCalledWith(cssClasses.ANIMATE);
233+
expect(mockAdapter.removeClass).toHaveBeenCalledWith(cssClasses.OPENING);
234+
expect(mockAdapter.removeClass).toHaveBeenCalledWith(cssClasses.CLOSING);
235+
});
236+
237+
it('#handleTransitionEnd removes open class after closing, restores the focus and calls notifyClose',
238+
() => {
239+
const {foundation, mockAdapter} = setupTest();
240+
const mockEventTarget = getFixture(`<div>root</div>`);
241+
mockAdapter.elementHasClass.withArgs(mockEventTarget, cssClasses.ROOT)
242+
.and.returnValue(true);
243+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(true);
244+
245+
foundation.handleTransitionEnd({target: mockEventTarget});
246+
expect(mockAdapter.removeClass).toHaveBeenCalledWith(cssClasses.OPEN);
247+
expect(mockAdapter.restoreFocus).toHaveBeenCalledTimes(1);
248+
expect(mockAdapter.notifyClose).toHaveBeenCalledTimes(1);
249+
});
250+
251+
it(`#handleTransitionEnd doesn\'t remove open class after opening,
252+
focuses on active navigation item and calls notifyOpen`,
253+
() => {
254+
const {foundation, mockAdapter} = setupTest();
255+
const mockEventTarget = getFixture(`<div>root</div>`);
256+
mockAdapter.elementHasClass.withArgs(mockEventTarget, cssClasses.ROOT)
257+
.and.returnValue(true);
258+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(false);
259+
260+
foundation.handleTransitionEnd({target: mockEventTarget});
261+
expect(mockAdapter.removeClass)
262+
.not.toHaveBeenCalledWith(cssClasses.OPEN);
263+
expect(mockAdapter.focusActiveNavigationItem).toHaveBeenCalledTimes(1);
264+
expect(mockAdapter.notifyOpen).toHaveBeenCalledTimes(1);
265+
});
266+
267+
it('#handleTransitionEnd doesn\'t do anything if event is not triggered by root element',
268+
() => {
269+
const {foundation, mockAdapter} = setupTest();
270+
const mockEventTarget = getFixture(`<div>child</div>`);
271+
mockAdapter.elementHasClass.withArgs(mockEventTarget, cssClasses.ROOT)
272+
.and.returnValue(false);
273+
274+
foundation.handleTransitionEnd({target: mockEventTarget});
275+
expect(mockAdapter.removeClass)
276+
.not.toHaveBeenCalledWith(cssClasses.OPEN);
277+
expect(mockAdapter.removeClass)
278+
.not.toHaveBeenCalledWith(cssClasses.ANIMATE);
279+
expect(mockAdapter.notifyOpen).not.toHaveBeenCalled();
280+
expect(mockAdapter.notifyClose).not.toHaveBeenCalled();
281+
});
282+
283+
it('#handleTransitionEnd doesn\'t do anything if event is emitted with a non-element target',
284+
() => {
285+
const {foundation, mockAdapter} = setupTest();
286+
287+
foundation.handleTransitionEnd({target: {}});
288+
expect(mockAdapter.elementHasClass)
289+
.not.toHaveBeenCalledWith(jasmine.anything(), jasmine.any(String));
290+
expect(mockAdapter.removeClass)
291+
.not.toHaveBeenCalledWith(cssClasses.OPEN);
292+
expect(mockAdapter.removeClass)
293+
.not.toHaveBeenCalledWith(cssClasses.ANIMATE);
294+
expect(mockAdapter.notifyOpen).not.toHaveBeenCalled();
295+
expect(mockAdapter.notifyClose).not.toHaveBeenCalled();
296+
});
297+
298+
it('#handleTransitionEnd restores the focus.', () => {
299+
const {foundation, mockAdapter} = setupTest();
300+
const mockEventTarget = getFixture(`<div class="foo">bar</div>`);
301+
302+
mockAdapter.elementHasClass.withArgs(mockEventTarget, cssClasses.ROOT)
303+
.and.returnValue(true);
304+
mockAdapter.hasClass.withArgs(cssClasses.CLOSING).and.returnValue(true);
305+
foundation.handleTransitionEnd({target: mockEventTarget});
306+
307+
expect(mockAdapter.restoreFocus).toHaveBeenCalled();
308+
});
309+
});

0 commit comments

Comments
 (0)