diff --git a/packages/svelte/tests/html_equal.js b/packages/svelte/tests/html_equal.js index 4c9e2a7253..6948d8db32 100644 --- a/packages/svelte/tests/html_equal.js +++ b/packages/svelte/tests/html_equal.js @@ -1,8 +1,20 @@ import { assert } from 'vitest'; -/** @param {Element} node */ -function clean_children(node) { +/** + * @param {Element} node + * @param {{ preserveComments: boolean }} opts + */ +function clean_children(node, opts) { let previous = null; + let has_element_children = false; + let template = + node.nodeName === 'TEMPLATE' ? /** @type {HTMLTemplateElement} */ (node) : undefined; + + if (template) { + const div = document.createElement('div'); + div.append(template.content); + node = div; + } // sort attributes const attributes = Array.from(node.attributes).sort((a, b) => { @@ -14,6 +26,11 @@ function clean_children(node) { }); attributes.forEach((attr) => { + // Strip out the special onload/onerror hydration events from the test output + if ((attr.name === 'onload' || attr.name === 'onerror') && attr.value === 'this.__e=event') { + return; + } + node.setAttribute(attr.name, attr.value); }); @@ -27,24 +44,43 @@ function clean_children(node) { node.tagName !== 'tspan' ) { node.removeChild(child); + continue; } - text.data = text.data.replace(/[ \t\n\r\f]+/g, '\n'); + text.data = text.data.replace(/[^\S]+/g, ' '); if (previous && previous.nodeType === 3) { const prev = /** @type {Text} */ (previous); prev.data += text.data; - prev.data = prev.data.replace(/[ \t\n\r\f]+/g, '\n'); - node.removeChild(text); + text = prev; + text.data = text.data.replace(/[^\S]+/g, ' '); + + continue; } - } else if (child.nodeType === 8) { + } + + if (child.nodeType === 8 && !opts.preserveComments) { // comment - // do nothing - } else { - clean_children(/** @type {Element} */ (child)); + child.remove(); + continue; + } + + // add newlines for better readability and potentially recurse into children + if (child.nodeType === 1 || child.nodeType === 8) { + if (previous?.nodeType === 3) { + const prev = /** @type {Text} */ (previous); + prev.data = prev.data.replace(/^[^\S]+$/, '\n'); + } else if (previous?.nodeType === 1 || previous?.nodeType === 8) { + node.insertBefore(document.createTextNode('\n'), child); + } + + if (child.nodeType === 1) { + has_element_children = true; + clean_children(/** @type {Element} */ (child), opts); + } } previous = child; @@ -53,37 +89,36 @@ function clean_children(node) { // collapse whitespace if (node.firstChild && node.firstChild.nodeType === 3) { const text = /** @type {Text} */ (node.firstChild); - text.data = text.data.replace(/^[ \t\n\r\f]+/, ''); - if (!text.data.length) node.removeChild(text); + text.data = text.data.trimStart(); } if (node.lastChild && node.lastChild.nodeType === 3) { const text = /** @type {Text} */ (node.lastChild); - text.data = text.data.replace(/[ \t\n\r\f]+$/, ''); - if (!text.data.length) node.removeChild(text); + text.data = text.data.trimEnd(); + } + + // indent code for better readability + if (has_element_children && node.parentNode) { + node.innerHTML = `\n\ ${node.innerHTML.replace(/\n/g, '\n ')}\n`; + } + + if (template) { + template.innerHTML = node.innerHTML; } } /** * @param {Window} window * @param {string} html - * @param {{ removeDataSvelte?: boolean, preserveComments?: boolean }} param2 + * @param {{ preserveComments?: boolean }} opts */ -export function normalize_html( - window, - html, - { removeDataSvelte = false, preserveComments = false } -) { +export function normalize_html(window, html, { preserveComments = false } = {}) { try { const node = window.document.createElement('div'); - node.innerHTML = html - .replace(/()/g, preserveComments ? '$1' : '') - .replace(/(data-svelte-h="[^"]+")/g, removeDataSvelte ? '' : '$1') - .replace(/>[ \t\n\r\f]+<') - // Strip out the special onload/onerror hydration events from the test output - .replace(/\s?onerror="this.__e=event"|\s?onload="this.__e=event"/g, '') - .trim(); - clean_children(node); + + node.innerHTML = html.trim(); + clean_children(node, { preserveComments }); + return node.innerHTML; } catch (err) { throw new Error(`Failed to normalize HTML:\n${html}\nCause: ${err}`); @@ -99,67 +134,52 @@ export function normalize_new_line(html) { } /** - * @param {{ removeDataSvelte?: boolean }} options + * @param {string} actual + * @param {string} expected + * @param {string} [message] */ -export function setup_html_equal(options = {}) { - /** - * @param {string} actual - * @param {string} expected - * @param {string} [message] - */ - const assert_html_equal = (actual, expected, message) => { - try { - assert.deepEqual( - normalize_html(window, actual, options), - normalize_html(window, expected, options), - message - ); - } catch (e) { - if (Error.captureStackTrace) - Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal); - throw e; - } - }; - - /** - * - * @param {string} actual - * @param {string} expected - * @param {{ preserveComments?: boolean, withoutNormalizeHtml?: boolean }} param2 - * @param {string} [message] - */ - const assert_html_equal_with_options = ( - actual, - expected, - { preserveComments, withoutNormalizeHtml }, - message - ) => { - try { - assert.deepEqual( - withoutNormalizeHtml - ? normalize_new_line(actual.trim()) - .replace(/(\sdata-svelte-h="[^"]+")/g, options.removeDataSvelte ? '' : '$1') - .replace(/()/g, preserveComments !== false ? '$1' : '') - : normalize_html(window, actual.trim(), { ...options, preserveComments }), - withoutNormalizeHtml - ? normalize_new_line(expected.trim()) - .replace(/(\sdata-svelte-h="[^"]+")/g, options.removeDataSvelte ? '' : '$1') - .replace(/()/g, preserveComments !== false ? '$1' : '') - : normalize_html(window, expected.trim(), { ...options, preserveComments }), - message - ); - } catch (e) { - if (Error.captureStackTrace) - Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal_with_options); - throw e; - } - }; - - return { - assert_html_equal, - assert_html_equal_with_options - }; -} +export const assert_html_equal = (actual, expected, message) => { + try { + assert.deepEqual(normalize_html(window, actual), normalize_html(window, expected), message); + } catch (e) { + if (Error.captureStackTrace) + Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal); + throw e; + } +}; -// Common case without options -export const { assert_html_equal, assert_html_equal_with_options } = setup_html_equal(); +/** + * + * @param {string} actual + * @param {string} expected + * @param {{ preserveComments?: boolean, withoutNormalizeHtml?: boolean }} param2 + * @param {string} [message] + */ +export const assert_html_equal_with_options = ( + actual, + expected, + { preserveComments, withoutNormalizeHtml }, + message +) => { + try { + assert.deepEqual( + withoutNormalizeHtml + ? normalize_new_line(actual.trim()).replace( + /()/g, + preserveComments !== false ? '$1' : '' + ) + : normalize_html(window, actual.trim(), { preserveComments }), + withoutNormalizeHtml + ? normalize_new_line(expected.trim()).replace( + /()/g, + preserveComments !== false ? '$1' : '' + ) + : normalize_html(window, expected.trim(), { preserveComments }), + message + ); + } catch (e) { + if (Error.captureStackTrace) + Error.captureStackTrace(/** @type {Error} */ (e), assert_html_equal_with_options); + throw e; + } +}; diff --git a/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js b/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js index 2507f5fc83..996f68e39f 100644 --- a/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/binding-select/_config.js @@ -25,7 +25,7 @@ export default test({
selected: one
@@ -54,7 +54,7 @@ export default test({selected: two
diff --git a/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js b/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js index fe6a29207d..1e95aaafa6 100644 --- a/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/input-list/_config.js @@ -4,7 +4,9 @@ export default test({ html: ` ` }); diff --git a/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js b/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js index 3be9f0e925..b7ecd04def 100644 --- a/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js +++ b/packages/svelte/tests/runtime-legacy/samples/namespace-html/_config.js @@ -9,7 +9,7 @@ export default test({