瀏覽代碼

Pasted image blobs should be handled in 'UploadImage' plugin.

Krzysztof Krztoń 7 年之前
父節點
當前提交
1ead21ce31
共有 16 個文件被更改,包括 24 次插入383 次删除
  1. 6 73
      packages/ckeditor5-paste-from-office/src/filters/image.js
  2. 3 4
      packages/ckeditor5-paste-from-office/src/pastefromoffice.js
  3. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/adjacent-groups/input.safari.word2016.txt
  4. 1 1
      packages/ckeditor5-paste-from-office/tests/_data/image/adjacent-groups/model.safari.word2016.html
  5. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/alternative-text/input.safari.word2016.txt
  6. 1 22
      packages/ckeditor5-paste-from-office/tests/_data/image/index.js
  7. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/linked/input.safari.word2016.txt
  8. 0 3
      packages/ckeditor5-paste-from-office/tests/_data/image/offline/input.safari.word2016.txt
  9. 0 5
      packages/ckeditor5-paste-from-office/tests/_data/image/online-offline/input.safari.word2016.txt
  10. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/reflection/input.safari.word2016.txt
  11. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/rotated/input.safari.word2016.txt
  12. 0 5
      packages/ckeditor5-paste-from-office/tests/_data/image/shapes-online-offline/input.safari.word2016.txt
  13. 0 1
      packages/ckeditor5-paste-from-office/tests/_data/image/wrapped/input.safari.word2016.txt
  14. 11 140
      packages/ckeditor5-paste-from-office/tests/_utils/utils.js
  15. 2 11
      packages/ckeditor5-paste-from-office/tests/data/integration.js
  16. 0 113
      packages/ckeditor5-paste-from-office/tests/filters/image.js

+ 6 - 73
packages/ckeditor5-paste-from-office/src/filters/image.js

@@ -7,8 +7,6 @@
  * @module paste-from-office/filters/image
  */
 
-/* globals XMLHttpRequest, FileReader */
-
 import ViewMatcher from '@ckeditor/ckeditor5-engine/src/view/matcher';
 import UpcastWriter from '@ckeditor/ckeditor5-engine/src/view/upcastwriter';
 
@@ -20,9 +18,8 @@ import { convertHexToBase64 } from './utils';
  *
  * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment on which transform images.
  * @param {String} rtfData The RTF data from which images representation will be used.
- * @param {module:engine/model/model~Model} model Editor model.
  */
-export function replaceImagesSourceWithBase64( documentFragment, rtfData, model ) {
+export function replaceImagesSourceWithBase64( documentFragment, rtfData ) {
 	if ( !documentFragment.childCount ) {
 		return;
 	}
@@ -35,12 +32,8 @@ export function replaceImagesSourceWithBase64( documentFragment, rtfData, model
 
 	const images = findAllImageElementsWithLocalSource( documentFragment, upcastWriter );
 
-	if ( images.file.length ) {
-		replaceImagesFileSourceWithInlineRepresentation( images.file, extractImageDataFromRtf( rtfData ), upcastWriter );
-	}
-
-	if ( images.blob.length ) {
-		replaceImagesBlobSourceWithInlineRepresentation( images.blob, model );
+	if ( images.length ) {
+		replaceImagesFileSourceWithInlineRepresentation( images, extractImageDataFromRtf( rtfData ), upcastWriter );
 	}
 }
 
@@ -130,7 +123,7 @@ function removeAllShapeElements( documentFragment, writer ) {
 	}
 }
 
-// Finds all `<img>` elements in a given document fragment which have source pointing to local `file://` or `blob:` resource.
+// Finds all `<img>` elements in a given document fragment which have source pointing to local `file://` resource.
 //
 // @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment in which to look for `<img>` elements.
 // @param {module:engine/view/upcastwriter~UpcastWriter} writer
@@ -144,17 +137,12 @@ function findAllImageElementsWithLocalSource( documentFragment, writer ) {
 		name: 'img'
 	} );
 
-	const imgs = {
-		file: [],
-		blob: []
-	};
+	const imgs = [];
 
 	for ( const value of range ) {
 		if ( imageElementsMatcher.match( value.item ) ) {
 			if ( value.item.getAttribute( 'src' ).indexOf( 'file://' ) === 0 ) {
-				imgs.file.push( value.item );
-			} else if ( value.item.getAttribute( 'src' ).indexOf( 'blob:' ) === 0 ) {
-				imgs.blob.push( value.item );
+				imgs.push( value.item );
 			}
 		}
 	}
@@ -216,58 +204,3 @@ function replaceImagesFileSourceWithInlineRepresentation( imageElements, imagesH
 		}
 	}
 }
-
-// Extracts all view images sources data from blob url, converts it to base64 and replaces source in the corresponding model images.
-//
-// @param {Array.<module:engine/view/element~Element>} imageElements Image elements from which sources extract blob data.
-// @param {module:engine/model/model~Model} model Model containing corresponding image elements which sources will be updated.
-function replaceImagesBlobSourceWithInlineRepresentation( imageElements, model ) {
-	for ( const img of imageElements ) {
-		const src = img.getAttribute( 'src' );
-
-		fetchImageDataFromBlobUrlAsBase64( src )
-			.then( data => {
-				model.enqueueChange( 'transparent', writer => {
-					const root = model.document.getRoot();
-					const range = writer.createRangeIn( root );
-
-					for ( const value of range ) {
-						if ( value.item.is( 'element', 'image' ) && value.item.getAttribute( 'src' ) === src ) {
-							writer.setAttribute( 'src', data, value.item );
-						}
-					}
-				} );
-			} )
-			// In case of error we basically can't do nothing. Still images with blob URLs are locally
-			// visible so it is not noticeable until the content will be opened in a new browser tab.
-			.catch();
-	}
-}
-
-// Fetches blob data via XHR and converts it to base64 representation.
-//
-// @param {String} url Blob url from which to fetch blob data.
-// @returns {Promise} A promise which resolves once blob data is successfully fetched and converted to base64.
-function fetchImageDataFromBlobUrlAsBase64( url ) {
-	return new Promise( ( resolve, reject ) => {
-		const xhr = new XMLHttpRequest();
-
-		xhr.open( 'GET', url, true );
-
-		xhr.responseType = 'blob';
-
-		xhr.addEventListener( 'error', () => reject() );
-		xhr.addEventListener( 'abort', () => reject() );
-		xhr.addEventListener( 'load', () => {
-			const reader = new FileReader();
-
-			reader.onloadend = () => {
-				resolve( reader.result );
-			};
-
-			reader.readAsDataURL( xhr.response );
-		} );
-
-		xhr.send();
-	} );
-}

+ 3 - 4
packages/ckeditor5-paste-from-office/src/pastefromoffice.js

@@ -42,7 +42,7 @@ export default class PasteFromOffice extends Plugin {
 			const html = data.dataTransfer.getData( 'text/html' );
 
 			if ( isWordInput( html ) ) {
-				data.content = this._normalizeWordInput( html, data.dataTransfer, editor.editing.model );
+				data.content = this._normalizeWordInput( html, data.dataTransfer );
 			}
 		}, { priority: 'high' } );
 	}
@@ -55,14 +55,13 @@ export default class PasteFromOffice extends Plugin {
 	 * @protected
 	 * @param {String} input Word input.
 	 * @param {module:clipboard/datatransfer~DataTransfer} dataTransfer Data transfer instance.
-	 * @param {module:engine/model/model~Model} model Editor model.
 	 * @returns {module:engine/view/documentfragment~DocumentFragment} Normalized input.
 	 */
-	_normalizeWordInput( input, dataTransfer, model ) {
+	_normalizeWordInput( input, dataTransfer ) {
 		const { body, stylesString } = parseHtml( input );
 
 		transformListItemLikeElementsIntoLists( body, stylesString );
-		replaceImagesSourceWithBase64( body, dataTransfer.getData( 'text/rtf' ), model );
+		replaceImagesSourceWithBase64( body, dataTransfer.getData( 'text/rtf' ) );
 
 		return body;
 	}

文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/adjacent-groups/input.safari.word2016.txt


+ 1 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/adjacent-groups/model.safari.word2016.html

@@ -26,4 +26,4 @@
 <paragraph></paragraph>
 <paragraph></paragraph>
 
-<image src="blob:http://localhost:9876/6948ce58-ac20-4424-8356-f3c727f18ee6"></image>
+<image src="blob:http://localhost:9876/4fe9e7ab-3fec-4cce-98b8-7773476dbbf0"></image>

文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/alternative-text/input.safari.word2016.txt


+ 1 - 22
packages/ckeditor5-paste-from-office/tests/_data/image/index.js

@@ -121,16 +121,6 @@ import adjacentGroupsSafari from './adjacent-groups/input.safari.word2016.html';
 import onlineOfflineSafari from './online-offline/input.safari.word2016.html';
 import shapesOnlineOfflineSafari from './shapes-online-offline/input.safari.word2016.html';
 
-import offlineBlobSafari from './offline/input.safari.word2016.txt';
-import linkedBlobSafari from './linked/input.safari.word2016.txt';
-import rotatedBlobSafari from './rotated/input.safari.word2016.txt';
-import wrappedBlobSafari from './wrapped/input.safari.word2016.txt';
-import alternativeTextBlobSafari from './alternative-text/input.safari.word2016.txt';
-import reflectionBlobSafari from './reflection/input.safari.word2016.txt';
-import adjacentGroupsBlobSafari from './adjacent-groups/input.safari.word2016.txt';
-import onlineOfflineBlobSafari from './online-offline/input.safari.word2016.txt';
-import shapesOnlineOfflineBlobSafari from './shapes-online-offline/input.safari.word2016.txt';
-
 import offlineNormalizedSafari from './offline/normalized.safari.word2016.html';
 import linkedNormalizedSafari from './linked/normalized.safari.word2016.html';
 import rotatedNormalizedSafari from './rotated/normalized.safari.word2016.html';
@@ -240,17 +230,6 @@ export const browserFixtures = {
 			onlineOffline: onlineOfflineModelSafari,
 			shapesOnlineOffline: shapesOnlineOfflineModelSafari
 		},
-		inputRtf: {},
-		inputBlob: {
-			offline: offlineBlobSafari,
-			linked: linkedBlobSafari,
-			rotated: rotatedBlobSafari,
-			wrapped: wrappedBlobSafari,
-			alternativeText: alternativeTextBlobSafari,
-			reflection: reflectionBlobSafari,
-			adjacentGroups: adjacentGroupsBlobSafari,
-			onlineOffline: onlineOfflineBlobSafari,
-			shapesOnlineOffline: shapesOnlineOfflineBlobSafari
-		}
+		inputRtf: {}
 	}
 };

文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/linked/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 3
packages/ckeditor5-paste-from-office/tests/_data/image/offline/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 5
packages/ckeditor5-paste-from-office/tests/_data/image/online-offline/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/reflection/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/rotated/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 5
packages/ckeditor5-paste-from-office/tests/_data/image/shapes-online-offline/input.safari.word2016.txt


文件差異過大導致無法顯示
+ 0 - 1
packages/ckeditor5-paste-from-office/tests/_data/image/wrapped/input.safari.word2016.txt


+ 11 - 140
packages/ckeditor5-paste-from-office/tests/_utils/utils.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md.
  */
 
-/* globals document, atob, Blob, URL */
+/* globals document */
 
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
@@ -49,9 +49,6 @@ export function createDataTransfer( data ) {
  *		{
  *			browserName: [ fixtureName1, fixtureName2 ]
  *		}
- *
- * @param {Boolean} [config.withBlobsHandling = false] If special flow with generating and asserting blob URLs data
- * should be used. This param has effect only for integration tests.
  */
 export function generateTests( config ) {
 	if ( [ 'normalization', 'integration' ].indexOf( config.type ) === -1 ) {
@@ -66,7 +63,6 @@ export function generateTests( config ) {
 		throw new Error( 'No or empty `config.browsers` option provided.' );
 	}
 
-	const withBlobsHandling = config.withBlobsHandling || false;
 	const groups = groupFixturesByBrowsers( config.browsers, config.input, config.skip );
 	const generateSuiteFn = config.type === 'normalization' ? generateNormalizationTests : generateIntegrationTests;
 
@@ -78,7 +74,7 @@ export function generateTests( config ) {
 				const skip = config.skip && config.skip[ group ] ? config.skip[ group ] : [];
 
 				if ( groups[ group ] ) {
-					generateSuiteFn( group, groups[ group ], editorConfig, skip, withBlobsHandling );
+					generateSuiteFn( group, groups[ group ], editorConfig, skip );
 				}
 			}
 		} );
@@ -174,8 +170,7 @@ function generateNormalizationTests( title, fixtures, editorConfig, skip ) {
 // @param {Object} fixtures Object containing fixtures.
 // @param {Object} editorConfig Editor config with which test editor will be created.
 // @param {Array.<String>} skip Array of fixtures names which tests should be skipped.
-// @param {Boolean} [withBlobsHandling = false] If special `expectModelWithBlobs()` function should be used to assert model data.
-function generateIntegrationTests( title, fixtures, editorConfig, skip, withBlobsHandling ) {
+function generateIntegrationTests( title, fixtures, editorConfig, skip ) {
 	describe( title, () => {
 		let element, editor;
 		let data = {};
@@ -219,19 +214,11 @@ function generateIntegrationTests( title, fixtures, editorConfig, skip, withBlob
 		} );
 
 		for ( const name of Object.keys( fixtures.input ) ) {
-			if ( !withBlobsHandling ) {
-				( skip.indexOf( name ) !== -1 ? it.skip : it )( name, () => {
-					data.input = fixtures.input[ name ];
-					data.model = fixtures.model[ name ];
-					expectModel( data, editor, fixtures.inputRtf && fixtures.inputRtf[ name ] );
-				} );
-			} else {
-				( skip.indexOf( name ) !== -1 ? it.skip : it )( name, done => {
-					data.input = fixtures.input[ name ];
-					data.model = fixtures.model[ name ];
-					expectModelWithBlobs( data, editor, fixtures.inputBlob && fixtures.inputBlob[ name ], done );
-				} );
-			}
+			( skip.indexOf( name ) !== -1 ? it.skip : it )( name, () => {
+				data.input = fixtures.input[ name ];
+				data.model = fixtures.model[ name ];
+				expectModel( data, editor, fixtures.inputRtf && fixtures.inputRtf[ name ] );
+			} );
 		}
 	} );
 }
@@ -292,64 +279,6 @@ function expectModel( data, editor, inputRtf = null ) {
 	compareContentWithBase64Images( data.actual, inlineData( data.model ) );
 }
 
-// Compares two models string representations. The input HTML is processed through paste
-// pipeline where it is transformed into model. This function hooks into {@link module:engine/model/model~Model#insertContent}
-// to get the model representation before it is inserted.
-//
-// @param {Object} data
-// @param {String} data.input Input HTML which will be pasted into the editor.
-// @param {String} data.actual Actual model data.
-// @param {String} data.model Expected model data.
-// @param {module:core/editor/editor~Editor} editor Editor instance.
-// @param {String} inputBlobs Additional data which will be used to generate blobs for a test.
-// @param {Function} done The callback function which should be called when test has finished.
-function expectModelWithBlobs( data, editor, inputBlobs, done ) {
-	// If the browser stores images as blob urls, we need to generate blobs based on a provided images base64 data
-	// and then replace original blob urls with the local ones. This allows Paste from Office to correctly extract
-	// data checking if the transformations flow works in real use cases.
-	const base64 = inputBlobs.split( '------' ).map( item => item.replace( /\s/g, '' ) );
-	const blobUrls = createBlobsFromBase64Data( base64 );
-
-	const input = replaceBlobUrls( data.input, blobUrls );
-	const expected = replaceBlobUrls( inlineData( data.model ), blobUrls );
-
-	let counter = 0;
-	const onChange = function() {
-		counter++;
-
-		// Each blob is fetched asynchronously generating separate `change` event. Also first content insertion
-		// (with blob urls still) generates one `change` event. This means the content is fully transformed when
-		// number of change events is number of blob urls in the content + 1.
-		if ( counter > blobUrls.length ) {
-			editor.editing.model.document.off( 'change', onChange );
-			counter = 0;
-
-			const expectedData = replaceBlobUrls( expected, base64 );
-			const actualData = replaceBlobUrls( data.actual, base64 );
-
-			try {
-				compareContentWithBase64Images( actualData, expectedData );
-				done();
-			} catch ( err ) {
-				done( err );
-			}
-		}
-	};
-
-	editor.editing.model.document.on( 'change', onChange );
-
-	firePasteEvent( editor, {
-		'text/html': input
-	} );
-
-	// In some rare cases there might be `&nbsp;` in a model data
-	// (see https://github.com/ckeditor/ckeditor5-paste-from-office/issues/27).
-	data.actual = data.actual.replace( /\u00A0/g, ' ' );
-
-	// Check if initial data with blob urls is correct.
-	expect( data.actual ).to.equal( expected );
-}
-
 // Compares actual and expected content. Before comparison the base64 images data is extracted so data diff is more readable.
 // If there were any images extracted their base64 data is also compared.
 //
@@ -360,7 +289,9 @@ function compareContentWithBase64Images( actual, expected ) {
 	const { data: actualModel, images: actualImages } = extractBase64Srcs( actual );
 	const { data: expectedModel, images: expectedImages } = extractBase64Srcs( expected );
 
-	expect( actualModel ).to.equal( expectedModel );
+	// In some rare cases there might be `&nbsp;` in a model data
+	// (see https://github.com/ckeditor/ckeditor5-paste-from-office/issues/27).
+	expect( actualModel.replace( /\u00A0/g, ' ' ) ).to.equal( expectedModel );
 
 	if ( actualImages.length > 0 && expectedImages.length > 0 ) {
 		expect( actualImages.length ).to.equal( expectedImages.length );
@@ -413,63 +344,3 @@ function firePasteEvent( editor, data ) {
 		preventDefault() {}
 	} );
 }
-
-// Replaces all blob urls (`blob:`) in the given HTML with given replacements data.
-//
-// @param {String} html The HTML data in which blob urls will be replaced.
-// @param {Array.<String>} replacements Array of string which will replace found blobs in the order of occurrence.
-// @returns {String} The HTML data with all blob urls replaced.
-function replaceBlobUrls( html, replacements ) {
-	const blobRegexp = /src="(blob:[^"]*)"/g;
-	const toReplace = [];
-
-	let match;
-	while ( ( match = blobRegexp.exec( html ) ) !== null ) {
-		toReplace.push( match[ 1 ] );
-	}
-
-	for ( let i = 0; i < toReplace.length; i++ ) {
-		if ( replacements[ i ] ) {
-			html = html.replace( toReplace[ i ], replacements[ i ] );
-		}
-	}
-
-	return html;
-}
-
-// Creates blob urls from the given base64 data.
-//
-// @param {Array.<String>} base64Data Array of base64 strings from which blob urls will be generated.
-// @returns {Array} Array of generated blob urls.
-function createBlobsFromBase64Data( base64Data ) {
-	const blobUrls = [];
-
-	for ( const data of base64Data ) {
-		blobUrls.push( URL.createObjectURL( base64toBlob( data.trim() ) ) );
-	}
-
-	return blobUrls;
-}
-
-// Transforms base64 data into a blob object.
-//
-// @param {String} The base64 data to be transformed.
-// @returns {Blob} Blob object representing given base64 data.
-function base64toBlob( base64Data ) {
-	const [ type, data ] = base64Data.split( ',' );
-	const byteCharacters = atob( data );
-	const byteArrays = [];
-
-	for ( let offset = 0; offset < byteCharacters.length; offset += 512 ) {
-		const slice = byteCharacters.slice( offset, offset + 512 );
-		const byteNumbers = new Array( slice.length );
-
-		for ( let i = 0; i < slice.length; i++ ) {
-			byteNumbers[ i ] = slice.charCodeAt( i );
-		}
-
-		byteArrays.push( new Uint8Array( byteNumbers ) );
-	}
-
-	return new Blob( byteArrays, { type } );
-}

+ 2 - 11
packages/ckeditor5-paste-from-office/tests/data/integration.js

@@ -37,28 +37,19 @@ generateTests( {
 generateTests( {
 	input: 'image',
 	type: 'integration',
-	browsers: [ 'chrome', 'firefox', 'edge' ],
+	browsers,
 	editorConfig: {
 		plugins: [ Clipboard, Paragraph, Image, Table, PasteFromOffice ]
 	},
 	skip: {
 		chrome: ( env.isEdge ? [ 'adjacentGroups' ] : [] ),
 		firefox: ( env.isEdge ? [ 'adjacentGroups' ] : [] ),
+		safari: ( env.isEdge ? [ 'adjacentGroups' ] : [] ),
 		edge: ( env.isEdge ? [] : [ 'adjacentGroups' ] )
 	}
 } );
 
 generateTests( {
-	input: 'image',
-	type: 'integration',
-	browsers: [ 'safari' ],
-	editorConfig: {
-		plugins: [ Clipboard, Paragraph, Image, Table, PasteFromOffice ]
-	},
-	withBlobsHandling: true
-} );
-
-generateTests( {
 	input: 'link',
 	type: 'integration',
 	browsers,

+ 0 - 113
packages/ckeditor5-paste-from-office/tests/filters/image.js

@@ -3,19 +3,11 @@
  * For licensing, see LICENSE.md.
  */
 
-/* globals document, setTimeout */
-
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
-import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
-import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
-import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import Image from '@ckeditor/ckeditor5-image/src/image';
 
 import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml';
-import { setData, stringify as stringifyModel } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { stringify as stringifyView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
-import PasteFromOffice from '../../src/pastefromoffice';
 import { parseHtml } from '../../src/filters/parse';
 import { replaceImagesSourceWithBase64 } from '../../src/filters/image';
 import { browserFixtures } from '../_data/image/index';
@@ -68,111 +60,6 @@ describe( 'Filters', () => {
 					expect( stringifyView( body ) ).to.equal( normalizeHtml( input ) );
 				} );
 			} );
-
-			describe( 'with Blob', () => {
-				let xhr, requests, element;
-
-				before( () => {
-					element = document.createElement( 'div' );
-
-					document.body.appendChild( element );
-
-					return ClassicTestEditor
-						.create( element, {
-							plugins: [ Clipboard, Paragraph, Image, PasteFromOffice ]
-						} )
-						.then( editorInstance => {
-							editor = editorInstance;
-						} );
-				} );
-
-				beforeEach( () => {
-					setData( editor.model, '<paragraph>[]</paragraph>' );
-
-					xhr = sinon.useFakeXMLHttpRequest();
-					requests = [];
-
-					xhr.onCreate = function( xhrInstance ) {
-						requests.push( xhrInstance );
-					};
-				} );
-
-				afterEach( () => {
-					xhr.restore();
-				} );
-
-				after( () => {
-					editor.destroy();
-
-					element.remove();
-				} );
-
-				it( 'should replace image source when blob successfully fetched', done => {
-					const model = editor.editing.model;
-					const modelData = '<paragraph>Foo</paragraph><image src="blob://http://local/test.jpg"></image>';
-					const input = '<p>Foo<img src="blob://http://local/test.jpg" /></p>';
-					const { body } = parseHtml( input );
-
-					setData( model, modelData );
-
-					replaceImagesSourceWithBase64( body, '', model );
-
-					editor.editing.model.document.on( 'change', () => {
-						const expectedModel = '<paragraph>Foo</paragraph><image src="data:image/jpeg;base64,Rm9vQmFy"></image>';
-
-						try {
-							expect( stringifyModel( model.document.getRoot() ) ).to.equal( expectedModel );
-							done();
-						} catch ( err ) {
-							done( err );
-						}
-					} );
-
-					requests[ 0 ].respond( 200, { 'Content-type': 'image/jpeg' }, 'FooBar' );
-				} );
-
-				it( 'should not replace image source if blob fetching errored', done => {
-					const model = editor.editing.model;
-					const modelData = '<paragraph>Foo</paragraph><image src="blob://http://local/test.jpg"></image>';
-					const input = '<p>Foo<img src="blob://http://local/test.jpg" /></p>';
-					const { body } = parseHtml( input );
-
-					setData( model, modelData );
-
-					replaceImagesSourceWithBase64( body, '', model );
-
-					requests[ 0 ].addEventListener( 'error', () => {
-						// Wait 50ms to validate model.
-						setTimeout( () => {
-							expect( stringifyModel( model.document.getRoot() ) ).to.equal( modelData );
-							done();
-						}, 50 );
-					} );
-
-					requests[ 0 ].error();
-				} );
-
-				it( 'should not replace image source if blob fetching aborted', done => {
-					const model = editor.editing.model;
-					const modelData = '<paragraph>Foo</paragraph><image src="blob://http://local/test.jpg"></image>';
-					const input = '<p>Foo<img src="blob://http://local/test.jpg" /></p>';
-					const { body } = parseHtml( input );
-
-					setData( model, modelData );
-
-					replaceImagesSourceWithBase64( body, '', model );
-
-					requests[ 0 ].addEventListener( 'abort', () => {
-						// Wait 50ms to validate model.
-						setTimeout( () => {
-							expect( stringifyModel( model.document.getRoot() ) ).to.equal( modelData );
-							done();
-						}, 50 );
-					} );
-
-					requests[ 0 ].abort();
-				} );
-			} );
 		} );
 	} );
 } );