1/**
2 * Copyright 2020 Google Inc. All rights reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import { EventEmitter } from '../lib/cjs/puppeteer/common/EventEmitter.js';
18import sinon from 'sinon';
19import expect from 'expect';
20
21describe('EventEmitter', () => {
22  let emitter;
23
24  beforeEach(() => {
25    emitter = new EventEmitter();
26  });
27
28  describe('on', () => {
29    const onTests = (methodName: 'on' | 'addListener'): void => {
30      it(`${methodName}: adds an event listener that is fired when the event is emitted`, () => {
31        const listener = sinon.spy();
32        emitter[methodName]('foo', listener);
33        emitter.emit('foo');
34        expect(listener.callCount).toEqual(1);
35      });
36
37      it(`${methodName} sends the event data to the handler`, () => {
38        const listener = sinon.spy();
39        const data = {};
40        emitter[methodName]('foo', listener);
41        emitter.emit('foo', data);
42        expect(listener.callCount).toEqual(1);
43        expect(listener.firstCall.args[0]).toBe(data);
44      });
45
46      it(`${methodName}: supports chaining`, () => {
47        const listener = sinon.spy();
48        const returnValue = emitter[methodName]('foo', listener);
49        expect(returnValue).toBe(emitter);
50      });
51    };
52    onTests('on');
53    // we support addListener for legacy reasons
54    onTests('addListener');
55  });
56
57  describe('off', () => {
58    const offTests = (methodName: 'off' | 'removeListener'): void => {
59      it(`${methodName}: removes the listener so it is no longer called`, () => {
60        const listener = sinon.spy();
61        emitter.on('foo', listener);
62        emitter.emit('foo');
63        expect(listener.callCount).toEqual(1);
64        emitter.off('foo', listener);
65        emitter.emit('foo');
66        expect(listener.callCount).toEqual(1);
67      });
68
69      it(`${methodName}: supports chaining`, () => {
70        const listener = sinon.spy();
71        emitter.on('foo', listener);
72        const returnValue = emitter.off('foo', listener);
73        expect(returnValue).toBe(emitter);
74      });
75    };
76    offTests('off');
77    // we support removeListener for legacy reasons
78    offTests('removeListener');
79  });
80
81  describe('once', () => {
82    it('only calls the listener once and then removes it', () => {
83      const listener = sinon.spy();
84      emitter.once('foo', listener);
85      emitter.emit('foo');
86      expect(listener.callCount).toEqual(1);
87      emitter.emit('foo');
88      expect(listener.callCount).toEqual(1);
89    });
90
91    it('supports chaining', () => {
92      const listener = sinon.spy();
93      const returnValue = emitter.once('foo', listener);
94      expect(returnValue).toBe(emitter);
95    });
96  });
97
98  describe('emit', () => {
99    it('calls all the listeners for an event', () => {
100      const listener1 = sinon.spy();
101      const listener2 = sinon.spy();
102      const listener3 = sinon.spy();
103      emitter.on('foo', listener1).on('foo', listener2).on('bar', listener3);
104
105      emitter.emit('foo');
106
107      expect(listener1.callCount).toEqual(1);
108      expect(listener2.callCount).toEqual(1);
109      expect(listener3.callCount).toEqual(0);
110    });
111
112    it('passes data through to the listener', () => {
113      const listener = sinon.spy();
114      emitter.on('foo', listener);
115      const data = {};
116
117      emitter.emit('foo', data);
118      expect(listener.callCount).toEqual(1);
119      expect(listener.firstCall.args[0]).toBe(data);
120    });
121
122    it('returns true if the event has listeners', () => {
123      const listener = sinon.spy();
124      emitter.on('foo', listener);
125      expect(emitter.emit('foo')).toBe(true);
126    });
127
128    it('returns false if the event has listeners', () => {
129      const listener = sinon.spy();
130      emitter.on('foo', listener);
131      expect(emitter.emit('notFoo')).toBe(false);
132    });
133  });
134
135  describe('listenerCount', () => {
136    it('returns the number of listeners for the given event', () => {
137      emitter.on('foo', () => {});
138      emitter.on('foo', () => {});
139      emitter.on('bar', () => {});
140      expect(emitter.listenerCount('foo')).toEqual(2);
141      expect(emitter.listenerCount('bar')).toEqual(1);
142      expect(emitter.listenerCount('noListeners')).toEqual(0);
143    });
144  });
145
146  describe('removeAllListeners', () => {
147    it('removes every listener from all events by default', () => {
148      emitter.on('foo', () => {}).on('bar', () => {});
149
150      emitter.removeAllListeners();
151      expect(emitter.emit('foo')).toBe(false);
152      expect(emitter.emit('bar')).toBe(false);
153    });
154
155    it('returns the emitter for chaining', () => {
156      expect(emitter.removeAllListeners()).toBe(emitter);
157    });
158
159    it('can filter to remove only listeners for a given event name', () => {
160      emitter
161        .on('foo', () => {})
162        .on('bar', () => {})
163        .on('bar', () => {});
164
165      emitter.removeAllListeners('bar');
166      expect(emitter.emit('foo')).toBe(true);
167      expect(emitter.emit('bar')).toBe(false);
168    });
169  });
170});
171