element
+ const inner_div = window.document.createElement('div');
+ div.insertBefore(inner_div, null);
+ event = new window.InputEvent('input');
+ await div.dispatchEvent(event);
+ assert.equal(div.textContent, 'a');
+
+ text = window.document.createTextNode('b');
+ inner_div.insertBefore(text, null);
+ event = new window.InputEvent('input');
+ await div.dispatchEvent(event);
+ assert.equal(div.textContent, 'ab');
+ }
+};
diff --git a/test/runtime/samples/component-event-handler-contenteditable/main.svelte b/test/runtime-puppeteer/samples/component-event-handler-contenteditable/main.svelte
similarity index 100%
rename from test/runtime/samples/component-event-handler-contenteditable/main.svelte
rename to test/runtime-puppeteer/samples/component-event-handler-contenteditable/main.svelte
diff --git a/test/runtime/samples/action-document/_config.js b/test/runtime/samples/action-document/_config.js
new file mode 100644
index 0000000000..d33cdb3fa4
--- /dev/null
+++ b/test/runtime/samples/action-document/_config.js
@@ -0,0 +1,14 @@
+export default {
+ html: '
',
+
+ async test({ assert, target, window }) {
+ const visibility = new window.Event('visibilitychange');
+
+ await window.document.dispatchEvent(visibility);
+ assert.htmlEqual(target.innerHTML, `
+
+ `);
+ }
+};
diff --git a/test/runtime/samples/action-document/main.svelte b/test/runtime/samples/action-document/main.svelte
new file mode 100644
index 0000000000..eec4b3fecd
--- /dev/null
+++ b/test/runtime/samples/action-document/main.svelte
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/test/runtime/samples/attribute-boolean-hidden/_config.js b/test/runtime/samples/attribute-boolean-hidden/_config.js
new file mode 100644
index 0000000000..cb38feb93d
--- /dev/null
+++ b/test/runtime/samples/attribute-boolean-hidden/_config.js
@@ -0,0 +1,10 @@
+export default {
+ props: {
+ hidden: true
+ },
+ html: '
',
+ test({ assert, component, target }) {
+ component.hidden = false;
+ assert.htmlEqual(target.innerHTML, '
');
+ }
+};
diff --git a/test/runtime/samples/attribute-boolean-hidden/main.svelte b/test/runtime/samples/attribute-boolean-hidden/main.svelte
new file mode 100644
index 0000000000..f8d7a26910
--- /dev/null
+++ b/test/runtime/samples/attribute-boolean-hidden/main.svelte
@@ -0,0 +1,5 @@
+
+
+
diff --git a/test/runtime/samples/attribute-boolean-itemscope/_config.js b/test/runtime/samples/attribute-boolean-itemscope/_config.js
deleted file mode 100644
index b5b21cfc47..0000000000
--- a/test/runtime/samples/attribute-boolean-itemscope/_config.js
+++ /dev/null
@@ -1,11 +0,0 @@
-export default {
- props: {
- itemscope: true
- },
- test({ assert, target, component }) {
- const div = target.querySelector('div');
- assert.ok(div.itemscope);
- component.itemscope = false;
- assert.ok(!div.itemscope);
- }
-};
diff --git a/test/runtime/samples/attribute-boolean-itemscope/main.svelte b/test/runtime/samples/attribute-boolean-itemscope/main.svelte
deleted file mode 100644
index 83265a983c..0000000000
--- a/test/runtime/samples/attribute-boolean-itemscope/main.svelte
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
diff --git a/test/runtime/samples/attribute-escape/_config.js b/test/runtime/samples/attribute-escape/_config.js
new file mode 100644
index 0000000000..25313f01dc
--- /dev/null
+++ b/test/runtime/samples/attribute-escape/_config.js
@@ -0,0 +1,4 @@
+export default {
+ html: '
',
+ ssrHtml: '
'
+};
diff --git a/test/runtime/samples/attribute-escape/main.svelte b/test/runtime/samples/attribute-escape/main.svelte
new file mode 100644
index 0000000000..1b1fdf62a0
--- /dev/null
+++ b/test/runtime/samples/attribute-escape/main.svelte
@@ -0,0 +1 @@
+
`} />
diff --git a/test/runtime/samples/attribute-microdata/_config.js b/test/runtime/samples/attribute-microdata/_config.js
new file mode 100644
index 0000000000..8c6a7765ea
--- /dev/null
+++ b/test/runtime/samples/attribute-microdata/_config.js
@@ -0,0 +1,25 @@
+// There is no relationship between the attribute and the dom node with regards to microdata attributes https://developer.mozilla.org/en-US/docs/Web/HTML/Microdata
+export default {
+ html: `
+
Game - REQUIRES
+
OS
+
+
+
RATING:
+ 4.6 (
+ 8864 ratings )
+
+
+
+
+ Price: $1.00
+
+
+ `
+};
diff --git a/test/runtime/samples/attribute-microdata/main.svelte b/test/runtime/samples/attribute-microdata/main.svelte
new file mode 100644
index 0000000000..f03a6943d9
--- /dev/null
+++ b/test/runtime/samples/attribute-microdata/main.svelte
@@ -0,0 +1,31 @@
+
+
+
Game - REQUIRES
+
OS
+
+
+
+ RATING:
+ 4.6 (
+ 8864 ratings )
+
+
+
+
+
+ Price: $1.00
+
+
diff --git a/test/runtime/samples/await-then-destruct-computed-props/_config.js b/test/runtime/samples/await-then-destruct-computed-props/_config.js
new file mode 100644
index 0000000000..6d147535e3
--- /dev/null
+++ b/test/runtime/samples/await-then-destruct-computed-props/_config.js
@@ -0,0 +1,32 @@
+export default {
+ async test({ assert, component, target }) {
+ await Promise.resolve();
+
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
propA: 3
+
propB: 7
+
num: 3
+
rest: {"prop3":{"prop9":9,"prop10":10}}
+
propZ: 5
+
propY: 6
+
rest: {"propX":7,"propW":8}
+ `
+ );
+
+ await (component.object = Promise.resolve({ prop1: 'one', prop2: 'two', prop3: { prop7: 'seven' }, prop4: { prop10: 'ten' }}));
+ assert.htmlEqual(
+ target.innerHTML,
+ `
+
propA: seven
+
propB: ten
+
num: 5
+
rest: {"prop1":"one","prop2":"two"}
+
propZ: 5
+
propY: 6
+
rest: {"propX":7,"propW":8}
+ `
+ );
+ }
+};
diff --git a/test/runtime/samples/await-then-destruct-computed-props/main.svelte b/test/runtime/samples/await-then-destruct-computed-props/main.svelte
new file mode 100644
index 0000000000..71756d6cf3
--- /dev/null
+++ b/test/runtime/samples/await-then-destruct-computed-props/main.svelte
@@ -0,0 +1,23 @@
+
+
+{#await object then { [`prop${num++}`]: { [`prop${num + 3}`]: propA }, [`prop${num++}`]: { [`prop${num + 5}`]: propB }, ...rest }}
+
propA: {propA}
+
propB: {propB}
+
num: {num}
+
rest: {JSON.stringify(rest)}
+{/await}
+
+{#await objectReject then value}
+ resolved
+{:catch { [`${prop}Z`]: propZ, [`${prop}Y`]: propY, ...rest }}
+
propZ: {propZ}
+
propY: {propY}
+
rest: {JSON.stringify(rest)}
+{/await}
+
diff --git a/test/runtime/samples/binding-contenteditable-innertext/_config.js b/test/runtime/samples/binding-contenteditable-innertext/_config.js
new file mode 100644
index 0000000000..36e762ac24
--- /dev/null
+++ b/test/runtime/samples/binding-contenteditable-innertext/_config.js
@@ -0,0 +1,25 @@
+export default {
+ props: {
+ name: 'world'
+ },
+
+ ssrHtml: `
+
world
+
hello world
+ `,
+
+ async test({ assert, component, target, window }) {
+ // JSDom doesn't support innerText yet, so the test is not ideal
+ // https://github.com/jsdom/jsdom/issues/1245
+ const el = target.querySelector('editor');
+ assert.equal(el.innerText, 'world');
+
+ const event = new window.Event('input');
+ el.innerText = 'everybody';
+ await el.dispatchEvent(event);
+ assert.equal(component.name, 'everybody');
+
+ component.name = 'goodbye';
+ assert.equal(el.innerText, 'goodbye');
+ }
+};
diff --git a/test/runtime/samples/binding-contenteditable-innertext/main.svelte b/test/runtime/samples/binding-contenteditable-innertext/main.svelte
new file mode 100644
index 0000000000..d65350d1a8
--- /dev/null
+++ b/test/runtime/samples/binding-contenteditable-innertext/main.svelte
@@ -0,0 +1,6 @@
+
+
+
+
hello {name}
diff --git a/test/runtime/samples/binding-select-null-placeholder/_config.js b/test/runtime/samples/binding-select-null-placeholder/_config.js
new file mode 100644
index 0000000000..b453e6869a
--- /dev/null
+++ b/test/runtime/samples/binding-select-null-placeholder/_config.js
@@ -0,0 +1,28 @@
+const items = [ { id: 'a' }, { id: 'b' } ];
+
+export default {
+ props: {
+ foo: null,
+ items
+ },
+
+ test({ assert, component, target }) {
+ const select = target.querySelector( 'select' );
+ const options = target.querySelectorAll( 'option' );
+
+ assert.equal( options[0].selected, true );
+ assert.equal( options[0].disabled, true );
+ assert.equal( options[1].selected, false );
+ assert.equal( options[1].disabled, false );
+
+ // placeholder option value must be blank string for native required field validation
+ assert.equal( options[0].value, '' );
+ assert.equal( select.checkValidity(), false );
+
+ component.foo = items[0];
+
+ assert.equal( options[0].selected, false );
+ assert.equal( options[1].selected, true );
+ assert.equal( select.checkValidity(), true );
+ }
+};
diff --git a/test/runtime/samples/binding-select-null-placeholder/main.svelte b/test/runtime/samples/binding-select-null-placeholder/main.svelte
new file mode 100644
index 0000000000..65cab99495
--- /dev/null
+++ b/test/runtime/samples/binding-select-null-placeholder/main.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/test/runtime/samples/component-event-handler-contenteditable/_config.js b/test/runtime/samples/component-event-handler-contenteditable/_config.js
deleted file mode 100644
index 1628e22d01..0000000000
--- a/test/runtime/samples/component-event-handler-contenteditable/_config.js
+++ /dev/null
@@ -1,15 +0,0 @@
-export default {
- html: `
-
- `,
-
- async test({ assert, target, window }) {
- const div = target.querySelector('div');
- const text = window.document.createTextNode('a');
- div.insertBefore(text, null);
- const event = new window.InputEvent('input');
- await div.dispatchEvent(event);
-
- assert.equal(div.textContent, 'a');
- }
-};
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/_config.js b/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/_config.js
new file mode 100644
index 0000000000..9ec4a3e3e6
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/_config.js
@@ -0,0 +1,20 @@
+export default {
+ html: `
+
4, 12, 60
+ `,
+
+ async test({ component, target, assert }) {
+ component.permutation = [2, 3, 1];
+ await (component.promise1 = Promise.resolve({length: 1, width: 2, height: 3}));
+ try {
+ await (component.promise2 = Promise.reject({length: 97, width: 98, height: 99}));
+ } catch (e) {
+ // nothing
+ }
+
+ assert.htmlEqual(target.innerHTML, `
+
2, 11, 2
+
9506, 28811, 98
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/main.svelte b/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/main.svelte
new file mode 100644
index 0000000000..285fc44348
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-computed-in-computed/main.svelte
@@ -0,0 +1,27 @@
+
+
+{#await promise1 then { length, width, height }}
+ {@const { [0]: a, [1]: b, [2]: c } = permutation}
+ {@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
+
{first}, {second}, {third}
+{/await}
+
+{#await promise2 catch { [`leng${th}`]: l, [`wid${th}`]: w, height: h }}
+ {@const [a, b, c] = permutation}
+ {@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(l, w, h) }
+
{first}, {second}, {third}
+{/await}
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-computed-props/_config.js b/test/runtime/samples/const-tag-await-then-destructuring-computed-props/_config.js
new file mode 100644
index 0000000000..9ec4a3e3e6
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-computed-props/_config.js
@@ -0,0 +1,20 @@
+export default {
+ html: `
+
4, 12, 60
+ `,
+
+ async test({ component, target, assert }) {
+ component.permutation = [2, 3, 1];
+ await (component.promise1 = Promise.resolve({length: 1, width: 2, height: 3}));
+ try {
+ await (component.promise2 = Promise.reject({length: 97, width: 98, height: 99}));
+ } catch (e) {
+ // nothing
+ }
+
+ assert.htmlEqual(target.innerHTML, `
+
2, 11, 2
+
9506, 28811, 98
+ `);
+ }
+};
diff --git a/test/runtime/samples/const-tag-await-then-destructuring-computed-props/main.svelte b/test/runtime/samples/const-tag-await-then-destructuring-computed-props/main.svelte
new file mode 100644
index 0000000000..fbc54e4d0b
--- /dev/null
+++ b/test/runtime/samples/const-tag-await-then-destructuring-computed-props/main.svelte
@@ -0,0 +1,25 @@
+
+
+{#await promise1 then { length, width, height }}
+ {@const [a, b, c] = permutation}
+ {@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
+
{first}, {second}, {third}
+{/await}
+
+{#await promise2 catch { length, width, height }}
+ {@const [a, b, c] = permutation}
+ {@const { [`${a}-Dimensions`]: { [c - 1]: first }, [`${b}-Dimensions`]: { [b - 1]: second }, [`${c}-Dimensions`]: { [a - 1]: third } } = calculate(length, width, height) }
+
{first}, {second}, {third}
+{/await}
diff --git a/test/runtime/samples/const-tag-each-destructure-computed-in-computed/_config.js b/test/runtime/samples/const-tag-each-destructure-computed-in-computed/_config.js
new file mode 100644
index 0000000000..c299f19da6
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-computed-in-computed/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `
+
+
+
+ `,
+
+ async test({ component, target, assert }) {
+ component.boxes = [{ length: 10, width: 20, height: 30 }];
+
+ assert.htmlEqual(target.innerHTML,
+ '
'
+ );
+ }
+};
diff --git a/test/runtime/samples/const-tag-each-destructure-computed-in-computed/main.svelte b/test/runtime/samples/const-tag-each-destructure-computed-in-computed/main.svelte
new file mode 100644
index 0000000000..26dc5557ed
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-computed-in-computed/main.svelte
@@ -0,0 +1,44 @@
+
+
+{#each boxes as { [`leng${th}`]: length, [`wid${th}`]: width, height }}
+ {@const {
+ [`two${dimension}`]: areas,
+ [`three${dimension}`]: {
+ volume
+ }
+ } = calculate(length, width, height)}
+ {@const {
+ i = 1,
+ [`bottom${area}`]: bottom,
+ [`side${area}${i++}`]: sideone,
+ [`side${area}${i++}`]: sidetwo
+ } = areas}
+
+{/each}
diff --git a/test/runtime/samples/const-tag-each-destructure-computed-props/_config.js b/test/runtime/samples/const-tag-each-destructure-computed-props/_config.js
new file mode 100644
index 0000000000..c299f19da6
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-computed-props/_config.js
@@ -0,0 +1,15 @@
+export default {
+ html: `
+
+
+
+ `,
+
+ async test({ component, target, assert }) {
+ component.boxes = [{ length: 10, width: 20, height: 30 }];
+
+ assert.htmlEqual(target.innerHTML,
+ '
'
+ );
+ }
+};
diff --git a/test/runtime/samples/const-tag-each-destructure-computed-props/main.svelte b/test/runtime/samples/const-tag-each-destructure-computed-props/main.svelte
new file mode 100644
index 0000000000..c8c7e1977a
--- /dev/null
+++ b/test/runtime/samples/const-tag-each-destructure-computed-props/main.svelte
@@ -0,0 +1,42 @@
+
+
+{#each boxes as { length, width, height }}
+ {@const {
+ [`two${dimension}`]: {
+ i = 1,
+ [`bottom${area}`]: bottom,
+ [`side${area}${i++}`]: sideone,
+ [`side${area}${i++}`]: sidetwo
+ },
+ [`three${dimension}`]: {
+ volume
+ }
+ } = calculate(length, width, height)}
+
+{/each}
diff --git a/test/runtime/samples/each-block-destructured-array-computed-props/_config.js b/test/runtime/samples/each-block-destructured-array-computed-props/_config.js
new file mode 100644
index 0000000000..e400e2f280
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-array-computed-props/_config.js
@@ -0,0 +1,24 @@
+export default {
+ props: {
+ array: [
+ [1, 2, 3, 4, 5],
+ [6, 7, 8],
+ [9, 10, 11, 12],
+ [13, 14, 15, 16, 17, 18, 19, 20, 21, 22]
+ ]
+ },
+
+ html: `
+
First: 1, Half: 3, Last: 5, Length: 5
+
First: 6, Half: 7, Last: 8, Length: 3
+
First: 9, Half: 11, Last: 12, Length: 4
+
First: 13, Half: 18, Last: 22, Length: 10
+ `,
+
+ test({ assert, component, target }) {
+ component.array = [[23, 24, 25, 26, 27, 28, 29]];
+ assert.htmlEqual( target.innerHTML, `
+
First: 23, Half: 26, Last: 29, Length: 7
+ `);
+ }
+};
diff --git a/test/runtime/samples/each-block-destructured-array-computed-props/main.svelte b/test/runtime/samples/each-block-destructured-array-computed-props/main.svelte
new file mode 100644
index 0000000000..00a9a99a89
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-array-computed-props/main.svelte
@@ -0,0 +1,7 @@
+
+
+{#each array as { 0: first, length, [length - 1]: last, [Math.floor(length / 2)]: half }}
+
First: {first}, Half: {half}, Last: {last}, Length: {length}
+{/each}
diff --git a/test/runtime/samples/each-block-destructured-object-computed-props/_config.js b/test/runtime/samples/each-block-destructured-object-computed-props/_config.js
new file mode 100644
index 0000000000..7e98fab913
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-object-computed-props/_config.js
@@ -0,0 +1,26 @@
+export default {
+ props: {
+ firstString: 'cats',
+ secondString: 'dogs',
+ objectsArray: [
+ { dogs: 'woof', cats: 'meow', stac: 'stack', DOGS: 'WOOF' },
+ { dogs: 'A German sheppard', cats: 'A tailless cat', stac: 'A jenga tower', DOGS: 'A GERMAN SHEPPARD' },
+ { dogs: 'dogs', cats: 'cats', stac: 'stac', DOGS: 'DOGS' }
+ ]
+ },
+
+ html: `
+
cats: meow
+
dogs: woof
+
stac: stack
+
DOGS: WOOF
+
cats: A tailless cat
+
dogs: A German sheppard
+
stac: A jenga tower
+
DOGS: A GERMAN SHEPPARD
+
cats: cats
+
dogs: dogs
+
stac: stac
+
DOGS: DOGS
+ `
+};
diff --git a/test/runtime/samples/each-block-destructured-object-computed-props/main.svelte b/test/runtime/samples/each-block-destructured-object-computed-props/main.svelte
new file mode 100644
index 0000000000..9c2ae29617
--- /dev/null
+++ b/test/runtime/samples/each-block-destructured-object-computed-props/main.svelte
@@ -0,0 +1,12 @@
+
+
+{#each objectsArray as { [firstString]: firstProp, [secondString]: secondProp, [firstString.split('').reverse().join('')]: reverseFirst, [secondString.toUpperCase()]: upperSecond } }
+
{firstString}: {firstProp}
+
{secondString}: {secondProp}
+
{firstString.split('').reverse().join('')}: {reverseFirst}
+
{secondString.toUpperCase()}: {upperSecond}
+{/each}
diff --git a/test/runtime/samples/html-entities-inside-component-slot/Component.svelte b/test/runtime/samples/html-entities-inside-component-slot/Component.svelte
new file mode 100644
index 0000000000..8063609738
--- /dev/null
+++ b/test/runtime/samples/html-entities-inside-component-slot/Component.svelte
@@ -0,0 +1,3 @@
+
+
+
diff --git a/test/runtime/samples/html-entities-inside-component-slot/_config.js b/test/runtime/samples/html-entities-inside-component-slot/_config.js
new file mode 100644
index 0000000000..33777a4919
--- /dev/null
+++ b/test/runtime/samples/html-entities-inside-component-slot/_config.js
@@ -0,0 +1,11 @@
+export default {
+ html: `
+
+
+
+
+
+
+
+ `
+};
diff --git a/test/runtime/samples/html-entities-inside-component-slot/main.svelte b/test/runtime/samples/html-entities-inside-component-slot/main.svelte
new file mode 100644
index 0000000000..8baecfedcc
--- /dev/null
+++ b/test/runtime/samples/html-entities-inside-component-slot/main.svelte
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+ {@html " "}
+
diff --git a/test/runtime/samples/reactive-statement-indirect/_config.js b/test/runtime/samples/reactive-statement-indirect/_config.js
new file mode 100644
index 0000000000..9265ded073
--- /dev/null
+++ b/test/runtime/samples/reactive-statement-indirect/_config.js
@@ -0,0 +1,14 @@
+export default {
+ html: `
+
2
+
+ `,
+ async test({ assert, target }) {
+ await target.querySelector('button').dispatchEvent(new window.MouseEvent('click'));
+
+ assert.htmlEqual(target.innerHTML, `
+
4
+
+ `);
+ }
+};
diff --git a/test/runtime/samples/reactive-statement-indirect/main.svelte b/test/runtime/samples/reactive-statement-indirect/main.svelte
new file mode 100644
index 0000000000..ab75c19127
--- /dev/null
+++ b/test/runtime/samples/reactive-statement-indirect/main.svelte
@@ -0,0 +1,11 @@
+
+
+
{indirect_double}
+
diff --git a/test/runtime/samples/reactive-values-text-node/_config.js b/test/runtime/samples/reactive-values-text-node/_config.js
new file mode 100644
index 0000000000..fb859285dd
--- /dev/null
+++ b/test/runtime/samples/reactive-values-text-node/_config.js
@@ -0,0 +1,9 @@
+export default {
+ html:'
same text
',
+ async test({ assert, target }) {
+ await new Promise(f => setTimeout(f, 10));
+ assert.htmlEqual(target.innerHTML, `
+
same text text
+ `);
+ }
+};
diff --git a/test/runtime/samples/reactive-values-text-node/main.svelte b/test/runtime/samples/reactive-values-text-node/main.svelte
new file mode 100644
index 0000000000..0982622b1a
--- /dev/null
+++ b/test/runtime/samples/reactive-values-text-node/main.svelte
@@ -0,0 +1,8 @@
+
+
+
{text} text
diff --git a/test/runtime/samples/spread-width-height-attributes/_config.js b/test/runtime/samples/spread-width-height-attributes/_config.js
new file mode 100644
index 0000000000..cf2dc7efde
--- /dev/null
+++ b/test/runtime/samples/spread-width-height-attributes/_config.js
@@ -0,0 +1,4 @@
+export default {
+ // https://github.com/sveltejs/svelte/issues/6752
+ html: '
![]()
'
+};
diff --git a/test/runtime/samples/spread-width-height-attributes/main.svelte b/test/runtime/samples/spread-width-height-attributes/main.svelte
new file mode 100644
index 0000000000..b91b008457
--- /dev/null
+++ b/test/runtime/samples/spread-width-height-attributes/main.svelte
@@ -0,0 +1 @@
+
![]()
diff --git a/test/runtime/samples/transition-js-deferred-option-direction/_config.js b/test/runtime/samples/transition-js-deferred-option-direction/_config.js
index 2bf448d9c7..71adc5539f 100644
--- a/test/runtime/samples/transition-js-deferred-option-direction/_config.js
+++ b/test/runtime/samples/transition-js-deferred-option-direction/_config.js
@@ -4,16 +4,19 @@ export default {
const div_in = target.querySelector('#in');
const div_out = target.querySelector('#out');
- const div_both = target.querySelector('#both');
+ const div_bothin = target.querySelector('#both-in');
+ const div_bothout = target.querySelector('#both-out');
- assert.equal(div_in.initial, 'in');
- assert.equal(div_out.initial, 'out');
- assert.equal(div_both.initial, 'both');
+ assert.equal(div_in.directions, 'in,in');
+ assert.equal(div_out.directions, 'out');
+ assert.equal(div_bothin.directions, 'both');
+ assert.equal(div_bothout.directions, 'both');
return Promise.resolve().then(() => {
- assert.equal(div_in.later, 'in');
- assert.equal(div_out.later, 'out');
- assert.equal(div_both.later, 'both');
+ assert.equal(div_in.directions, 'in,in');
+ assert.equal(div_out.directions, 'out,out');
+ assert.equal(div_bothin.directions, 'both,in');
+ assert.equal(div_bothout.directions, 'both,out');
});
}
};
diff --git a/test/runtime/samples/transition-js-deferred-option-direction/main.svelte b/test/runtime/samples/transition-js-deferred-option-direction/main.svelte
index 9ac816ce75..76bca2b5e2 100644
--- a/test/runtime/samples/transition-js-deferred-option-direction/main.svelte
+++ b/test/runtime/samples/transition-js-deferred-option-direction/main.svelte
@@ -2,10 +2,10 @@
export let visible;
function foo(node, _params, options) {
- node.initial = options.direction;
+ node.directions = options.direction;
return (opts) => {
- node.later = opts.direction;
+ node.directions += "," + opts.direction;
return {
duration: 10
@@ -15,10 +15,11 @@
{#if visible}
-
-
+
+
{/if}
{#if !visible}
-
+
+
{/if}
diff --git a/test/types/actions.ts b/test/types/actions.ts
new file mode 100644
index 0000000000..2a604151a8
--- /dev/null
+++ b/test/types/actions.ts
@@ -0,0 +1,153 @@
+import type { Action, ActionReturn } from '$runtime/action';
+
+// ---------------- Action
+
+const href: Action
= (node) => {
+ node.href = '';
+ // @ts-expect-error
+ node.href = 1;
+};
+href;
+
+const required: Action = (node, param) => {
+ node;
+ param;
+};
+required(null as any, true);
+// @ts-expect-error (only in strict mode) boolean missing
+required(null as any);
+// @ts-expect-error no boolean
+required(null as any, 'string');
+
+const required1: Action = (node, param) => {
+ node;
+ param;
+ return {
+ update: (p) => p === true,
+ destroy: () => {}
+ };
+};
+required1;
+
+const required2: Action = (node) => {
+ node;
+};
+required2;
+
+const required3: Action = (node, param) => {
+ node;
+ param;
+ return {
+ // @ts-expect-error comparison always resolves to false
+ update: (p) => p === 'd',
+ destroy: () => {}
+ };
+};
+required3;
+
+const optional: Action = (node, param?) => {
+ node;
+ param;
+};
+optional(null as any, true);
+optional(null as any);
+// @ts-expect-error no boolean
+optional(null as any, 'string');
+
+const optional1: Action = (node, param?) => {
+ node;
+ param;
+ return {
+ update: (p) => p === true,
+ destroy: () => {}
+ };
+};
+optional1;
+
+const optional2: Action = (node) => {
+ node;
+};
+optional2;
+
+const optional3: Action = (node, param) => {
+ node;
+ param;
+};
+optional3;
+
+const optional4: Action = (node, param?) => {
+ node;
+ param;
+ return {
+ // @ts-expect-error comparison always resolves to false
+ update: (p) => p === 'd',
+ destroy: () => {}
+ };
+};
+optional4;
+
+const no: Action = (node) => {
+ node;
+};
+// @ts-expect-error second param
+no(null as any, true);
+no(null as any);
+// @ts-expect-error second param
+no(null as any, 'string');
+
+const no1: Action = (node) => {
+ node;
+ return {
+ destroy: () => {}
+ };
+};
+no1;
+
+// @ts-expect-error param given
+const no2: Action = (node, param?) => {};
+no2;
+
+// @ts-expect-error param given
+const no3: Action = (node, param) => {};
+no3;
+
+// @ts-expect-error update method given
+const no4: Action = (node) => {
+ return {
+ update: () => {},
+ destroy: () => {}
+ };
+};
+no4;
+
+// ---------------- ActionReturn
+
+const requiredReturn: ActionReturn = {
+ update: (p) => p.toString()
+};
+requiredReturn;
+
+const optionalReturn: ActionReturn = {
+ update: (p) => {
+ p === true;
+ // @ts-expect-error could be undefined
+ p.toString();
+ }
+};
+optionalReturn;
+
+const invalidProperty: ActionReturn = {
+ // @ts-expect-error invalid property
+ invalid: () => {}
+};
+invalidProperty;
+
+type Attributes = ActionReturn['$$_attributes'];
+const attributes: Attributes = { a: 'a' };
+attributes;
+// @ts-expect-error wrong type
+const invalidAttributes1: Attributes = { a: 1 };
+invalidAttributes1;
+// @ts-expect-error missing prop
+const invalidAttributes2: Attributes = {};
+invalidAttributes2;
diff --git a/test/types/create-event-dispatcher.ts b/test/types/create-event-dispatcher.ts
new file mode 100644
index 0000000000..d9fc6c65bd
--- /dev/null
+++ b/test/types/create-event-dispatcher.ts
@@ -0,0 +1,43 @@
+import { createEventDispatcher } from '$runtime/internal/lifecycle';
+
+const dispatch = createEventDispatcher<{
+ loaded: never
+ change: string
+ valid: boolean
+ optional: number | null
+}>();
+
+// @ts-expect-error: dispatch invalid event
+dispatch('some-event');
+
+dispatch('loaded');
+dispatch('loaded', null);
+dispatch('loaded', undefined);
+dispatch('loaded', undefined, { cancelable: true });
+// @ts-expect-error: no detail accepted
+dispatch('loaded', 123);
+
+// @ts-expect-error: detail not provided
+dispatch('change');
+dispatch('change', 'string');
+dispatch('change', 'string', { cancelable: true });
+// @ts-expect-error: wrong type of detail
+dispatch('change', 123);
+// @ts-expect-error: wrong type of detail
+dispatch('change', undefined);
+
+dispatch('valid', true);
+dispatch('valid', true, { cancelable: true });
+// @ts-expect-error: wrong type of detail
+dispatch('valid', 'string');
+
+dispatch('optional');
+dispatch('optional', 123);
+dispatch('optional', 123, { cancelable: true });
+dispatch('optional', null);
+dispatch('optional', undefined);
+dispatch('optional', undefined, { cancelable: true });
+// @ts-expect-error: wrong type of optional detail
+dispatch('optional', 'string');
+// @ts-expect-error: wrong type of option
+dispatch('optional', undefined, { cancelabled: true });
diff --git a/test/types/tsconfig.json b/test/types/tsconfig.json
new file mode 100644
index 0000000000..108ed2a2b2
--- /dev/null
+++ b/test/types/tsconfig.json
@@ -0,0 +1,16 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "rootDir": "../..",
+ "baseUrl": "../../",
+ "paths": {
+ "$runtime/*": ["src/runtime/*"]
+ },
+ // enable strictest options
+ "allowUnreachableCode": false,
+ "noFallthroughCasesInSwitch": true,
+ "noImplicitReturns": true,
+ "strict": true,
+ },
+ "include": ["."]
+}
\ No newline at end of file
diff --git a/test/utils/index.ts b/test/utils/index.ts
index a8aed9387a..37ba63b1c7 100644
--- a/test/utils/index.ts
+++ b/test/utils/index.ts
@@ -1,5 +1,6 @@
import * as assert from 'assert';
import { trim_start, trim_end } from '../../src/compiler/utils/trim';
+import { split_css_unit } from '../../src/runtime/internal/utils';
describe('utils', () => {
describe('trim', () => {
@@ -13,4 +14,17 @@ describe('utils', () => {
assert.equal(value, ' \r\n\t svelte content');
});
});
+
+ describe('split_css_unit', () => {
+ it('should use px as default', () => {
+ assert.deepEqual(split_css_unit(10), [10, 'px']);
+ assert.deepEqual(split_css_unit('10'), [10, 'px']);
+ });
+
+ it('should split the css notation into value and unit', () => {
+ assert.deepEqual(split_css_unit('-50%'), [-50, '%']);
+ assert.deepEqual(split_css_unit('0.1rem'), [0.1, 'rem']);
+ assert.deepEqual(split_css_unit('.1rem'), [0.1, 'rem']);
+ });
+ });
});
diff --git a/test/validator/samples/a11y-click-events-have-key-events/input.svelte b/test/validator/samples/a11y-click-events-have-key-events/input.svelte
index 8737f04ec5..3fb1ded53d 100644
--- a/test/validator/samples/a11y-click-events-have-key-events/input.svelte
+++ b/test/validator/samples/a11y-click-events-have-key-events/input.svelte
@@ -9,13 +9,20 @@
+
+
+
+
+
+
+
@@ -28,24 +35,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/validator/samples/a11y-click-events-have-key-events/warnings.json b/test/validator/samples/a11y-click-events-have-key-events/warnings.json
index 904c733a99..f508c12785 100644
--- a/test/validator/samples/a11y-click-events-have-key-events/warnings.json
+++ b/test/validator/samples/a11y-click-events-have-key-events/warnings.json
@@ -3,11 +3,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 12,
+ "line": 13,
"column": 0
},
"end": {
- "line": 12,
+ "line": 13,
"column": 23
}
},
@@ -15,11 +15,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 13,
+ "line": 15,
"column": 0
},
"end": {
- "line": 13,
+ "line": 15,
"column": 43
}
},
@@ -27,11 +27,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 15,
+ "line": 18,
"column": 0
},
"end": {
- "line": 15,
+ "line": 18,
"column": 27
}
},
@@ -39,11 +39,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 16,
+ "line": 20,
"column": 0
},
"end": {
- "line": 16,
+ "line": 20,
"column": 24
}
},
@@ -51,11 +51,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 17,
+ "line": 22,
"column": 0
},
"end": {
- "line": 17,
+ "line": 22,
"column": 27
}
},
@@ -63,11 +63,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 18,
+ "line": 24,
"column": 0
},
"end": {
- "line": 18,
+ "line": 24,
"column": 26
}
},
@@ -75,11 +75,11 @@
"code": "a11y-click-events-have-key-events",
"message": "A11y: visible, non-interactive elements with an on:click event must be accompanied by an on:keydown, on:keyup, or on:keypress event.",
"start": {
- "line": 19,
+ "line": 26,
"column": 0
},
"end": {
- "line": 19,
+ "line": 26,
"column": 26
}
}
diff --git a/test/validator/samples/a11y-contenteditable-element-without-child/errors.json b/test/validator/samples/a11y-contenteditable-element-without-child/errors.json
index b4bd522c40..2d803d755a 100644
--- a/test/validator/samples/a11y-contenteditable-element-without-child/errors.json
+++ b/test/validator/samples/a11y-contenteditable-element-without-child/errors.json
@@ -1,7 +1,7 @@
[
{
"code": "missing-contenteditable-attribute",
- "message": "'contenteditable' attribute is required for textContent and innerHTML two-way bindings",
+ "message": "'contenteditable' attribute is required for textContent, innerHTML and innerText two-way bindings",
"start": {
"line": 6,
"column": 3
diff --git a/test/validator/samples/a11y-interactive-supports-focus/input.svelte b/test/validator/samples/a11y-interactive-supports-focus/input.svelte
new file mode 100644
index 0000000000..9caa9ccfaf
--- /dev/null
+++ b/test/validator/samples/a11y-interactive-supports-focus/input.svelte
@@ -0,0 +1,32 @@
+
+ {}} />
+
{}} />
+
{}} />
+
{}} />
+