88import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor' ;
99
1010import Plugin from '@ckeditor/ckeditor5-core/src/plugin' ;
11+ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard' ;
1112import ImageEditing from '../../src/image/imageediting' ;
1213import ImageUploadEditing from '../../src/imageupload/imageuploadediting' ;
1314import ImageUploadCommand from '../../src/imageupload/imageuploadcommand' ;
@@ -23,6 +24,7 @@ import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils
2324import Range from '@ckeditor/ckeditor5-engine/src/model/range' ;
2425import Position from '@ckeditor/ckeditor5-engine/src/model/position' ;
2526
27+ import log from '@ckeditor/ckeditor5-utils/src/log' ;
2628import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils' ;
2729import Notification from '@ckeditor/ckeditor5-ui/src/notification/notification' ;
2830
@@ -65,6 +67,10 @@ describe( 'ImageUploadEditing', () => {
6567 } ) ;
6668 } ) ;
6769
70+ afterEach ( ( ) => {
71+ return editor . destroy ( ) ;
72+ } ) ;
73+
6874 it ( 'should register proper schema rules' , ( ) => {
6975 expect ( model . schema . checkAttribute ( [ '$root' , 'image' ] , 'uploadId' ) ) . to . be . true ;
7076 } ) ;
@@ -73,8 +79,7 @@ describe( 'ImageUploadEditing', () => {
7379 expect ( editor . commands . get ( 'imageUpload' ) ) . to . be . instanceOf ( ImageUploadCommand ) ;
7480 } ) ;
7581
76- it ( 'should execute imageUpload command when image is pasted' , ( ) => {
77- const spy = sinon . spy ( editor , 'execute' ) ;
82+ it ( 'should insert image when is pasted' , ( ) => {
7883 const fileMock = createNativeFileMock ( ) ;
7984 const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
8085 setModelData ( model , '<paragraph>[]foo</paragraph>' ) ;
@@ -84,17 +89,13 @@ describe( 'ImageUploadEditing', () => {
8489
8590 viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
8691
87- sinon . assert . calledOnce ( spy ) ;
88- sinon . assert . calledWith ( spy , 'imageUpload' ) ;
89-
9092 const id = fileRepository . getLoader ( fileMock ) . id ;
9193 expect ( getModelData ( model ) ) . to . equal (
9294 `<paragraph>foo</paragraph>[<image uploadId="${ id } " uploadStatus="reading"></image>]`
9395 ) ;
9496 } ) ;
9597
96- it ( 'should execute imageUpload command with an optimized position when image is pasted' , ( ) => {
97- const spy = sinon . spy ( editor , 'execute' ) ;
98+ it ( 'should insert image at optimized position when is pasted' , ( ) => {
9899 const fileMock = createNativeFileMock ( ) ;
99100 const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
100101 setModelData ( model , '<paragraph>[]foo</paragraph>' ) ;
@@ -105,17 +106,13 @@ describe( 'ImageUploadEditing', () => {
105106
106107 viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
107108
108- sinon . assert . calledOnce ( spy ) ;
109- sinon . assert . calledWith ( spy , 'imageUpload' ) ;
110-
111109 const id = fileRepository . getLoader ( fileMock ) . id ;
112110 expect ( getModelData ( model ) ) . to . equal (
113111 `[<image uploadId="${ id } " uploadStatus="reading"></image>]<paragraph>foo</paragraph>`
114112 ) ;
115113 } ) ;
116114
117- it ( 'should execute imageUpload command when multiple files image are pasted' , ( ) => {
118- const spy = sinon . spy ( editor , 'execute' ) ;
115+ it ( 'should insert multiple image files when are pasted' , ( ) => {
119116 const files = [ createNativeFileMock ( ) , createNativeFileMock ( ) ] ;
120117 const dataTransfer = new DataTransfer ( { files, types : [ 'Files' ] } ) ;
121118 setModelData ( model , '<paragraph>[]foo</paragraph>' ) ;
@@ -125,9 +122,6 @@ describe( 'ImageUploadEditing', () => {
125122
126123 viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
127124
128- sinon . assert . calledTwice ( spy ) ;
129- sinon . assert . calledWith ( spy , 'imageUpload' ) ;
130-
131125 const id1 = fileRepository . getLoader ( files [ 0 ] ) . id ;
132126 const id2 = fileRepository . getLoader ( files [ 1 ] ) . id ;
133127
@@ -138,8 +132,56 @@ describe( 'ImageUploadEditing', () => {
138132 ) ;
139133 } ) ;
140134
141- it ( 'should not execute imageUpload command when file is not an image' , ( ) => {
142- const spy = sinon . spy ( editor , 'execute' ) ;
135+ it ( 'should insert image when is pasted on allowed position when ImageUploadCommand is disabled' , ( ) => {
136+ const fileMock = createNativeFileMock ( ) ;
137+ const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
138+ setModelData ( model , '<paragraph>[]foo</paragraph>' ) ;
139+
140+ const command = editor . commands . get ( 'imageUpload' ) ;
141+
142+ command . on ( 'beforeChange:isEnabled' , evt => {
143+ evt . return = false ;
144+ evt . stop ( ) ;
145+ } , { priority : 'highest' } ) ;
146+
147+ command . isEnabled = false ;
148+
149+ const targetRange = Range . createFromParentsAndOffsets ( doc . getRoot ( ) , 1 , doc . getRoot ( ) , 1 ) ;
150+ const targetViewRange = editor . editing . mapper . toViewRange ( targetRange ) ;
151+
152+ viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
153+
154+ const id = fileRepository . getLoader ( fileMock ) . id ;
155+ expect ( getModelData ( model ) ) . to . equal (
156+ `<paragraph>foo</paragraph>[<image uploadId="${ id } " uploadStatus="reading"></image>]`
157+ ) ;
158+ } ) ;
159+
160+ it ( 'should not insert image when editor is in read-only mode' , ( ) => {
161+ // Clipboard plugin is required for this test.
162+ return VirtualTestEditor
163+ . create ( {
164+ plugins : [ ImageEditing , ImageUploadEditing , Paragraph , UploadAdapterPluginMock , Clipboard ]
165+ } )
166+ . then ( editor => {
167+ const fileMock = createNativeFileMock ( ) ;
168+ const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
169+ setModelData ( editor . model , '<paragraph>[]foo</paragraph>' ) ;
170+
171+ const targetRange = editor . model . document . selection . getFirstRange ( ) ;
172+ const targetViewRange = editor . editing . mapper . toViewRange ( targetRange ) ;
173+
174+ editor . isReadOnly = true ;
175+
176+ editor . editing . view . document . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
177+
178+ expect ( getModelData ( editor . model ) ) . to . equal ( '<paragraph>[]foo</paragraph>' ) ;
179+
180+ return editor . destroy ( ) ;
181+ } ) ;
182+ } ) ;
183+
184+ it ( 'should not insert image when file is not an image' , ( ) => {
143185 const viewDocument = editor . editing . view . document ;
144186 const fileMock = {
145187 type : 'media/mp3' ,
@@ -149,16 +191,15 @@ describe( 'ImageUploadEditing', () => {
149191
150192 setModelData ( model , '<paragraph>foo[]</paragraph>' ) ;
151193
152- const targetRange = Range . createFromParentsAndOffsets ( doc . getRoot ( ) , 1 , doc . getRoot ( ) , 1 ) ;
194+ const targetRange = doc . selection . getFirstRange ( ) ;
153195 const targetViewRange = editor . editing . mapper . toViewRange ( targetRange ) ;
154196
155197 viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
156198
157- sinon . assert . notCalled ( spy ) ;
199+ expect ( getModelData ( model ) ) . to . equal ( '<paragraph>foo[]</paragraph>' ) ;
158200 } ) ;
159201
160- it ( 'should not execute imageUpload command when there is non-empty HTML content pasted' , ( ) => {
161- const spy = sinon . spy ( editor , 'execute' ) ;
202+ it ( 'should not insert image when there is non-empty HTML content pasted' , ( ) => {
162203 const fileMock = createNativeFileMock ( ) ;
163204 const dataTransfer = new DataTransfer ( {
164205 files : [ fileMock ] ,
@@ -172,7 +213,49 @@ describe( 'ImageUploadEditing', () => {
172213
173214 viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
174215
175- sinon . assert . notCalled ( spy ) ;
216+ expect ( getModelData ( model ) ) . to . equal ( '<paragraph>[]foo</paragraph>' ) ;
217+ } ) ;
218+
219+ it ( 'should not insert image nor crash when pasted image could not be inserted' , ( ) => {
220+ model . schema . register ( 'other' , {
221+ allowIn : '$root' ,
222+ isLimit : true
223+ } ) ;
224+ model . schema . extend ( '$text' , { allowIn : 'other' } ) ;
225+
226+ editor . conversion . elementToElement ( { model : 'other' , view : 'p' } ) ;
227+
228+ setModelData ( model , '<other>[]</other>' ) ;
229+
230+ const fileMock = createNativeFileMock ( ) ;
231+ const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
232+
233+ const targetRange = doc . selection . getFirstRange ( ) ;
234+ const targetViewRange = editor . editing . mapper . toViewRange ( targetRange ) ;
235+
236+ viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
237+
238+ expect ( getModelData ( model ) ) . to . equal ( '<other>[]</other>' ) ;
239+ } ) ;
240+
241+ it ( 'should not throw when upload adapter is not set (FileRepository will log an error anyway) when image is pasted' , ( ) => {
242+ const fileMock = createNativeFileMock ( ) ;
243+ const dataTransfer = new DataTransfer ( { files : [ fileMock ] , types : [ 'Files' ] } ) ;
244+ const logStub = testUtils . sinon . stub ( log , 'error' ) ;
245+
246+ setModelData ( model , '<paragraph>[]foo</paragraph>' ) ;
247+
248+ fileRepository . createUploadAdapter = undefined ;
249+
250+ const targetRange = Range . createFromParentsAndOffsets ( doc . getRoot ( ) , 1 , doc . getRoot ( ) , 1 ) ;
251+ const targetViewRange = editor . editing . mapper . toViewRange ( targetRange ) ;
252+
253+ expect ( ( ) => {
254+ viewDocument . fire ( 'clipboardInput' , { dataTransfer, targetRanges : [ targetViewRange ] } ) ;
255+ } ) . to . not . throw ( ) ;
256+
257+ expect ( getModelData ( model ) ) . to . equal ( '<paragraph>[]foo</paragraph>' ) ;
258+ sinon . assert . calledOnce ( logStub ) ;
176259 } ) ;
177260
178261 // https://github.com/ckeditor/ckeditor5-upload/issues/70
0 commit comments