浏览代码

Merge branch 'master' into t/21

Krzysztof Krztoń 7 年之前
父节点
当前提交
2997e25a2c

+ 1 - 1
packages/ckeditor5-paste-from-office/CONTRIBUTING.md

@@ -1,4 +1,4 @@
 Contributing
 ========================================
 
-Information about contributing can be found on the following page: <https://github.com/ckeditor/ckeditor5/blob/master/CONTRIBUTING.md>.
+See the [official contributors' guide to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html) to learn more.

+ 96 - 0
packages/ckeditor5-paste-from-office/src/filters/parse.js

@@ -0,0 +1,96 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module paste-from-office/filters/parse
+ */
+
+/* globals DOMParser */
+
+import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter';
+import { NBSP_FILLER } from '@ckeditor/ckeditor5-engine/src/view/filler';
+
+import { normalizeSpacing, normalizeSpacerunSpans } from './space';
+
+/**
+ * Parses provided HTML extracting contents of `<body>` and `<style>` tags.
+ *
+ * @param {String} htmlString HTML string to be parsed.
+ * @returns {Object} result
+ * @returns {module:engine/view/documentfragment~DocumentFragment} result.body Parsed body
+ * content as a traversable structure.
+ * @returns {String} result.bodyString Entire body content as a string.
+ * @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` objects, each representing
+ * separate `style` tag from the source HTML.
+ * @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence into one string.
+ */
+export function parseHtml( htmlString ) {
+	const domParser = new DOMParser();
+
+	// Remove Word specific "if comments" so content inside is not omitted by the parser.
+	htmlString = htmlString.replace( /<!--\[if gte vml 1]>/g, '' );
+
+	// Parse htmlString as native Document object.
+	const htmlDocument = domParser.parseFromString( normalizeSpacing( htmlString ), 'text/html' );
+
+	normalizeSpacerunSpans( htmlDocument );
+
+	// Get `innerHTML` first as transforming to View modifies the source document.
+	const bodyString = htmlDocument.body.innerHTML;
+
+	// Transform document.body to View.
+	const bodyView = documentToView( htmlDocument );
+
+	// Extract stylesheets.
+	const stylesObject = extractStyles( htmlDocument );
+
+	return {
+		body: bodyView,
+		bodyString,
+		styles: stylesObject.styles,
+		stylesString: stylesObject.stylesString
+	};
+}
+
+// Transforms native `Document` object into {@link module:engine/view/documentfragment~DocumentFragment}.
+//
+// @param {Document} htmlDocument Native `Document` object to be transformed.
+// @returns {module:engine/view/documentfragment~DocumentFragment}
+function documentToView( htmlDocument ) {
+	const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
+	const fragment = htmlDocument.createDocumentFragment();
+	const nodes = htmlDocument.body.childNodes;
+
+	while ( nodes.length > 0 ) {
+		fragment.appendChild( nodes[ 0 ] );
+	}
+
+	return domConverter.domToView( fragment );
+}
+
+// Extracts both `CSSStyleSheet` and string representation from all `style` elements available in a provided `htmlDocument`.
+//
+// @param {Document} htmlDocument Native `Document` object from which styles will be extracted.
+// @returns {Object} result
+// @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` object, each representing
+// separate `style` tag from the source object.
+// @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence as one string.
+function extractStyles( htmlDocument ) {
+	const styles = [];
+	const stylesString = [];
+	const styleTags = Array.from( htmlDocument.getElementsByTagName( 'style' ) );
+
+	for ( const style of styleTags ) {
+		if ( style.sheet && style.sheet.cssRules && style.sheet.cssRules.length ) {
+			styles.push( style.sheet );
+			stylesString.push( style.innerHTML );
+		}
+	}
+
+	return {
+		styles,
+		stylesString: stylesString.join( ' ' )
+	};
+}

+ 57 - 0
packages/ckeditor5-paste-from-office/src/filters/space.js

@@ -0,0 +1,57 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module paste-from-office/filters/space
+ */
+
+/**
+ * Replaces last space preceding elements closing tag with `&nbsp;`. Such operation prevents spaces from being removed
+ * during further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
+ * This method also takes into account Word specific `<o:p></o:p>` empty tags.
+ * Additionally multiline sequences of spaces and new lines between tags are removed.
+ *
+ * @param {String} htmlString HTML string in which spacing should be normalized.
+ * @returns {String} Input HTML with spaces normalized.
+ */
+export function normalizeSpacing( htmlString ) {
+	return normalizeSafariSpaceSpans( normalizeSafariSpaceSpans( htmlString ) ) // Run normalization two times to cover nested spans.
+		.replace( / <\//g, '\u00A0</' )
+		.replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' )
+		.replace( />(\s*(\r\n?|\n)\s*)+</g, '><' );
+}
+
+/**
+ * Normalizes spacing in special Word `spacerun spans` (`<span style='mso-spacerun:yes'>\s+</span>`) by replacing
+ * all spaces with `&nbsp; ` pairs. This prevents spaces from being removed during further DOM/View processing
+ * (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
+ *
+ * @param {Document} htmlDocument Native `Document` object in which spacing should be normalized.
+ */
+export function normalizeSpacerunSpans( htmlDocument ) {
+	htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
+		// Use `el.childNodes[ 0 ].data.length` instead of `el.innerText.length`. For `el.innerText.length` which
+		// contains spaces mixed with `&nbsp;` Edge browser returns incorrect length.
+		const innerTextLength = el.childNodes[ 0 ].data.length;
+
+		el.innerHTML = Array( innerTextLength + 1 ).join( '\u00A0 ' ).substr( 0, innerTextLength );
+	} );
+}
+
+// Normalizes specific spacing generated by Safari when content pasted from Word (`<span class="Apple-converted-space"> </span>`)
+// by replacing all spaces sequences longer than 1 space with `&nbsp; ` pairs. This prevents spaces from being removed during
+// further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
+//
+// This function is similar to {@link module:clipboard/utils/normalizeclipboarddata normalizeClipboardData util} but uses
+// regular spaces / &nbsp; sequence for replacement.
+//
+// @param {String} htmlString HTML string in which spacing should be normalized
+// @returns {String} Input HTML with spaces normalized.
+function normalizeSafariSpaceSpans( htmlString ) {
+	return htmlString.replace( /<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, ( fullMatch, spaces ) => {
+		return spaces.length === 1 ? ' ' : Array( spaces.length + 1 ).join( '\u00A0 ' ).substr( 0, spaces.length );
+	} );
+}
+

+ 1 - 129
packages/ckeditor5-paste-from-office/src/filters/utils.js

@@ -7,50 +7,7 @@
  * @module paste-from-office/filters/utils
  */
 
-/* globals DOMParser, btoa */
-
-import DomConverter from '@ckeditor/ckeditor5-engine/src/view/domconverter';
-import { NBSP_FILLER } from '@ckeditor/ckeditor5-engine/src/view/filler';
-
-/**
- * Parses provided HTML extracting contents of `<body>` and `<style>` tags.
- *
- * @param {String} htmlString HTML string to be parsed.
- * @returns {Object} result
- * @returns {module:engine/view/documentfragment~DocumentFragment} result.body Parsed body
- * content as a traversable structure.
- * @returns {String} result.bodyString Entire body content as a string.
- * @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` objects, each representing
- * separate `style` tag from the source HTML.
- * @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence into one string.
- */
-export function parseHtml( htmlString ) {
-	const domParser = new DOMParser();
-
-	// Remove Word specific "if comments" so content inside is not omitted by the parser.
-	htmlString = htmlString.replace( /<!--\[if gte vml 1]>/g, '' );
-
-	// Parse htmlString as native Document object.
-	const htmlDocument = domParser.parseFromString( normalizeSpacing( htmlString ), 'text/html' );
-
-	normalizeSpacerunSpans( htmlDocument );
-
-	// Get `innerHTML` first as transforming to View modifies the source document.
-	const bodyString = htmlDocument.body.innerHTML;
-
-	// Transform document.body to View.
-	const bodyView = documentToView( htmlDocument );
-
-	// Extract stylesheets.
-	const stylesObject = extractStyles( htmlDocument );
-
-	return {
-		body: bodyView,
-		bodyString,
-		styles: stylesObject.styles,
-		stylesString: stylesObject.stylesString
-	};
-}
+/* globals btoa */
 
 /**
  * Converts given HEX string to base64 representation.
@@ -63,88 +20,3 @@ export function convertHexToBase64( hexString ) {
 		return String.fromCharCode( parseInt( char, 16 ) );
 	} ).join( '' ) );
 }
-
-// Transforms native `Document` object into {@link module:engine/view/documentfragment~DocumentFragment}.
-//
-// @param {Document} htmlDocument Native `Document` object to be transformed.
-// @returns {module:engine/view/documentfragment~DocumentFragment}
-function documentToView( htmlDocument ) {
-	const domConverter = new DomConverter( { blockFiller: NBSP_FILLER } );
-	const fragment = htmlDocument.createDocumentFragment();
-	const nodes = htmlDocument.body.childNodes;
-
-	while ( nodes.length > 0 ) {
-		fragment.appendChild( nodes[ 0 ] );
-	}
-
-	return domConverter.domToView( fragment );
-}
-
-// Extracts both `CSSStyleSheet` and string representation from all `style` elements available in a provided `htmlDocument`.
-//
-// @param {Document} htmlDocument Native `Document` object from which styles will be extracted.
-// @returns {Object} result
-// @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` object, each representing
-// separate `style` tag from the source object.
-// @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence as one string.
-function extractStyles( htmlDocument ) {
-	const styles = [];
-	const stylesString = [];
-	const styleTags = Array.from( htmlDocument.getElementsByTagName( 'style' ) );
-
-	for ( const style of styleTags ) {
-		if ( style.sheet && style.sheet.cssRules && style.sheet.cssRules.length ) {
-			styles.push( style.sheet );
-			stylesString.push( style.innerHTML );
-		}
-	}
-
-	return {
-		styles,
-		stylesString: stylesString.join( ' ' )
-	};
-}
-
-// Replaces last space preceding elements closing tag and Word specific empty `<o:p></o:p>` tags with `&nbsp;`.
-// Such operation prevents spaces from being removed during further DOM/View processing (see
-// especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
-// Also multiline sequences of spaces and new lines between tags are removed.
-//
-// @param {String} htmlString HTML string in which spacing should be normalized.
-// @returns {String} Input HTML with spaces normalized.
-function normalizeSpacing( htmlString ) {
-	return normalizeSafariSpaceSpans( normalizeSafariSpaceSpans( htmlString ) ) // Run normalization two times to cover nested spans.
-		.replace( / <\//g, '\u00A0</' )
-		.replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' )
-		.replace( />(\s*(\r\n?|\n)\s*)+</g, '><' );
-}
-
-// Normalizes specific spacing generated by Safari when content pasted from Word (`<span class="Apple-converted-space"> </span>`)
-// by replacing all spaces sequences longer than 1 space with `&nbsp; ` pairs. This prevents spaces from being removed during
-// further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
-//
-// This function is similar to {@link module:clipboard/utils/normalizeclipboarddata normalizeClipboardData util} but uses
-// regular spaces / &nbsp; sequence for replacement.
-//
-// @param {String} htmlString HTML string in which spacing should be normalized
-// @returns {String} Input HTML with spaces normalized.
-function normalizeSafariSpaceSpans( htmlString ) {
-	return htmlString.replace( /<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, ( fullMatch, spaces ) => {
-		return spaces.length === 1 ? ' ' : Array( spaces.length + 1 ).join( '\u00A0 ' ).substr( 0, spaces.length );
-	} );
-}
-
-// Normalizes spacing in special Word `spacerun spans` (`<span style='mso-spacerun:yes'>\s+</span>`) by replacing
-// all spaces with `&nbsp; ` pairs. This prevents spaces from being removed during further DOM/View processing
-// (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
-//
-// @param {Document} htmlDocument Native `Document` object in which spacing should be normalized.
-function normalizeSpacerunSpans( htmlDocument ) {
-	htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
-		// Use `el.childNodes[ 0 ].data.length` instead of `el.innerText.length`. For `el.innerText.length` which
-		// contains spaces mixed with `&nbsp;` Edge browser returns incorrect length.
-		const innerTextLength = el.childNodes[ 0 ].data.length;
-
-		el.innerHTML = Array( innerTextLength + 1 ).join( '\u00A0 ' ).substr( 0, innerTextLength );
-	} );
-}

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

@@ -10,7 +10,7 @@
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 
-import { parseHtml } from './filters/utils';
+import { parseHtml } from './filters/parse';
 import { transformListItemLikeElementsIntoLists } from './filters/list';
 import { replaceImagesSourceWithBase64 } from './filters/image';
 

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

@@ -16,7 +16,7 @@ import { setData, stringify as stringifyModel } from '@ckeditor/ckeditor5-engine
 import { stringify as stringifyView } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
 
 import PasteFromOffice from '../../src/pastefromoffice';
-import { parseHtml } from '../../src/filters/utils';
+import { parseHtml } from '../../src/filters/parse';
 import { replaceImagesSourceWithBase64 } from '../../src/filters/image';
 import { browserFixtures } from '../_data/image/index';
 
@@ -24,7 +24,7 @@ describe( 'Filters', () => {
 	describe( 'image', () => {
 		let editor;
 
-		describe( 'replaceImagesSourceWithBase64', () => {
+		describe( 'replaceImagesSourceWithBase64()', () => {
 			describe( 'with RTF', () => {
 				beforeEach( () => {
 					return VirtualTestEditor

+ 1 - 1
packages/ckeditor5-paste-from-office/tests/filters/list.js

@@ -13,7 +13,7 @@ describe( 'Filters', () => {
 	describe( 'list', () => {
 		const htmlDataProcessor = new HtmlDataProcessor();
 
-		describe( 'transformListItemLikeElementsIntoLists', () => {
+		describe( 'transformListItemLikeElementsIntoLists()', () => {
 			it( 'replaces list-like elements with semantic lists', () => {
 				const html = '<p style="mso-list:l0 level1 lfo0"><span style="mso-list:Ignore">1.</span>Item 1</p>';
 				const view = htmlDataProcessor.toView( html );

+ 96 - 0
packages/ckeditor5-paste-from-office/tests/filters/parse.js

@@ -0,0 +1,96 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals CSSStyleSheet */
+
+import DocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment';
+
+import { parseHtml } from '../../src/filters/parse';
+
+describe( 'Filters', () => {
+	describe( 'parse', () => {
+		describe( 'parseHtml()', () => {
+			it( 'correctly parses HTML with body and one style tag', () => {
+				const html = '<head><style>p { color: red; } a { font-size: 12px; }</style></head><body><p>Foo Bar</p></body>';
+				const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+				expect( body ).to.instanceof( DocumentFragment );
+				expect( body.childCount ).to.equal( 1, 'body.childCount' );
+
+				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
+
+				expect( styles.length ).to.equal( 1, 'styles.length' );
+				expect( styles[ 0 ] ).to.instanceof( CSSStyleSheet );
+				expect( styles[ 0 ].cssRules.length ).to.equal( 2 );
+				expect( styles[ 0 ].cssRules[ 0 ].style.color ).to.equal( 'red' );
+				expect( styles[ 0 ].cssRules[ 1 ].style[ 'font-size' ] ).to.equal( '12px' );
+
+				expect( stylesString ).to.equal( 'p { color: red; } a { font-size: 12px; }' );
+			} );
+
+			it( 'correctly parses HTML with body contents only', () => {
+				const html = '<p>Foo Bar</p>';
+				const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+				expect( body ).to.instanceof( DocumentFragment );
+				expect( body.childCount ).to.equal( 1 );
+
+				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
+
+				expect( styles.length ).to.equal( 0 );
+
+				expect( stylesString ).to.equal( '' );
+			} );
+
+			it( 'correctly parses HTML with no body and multiple style tags', () => {
+				const html = '<html><head><style>p { color: blue; }</style><style>a { color: green; }</style></head></html>';
+				const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+				expect( body ).to.instanceof( DocumentFragment );
+				expect( body.childCount ).to.equal( 0 );
+
+				expect( bodyString ).to.equal( '' );
+
+				expect( styles.length ).to.equal( 2 );
+				expect( styles[ 0 ] ).to.instanceof( CSSStyleSheet );
+				expect( styles[ 1 ] ).to.instanceof( CSSStyleSheet );
+				expect( styles[ 0 ].cssRules.length ).to.equal( 1 );
+				expect( styles[ 1 ].cssRules.length ).to.equal( 1 );
+				expect( styles[ 0 ].cssRules[ 0 ].style.color ).to.equal( 'blue' );
+				expect( styles[ 1 ].cssRules[ 0 ].style.color ).to.equal( 'green' );
+
+				expect( stylesString ).to.equal( 'p { color: blue; } a { color: green; }' );
+			} );
+
+			it( 'correctly parses HTML with no body and no style tags', () => {
+				const html = '<html><head><meta name="Foo" content="Bar"></head></html>';
+				const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+				expect( body ).to.instanceof( DocumentFragment );
+				expect( body.childCount ).to.equal( 0 );
+
+				expect( bodyString ).to.equal( '' );
+
+				expect( styles.length ).to.equal( 0 );
+
+				expect( stylesString ).to.equal( '' );
+			} );
+
+			it( 'correctly parses HTML with body contents and empty style tag', () => {
+				const html = '<head><style></style></head><body><p>Foo Bar</p></body>';
+				const { body, bodyString, styles, stylesString } = parseHtml( html );
+
+				expect( body ).to.instanceof( DocumentFragment );
+				expect( body.childCount ).to.equal( 1 );
+
+				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
+
+				expect( styles.length ).to.equal( 0 );
+
+				expect( stylesString ).to.equal( '' );
+			} );
+		} );
+	} );
+} );

+ 68 - 0
packages/ckeditor5-paste-from-office/tests/filters/space.js

@@ -0,0 +1,68 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/* globals DOMParser */
+
+import { normalizeSpacing, normalizeSpacerunSpans } from '../../src/filters/space';
+
+describe( 'Filters', () => {
+	describe( 'space', () => {
+		describe( 'normalizeSpacing()', () => {
+			it( 'should replace last space before closing tag with NBSP', () => {
+				const input = '<p>Foo </p><p><span> Bar  </span> Baz </p>';
+				const expected = '<p>Foo\u00A0</p><p><span> Bar \u00A0</span> Baz\u00A0</p>';
+
+				expect( normalizeSpacing( input ) ).to.equal( expected );
+			} );
+
+			it( 'should replace last space before special "o:p" tag with NBSP', () => {
+				const input = '<p>Foo  <o:p></o:p><span> <o:p></o:p> Bar</span></p>';
+				const expected = '<p>Foo \u00A0<o:p></o:p><span>\u00A0<o:p></o:p> Bar</span></p>';
+
+				expect( normalizeSpacing( input ) ).to.equal( expected );
+			} );
+
+			it( 'should remove multiline sequences of whitespaces', () => {
+				const input = '<p>Foo</p> \n\n   \n<p>Bar</p>   \r\n\r\n  <p>Baz</p>';
+				const expected = '<p>Foo</p><p>Bar</p><p>Baz</p>';
+
+				expect( normalizeSpacing( input ) ).to.equal( expected );
+			} );
+
+			it( 'should normalize Safari "space spans"', () => {
+				const input = '<p>Foo <span class="Apple-converted-space">   </span> Baz <span>  </span></p>';
+				const expected = '<p>Foo \u00A0 \u00A0 Baz \u00A0\u00A0</p>';
+
+				expect( normalizeSpacing( input ) ).to.equal( expected );
+			} );
+
+			it( 'should normalize nested Safari "space spans"', () => {
+				const input = '<p> Foo <span class="Apple-converted-space"> <span class="Apple-converted-space">    </span></span> Baz</p>';
+				const expected = '<p> Foo \u00A0 \u00A0 \u00A0 Baz</p>';
+
+				expect( normalizeSpacing( input ) ).to.equal( expected );
+			} );
+		} );
+
+		describe( 'normalizeSpacerunSpans()', () => {
+			it( 'should normalize spaces inside special "span.spacerun" elements', () => {
+				const input = '<p> <span style=\'mso-spacerun:yes\'>   </span>Foo</p>' +
+					'<p> Baz <span style=\'mso-spacerun:yes\'>      </span></p>';
+
+				const expected = '<p> <span style="mso-spacerun:yes">&nbsp; &nbsp;</span>Foo</p>' +
+					'<p> Baz <span style="mso-spacerun:yes">&nbsp; &nbsp; &nbsp; </span></p>';
+
+				const domParser = new DOMParser();
+				const htmlDocument = domParser.parseFromString( input, 'text/html' );
+
+				expect( htmlDocument.body.innerHTML.replace( /'/g, '"' ).replace( /: /g, ':' ) ).to.not.equal( expected );
+
+				normalizeSpacerunSpans( htmlDocument );
+
+				expect( htmlDocument.body.innerHTML.replace( /'/g, '"' ).replace( /: /g, ':' ) ).to.equal( expected );
+			} );
+		} );
+	} );
+} );

+ 3 - 89
packages/ckeditor5-paste-from-office/tests/filters/utils.js

@@ -3,97 +3,11 @@
  * For licensing, see LICENSE.md.
  */
 
-/* globals CSSStyleSheet */
-
-import DocumentFragment from '@ckeditor/ckeditor5-engine/src/view/documentfragment';
-
-import { parseHtml, convertHexToBase64 } from '../../src/filters/utils';
+import { convertHexToBase64 } from '../../src/filters/utils';
 
 describe( 'Filters', () => {
-	describe( 'Utils', () => {
-		describe( 'parseHtml', () => {
-			it( 'correctly parses HTML with body and one style tag', () => {
-				const html = '<head><style>p { color: red; } a { font-size: 12px; }</style></head><body><p>Foo Bar</p></body>';
-				const { body, bodyString, styles, stylesString } = parseHtml( html );
-
-				expect( body ).to.instanceof( DocumentFragment );
-				expect( body.childCount ).to.equal( 1, 'body.childCount' );
-
-				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
-
-				expect( styles.length ).to.equal( 1, 'styles.length' );
-				expect( styles[ 0 ] ).to.instanceof( CSSStyleSheet );
-				expect( styles[ 0 ].cssRules.length ).to.equal( 2 );
-				expect( styles[ 0 ].cssRules[ 0 ].style.color ).to.equal( 'red' );
-				expect( styles[ 0 ].cssRules[ 1 ].style[ 'font-size' ] ).to.equal( '12px' );
-
-				expect( stylesString ).to.equal( 'p { color: red; } a { font-size: 12px; }' );
-			} );
-
-			it( 'correctly parses HTML with body contents only', () => {
-				const html = '<p>Foo Bar</p>';
-				const { body, bodyString, styles, stylesString } = parseHtml( html );
-
-				expect( body ).to.instanceof( DocumentFragment );
-				expect( body.childCount ).to.equal( 1 );
-
-				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
-
-				expect( styles.length ).to.equal( 0 );
-
-				expect( stylesString ).to.equal( '' );
-			} );
-
-			it( 'correctly parses HTML with no body and multiple style tags', () => {
-				const html = '<html><head><style>p { color: blue; }</style><style>a { color: green; }</style></head></html>';
-				const { body, bodyString, styles, stylesString } = parseHtml( html );
-
-				expect( body ).to.instanceof( DocumentFragment );
-				expect( body.childCount ).to.equal( 0 );
-
-				expect( bodyString ).to.equal( '' );
-
-				expect( styles.length ).to.equal( 2 );
-				expect( styles[ 0 ] ).to.instanceof( CSSStyleSheet );
-				expect( styles[ 1 ] ).to.instanceof( CSSStyleSheet );
-				expect( styles[ 0 ].cssRules.length ).to.equal( 1 );
-				expect( styles[ 1 ].cssRules.length ).to.equal( 1 );
-				expect( styles[ 0 ].cssRules[ 0 ].style.color ).to.equal( 'blue' );
-				expect( styles[ 1 ].cssRules[ 0 ].style.color ).to.equal( 'green' );
-
-				expect( stylesString ).to.equal( 'p { color: blue; } a { color: green; }' );
-			} );
-
-			it( 'correctly parses HTML with no body and no style tags', () => {
-				const html = '<html><head><meta name="Foo" content="Bar"></head></html>';
-				const { body, bodyString, styles, stylesString } = parseHtml( html );
-
-				expect( body ).to.instanceof( DocumentFragment );
-				expect( body.childCount ).to.equal( 0 );
-
-				expect( bodyString ).to.equal( '' );
-
-				expect( styles.length ).to.equal( 0 );
-
-				expect( stylesString ).to.equal( '' );
-			} );
-
-			it( 'correctly parses HTML with body contents and empty style tag', () => {
-				const html = '<head><style></style></head><body><p>Foo Bar</p></body>';
-				const { body, bodyString, styles, stylesString } = parseHtml( html );
-
-				expect( body ).to.instanceof( DocumentFragment );
-				expect( body.childCount ).to.equal( 1 );
-
-				expect( bodyString ).to.equal( '<p>Foo Bar</p>' );
-
-				expect( styles.length ).to.equal( 0 );
-
-				expect( stylesString ).to.equal( '' );
-			} );
-		} );
-
-		describe( 'convertHexToBase64', () => {
+	describe( 'utils', () => {
+		describe( 'convertHexToBase64()', () => {
 			it( '#1', () => {
 				const hex = '48656c6c6f20576f726c6421';
 				const base64 = 'SGVsbG8gV29ybGQh';