replace skip_if_ssr and skip_if_hydrate with modes (#10956)

pull/10965/head
Rich Harris 1 year ago committed by GitHub
parent 326e2b4840
commit 2079e675ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -111,9 +111,9 @@ function normalize_children(node) {
* @template Props * @template Props
* @param {{ * @param {{
* skip?: boolean; * skip?: boolean;
* skip_if_ssr?: boolean | 'permanent';
* skip_if_hydrate?: boolean | 'permanent';
* solo?: boolean; * solo?: boolean;
* mode?: Array<'server' | 'client' | 'hydrate'>;
* skip_mode?: Array<'server' | 'client' | 'hydrate'>;
* html?: string; * html?: string;
* ssrHtml?: string; * ssrHtml?: string;
* props?: Props; * props?: Props;

@ -1,8 +1,8 @@
import { test } from '../../assert'; import { test } from '../../assert';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client'],
skip_if_hydrate: 'permanent',
props: { props: {
tag: /** @type {string | null} */ ('my-custom-element'), tag: /** @type {string | null} */ ('my-custom-element'),
name: /** @type {string | null | undefined} */ (null) name: /** @type {string | null | undefined} */ (null)

@ -4,6 +4,5 @@ export default test({
// Test that @html does not execute scripts when instantiated in the client. // Test that @html does not execute scripts when instantiated in the client.
// Needs to be in this test suite because JSDOM does not quite get this right. // Needs to be in this test suite because JSDOM does not quite get this right.
html: `<div></div><script>document.body.innerHTML = 'this should not be executed'</script>`, html: `<div></div><script>document.body.innerHTML = 'this should not be executed'</script>`,
skip_if_ssr: 'permanent', mode: ['client']
skip_if_hydrate: 'permanent'
}); });

@ -46,7 +46,8 @@ export async function run_ssr_test(
} }
const { run } = suite<ReturnType<typeof import('./assert').test>>(async (config, test_dir) => { const { run } = suite<ReturnType<typeof import('./assert').test>>(async (config, test_dir) => {
if (config.skip_if_ssr) return; if (config.mode && !config.mode.includes('server')) return;
if (config.skip_mode?.includes('server')) return;
await run_ssr_test(config, test_dir); await run_ssr_test(config, test_dir);
}); });

@ -28,7 +28,11 @@ const { run: run_browser_tests } = suite_with_variants<
>( >(
['dom', 'hydrate'], ['dom', 'hydrate'],
(variant, config) => { (variant, config) => {
if (variant === 'hydrate' && config.skip_if_hydrate) return true; if (variant === 'hydrate') {
if (config.mode && !config.mode.includes('hydrate')) return 'no-test';
if (config.skip_mode?.includes('hydrate')) return true;
}
return false; return false;
}, },
() => {}, () => {},

@ -1,7 +1,7 @@
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // unnecessary to test this in ssr mode mode: ['client', 'hydrate'],
html: '<button>10</button>', html: '<button>10</button>',

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
get props() { get props() {
return { value: 'hello!' }; return { value: 'hello!' };

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
get props() { get props() {
return { value: 'hello!' }; return { value: 'hello!' };

@ -1,8 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client'],
skip_if_hydrate: 'permanent',
html: ` html: `
<my-custom-element>Hello World!</my-custom-element> <my-custom-element>Hello World!</my-custom-element>
` `

@ -9,11 +9,11 @@ export default test({
<text wordWrap="true"></text> <text wordWrap="true"></text>
</page> </page>
`, `,
skip_if_hydrate: true,
compileOptions: { compileOptions: {
namespace: 'foreign' namespace: 'foreign'
}, },
test({ assert, target }) { test({ assert, target }) {
// @ts-ignore // @ts-ignore
const attr = (/** @type {string} */ sel) => target.querySelector(sel).attributes[0].name; const attr = (/** @type {string} */ sel) => target.querySelector(sel).attributes[0].name;

@ -8,7 +8,6 @@ export default test({
<button textWrap="true" text="button"> <button textWrap="true" text="button">
</page> </page>
`, `,
skip_if_hydrate: true,
test({ assert, target }) { test({ assert, target }) {
// @ts-ignore // @ts-ignore

@ -3,8 +3,6 @@ import { test } from '../../test';
export default test({ export default test({
skip: true, // TODO: needs fixing skip: true, // TODO: needs fixing
skip_if_ssr: true,
skip_if_hydrate: true,
html: ` html: `
<my-custom-inheritance-element>Hello World!</my-custom-inheritance-element> <my-custom-inheritance-element>Hello World!</my-custom-inheritance-element>
` `

@ -3,8 +3,6 @@ import { test } from '../../test';
export default test({ export default test({
skip: true, // TODO: needs fixing skip: true, // TODO: needs fixing
skip_if_ssr: true,
html: ` html: `
<span>3</span> <span>3</span>
<span>2</span> <span>2</span>

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
get props() { get props() {
return { value: 'hello!' }; return { value: 'hello!' };

@ -5,7 +5,7 @@ import { flushSync } from 'svelte';
let tasks = []; let tasks = [];
export default test({ export default test({
skip_if_ssr: 'permanent', // unnecessary to test this in ssr mode mode: ['client', 'hydrate'], // unnecessary to test this in ssr mode
get props() { get props() {
tasks = [ tasks = [

@ -1,7 +1,7 @@
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<p>selected: b</p> <p>selected: b</p>

@ -1,7 +1,7 @@
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<p>selected: a</p> <p>selected: a</p>

@ -1,7 +1,7 @@
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<p>selected: a</p> <p>selected: a</p>

@ -2,7 +2,7 @@ import { ok, test } from '../../test';
// test select binding behavior when a selected option is removed // test select binding behavior when a selected option is removed
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: `<p>selected: a</p><select><option value="a">a</option><option value="b">b</option><option value="c">c</option></select>`, html: `<p>selected: a</p><select><option value="a">a</option><option value="b">b</option><option value="c">c</option></select>`,

@ -1,7 +1,7 @@
import { ok, test } from '../../test'; import { ok, test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<input type="checkbox"> <input type="checkbox">
<input type="checkbox"> <input type="checkbox">

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
html: ` html: `
<div>foo</div> <div>foo</div>

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
html: ` html: `
<div>foo</div> <div>foo</div>
<div>first has foo: true</div> <div>first has foo: true</div>

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
html: ` html: `
<div>foo</div> <div>foo</div>

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
html: ` html: `
<div>foo</div> <div>foo</div>

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
get props() { get props() {
return { visible: true }; return { visible: true };
}, },

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // there's no class instance to retrieve in SSR mode mode: ['client', 'hydrate'], // there's no class instance to retrieve in SSR mode
html: '<div>has div: true</div>' html: '<div>has div: true</div>'
}); });

@ -1,6 +1,6 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // doesn't work in SSR mode: ['client', 'hydrate'], // doesn't work in SSR
html: '<div>object</div>' html: '<div>object</div>'
}); });

@ -2,7 +2,7 @@ import { ok, test } from '../../test';
import { flushSync } from 'svelte'; import { flushSync } from 'svelte';
export default test({ export default test({
skip_if_ssr: 'permanent', // relies on onMount firing, which does not happen in SSR mode mode: ['client', 'hydrate'], // relies on onMount firing, which does not happen in SSR mode
get props() { get props() {
return { count: 3 }; return { count: 3 };

@ -2,7 +2,7 @@ import { ok, test } from '../../test';
import { flushSync } from 'svelte'; import { flushSync } from 'svelte';
export default test({ export default test({
skip_if_ssr: 'permanent', // relies on onMount firing, which does not happen in SSR mode mode: ['client', 'hydrate'], // relies on onMount firing, which does not happen in SSR mode
get props() { get props() {
return { count: 3 }; return { count: 3 };

@ -1,10 +1,11 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client'],
skip_if_hydrate: 'permanent',
compileOptions: { compileOptions: {
dev: true dev: true
}, },
error: 'this={...} of <svelte:component> should specify a Svelte component.' error: 'this={...} of <svelte:component> should specify a Svelte component.'
}); });

@ -1,8 +1,8 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client'],
skip_if_hydrate: 'permanent',
get props() { get props() {
return { selected: false }; return { selected: false };
}, },

@ -1,7 +1,8 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
html: ` html: `
<div><div>Value in child component: </div></div> <div><div>Value in child component: </div></div>
` `

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<p>Reactive: foo</p> <p>Reactive: foo</p>

@ -9,7 +9,7 @@ let originalSpanGetBoundingClientRect;
let originalParagraphGetBoundingClientRect; let originalParagraphGetBoundingClientRect;
export default test({ export default test({
skip_if_ssr: 'permanent', // no animations in SSR mode: ['client', 'hydrate'], // no animations in SSR
get props() { get props() {
return { return {
things: [ things: [

@ -1,12 +1,15 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_hydrate: 'permanent', // SSR errors on render already mode: ['client', 'server'], // SSR errors on render already
compileOptions: { compileOptions: {
dev: true dev: true
}, },
get props() { get props() {
return { tag: 123 }; return { tag: 123 };
}, },
error: '<svelte:element> expects "this" attribute to be a string.' error: '<svelte:element> expects "this" attribute to be a string.'
}); });

@ -1,6 +1,6 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: '<div>object</div>' html: '<div>object</div>'
}); });

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // uses oncreate mode: ['client', 'hydrate'], // uses oncreate
html: '<div><p>true</p>\n<p>true</p></div>' html: '<div><p>true</p>\n<p>true</p></div>'
}); });

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // uses oncreate mode: ['client', 'hydrate'], // uses oncreate
html: '<div><p>true</p></div>', html: '<div><p>true</p></div>',

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_hydrate: 'permanent', // output is correct, but test suite chokes on the extra ssr comment which is harmless mode: ['client', 'server'], // output is correct, but test suite chokes on the extra ssr comment which is harmless
withoutNormalizeHtml: true, withoutNormalizeHtml: true,
html: get_html(false), html: get_html(false),
ssrHtml: get_html(true) ssrHtml: get_html(true)

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // a separate SSR test exists mode: ['client', 'hydrate'], // a separate SSR test exists
compileOptions: { compileOptions: {
preserveComments: true preserveComments: true

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // uses oncreate mode: ['client', 'hydrate'], // uses oncreate
html: '<p>2</p>' html: '<p>2</p>'
}); });

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // DOM and SSR output is different, a separate SSR test exists mode: ['client', 'hydrate'], // DOM and SSR output is different, a separate SSR test exists
html: '<input form="qux" list="quu" />', html: '<input form="qux" list="quu" />',
test({ assert, target }) { test({ assert, target }) {

@ -1,10 +1,12 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
compileOptions: { compileOptions: {
cssHash: () => 'svelte-xyz' cssHash: () => 'svelte-xyz'
}, },
async test({ assert, component, window }) { async test({ assert, component, window }) {
assert.htmlEqual( assert.htmlEqual(
window.document.head.innerHTML, window.document.head.innerHTML,

@ -1,10 +1,12 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
compileOptions: { compileOptions: {
cssHash: () => 'svelte-xyz' cssHash: () => 'svelte-xyz'
}, },
async test({ assert, component, window }) { async test({ assert, component, window }) {
assert.htmlEqual( assert.htmlEqual(
window.document.head.innerHTML, window.document.head.innerHTML,

@ -1,10 +1,12 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: true, skip_mode: ['server'],
compileOptions: { compileOptions: {
cssHash: () => 'svelte-xyz' cssHash: () => 'svelte-xyz'
}, },
async test({ assert, component, window }) { async test({ assert, component, window }) {
assert.htmlEqual( assert.htmlEqual(
window.document.head.innerHTML, window.document.head.innerHTML,

@ -1,7 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', // SSR behaviour is awkwardly different mode: ['client', 'hydrate'], // SSR behaviour is awkwardly different
get props() { get props() {
return { foo: 42 }; return { foo: 42 };

@ -3,7 +3,7 @@ import { test } from '../../test';
export default test({ export default test({
intro: true, intro: true,
skip_if_hydrate: 'permanent', mode: ['client', 'server'],
test({ assert, target, raf }) { test({ assert, target, raf }) {
const div = /** @type {HTMLDivElement & { foo: number }} */ (target.querySelector('div')); const div = /** @type {HTMLDivElement & { foo: number }} */ (target.querySelector('div'));

@ -20,7 +20,7 @@ export default test({
}); });
}, },
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
async test({ assert, target, window }) { async test({ assert, target, window }) {
const event = new window.Event('resize'); const event = new window.Event('resize');

@ -27,10 +27,10 @@ type Assert = typeof import('vitest').assert & {
export interface RuntimeTest<Props extends Record<string, any> = Record<string, any>> export interface RuntimeTest<Props extends Record<string, any> = Record<string, any>>
extends BaseTest { extends BaseTest {
/** Use `true` to signal a temporary skip, and `"permanent"` to signal that this test is never intended to run in ssr mode */ /** Use e.g. `mode: ['client']` to indicate that this test should never run in server/hydrate modes */
skip_if_ssr?: boolean | 'permanent'; mode?: Array<'server' | 'client' | 'hydrate'>;
/** Use `true` to signal a temporary skip, and `"permanent"` to signal that this test is never intended to run in hydration mode */ /** Temporarily skip specific modes, without skipping the entire test */
skip_if_hydrate?: boolean | 'permanent'; skip_mode?: Array<'server' | 'client' | 'hydrate'>;
html?: string; html?: string;
ssrHtml?: string; ssrHtml?: string;
compileOptions?: Partial<CompileOptions>; compileOptions?: Partial<CompileOptions>;
@ -96,12 +96,13 @@ export function runtime_suite(runes: boolean) {
['dom', 'hydrate', 'ssr'], ['dom', 'hydrate', 'ssr'],
(variant, config) => { (variant, config) => {
if (variant === 'hydrate') { if (variant === 'hydrate') {
if (config.skip_if_hydrate === 'permanent') return 'no-test'; if (config.mode && !config.mode.includes('hydrate')) return 'no-test';
if (config.skip_if_hydrate) return true; if (config.skip_mode?.includes('hydrate')) return true;
} }
if (variant === 'ssr') { if (variant === 'ssr') {
if ( if (
config.skip_if_ssr === 'permanent' || (config.mode && !config.mode.includes('server')) ||
(!config.test_ssr && (!config.test_ssr &&
config.html === undefined && config.html === undefined &&
config.ssrHtml === undefined && config.ssrHtml === undefined &&
@ -109,7 +110,7 @@ export function runtime_suite(runes: boolean) {
) { ) {
return 'no-test'; return 'no-test';
} }
if (config.skip_if_ssr) return true; if (config.skip_mode?.includes('server')) return true;
} }
return false; return false;

@ -4,7 +4,7 @@ import { log } from './log.js';
export default test({ export default test({
// The component context class instance gets shared between tests, strangely, causing hydration to fail? // The component context class instance gets shared between tests, strangely, causing hydration to fail?
skip_if_hydrate: 'permanent', mode: ['client', 'server'],
before_test() { before_test() {
log.length = 0; log.length = 0;

@ -1,8 +1,9 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
mode: ['client', 'server'],
html: '<div>d2: 3</div><div>d3: 3</div><div>d4: 3</div>', html: '<div>d2: 3</div><div>d3: 3</div><div>d4: 3</div>',
skip_if_hydrate: 'permanent',
async test({ assert, target }) { async test({ assert, target }) {
await Promise.resolve(); await Promise.resolve();

@ -2,7 +2,8 @@ import { flushSync } from 'svelte';
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_hydrate: 'permanent', mode: ['client', 'server'],
async test({ assert, target }) { async test({ assert, target }) {
let [btn1, btn2] = target.querySelectorAll('button'); let [btn1, btn2] = target.querySelectorAll('button');
const input = target.querySelector('input'); const input = target.querySelector('input');

@ -2,9 +2,9 @@ import { flushSync } from 'svelte';
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
mode: ['client'],
html: `<p>test costs $1</p><p>test 2 costs $2</p><p>test costs $1</p><p>test 2 costs $2</p><button>add</button><button>change</button><button>reload</button>`, html: `<p>test costs $1</p><p>test 2 costs $2</p><p>test costs $1</p><p>test 2 costs $2</p><button>add</button><button>change</button><button>reload</button>`,
skip_if_ssr: 'permanent',
skip_if_hydrate: 'permanent',
async test({ assert, target }) { async test({ assert, target }) {
const [btn1, btn2, btn3] = target.querySelectorAll('button'); const [btn1, btn2, btn3] = target.querySelectorAll('button');

@ -1,8 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_hydrate: 'permanent', // unnecessary to test mode: ['client'],
skip_if_ssr: 'permanent', // unnecessary to test
async test({ assert, target }) { async test({ assert, target }) {
const [b1, b2] = target.querySelectorAll('button'); const [b1, b2] = target.querySelectorAll('button');

@ -1,8 +1,7 @@
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_hydrate: 'permanent', // unnecessary to test mode: ['client'],
skip_if_ssr: 'permanent', // unnecessary to test
async test({ assert, target }) { async test({ assert, target }) {
const [b1, b2] = target.querySelectorAll('button'); const [b1, b2] = target.querySelectorAll('button');

@ -10,8 +10,8 @@ let log;
let original_log; let original_log;
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client'],
skip_if_hydrate: 'permanent', // log patching will be too late
before_test() { before_test() {
log = []; log = [];
original_log = console.log; original_log = console.log;

@ -4,17 +4,20 @@ let math_random = Math.random;
let calls = 0; let calls = 0;
export default test({ export default test({
skip_if_hydrate: 'permanent', mode: ['client', 'server'],
before_test() { before_test() {
Math.random = function () { Math.random = function () {
calls++; calls++;
return math_random.call(this); return math_random.call(this);
}; };
}, },
after_test() { after_test() {
Math.random = math_random; Math.random = math_random;
calls = 0; calls = 0;
}, },
test({ assert }) { test({ assert }) {
assert.equal(calls, 1); assert.equal(calls, 1);
} }

@ -2,7 +2,7 @@ import { flushSync } from 'svelte';
import { test } from '../../test'; import { test } from '../../test';
export default test({ export default test({
skip_if_ssr: 'permanent', mode: ['client', 'hydrate'],
html: ` html: `
<input><input><input><div>3</div> <input><input><input><div>3</div>
` `

Loading…
Cancel
Save