|
3 | 3 | * For licensing, see LICENSE.md. |
4 | 4 | */ |
5 | 5 |
|
6 | | -/* globals setTimeout, setInterval, clearInterval, document */ |
| 6 | +/* globals setTimeout, document */ |
7 | 7 |
|
8 | 8 | import ViewRange from '../../../src/view/range'; |
9 | 9 | import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils'; |
@@ -136,98 +136,79 @@ describe( 'SelectionObserver', () => { |
136 | 136 | changeDomSelection(); |
137 | 137 | } ); |
138 | 138 |
|
139 | | - it( 'should warn and not enter infinite loop', ( done ) => { |
140 | | - // Reset infinite loop counters so other tests won't mess up with this test. |
141 | | - selectionObserver._clearInfiniteLoop(); |
142 | | - clearInterval( selectionObserver._clearInfiniteLoopInterval ); |
143 | | - selectionObserver._clearInfiniteLoopInterval = setInterval( () => selectionObserver._clearInfiniteLoop(), 2000 ); |
144 | | - |
145 | | - let counter = 100; |
| 139 | + it( 'should warn and not enter infinite loop', () => { |
| 140 | + // Selectionchange event is called twice per `changeDomSelection()` execution. |
| 141 | + let counter = 35; |
146 | 142 |
|
147 | 143 | const viewFoo = viewDocument.getRoot().getChild( 0 ).getChild( 0 ); |
148 | 144 | viewDocument.selection.addRange( ViewRange.createFromParentsAndOffsets( viewFoo, 0, viewFoo, 0 ) ); |
149 | 145 |
|
150 | | - viewDocument.on( 'selectionChange', () => { |
151 | | - counter--; |
152 | | - |
153 | | - if ( counter > 0 ) { |
154 | | - setTimeout( changeDomSelection ); |
155 | | - } else { |
156 | | - throw 'Infinite loop!'; |
157 | | - } |
158 | | - } ); |
| 146 | + return new Promise( ( resolve, reject ) => { |
| 147 | + testUtils.sinon.stub( log, 'warn', ( msg ) => { |
| 148 | + expect( msg ).to.match( /^selectionchange-infinite-loop/ ); |
159 | 149 |
|
160 | | - let warnedOnce = false; |
| 150 | + resolve(); |
| 151 | + } ); |
161 | 152 |
|
162 | | - testUtils.sinon.stub( log, 'warn', ( msg ) => { |
163 | | - if ( !warnedOnce ) { |
164 | | - warnedOnce = true; |
| 153 | + viewDocument.on( 'selectionChangeDone', () => { |
| 154 | + if ( !counter ) { |
| 155 | + reject( new Error( 'Infinite loop warning was not logged.' ) ); |
| 156 | + } |
| 157 | + } ); |
165 | 158 |
|
166 | | - setTimeout( () => { |
167 | | - expect( msg ).to.match( /^selectionchange-infinite-loop/ ); |
168 | | - done(); |
169 | | - }, 200 ); |
| 159 | + while ( counter > 0 ) { |
| 160 | + changeDomSelection(); |
| 161 | + counter--; |
170 | 162 | } |
171 | 163 | } ); |
172 | | - |
173 | | - changeDomSelection(); |
174 | 164 | } ); |
175 | 165 |
|
176 | 166 | it( 'should not be treated as an infinite loop if selection is changed only few times', ( done ) => { |
177 | 167 | const viewFoo = viewDocument.getRoot().getChild( 0 ).getChild( 0 ); |
178 | | - |
179 | | - // Reset infinite loop counters so other tests won't mess up with this test. |
180 | | - selectionObserver._clearInfiniteLoop(); |
181 | | - |
182 | 168 | viewDocument.selection.addRange( ViewRange.createFromParentsAndOffsets( viewFoo, 0, viewFoo, 0 ) ); |
183 | | - |
184 | 169 | const spy = testUtils.sinon.spy( log, 'warn' ); |
185 | 170 |
|
| 171 | + viewDocument.on( 'selectionChangeDone', () => { |
| 172 | + expect( spy.called ).to.be.false; |
| 173 | + done(); |
| 174 | + } ); |
| 175 | + |
186 | 176 | for ( let i = 0; i < 10; i++ ) { |
187 | 177 | changeDomSelection(); |
188 | 178 | } |
189 | | - |
190 | | - setTimeout( () => { |
191 | | - expect( spy.called ).to.be.false; |
192 | | - done(); |
193 | | - }, 400 ); |
194 | 179 | } ); |
195 | 180 |
|
196 | | - it( 'should not be treated as an infinite loop if changes are not often', ( done ) => { |
| 181 | + it( 'should not be treated as an infinite loop if changes are not often', () => { |
197 | 182 | const clock = testUtils.sinon.useFakeTimers( 'setInterval', 'clearInterval' ); |
198 | | - const spy = testUtils.sinon.spy( log, 'warn' ); |
| 183 | + const stub = testUtils.sinon.stub( log, 'warn' ); |
199 | 184 |
|
200 | 185 | // We need to recreate SelectionObserver, so it will use mocked setInterval. |
201 | 186 | selectionObserver.disable(); |
202 | 187 | selectionObserver.destroy(); |
203 | 188 | viewDocument._observers.delete( SelectionObserver ); |
204 | 189 | viewDocument.addObserver( SelectionObserver ); |
205 | 190 |
|
206 | | - // Inf-loop kicks in after 50th time the selection is changed in 2s. |
207 | | - // We will test 30 times, tick sinon clock to clean counter and then test 30 times again. |
208 | | - // Note that `changeDomSelection` fires two events. |
209 | | - let changeCount = 15; |
210 | | - |
211 | | - for ( let i = 0; i < changeCount; i++ ) { |
212 | | - setTimeout( () => { |
213 | | - changeDomSelection(); |
214 | | - }, i * 20 ); |
215 | | - } |
216 | | - |
217 | | - setTimeout( () => { |
218 | | - // Move the clock by 2100ms which will trigger callback added to `setInterval` and reset the inf-loop counter. |
219 | | - clock.tick( 2100 ); |
220 | | - |
221 | | - for ( let i = 0; i < changeCount; i++ ) { |
222 | | - changeDomSelection(); |
223 | | - } |
224 | | - |
225 | | - setTimeout( () => { |
226 | | - expect( spy.called ).to.be.false; |
| 191 | + return doChanges() |
| 192 | + .then( doChanges ) |
| 193 | + .then( () => { |
| 194 | + sinon.assert.notCalled( stub ); |
227 | 195 | clock.restore(); |
228 | | - done(); |
229 | | - }, 200 ); |
230 | | - }, 400 ); |
| 196 | + } ); |
| 197 | + |
| 198 | + // Selectionchange event is called twice per `changeDomSelection()` execution. We call it 25 times to get |
| 199 | + // 50 events. Infinite loop counter is reset, so calling this method twice should not show any warning. |
| 200 | + function doChanges() { |
| 201 | + return new Promise( resolve => { |
| 202 | + viewDocument.once( 'selectionChangeDone', () => { |
| 203 | + clock.tick( 1100 ); |
| 204 | + resolve(); |
| 205 | + } ); |
| 206 | + |
| 207 | + for ( let i = 0; i < 30; i++ ) { |
| 208 | + changeDomSelection(); |
| 209 | + } |
| 210 | + } ); |
| 211 | + } |
231 | 212 | } ); |
232 | 213 |
|
233 | 214 | it( 'should fire `selectionChangeDone` event after selection stop changing', ( done ) => { |
|
0 commit comments