@@ -173,12 +173,17 @@ suite('Keyboard Shortcut Items', function () {
173173 } ) ;
174174 } ) ;
175175 } ) ;
176- // Do not copy a block if a workspace is in readonly mode.
177- suite ( 'Not called when readOnly is true' , function ( ) {
176+ // Allow copying a block if a workspace is in readonly mode.
177+ suite ( 'Called when readOnly is true' , function ( ) {
178178 testCases . forEach ( function ( testCase ) {
179179 const testCaseName = testCase [ 0 ] ;
180180 const keyEvent = testCase [ 1 ] ;
181- runReadOnlyTest ( keyEvent , testCaseName ) ;
181+ test ( testCaseName , function ( ) {
182+ this . workspace . setIsReadOnly ( true ) ;
183+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
184+ sinon . assert . calledOnce ( this . copySpy ) ;
185+ sinon . assert . calledOnce ( this . hideChaffSpy ) ;
186+ } ) ;
182187 } ) ;
183188 } ) ;
184189 // Do not copy a block if a drag is in progress.
@@ -238,6 +243,118 @@ suite('Keyboard Shortcut Items', function () {
238243 } ) ;
239244 } ) ;
240245
246+ suite ( 'Cut' , function ( ) {
247+ setup ( function ( ) {
248+ this . block = setSelectedBlock ( this . workspace ) ;
249+ this . copySpy = sinon . spy ( this . block , 'toCopyData' ) ;
250+ this . disposeSpy = sinon . spy ( this . block , 'dispose' ) ;
251+ this . hideChaffSpy = sinon . spy (
252+ Blockly . WorkspaceSvg . prototype ,
253+ 'hideChaff' ,
254+ ) ;
255+ } ) ;
256+ const testCases = [
257+ [
258+ 'Control X' ,
259+ createKeyDownEvent ( Blockly . utils . KeyCodes . X , [
260+ Blockly . utils . KeyCodes . CTRL ,
261+ ] ) ,
262+ ] ,
263+ [
264+ 'Meta X' ,
265+ createKeyDownEvent ( Blockly . utils . KeyCodes . X , [
266+ Blockly . utils . KeyCodes . META ,
267+ ] ) ,
268+ ] ,
269+ ] ;
270+ // Cut a block.
271+ suite ( 'Simple' , function ( ) {
272+ testCases . forEach ( function ( testCase ) {
273+ const testCaseName = testCase [ 0 ] ;
274+ const keyEvent = testCase [ 1 ] ;
275+ test ( testCaseName , function ( ) {
276+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
277+ sinon . assert . calledOnce ( this . copySpy ) ;
278+ sinon . assert . calledOnce ( this . disposeSpy ) ;
279+ sinon . assert . calledOnce ( this . hideChaffSpy ) ;
280+ } ) ;
281+ } ) ;
282+ } ) ;
283+ // Do not cut a block if a workspace is in readonly mode.
284+ suite ( 'Not called when readOnly is true' , function ( ) {
285+ testCases . forEach ( function ( testCase ) {
286+ const testCaseName = testCase [ 0 ] ;
287+ const keyEvent = testCase [ 1 ] ;
288+ test ( testCaseName , function ( ) {
289+ this . workspace . setIsReadOnly ( true ) ;
290+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
291+ sinon . assert . notCalled ( this . copySpy ) ;
292+ sinon . assert . notCalled ( this . disposeSpy ) ;
293+ sinon . assert . notCalled ( this . hideChaffSpy ) ;
294+ } ) ;
295+ } ) ;
296+ } ) ;
297+ // Do not cut a block if a drag is in progress.
298+ suite ( 'Drag in progress' , function ( ) {
299+ testCases . forEach ( function ( testCase ) {
300+ const testCaseName = testCase [ 0 ] ;
301+ const keyEvent = testCase [ 1 ] ;
302+ test ( testCaseName , function ( ) {
303+ sinon . stub ( this . workspace , 'isDragging' ) . returns ( true ) ;
304+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
305+ sinon . assert . notCalled ( this . copySpy ) ;
306+ sinon . assert . notCalled ( this . disposeSpy ) ;
307+ sinon . assert . notCalled ( this . hideChaffSpy ) ;
308+ } ) ;
309+ } ) ;
310+ } ) ;
311+ // Do not cut a block if is is not deletable.
312+ suite ( 'Block is not deletable' , function ( ) {
313+ testCases . forEach ( function ( testCase ) {
314+ const testCaseName = testCase [ 0 ] ;
315+ const keyEvent = testCase [ 1 ] ;
316+ test ( testCaseName , function ( ) {
317+ sinon
318+ . stub ( Blockly . common . getSelected ( ) , 'isOwnDeletable' )
319+ . returns ( false ) ;
320+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
321+ sinon . assert . notCalled ( this . copySpy ) ;
322+ sinon . assert . notCalled ( this . disposeSpy ) ;
323+ sinon . assert . notCalled ( this . hideChaffSpy ) ;
324+ } ) ;
325+ } ) ;
326+ } ) ;
327+ // Do not cut a block if it is not movable.
328+ suite ( 'Block is not movable' , function ( ) {
329+ testCases . forEach ( function ( testCase ) {
330+ const testCaseName = testCase [ 0 ] ;
331+ const keyEvent = testCase [ 1 ] ;
332+ test ( testCaseName , function ( ) {
333+ sinon
334+ . stub ( Blockly . common . getSelected ( ) , 'isOwnMovable' )
335+ . returns ( false ) ;
336+ this . injectionDiv . dispatchEvent ( keyEvent ) ;
337+ sinon . assert . notCalled ( this . copySpy ) ;
338+ sinon . assert . notCalled ( this . disposeSpy ) ;
339+ sinon . assert . notCalled ( this . hideChaffSpy ) ;
340+ } ) ;
341+ } ) ;
342+ } ) ;
343+ test ( 'Not called when connection is focused' , function ( ) {
344+ // Restore the stub behavior called during setup
345+ Blockly . getFocusManager ( ) . getFocusedNode . restore ( ) ;
346+
347+ setSelectedConnection ( this . workspace ) ;
348+ const event = createKeyDownEvent ( Blockly . utils . KeyCodes . C , [
349+ Blockly . utils . KeyCodes . CTRL ,
350+ ] ) ;
351+ this . injectionDiv . dispatchEvent ( event ) ;
352+ sinon . assert . notCalled ( this . copySpy ) ;
353+ sinon . assert . notCalled ( this . disposeSpy ) ;
354+ sinon . assert . notCalled ( this . hideChaffSpy ) ;
355+ } ) ;
356+ } ) ;
357+
241358 suite ( 'Undo' , function ( ) {
242359 setup ( function ( ) {
243360 this . undoSpy = sinon . spy ( this . workspace , 'undo' ) ;
0 commit comments