diff --git a/CHANGELOG.md b/CHANGELOG.md index 41d70eedcc..ccdf043a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Also: * Flush changes in newly attached block when using `{#await}` ([#3660](https://github.com/sveltejs/svelte/issues/3660)) * Throw exception immediately when calling `createEventDispatcher()` after component instantiation ([#3667](https://github.com/sveltejs/svelte/pull/3667)) * Fix globals shadowing contextual template scope ([#3674](https://github.com/sveltejs/svelte/issues/3674)) +* Fix `` bindings to stores ([#3832](https://github.com/sveltejs/svelte/issues/3832)) ## 3.12.1 diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte b/site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte index dae595e7d0..5e0dbca300 100644 --- a/site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte +++ b/site/content/tutorial/16-special-elements/07-svelte-options/app-a/Todo.svelte @@ -3,7 +3,6 @@ import flash from './flash.js'; export let todo; - export let toggle; let div; diff --git a/site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte b/site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte index 447ddc601c..e7ddcedc47 100644 --- a/site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte +++ b/site/content/tutorial/16-special-elements/07-svelte-options/app-b/Todo.svelte @@ -5,7 +5,6 @@ import flash from './flash.js'; export let todo; - export let toggle; let div; diff --git a/src/compiler/compile/render_dom/wrappers/Window.ts b/src/compiler/compile/render_dom/wrappers/Window.ts index 90846f8a7c..b9a32c03fd 100644 --- a/src/compiler/compile/render_dom/wrappers/Window.ts +++ b/src/compiler/compile/render_dom/wrappers/Window.ts @@ -127,7 +127,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(b` function ${id}() { - ${props.map(prop => b`$$invalidate('${prop.name}', ${prop.name} = @_window.${prop.value});`)} + ${props.map(prop => component.invalidate(prop.name, x`${prop.name} = @_window.${prop.value}`))} } `); @@ -167,7 +167,7 @@ export default class WindowWrapper extends Wrapper { component.partly_hoisted.push(b` function ${id}() { - $$invalidate('${name}', ${name} = @_navigator.onLine); + ${component.invalidate(name, x`${name} = @_navigator.onLine`)} } `); diff --git a/src/compiler/compile/render_dom/wrappers/shared/Tag.ts b/src/compiler/compile/render_dom/wrappers/shared/Tag.ts index 20bfd3597b..ccb7c6ea40 100644 --- a/src/compiler/compile/render_dom/wrappers/shared/Tag.ts +++ b/src/compiler/compile/render_dom/wrappers/shared/Tag.ts @@ -5,6 +5,7 @@ import Block from '../../Block'; import MustacheTag from '../../../nodes/MustacheTag'; import RawMustacheTag from '../../../nodes/RawMustacheTag'; import { Node } from 'estree'; +import { changed } from './changed'; export default class Tag extends Wrapper { node: MustacheTag | RawMustacheTag; @@ -39,10 +40,7 @@ export default class Tag extends Wrapper { if (this.node.should_cache) block.add_variable(value, snippet); // TODO may need to coerce snippet to string if (dependencies.length > 0) { - let condition = x`#changed.${dependencies[0]}`; - for (let i = 1; i < dependencies.length; i += 1) { - condition = x`${condition} || #changed.${dependencies[i]}`; - } + let condition = changed(dependencies); if (block.has_outros) { condition = x`!#current || ${condition}`; diff --git a/test/helpers.js b/test/helpers.js index 6ac20eed8b..d4f507fc87 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -52,12 +52,19 @@ global.document = window.document; global.navigator = window.navigator; global.getComputedStyle = window.getComputedStyle; global.requestAnimationFrame = null; // placeholder, filled in using set_raf +global.window = window; // add missing ecmascript globals to window for (const key of Object.getOwnPropertyNames(global)) { window[key] = window[key] || global[key]; } +// implement mock scroll +window.scrollTo = function(pageXOffset, pageYOffset) { + window.pageXOffset = pageXOffset; + window.pageYOffset = pageYOffset; +}; + export function env() { window.document.title = ''; window.document.body.innerHTML = '
'; @@ -204,3 +211,25 @@ export function spaces(i) { while (i--) result += ' '; return result; } + +// fake timers +const original_set_timeout = global.setTimeout; + +export function useFakeTimers() { + const callbacks = []; + + global.setTimeout = function(fn) { + callbacks.push(fn); + }; + + return { + flush() { + callbacks.forEach(fn => fn()); + callbacks.splice(0, callbacks.length); + }, + removeFakeTimers() { + callbacks.splice(0, callbacks.length); + global.setTimeout = original_set_timeout; + } + }; +} diff --git a/test/js/samples/window-binding-scroll/expected.js b/test/js/samples/window-binding-scroll/expected.js index 87f31505d7..4cd784e2bf 100644 --- a/test/js/samples/window-binding-scroll/expected.js +++ b/test/js/samples/window-binding-scroll/expected.js @@ -68,7 +68,7 @@ function instance($$self, $$props, $$invalidate) { let { y } = $$props; function onwindowscroll() { - $$invalidate("y", y = window.pageYOffset); + $$invalidate("y", y = window.pageYOffset) } $$self.$set = $$props => { diff --git a/test/runtime/samples/binding-select-in-each-block/_config.js b/test/runtime/samples/binding-select-in-each-block/_config.js index e8ff7f9f07..89f40f8d48 100644 --- a/test/runtime/samples/binding-select-in-each-block/_config.js +++ b/test/runtime/samples/binding-select-in-each-block/_config.js @@ -1,5 +1,16 @@ export default { - skip: true, // JSDOM... + + ssrHtml: ` + + + + `, html: ` diff --git a/test/runtime/samples/binding-select-multiple/_config.js b/test/runtime/samples/binding-select-multiple/_config.js index e1d21c0ea8..b7d6c3589f 100644 --- a/test/runtime/samples/binding-select-multiple/_config.js +++ b/test/runtime/samples/binding-select-multiple/_config.js @@ -1,49 +1,58 @@ export default { - skip: true, // JSDOM props: { selected: [ 'two', 'three' ] }, + ssrHtml: ` + + +

selected: two, three

+ `, + html: `

selected: two, three

`, - test({ assert, component, target, window }) { + async test({ assert, component, target, window }) { const select = target.querySelector( 'select' ); const options = [ ...target.querySelectorAll( 'option' ) ]; const change = new window.Event( 'change' ); options[1].selected = false; - select.dispatchEvent( change ); + await select.dispatchEvent( change ); assert.deepEqual( component.selected, [ 'three' ] ); assert.htmlEqual( target.innerHTML, `

selected: three

` ); options[0].selected = true; - select.dispatchEvent( change ); + await select.dispatchEvent( change ); assert.deepEqual( component.selected, [ 'one', 'three' ] ); assert.htmlEqual( target.innerHTML, `

selected: one, three

@@ -57,9 +66,9 @@ export default { assert.htmlEqual( target.innerHTML, `

selected: one, two

diff --git a/test/runtime/samples/binding-select-optgroup/_config.js b/test/runtime/samples/binding-select-optgroup/_config.js index 5927bce901..e8a1d40797 100644 --- a/test/runtime/samples/binding-select-optgroup/_config.js +++ b/test/runtime/samples/binding-select-optgroup/_config.js @@ -1,5 +1,15 @@ export default { - skip: true, // JSDOM + + ssrHtml: ` +

Hello undefined!

+ + + `, html: `

Hello Harry!

@@ -12,17 +22,17 @@ export default { `, - test({ assert, component, target, window }) { + async test({ assert, component, target, window }) { const select = target.querySelector('select'); const options = [...target.querySelectorAll('option')]; - assert.deepEqual(options, select.options); + assert.deepEqual(options, [...select.options]); assert.equal(component.name, 'Harry'); const change = new window.Event('change'); options[1].selected = true; - select.dispatchEvent(change); + await select.dispatchEvent(change); assert.equal(component.name, 'World'); assert.htmlEqual(target.innerHTML, ` diff --git a/test/runtime/samples/window-bind-scroll-update/_config.js b/test/runtime/samples/window-bind-scroll-update/_config.js index b9b620d1cd..cb13ea11c2 100644 --- a/test/runtime/samples/window-bind-scroll-update/_config.js +++ b/test/runtime/samples/window-bind-scroll-update/_config.js @@ -1,10 +1,37 @@ +import { env, useFakeTimers } from "../../../helpers"; + +let clock; + export default { - skip: true, // JSDOM + before_test() { + clock = useFakeTimers(); + + const window = env(); + Object.defineProperties(window, { + pageYOffset: { + value: 0, + configurable: true + }, + pageXOffset: { + value: 0, + configurable: true + } + }); + }, - test({ assert, component, target, window }) { + after_test() { + clock.removeFakeTimers(); + clock = null; + }, + + async test({ assert, component, target, window }) { assert.equal(window.pageYOffset, 0); + // clear the previous 'scrolling' state + clock.flush(); component.scrollY = 100; + + clock.flush(); assert.equal(window.pageYOffset, 100); - } -}; \ No newline at end of file + }, +}; diff --git a/test/runtime/samples/window-binding-resize/_config.js b/test/runtime/samples/window-binding-resize/_config.js index d8fbe69f75..1429f67db8 100644 --- a/test/runtime/samples/window-binding-resize/_config.js +++ b/test/runtime/samples/window-binding-resize/_config.js @@ -1,16 +1,25 @@ export default { html: `
1024x768
`, - skip: true, // some weird stuff happening with JSDOM 11 - // skip: /^v4/.test(process.version), // node 4 apparently does some dumb stuff + before_test() { + Object.defineProperties(window, { + innerWidth: { + value: 1024, + configurable: true + }, + innerHeight: { + value: 768, + configurable: true + } + }); + }, + skip_if_ssr: true, // there's some kind of weird bug with this test... it compiles with the wrong require.extensions hook for some bizarre reason async test({ assert, component, target, window }) { const event = new window.Event('resize'); - // JSDOM executes window event listeners with `global` rather than - // `window` (bug?) so we need to do this - Object.defineProperties(global, { + Object.defineProperties(window, { innerWidth: { value: 567, configurable: true diff --git a/test/runtime/samples/window-binding-scroll-store/_config.js b/test/runtime/samples/window-binding-scroll-store/_config.js new file mode 100644 index 0000000000..dbab4b36ac --- /dev/null +++ b/test/runtime/samples/window-binding-scroll-store/_config.js @@ -0,0 +1,29 @@ +export default { + skip_if_ssr: true, + before_test() { + Object.defineProperties(window, { + pageYOffset: { + value: 0, + configurable: true, + }, + }); + }, + async test({ assert, component, target, window }) { + assert.equal(window.pageYOffset, 0); + + const event = new window.Event('scroll'); + Object.defineProperties(window, { + pageYOffset: { + value: 234, + configurable: true, + }, + }); + + await window.dispatchEvent(event); + + assert.htmlEqual( + target.innerHTML, + `

scroll\ny\nis\n234.\n234\n*\n234\n=\n54756

` + ); + }, +}; diff --git a/test/runtime/samples/window-binding-scroll-store/main.svelte b/test/runtime/samples/window-binding-scroll-store/main.svelte new file mode 100644 index 0000000000..fe225520d7 --- /dev/null +++ b/test/runtime/samples/window-binding-scroll-store/main.svelte @@ -0,0 +1,15 @@ + + + + +

+ scroll y is {$y}. {$y} * {$y} = {$y_squared} +

+ +
+ +
\ No newline at end of file diff --git a/test/runtime/samples/window-event/_config.js b/test/runtime/samples/window-event/_config.js index 16e0f870ef..bd09487f5e 100644 --- a/test/runtime/samples/window-event/_config.js +++ b/test/runtime/samples/window-event/_config.js @@ -1,16 +1,12 @@ export default { html: `
undefinedxundefined
`, - skip: true, // some weird stuff happening with JSDOM 11 - // skip: /^v4/.test(process.version), // node 4 apparently does some dumb stuff skip_if_ssr: true, // there's some kind of weird bug with this test... it compiles with the wrong require.extensions hook for some bizarre reason async test({ assert, component, target, window }) { const event = new window.Event('resize'); - // JSDOM executes window event listeners with `global` rather than - // `window` (bug?) so we need to do this - Object.defineProperties(global, { + Object.defineProperties(window, { innerWidth: { value: 567, configurable: true diff --git a/test/runtime/samples/window-event/main.svelte b/test/runtime/samples/window-event/main.svelte index 1aa84a0890..e246902063 100644 --- a/test/runtime/samples/window-event/main.svelte +++ b/test/runtime/samples/window-event/main.svelte @@ -3,6 +3,6 @@ export let height; - +
{width}x{height}
\ No newline at end of file