From 783bd9899e16276786d3fb6e96a59083e13efe7f Mon Sep 17 00:00:00 2001 From: gtmnayan <50981692+gtm-nayan@users.noreply.github.com> Date: Thu, 18 May 2023 00:45:20 +0545 Subject: [PATCH] chore: swap mocha with vitest (#8584) Also swap out the require hook hacks with a less-hacky-but-still-somewhat-hacky loader for the Svelte files --------- Co-authored-by: Simon Holthausen Co-authored-by: Rich Harris --- .github/workflows/ci.yml | 27 +- .mocharc.js | 17 - .mocharc.unit.js | 15 - .prettierignore | 1 + codemod-lazy-props.mjs | 47 + package.json | 10 +- pnpm-lock.yaml | 907 +++++++++++++----- src/compiler/compile/Component.ts | 9 +- src/compiler/compile/utils/__test__.ts | 88 -- src/compiler/config.ts | 1 - test/css/{index.js => css.test.js} | 102 +- test/custom-elements/custom-elements.test.js | 118 +++ test/custom-elements/index.js | 107 --- test/helpers.js | 305 +++--- test/html_equal.js | 127 +++ test/hydration/hydration.test.js | 107 +++ test/hydration/index.js | 144 --- test/js/index.js | 92 -- test/js/js-output.test.js | 84 ++ .../component-store-file-invalidate/store.js | 2 +- .../samples/reactive-class-optimized/store.js | 2 +- test/motion/{index.js => motion.test.js} | 6 +- test/parser/{index.js => parser.test.js} | 34 +- test/preprocess/index.js | 39 - test/preprocess/preprocess.test.js | 42 + test/runtime-browser/browser.test.js | 173 ++++ test/runtime-browser/driver.js | 73 ++ test/runtime-browser/index.js | 230 ----- test/runtime/App.svelte | 0 test/runtime/{index.js => runtime.test.js} | 222 ++--- .../samples/$$rest-without-props/_config.js | 7 +- test/runtime/samples/$$rest/_config.js | 7 +- .../_config.js | 4 +- .../action-ternary-template/_config.js | 5 +- .../after-render-prevents-loop/_config.js | 4 +- .../after-render-triggers-update/_config.js | 4 +- test/runtime/samples/animation-css/_config.js | 18 +- .../samples/animation-js-delay/_config.js | 18 +- .../samples/animation-js-easing/_config.js | 18 +- .../samples/animation-js-easing/easing.js | 3 - .../samples/animation-js-easing/main.svelte | 5 +- test/runtime/samples/animation-js/_config.js | 18 +- .../apply-directives-in-order-2/_config.js | 7 +- .../apply-directives-in-order/_config.js | 4 +- .../attribute-boolean-hidden/_config.js | 4 +- .../_config.js | 4 +- .../attribute-boolean-inert/_config.js | 4 +- .../attribute-dynamic-multiple/_config.js | 14 +- .../samples/attribute-dynamic-type/_config.js | 5 +- .../samples/attribute-namespaced/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 5 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 5 +- .../attribute-prefer-expression/_config.js | 4 +- .../await-catch-no-expression/_config.js | 57 +- .../samples/await-catch-shorthand/_config.js | 37 +- .../await-component-oncreate/_config.js | 4 +- .../samples/await-containing-if/_config.js | 19 +- .../samples/await-function-promise/_config.js | 4 +- test/runtime/samples/await-in-each/_config.js | 4 +- .../samples/await-in-removed-if/_config.js | 4 +- .../await-then-catch-anchor/_config.js | 44 +- .../samples/await-then-catch-event/_config.js | 27 +- .../samples/await-then-catch-if/_config.js | 5 +- .../await-then-catch-in-slot/_config.js | 70 +- .../await-then-catch-multiple/_config.js | 30 +- .../await-then-catch-no-values/_config.js | 30 +- .../await-then-catch-non-promise/_config.js | 4 +- .../samples/await-then-catch-order/_config.js | 4 +- .../await-then-catch-static/_config.js | 4 +- .../samples/await-then-catch/_config.js | 45 +- .../_config.js | 6 +- .../await-then-destruct-array/_config.js | 6 +- .../_config.js | 6 +- .../await-then-destruct-object-if/_config.js | 6 +- .../await-then-destruct-object/_config.js | 6 +- test/runtime/samples/await-then-if/_config.js | 4 +- .../await-then-no-expression/_config.js | 41 +- .../samples/await-then-shorthand/_config.js | 30 +- .../samples/await-with-update-2/_config.js | 8 +- .../await-with-update-catch-scope/_config.js | 6 +- .../samples/await-with-update/_config.js | 8 +- .../samples/await-without-catch/_config.js | 54 +- .../before-render-prevents-loop/_config.js | 4 +- .../binding-contenteditable-html/_config.js | 4 +- .../_config.js | 4 +- .../binding-contenteditable-text/_config.js | 4 +- .../samples/binding-indirect/_config.js | 18 +- .../_config.js | 14 +- .../_config.js | 5 +- .../binding-input-checkbox-group/_config.js | 5 +- .../_config.js | 4 +- .../_config.js | 18 +- .../samples/binding-input-checkbox/_config.js | 4 +- .../binding-input-group-each-1/_config.js | 15 +- .../binding-input-group-each-3/_config.js | 14 +- .../samples/binding-input-number/_config.js | 4 +- .../binding-input-radio-group/_config.js | 5 +- .../binding-input-range-change/_config.js | 4 +- .../samples/binding-input-range/_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 14 +- .../binding-input-text-contextual/_config.js | 4 +- .../_config.js | 6 +- .../_config.js | 12 +- .../_config.js | 7 +- .../_config.js | 14 +- .../_config.js | 6 +- .../binding-input-text-deep/_config.js | 6 +- .../samples/binding-input-text/_config.js | 4 +- .../binding-input-with-event/_config.js | 4 +- .../_config.js | 5 +- .../binding-select-in-each-block/_config.js | 6 +- .../binding-select-in-yield/_config.js | 4 +- .../binding-select-initial-value/_config.js | 4 +- .../samples/binding-select-late-2/_config.js | 5 +- .../samples/binding-select-late-3/_config.js | 5 +- .../samples/binding-select-late/_config.js | 5 +- .../binding-select-multiple/_config.js | 4 +- .../_config.js | 5 +- .../runtime/samples/binding-select/_config.js | 4 +- .../samples/binding-textarea/_config.js | 4 +- .../_config.js | 4 +- .../samples/binding-this-unset/_config.js | 4 +- test/runtime/samples/class-helper/_config.js | 4 +- test/runtime/samples/class-in-each/_config.js | 8 +- .../class-shortcut-with-class/_config.js | 6 +- .../class-shortcut-with-transition/_config.js | 5 +- .../runtime/samples/class-shortcut/_config.js | 5 +- .../_config.js | 10 +- .../class-with-dynamic-attribute/_config.js | 4 +- .../class-with-spread-and-bind/_config.js | 4 +- .../samples/class-with-spread/_config.js | 10 +- .../component-binding-blowback-b/_config.js | 4 +- .../component-binding-blowback-c/_config.js | 4 +- .../component-binding-deep-b/_config.js | 5 +- .../component-binding-each-object/_config.js | 6 +- .../component-binding-nested/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../samples/component-data-dynamic/_config.js | 12 +- .../component-event-not-stale/_config.js | 4 +- .../samples/component-events-each/_config.js | 4 +- .../samples/component-events/_config.js | 4 +- .../samples/component-if-placement/_config.js | 4 +- .../samples/component-namespaced/_config.js | 4 +- .../component-nested-deeper/_config.js | 4 +- .../component-not-constructor/_config.js | 4 +- .../component-not-constructor2-dev/_config.js | 4 +- .../component-not-constructor2/_config.js | 4 +- .../component-slot-each-block/_config.js | 4 +- .../component-slot-let-aliased/_config.js | 4 +- .../_config.js | 6 +- .../component-slot-let-in-slot-2/_config.js | 5 +- .../component-slot-let-in-slot/_config.js | 4 +- .../_config.js | 6 +- .../component-slot-let-named/_config.js | 4 +- .../samples/component-slot-let/_config.js | 4 +- .../samples/component-slot-spread/_config.js | 6 +- .../_config.js | 4 +- .../_config.js | 6 +- .../_config.js | 4 +- .../_config.js | 4 +- .../component-svelte-fragment-let/_config.js | 4 +- .../component-yield-placement/_config.js | 4 +- .../samples/const-tag-if-else-if/_config.js | 4 +- .../const-tag-if-else-outro/_config.js | 4 +- .../samples/const-tag-if-else/_config.js | 4 +- test/runtime/samples/const-tag-if/_config.js | 4 +- .../constructor-pass-context/_config.js | 8 +- .../deconflict-component-refs/_config.js | 14 +- .../samples/deconflict-contexts/_config.js | 4 +- .../deconflict-contextual-action/_config.js | 4 +- .../samples/default-data-override/_config.js | 4 +- .../destructuring-between-exports/_config.js | 4 +- test/runtime/samples/destructuring/_config.js | 4 +- .../samples/dev-warning-helper/_config.js | 4 +- .../dev-warning-missing-data-each/_config.js | 23 +- .../dev-warning-readonly-computed/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../dynamic-component-bindings/_config.js | 4 +- .../dynamic-component-destroy-null/_config.js | 4 +- .../dynamic-component-dirty/_config.js | 4 +- .../dynamic-component-events/_config.js | 4 +- .../_config.js | 4 +- .../samples/dynamic-component-slot/_config.js | 4 +- .../_config.js | 4 +- .../samples/dynamic-component/_config.js | 4 +- .../dynamic-element-action-update/_config.js | 10 +- .../dynamic-element-animation-2/_config.js | 20 +- .../dynamic-element-animation/_config.js | 20 +- .../_config.js | 4 +- .../_config.js | 14 +- .../dynamic-element-attribute/_config.js | 4 +- .../dynamic-element-change-tag/_config.js | 4 +- .../dynamic-element-event-handler1/_config.js | 8 +- .../dynamic-element-event-handler2/_config.js | 9 +- .../dynamic-element-invalid-this/_config.js | 4 +- .../dynamic-element-pass-props/_config.js | 8 +- .../samples/dynamic-element-slot/_config.js | 4 +- .../_config.js | 4 +- .../dynamic-element-variable/_config.js | 5 +- .../dynamic-element-void-tag/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 6 +- .../_config.js | 14 +- .../_config.js | 16 +- .../_config.js | 16 +- .../_config.js | 12 +- .../each-block-destructured-array/_config.js | 12 +- .../_config.js | 44 +- .../_config.js | 6 +- .../_config.js | 38 +- .../_config.js | 26 +- .../_config.js | 29 +- .../_config.js | 12 +- .../each-block-destructured-object/_config.js | 12 +- .../each-block-dynamic-else-static/_config.js | 6 +- .../each-block-else-mount-or-intro/_config.js | 4 +- .../each-block-else-starts-empty/_config.js | 5 +- .../samples/each-block-else/_config.js | 8 +- .../samples/each-block-empty-outro/_config.js | 5 +- .../samples/each-block-in-if-block/_config.js | 8 +- .../samples/each-block-index-only/_config.js | 4 +- .../samples/each-block-indexed/_config.js | 6 +- .../each-block-keyed-changed/_config.js | 6 +- .../each-block-keyed-dynamic-key/_config.js | 13 +- .../each-block-keyed-dynamic/_config.js | 12 +- .../samples/each-block-keyed-else/_config.js | 8 +- .../samples/each-block-keyed-empty/_config.js | 4 +- .../each-block-keyed-non-prop/_config.js | 4 +- .../_config.js | 16 +- .../_config.js | 4 +- .../each-block-keyed-recursive/_config.js | 12 +- .../samples/each-block-keyed-shift/_config.js | 6 +- .../each-block-keyed-siblings/_config.js | 8 +- .../each-block-keyed-static/_config.js | 4 +- .../each-block-keyed-unshift/_config.js | 4 +- .../samples/each-block-keyed/_config.js | 18 +- .../each-block-random-permute/_config.js | 8 +- .../samples/each-block-static/_config.js | 4 +- .../samples/each-block-text-node/_config.js | 6 +- .../each-block-unkeyed-else-2/_config.js | 8 +- test/runtime/samples/each-block/_config.js | 6 +- .../samples/each-blocks-nested-b/_config.js | 32 +- .../samples/each-blocks-nested/_config.js | 8 +- .../event-handler-console-log/_config.js | 4 +- .../event-handler-deconflicted/_config.js | 4 +- .../event-handler-each-context/_config.js | 10 +- .../_config.js | 6 +- .../event-handler-each-this/_config.js | 4 +- .../samples/event-handler-each/_config.js | 8 +- .../samples/event-handler-hoisted/_config.js | 5 +- .../fragment-trailing-whitespace/_config.js | 4 +- .../samples/function-hoisting/_config.js | 4 +- .../samples/function-in-expression/_config.js | 6 +- .../samples/get-after-destroy/_config.js | 4 +- .../globals-not-dereferenced/_config.js | 4 +- .../_config.js | 27 +- .../globals-shadowed-by-data/_config.js | 4 +- .../globals-shadowed-by-helpers/_config.js | 4 +- .../samples/hash-in-attribute/_config.js | 8 +- test/runtime/samples/head-if-block/_config.js | 4 +- .../samples/head-if-else-block/_config.js | 4 +- .../head-if-else-raw-dynamic/_config.js | 6 +- .../samples/head-raw-dynamic/_config.js | 6 +- .../head-title-dynamic-simple/_config.js | 4 +- .../samples/head-title-dynamic/_config.js | 4 +- test/runtime/samples/hello-world/_config.js | 4 +- .../_config.js | 4 +- .../if-block-conservative-update/_config.js | 14 +- .../_config.js | 23 +- .../samples/if-block-else-in-each/_config.js | 4 +- .../if-block-else-partial-outro/_config.js | 5 +- test/runtime/samples/if-block-else/_config.js | 5 +- .../if-block-elseif-no-else/_config.js | 4 +- .../samples/if-block-elseif-text/_config.js | 4 +- .../samples/if-block-elseif/_config.js | 4 +- .../runtime/samples/if-block-first/_config.js | 4 +- test/runtime/samples/if-block-or/_config.js | 5 +- .../_config.js | 4 +- .../_config.js | 4 +- .../samples/if-block-widget/_config.js | 4 +- test/runtime/samples/if-block/_config.js | 4 +- .../samples/if-in-keyed-each/_config.js | 12 +- .../_config.js | 5 +- .../ignore-unchanged-attribute/_config.js | 5 +- .../samples/ignore-unchanged-raw/_config.js | 5 +- .../samples/ignore-unchanged-tag/_config.js | 5 +- .../samples/initial-state-assign/_config.js | 4 +- .../samples/inline-expressions/_config.js | 5 +- .../_config.js | 5 +- .../_config.js | 5 +- .../key-block-component-slot/_config.js | 11 +- .../key-block-transition-local/_config.js | 5 +- .../samples/mixed-let-export/_config.js | 4 +- .../names-deconflicted-nested/_config.js | 14 +- .../nested-transition-detach-each/_config.js | 10 +- .../_config.js | 5 +- .../observable-auto-subscribe/_config.js | 36 +- .../samples/option-without-select/_config.js | 4 +- .../paren-wrapped-expressions/_config.js | 6 +- .../runtime/samples/prop-accessors/_config.js | 12 +- test/runtime/samples/prop-const/_config.js | 5 +- test/runtime/samples/prop-exports/_config.js | 24 +- .../samples/prop-not-action/_config.js | 4 +- test/runtime/samples/prop-quoted/_config.js | 4 +- .../samples/prop-subscribable/_config.js | 6 +- .../prop-without-semicolon-b/_config.js | 4 +- .../samples/props-reactive-b/_config.js | 5 +- .../_config.js | 10 +- .../runtime/samples/props-reactive/_config.js | 7 +- test/runtime/samples/props/_config.js | 4 +- .../samples/raw-anchor-first-child/_config.js | 4 +- .../raw-anchor-first-last-child/_config.js | 4 +- .../samples/raw-anchor-last-child/_config.js | 4 +- .../_config.js | 4 +- .../raw-anchor-next-sibling/_config.js | 4 +- .../raw-anchor-previous-sibling/_config.js | 4 +- .../raw-mustaches-preserved/_config.js | 4 +- .../samples/raw-mustaches-td-tr/_config.js | 4 +- test/runtime/samples/raw-mustaches/_config.js | 6 +- .../store.js | 2 +- .../_config.js | 4 +- .../reactive-import-statement/_config.js | 4 +- .../samples/reactive-import-statement/data.js | 6 +- .../reactive-values-exported/_config.js | 4 +- .../_config.js | 5 +- .../reactive-values-non-cyclical/_config.js | 4 +- .../renamed-instance-exports/_config.js | 4 +- .../samples/select-bind-array/_config.js | 5 +- .../samples/select-bind-in-array/_config.js | 4 +- .../samples/select-change-handler/_config.js | 8 +- .../select-one-way-bind-object/_config.js | 5 +- .../samples/select-one-way-bind/_config.js | 4 +- test/runtime/samples/select/_config.js | 7 +- .../samples/self-reference-tree/_config.js | 48 +- .../runtime/samples/self-reference/_config.js | 4 +- .../samples/set-after-destroy/_config.js | 4 +- .../samples/set-null-text-node/_config.js | 4 +- .../samples/sigil-component-prop/_config.js | 4 +- .../_config.js | 0 .../main.svelte | 0 .../_config.js | 0 .../main.svelte | 0 .../samples/spread-component-2/_config.js | 32 +- .../_config.js | 7 +- .../_config.js | 7 +- .../_config.js | 6 +- .../spread-component-dynamic/_config.js | 6 +- .../spread-component-immutable/_config.js | 4 +- .../spread-component-side-effects/_config.js | 4 +- .../samples/spread-component/_config.js | 16 +- .../samples/spread-each-component/_config.js | 12 +- .../samples/spread-each-element/_config.js | 12 +- .../samples/spread-element-boolean/_config.js | 6 +- .../_config.js | 6 +- .../samples/spread-element-input/_config.js | 6 +- .../spread-element-multiple/_config.js | 15 +- .../samples/spread-own-props/_config.js | 12 +- .../samples/state-deconflicted/_config.js | 8 +- .../main.svelte | 2 +- .../_config.js | 8 +- .../main.svelte | 2 +- .../store-assignment-updates/_config.js | 6 +- .../store-assignment-updates/main.svelte | 1 - .../main.svelte | 2 +- .../store-auto-subscribe-implicit/_config.js | 6 +- .../store-auto-subscribe-in-each/_config.js | 8 +- .../_config.js | 6 +- .../store-auto-subscribe-in-script/_config.js | 9 +- .../store-auto-subscribe-nullish/_config.js | 2 +- .../_config.js | 2 +- .../samples/store-auto-subscribe/_config.js | 6 +- .../samples/store-contextual/_config.js | 6 +- .../samples/store-dev-mode-error/_config.js | 4 +- .../samples/store-imported-module-b/foo.js | 2 +- .../samples/store-imported-module/foo.js | 2 +- test/runtime/samples/store-imported/foo.js | 2 +- .../samples/store-imports-hoisted/foo.js | 2 +- .../main.svelte | 2 +- .../_config.js | 6 +- .../store-resubscribe-export/_config.js | 4 +- .../samples/store-unreferenced/store.js | 2 +- .../_config.js | 7 +- .../_config.js | 7 +- .../samples/svg-each-block-anchor/_config.js | 5 +- test/runtime/samples/svg-multiple/_config.js | 7 +- .../runtime/samples/svg-with-style/_config.js | 4 +- test/runtime/samples/svg-xmlns/_config.js | 7 +- test/runtime/samples/svg/_config.js | 7 +- .../samples/textarea-children/_config.js | 4 +- .../runtime/samples/textarea-value/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../transition-js-aborted-outro/_config.js | 4 +- .../_config.js | 10 +- .../transition-js-await-block/_config.js | 12 +- .../transition-js-deferred-b/_config.js | 1 + .../_config.js | 1 + .../samples/transition-js-deferred/_config.js | 1 + .../samples/transition-js-delay/_config.js | 1 + .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../transition-js-each-block-intro/_config.js | 4 +- .../_config.js | 6 +- .../_config.js | 6 +- .../_config.js | 7 +- .../transition-js-each-block-outro/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../transition-js-each-unchanged/_config.js | 4 +- .../transition-js-events-in-out/_config.js | 8 +- .../samples/transition-js-events/_config.js | 8 +- .../_config.js | 5 +- .../_config.js | 5 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 5 +- .../transition-js-local-and-global/_config.js | 5 +- .../_config.js | 5 +- .../_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 5 +- .../transition-js-local-nested-if/_config.js | 5 +- .../samples/transition-js-local/_config.js | 5 +- .../transition-js-nested-await/_config.js | 5 +- .../transition-js-nested-component/_config.js | 4 +- .../_config.js | 5 +- .../_config.js | 5 +- .../_config.js | 5 +- .../transition-js-nested-each/_config.js | 5 +- .../transition-js-nested-if/_config.js | 5 +- .../_config.js | 4 +- .../transition-js-slot-4-cancelled/_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../_config.js | 4 +- .../samples/transition-js-slot/_config.js | 4 +- .../samples/whitespace-each-block/_config.js | 4 +- .../samples/whitespace-list/_config.js | 7 +- .../samples/whitespace-normal/_config.js | 4 +- .../window-bind-scroll-update/_config.js | 27 +- .../samples/window-event-context/_config.js | 4 +- test/runtime/samples/window-event/main.svelte | 5 +- test/server-side-rendering/index.js | 247 ----- test/server-side-rendering/ssr-1.test.js | 103 ++ test/server-side-rendering/ssr-2.test.js | 118 +++ test/setup.js | 50 - .../samples/compile-option-dev/_config.js | 2 +- .../samples/compile-option-dev/test.js | 1 + .../samples/decoded-sourcemap/_config.js | 2 +- test/sourcemaps/samples/external/_config.js | 2 +- test/sourcemaps/samples/external/test.js | 2 +- .../samples/preprocessed-markup/_config.js | 2 +- .../samples/preprocessed-multiple/_config.js | 2 +- .../samples/preprocessed-no-map/test.js | 2 +- .../samples/preprocessed-script/_config.js | 2 +- .../samples/preprocessed-styles/_config.js | 2 +- .../_config.js | 2 +- .../samples/sourcemap-basename/_config.js | 2 +- .../samples/sourcemap-concat/_config.js | 2 +- .../samples/sourcemap-concat/test.js | 2 +- .../samples/sourcemap-names/_config.js | 2 +- .../samples/sourcemap-offsets/_config.js | 2 +- .../samples/sourcemap-offsets/test.js | 2 +- test/sourcemaps/samples/typescript/test.js | 2 +- .../{index.js => sourcemaps.test.js} | 27 +- test/stats/{index.js => stats.test.js} | 16 +- test/store/{index.js => store.test.js} | 6 +- test/test.js | 21 - test/tsconfig.json | 6 +- test/utils/index.js | 30 - test/utils/utils.test.js | 118 +++ .../validator/{index.js => validator.test.js} | 20 +- test/vars/index.js | 52 - .../vars/samples/$$props-logicless/_config.js | 3 + test/vars/samples/$$props/_config.js | 3 + test/vars/samples/actions/_config.js | 3 + test/vars/samples/animations/_config.js | 3 + test/vars/samples/assumed-global/_config.js | 3 + .../samples/component-namespaced/_config.js | 3 + .../vars/samples/duplicate-globals/_config.js | 3 + .../duplicate-non-hoistable/_config.js | 3 + test/vars/samples/duplicate-vars/_config.js | 3 + .../vars/samples/implicit-reactive/_config.js | 3 + test/vars/samples/imports/_config.js | 3 + test/vars/samples/modules-vars/_config.js | 3 + .../mutated-vs-reassigned-bindings/_config.js | 3 + .../samples/mutated-vs-reassigned/_config.js | 3 + test/vars/samples/props/_config.js | 3 + .../samples/referenced-from-script/_config.js | 3 + test/vars/samples/store-referenced/_config.js | 3 + .../samples/store-unreferenced/_config.js | 3 + .../samples/template-references/_config.js | 3 + test/vars/samples/transitions/_config.js | 3 + test/vars/samples/undeclared/_config.js | 3 + .../vars/samples/vars-report-false/_config.js | 3 + .../vars-report-full-noscript/_config.js | 3 + .../vars-report-full-script/_config.js | 4 + test/vars/samples/vars-report-full/_config.js | 4 + test/vars/vars.test.js | 48 + vitest.config.js | 19 + 516 files changed, 3885 insertions(+), 3449 deletions(-) delete mode 100644 .mocharc.js delete mode 100644 .mocharc.unit.js create mode 100644 codemod-lazy-props.mjs delete mode 100644 src/compiler/compile/utils/__test__.ts delete mode 100644 src/compiler/config.ts rename test/css/{index.js => css.test.js} (53%) create mode 100644 test/custom-elements/custom-elements.test.js delete mode 100644 test/custom-elements/index.js create mode 100644 test/html_equal.js create mode 100644 test/hydration/hydration.test.js delete mode 100644 test/hydration/index.js delete mode 100644 test/js/index.js create mode 100644 test/js/js-output.test.js rename test/motion/{index.js => motion.test.js} (79%) rename test/parser/{index.js => parser.test.js} (63%) delete mode 100644 test/preprocess/index.js create mode 100644 test/preprocess/preprocess.test.js create mode 100644 test/runtime-browser/browser.test.js create mode 100644 test/runtime-browser/driver.js delete mode 100644 test/runtime-browser/index.js create mode 100644 test/runtime/App.svelte rename test/runtime/{index.js => runtime.test.js} (52%) delete mode 100644 test/runtime/samples/animation-js-easing/easing.js rename test/runtime/samples/{sigil-static-@ => sigil-static-at}/_config.js (100%) rename test/runtime/samples/{sigil-static-@ => sigil-static-at}/main.svelte (100%) rename test/runtime/samples/{sigil-static-# => sigil-static-hash}/_config.js (100%) rename test/runtime/samples/{sigil-static-# => sigil-static-hash}/main.svelte (100%) delete mode 100644 test/server-side-rendering/index.js create mode 100644 test/server-side-rendering/ssr-1.test.js create mode 100644 test/server-side-rendering/ssr-2.test.js delete mode 100644 test/setup.js rename test/sourcemaps/{index.js => sourcemaps.test.js} (86%) rename test/stats/{index.js => stats.test.js} (76%) rename test/store/{index.js => store.test.js} (98%) delete mode 100644 test/test.js delete mode 100644 test/utils/index.js create mode 100644 test/utils/utils.test.js rename test/validator/{index.js => validator.test.js} (88%) delete mode 100644 test/vars/index.js create mode 100644 test/vars/vars.test.js create mode 100644 vitest.config.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9479e66a08..95a481ede7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm playwright install chromium - - run: pnpm test:integration + - run: pnpm test env: CI: true Lint: @@ -50,28 +50,3 @@ jobs: node-version: 16 cache: pnpm - run: 'pnpm i && pnpm format:check && pnpm lint' - Unit: - runs-on: ${{ matrix.os }} - timeout-minutes: 10 - strategy: - matrix: - include: - - node-version: 16 - os: ubuntu-latest - - node-version: 16 - os: windows-latest - - node-version: 16 - os: macOS-latest - - node-version: 18 - os: ubuntu-latest - - node-version: 20 - os: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: pnpm/action-setup@v2.2.4 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: pnpm - - run: pnpm install - - run: pnpm test:unit diff --git a/.mocharc.js b/.mocharc.js deleted file mode 100644 index c8ae961f89..0000000000 --- a/.mocharc.js +++ /dev/null @@ -1,17 +0,0 @@ -const is_unit_test = process.env.UNIT_TEST; - -module.exports = { - file: is_unit_test ? [] : ['test/test.js'], - require: [ - 'sucrase/register' - ], - "node-option": [ - "experimental-modules" - ] -}; - -// add coverage options when running 'npx c8 mocha' -if (process.env.NODE_V8_COVERAGE) { - module.exports.fullTrace = true; - module.exports.require.push('source-map-support/register'); -} diff --git a/.mocharc.unit.js b/.mocharc.unit.js deleted file mode 100644 index 387d70e7e0..0000000000 --- a/.mocharc.unit.js +++ /dev/null @@ -1,15 +0,0 @@ -module.exports = { - spec: [ - 'src/**/__test__.ts', - ], - require: [ - 'sucrase/register' - ], - recursive: true, -}; - -// add coverage options when running 'npx c8 mocha' -if (process.env.NODE_V8_COVERAGE) { - module.exports.fullTrace = true; - module.exports.require.push('source-map-support/register'); -} diff --git a/.prettierignore b/.prettierignore index 447573c618..b21b38dc35 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,4 +10,5 @@ src/compiler/compile/internal_exports.ts /test/**/_expected* /test/**/_actual* /test/**/expected* +/test/**/_output /types diff --git a/codemod-lazy-props.mjs b/codemod-lazy-props.mjs new file mode 100644 index 0000000000..d1a92c4348 --- /dev/null +++ b/codemod-lazy-props.mjs @@ -0,0 +1,47 @@ +import { existsSync, fstat, readFileSync, readdirSync, writeFileSync } from 'fs'; +import { resolve } from 'path'; +import { parse } from 'acorn'; +import { walk } from 'estree-walker'; +import { inspect } from 'util'; + +import { p, print } from 'code-red'; + +const samples = resolve(`vitest/runtime/runtime/samples`); + +for (const dir of readdirSync(samples)) { + const cwd = resolve(samples, dir); + const file = resolve(cwd, '_config.js'); + + if (!existsSync(file)) continue; + const contents = readFileSync(file, 'utf-8'); + const ast = parse(contents, { + sourceType: 'module', + ecmaVersion: 'latest', + sourceFile: file, + ranges: true + }); + + walk(ast, { + enter(node) { + if ( + node.type === 'ExportDefaultDeclaration' && + node.declaration.type === 'ObjectExpression' + ) { + this.skip(); + + const props = node.declaration.properties.find((prop) => prop.key.name === 'props'); + if (!props) return; + const { range } = props; + + const [start, end] = range; + + const code = + contents.slice(0, start) + + print(p`get ${props.key}() { return ${props.value}}`).code + + contents.slice(end); + + writeFileSync(file, code); + } + } + }); +} diff --git a/package.json b/package.json index da403b4b78..45123e4e7e 100644 --- a/package.json +++ b/package.json @@ -83,10 +83,7 @@ "scripts": { "format:fix": "prettier . --cache --plugin-search-dir=. --write", "format:check": "prettier . --cache --plugin-search-dir=. --check", - "test": "npm run test:unit && npm run test:integration && echo \"manually check that there are no type errors in test/types by opening the files in there\"", - "test:integration": "mocha --exit", - "test:unit": "mocha --config .mocharc.unit.js --exit", - "quicktest": "mocha --exit", + "test": "vitest run && echo \"manually check that there are no type errors in test/types by opening the files in there\"", "build": "rollup -c && npm run tsd", "prepare": "npm run build", "dev": "rollup -cw", @@ -140,12 +137,12 @@ "eslint-plugin-import": "^2.27.5", "eslint-plugin-svelte3": "^4.0.0", "estree-walker": "^3.0.3", + "happy-dom": "^9.18.3", "is-reference": "^3.0.1", "jsdom": "^21.1.1", "kleur": "^4.1.5", "locate-character": "^2.0.5", "magic-string": "^0.30.0", - "mocha": "^10.2.0", "periscopic": "^3.1.0", "prettier": "^2.8.8", "prettier-plugin-svelte": "^2.10.0", @@ -155,7 +152,8 @@ "tiny-glob": "^0.2.9", "tslib": "^2.5.0", "typescript": "^5.0.4", - "util": "^0.12.5" + "util": "^0.12.5", + "vitest": "^0.31.0" }, "packageManager": "pnpm@8.4.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5aebd10b5a..475c9adda5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,6 +85,9 @@ devDependencies: estree-walker: specifier: ^3.0.3 version: 3.0.3 + happy-dom: + specifier: ^9.18.3 + version: 9.18.3 is-reference: specifier: ^3.0.1 version: 3.0.1 @@ -100,9 +103,6 @@ devDependencies: magic-string: specifier: ^0.30.0 version: 0.30.0 - mocha: - specifier: ^10.2.0 - version: 10.2.0 periscopic: specifier: ^3.1.0 version: 3.1.0 @@ -133,6 +133,9 @@ devDependencies: util: specifier: ^0.12.5 version: 0.12.5 + vitest: + specifier: ^0.31.0 + version: 0.31.0(happy-dom@9.18.3)(jsdom@21.1.1) packages: @@ -144,6 +147,204 @@ packages: '@jridgewell/trace-mapping': 0.3.18 dev: true + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.4.0(eslint@8.35.0): resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -164,7 +365,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 espree: 9.5.1 globals: 13.20.0 ignore: 5.2.4 @@ -186,7 +387,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 1.2.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -400,6 +601,16 @@ packages: resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} dev: true + /@types/chai-subset@1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.5 + dev: true + + /@types/chai@4.3.5: + resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + dev: true + /@types/estree@1.0.0: resolution: {integrity: sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==} dev: true @@ -444,7 +655,7 @@ packages: '@typescript-eslint/scope-manager': 5.58.0 '@typescript-eslint/type-utils': 5.58.0(eslint@8.35.0)(typescript@5.0.4) '@typescript-eslint/utils': 5.58.0(eslint@8.35.0)(typescript@5.0.4) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 eslint: 8.35.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 @@ -469,7 +680,7 @@ packages: '@typescript-eslint/scope-manager': 5.58.0 '@typescript-eslint/types': 5.58.0 '@typescript-eslint/typescript-estree': 5.58.0(typescript@5.0.4) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 eslint: 8.35.0 typescript: 5.0.4 transitivePeerDependencies: @@ -496,7 +707,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 5.58.0(typescript@5.0.4) '@typescript-eslint/utils': 5.58.0(eslint@8.35.0)(typescript@5.0.4) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 eslint: 8.35.0 tsutils: 3.21.0(typescript@5.0.4) typescript: 5.0.4 @@ -520,7 +731,7 @@ packages: dependencies: '@typescript-eslint/types': 5.58.0 '@typescript-eslint/visitor-keys': 5.58.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 globby: 11.1.0 is-glob: 4.0.3 semver: 7.4.0 @@ -558,6 +769,45 @@ packages: eslint-visitor-keys: 3.4.0 dev: true + /@vitest/expect@0.31.0: + resolution: {integrity: sha512-Jlm8ZTyp6vMY9iz9Ny9a0BHnCG4fqBa8neCF6Pk/c/6vkUk49Ls6UBlgGAU82QnzzoaUs9E/mUhq/eq9uMOv/g==} + dependencies: + '@vitest/spy': 0.31.0 + '@vitest/utils': 0.31.0 + chai: 4.3.7 + dev: true + + /@vitest/runner@0.31.0: + resolution: {integrity: sha512-H1OE+Ly7JFeBwnpHTrKyCNm/oZgr+16N4qIlzzqSG/YRQDATBYmJb/KUn3GrZaiQQyL7GwpNHVZxSQd6juLCgw==} + dependencies: + '@vitest/utils': 0.31.0 + concordance: 5.0.4 + p-limit: 4.0.0 + pathe: 1.1.0 + dev: true + + /@vitest/snapshot@0.31.0: + resolution: {integrity: sha512-5dTXhbHnyUMTMOujZPB0wjFjQ6q5x9c8TvAsSPUNKjp1tVU7i9pbqcKPqntyu2oXtmVxKbuHCqrOd+Ft60r4tg==} + dependencies: + magic-string: 0.30.0 + pathe: 1.1.0 + pretty-format: 27.5.1 + dev: true + + /@vitest/spy@0.31.0: + resolution: {integrity: sha512-IzCEQ85RN26GqjQNkYahgVLLkULOxOm5H/t364LG0JYb3Apg0PsYCHLBYGA006+SVRMWhQvHlBBCyuByAMFmkg==} + dependencies: + tinyspy: 2.1.0 + dev: true + + /@vitest/utils@0.31.0: + resolution: {integrity: sha512-kahaRyLX7GS1urekRXN2752X4gIgOGVX4Wo8eDUGUkTWlGpXzf5ZS6N9RUUS+Re3XEE8nVGqNyxkSxF5HXlGhQ==} + dependencies: + concordance: 5.0.4 + loupe: 2.3.6 + pretty-format: 27.5.1 + dev: true + /abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} dev: true @@ -601,7 +851,7 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -615,11 +865,6 @@ packages: uri-js: 4.4.1 dev: true - /ansi-colors@4.1.1: - resolution: {integrity: sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==} - engines: {node: '>=6'} - dev: true - /ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -632,16 +877,13 @@ packages: color-convert: 2.0.1 dev: true - /any-promise@1.3.0: - resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} dev: true - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} - dependencies: - normalize-path: 3.0.0 - picomatch: 2.3.1 + /any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} dev: true /argparse@2.0.1: @@ -697,6 +939,10 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} dev: true @@ -716,9 +962,8 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /binary-extensions@2.2.0: - resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} - engines: {node: '>=8'} + /blueimp-md5@2.19.0: + resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} dev: true /brace-expansion@1.1.11: @@ -741,10 +986,6 @@ packages: fill-range: 7.0.1 dev: true - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true - /buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} dev: true @@ -754,6 +995,11 @@ packages: engines: {node: '>=6'} dev: true + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} dependencies: @@ -766,9 +1012,17 @@ packages: engines: {node: '>=6'} dev: true - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + /chai@4.3.7: + resolution: {integrity: sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 4.1.3 + get-func-name: 2.0.0 + loupe: 2.3.6 + pathval: 1.1.1 + type-detect: 4.0.8 dev: true /chalk@4.1.2: @@ -779,27 +1033,8 @@ packages: supports-color: 7.2.0 dev: true - /chokidar@3.5.3: - resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} - engines: {node: '>= 8.10.0'} - dependencies: - anymatch: 3.1.3 - braces: 3.0.2 - glob-parent: 5.1.2 - is-binary-path: 2.1.0 - is-glob: 4.0.3 - normalize-path: 3.0.0 - readdirp: 3.6.0 - optionalDependencies: - fsevents: 2.3.2 - dev: true - - /cliui@7.0.4: - resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + /check-error@1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} dev: true /code-red@1.0.0: @@ -843,6 +1078,20 @@ packages: resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} dev: true + /concordance@5.0.4: + resolution: {integrity: sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==} + engines: {node: '>=10.18.0 <11 || >=12.14.0 <13 || >=14'} + dependencies: + date-time: 3.1.0 + esutils: 2.0.3 + fast-diff: 1.2.0 + js-string-escape: 1.0.1 + lodash: 4.17.21 + md5-hex: 3.0.1 + semver: 7.4.0 + well-known-symbols: 2.0.0 + dev: true + /cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -860,6 +1109,10 @@ packages: source-map-js: 1.0.2 dev: true + /css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + dev: true + /cssstyle@3.0.0: resolution: {integrity: sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==} engines: {node: '>=14'} @@ -876,6 +1129,13 @@ packages: whatwg-url: 12.0.1 dev: true + /date-time@3.1.0: + resolution: {integrity: sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==} + engines: {node: '>=6'} + dependencies: + time-zone: 1.0.0 + dev: true + /debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} peerDependencies: @@ -887,7 +1147,7 @@ packages: ms: 2.1.3 dev: true - /debug@4.3.4(supports-color@8.1.1): + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} peerDependencies: @@ -897,18 +1157,19 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 8.1.1 - dev: true - - /decamelize@4.0.0: - resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} - engines: {node: '>=10'} dev: true /decimal.js@10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true + /deep-eql@4.1.3: + resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} + engines: {node: '>=6'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} dependencies: @@ -953,11 +1214,6 @@ packages: engines: {node: '>=0.4.0'} dev: true - /diff@5.0.0: - resolution: {integrity: sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==} - engines: {node: '>=0.3.1'} - dev: true - /dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} @@ -986,12 +1242,8 @@ packages: webidl-conversions: 7.0.0 dev: true - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - dev: true - - /entities@4.4.0: - resolution: {integrity: sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==} + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} dev: true @@ -1073,9 +1325,34 @@ packages: is-symbol: 1.0.4 dev: true - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 dev: true /escape-string-regexp@4.0.0: @@ -1274,7 +1551,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.1.1 @@ -1368,6 +1645,10 @@ packages: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} dev: true + /fast-diff@1.2.0: + resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==} + dev: true + /fast-glob@3.2.12: resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==} engines: {node: '>=8.6.0'} @@ -1423,11 +1704,6 @@ packages: rimraf: 3.0.2 dev: true - /flat@5.0.2: - resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} - hasBin: true - dev: true - /flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} dev: true @@ -1477,9 +1753,8 @@ packages: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} dev: true - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} + /get-func-name@2.0.0: + resolution: {integrity: sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==} dev: true /get-intrinsic@1.2.0: @@ -1523,17 +1798,6 @@ packages: path-is-absolute: 1.0.1 dev: true - /glob@7.2.0: - resolution: {integrity: sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==} - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - dev: true - /glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} dependencies: @@ -1600,6 +1864,17 @@ packages: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true + /happy-dom@9.18.3: + resolution: {integrity: sha512-b7iMGYeIXvUryNultA0AHEVU0FPpb2djJ/xSVlMDfP7HG4z7FomdqkCEpWtSv1zDL+t1gRUoBbpqFCoUBvjYtg==} + dependencies: + css.escape: 1.5.1 + entities: 4.5.0 + iconv-lite: 0.6.3 + webidl-conversions: 7.0.0 + whatwg-encoding: 2.0.0 + whatwg-mimetype: 3.0.0 + dev: true + /has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -1639,11 +1914,6 @@ packages: function-bind: 1.1.1 dev: true - /he@1.2.0: - resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} - hasBin: true - dev: true - /html-encoding-sniffer@3.0.0: resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} engines: {node: '>=12'} @@ -1657,7 +1927,7 @@ packages: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -1667,7 +1937,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -1739,13 +2009,6 @@ packages: has-bigints: 1.0.2 dev: true - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} - dependencies: - binary-extensions: 2.2.0 - dev: true - /is-boolean-object@1.1.2: resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} engines: {node: '>= 0.4'} @@ -1784,11 +2047,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - dev: true - /is-generator-function@1.0.10: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} @@ -1833,11 +2091,6 @@ packages: engines: {node: '>=8'} dev: true - /is-plain-obj@2.1.0: - resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} - engines: {node: '>=8'} - dev: true - /is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true @@ -1897,11 +2150,6 @@ packages: has-tostringtag: 1.0.0 dev: true - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true - /is-weakmap@2.0.1: resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} dev: true @@ -1931,6 +2179,11 @@ packages: resolution: {integrity: sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg==} dev: true + /js-string-escape@1.0.1: + resolution: {integrity: sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==} + engines: {node: '>= 0.8'} + dev: true + /js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -1994,6 +2247,10 @@ packages: minimist: 1.2.8 dev: true + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + /kleur@4.1.5: resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} engines: {node: '>=6'} @@ -2019,6 +2276,11 @@ packages: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} dev: true + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + /locate-character@2.0.5: resolution: {integrity: sha512-n2GmejDXtOPBAZdIiEFy5dJ5N38xBCXLNOtw2WpB9kGh6pnrEuKlwYI+Tkpofc4wDtVXHtoAOJaMRlYG/oYaxg==} dev: true @@ -2034,12 +2296,14 @@ packages: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} dev: true - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /loupe@2.3.6: + resolution: {integrity: sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==} dependencies: - chalk: 4.1.2 - is-unicode-supported: 0.1.0 + get-func-name: 2.0.0 dev: true /lru-cache@6.0.0: @@ -2063,6 +2327,13 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: true + /md5-hex@3.0.1: + resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} + engines: {node: '>=8'} + dependencies: + blueimp-md5: 2.19.0 + dev: true + /mdn-data@2.0.30: resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} dev: true @@ -2098,13 +2369,6 @@ packages: brace-expansion: 1.1.11 dev: true - /minimatch@5.0.1: - resolution: {integrity: sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==} - engines: {node: '>=10'} - dependencies: - brace-expansion: 2.0.1 - dev: true - /minimatch@5.1.6: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} @@ -2116,32 +2380,13 @@ packages: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} dev: true - /mocha@10.2.0: - resolution: {integrity: sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==} - engines: {node: '>= 14.0.0'} - hasBin: true + /mlly@1.2.1: + resolution: {integrity: sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==} dependencies: - ansi-colors: 4.1.1 - browser-stdout: 1.3.1 - chokidar: 3.5.3 - debug: 4.3.4(supports-color@8.1.1) - diff: 5.0.0 - escape-string-regexp: 4.0.0 - find-up: 5.0.0 - glob: 7.2.0 - he: 1.2.0 - js-yaml: 4.1.0 - log-symbols: 4.1.0 - minimatch: 5.0.1 - ms: 2.1.3 - nanoid: 3.3.3 - serialize-javascript: 6.0.0 - strip-json-comments: 3.1.1 - supports-color: 8.1.1 - workerpool: 6.2.1 - yargs: 16.2.0 - yargs-parser: 20.2.4 - yargs-unparser: 2.0.0 + acorn: 8.8.2 + pathe: 1.1.0 + pkg-types: 1.0.3 + ufo: 1.1.2 dev: true /ms@2.1.2: @@ -2160,8 +2405,8 @@ packages: thenify-all: 1.6.0 dev: true - /nanoid@3.3.3: - resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true dev: true @@ -2174,11 +2419,6 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - dev: true - /nwsapi@2.2.3: resolution: {integrity: sha512-jscxIO4/VKScHlbmFBdV1Z6LXnLO+ZR4VMtypudUdfwtKxUN3TQcNFIHLwKtrUbDyHN4/GycY9+oRGZ2XMXYPw==} dev: true @@ -2261,6 +2501,13 @@ packages: yocto-queue: 0.1.0 dev: true + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -2278,7 +2525,7 @@ packages: /parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} dependencies: - entities: 4.4.0 + entities: 4.5.0 dev: true /path-exists@4.0.0: @@ -2305,6 +2552,14 @@ packages: engines: {node: '>=8'} dev: true + /pathe@1.1.0: + resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==} + dev: true + + /pathval@1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -2313,6 +2568,10 @@ packages: is-reference: 3.0.1 dev: true + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + dev: true + /picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2323,12 +2582,29 @@ packages: engines: {node: '>= 6'} dev: true + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.2.1 + pathe: 1.1.0 + dev: true + /playwright-core@1.33.0: resolution: {integrity: sha512-aizyPE1Cj62vAECdph1iaMILpT0WUDCq3E6rW6I+dleSbBoGbktvJtzS6VHkZ4DKNEOG9qJpiom/ZxO+S15LAw==} engines: {node: '>=14'} hasBin: true dev: true + /postcss@8.4.23: + resolution: {integrity: sha512-bQ3qMcpF6A/YjR55xtoTr0jGOlnPOKAIMdOWiv0EIT6HVPEaJiJB4NLljSbiHoC2RX7DN5Uvjtpbg1NPdwv1oA==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /prelude-ls@1.1.2: resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==} engines: {node: '>= 0.8.0'} @@ -2355,6 +2631,15 @@ packages: hasBin: true dev: true + /pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + dev: true + /psl@1.9.0: resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} dev: true @@ -2372,17 +2657,8 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - dependencies: - safe-buffer: 5.2.1 - dev: true - - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} - dependencies: - picomatch: 2.3.1 + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} dev: true /regexp.prototype.flags@1.4.3: @@ -2399,11 +2675,6 @@ packages: engines: {node: '>=8'} dev: true - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true - /requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true @@ -2442,6 +2713,14 @@ packages: fsevents: 2.3.2 dev: true + /rollup@3.21.7: + resolution: {integrity: sha512-KXPaEuR8FfUoK2uHwNjxTmJ18ApyvD6zJpYv9FOJSqLStmt6xOY84l1IjK2dSolQmoXknrhEFRaPRgOPdqCT5w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /rrweb-cssom@0.6.0: resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} dev: true @@ -2452,10 +2731,6 @@ packages: queue-microtask: 1.2.3 dev: true - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true - /safe-regex-test@1.0.0: resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} dependencies: @@ -2488,12 +2763,6 @@ packages: lru-cache: 6.0.0 dev: true - /serialize-javascript@6.0.0: - resolution: {integrity: sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==} - dependencies: - randombytes: 2.1.0 - dev: true - /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2514,6 +2783,10 @@ packages: object-inspect: 1.12.3 dev: true + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /slash@3.0.0: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} @@ -2541,6 +2814,14 @@ packages: engines: {node: '>= 8'} dev: true + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + + /std-env@3.3.3: + resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} + dev: true + /stop-iteration-iterator@1.0.0: resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} engines: {node: '>= 0.4'} @@ -2548,15 +2829,6 @@ packages: internal-slot: 1.0.5 dev: true - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - dev: true - /string.prototype.trim@1.2.7: resolution: {integrity: sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg==} engines: {node: '>= 0.4'} @@ -2599,6 +2871,12 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal@1.0.1: + resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + dependencies: + acorn: 8.8.2 + dev: true + /sucrase@3.32.0: resolution: {integrity: sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==} engines: {node: '>=8'} @@ -2620,13 +2898,6 @@ packages: has-flag: 4.0.0 dev: true - /supports-color@8.1.1: - resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} - engines: {node: '>=10'} - dependencies: - has-flag: 4.0.0 - dev: true - /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -2658,6 +2929,11 @@ packages: any-promise: 1.3.0 dev: true + /time-zone@1.0.0: + resolution: {integrity: sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==} + engines: {node: '>=4'} + dev: true + /tiny-glob@0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} dependencies: @@ -2665,6 +2941,20 @@ packages: globrex: 0.1.2 dev: true + /tinybench@2.5.0: + resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true + + /tinypool@0.5.0: + resolution: {integrity: sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.1.0: + resolution: {integrity: sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==} + engines: {node: '>=14.0.0'} + dev: true + /to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2734,6 +3024,11 @@ packages: prelude-ls: 1.2.1 dev: true + /type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + /type-fest@0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} @@ -2753,6 +3048,10 @@ packages: hasBin: true dev: true + /ufo@1.1.2: + resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} + dev: true + /unbox-primitive@1.0.2: resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} dependencies: @@ -2790,6 +3089,127 @@ packages: which-typed-array: 1.1.9 dev: true + /vite-node@0.31.0(@types/node@14.14.31): + resolution: {integrity: sha512-8x1x1LNuPvE2vIvkSB7c1mApX5oqlgsxzHQesYF7l5n1gKrEmrClIiZuOFbFDQcjLsmcWSwwmrWrcGWm9Fxc/g==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4 + mlly: 1.2.1 + pathe: 1.1.0 + picocolors: 1.0.0 + vite: 4.3.5(@types/node@14.14.31) + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@4.3.5(@types/node@14.14.31): + resolution: {integrity: sha512-0gEnL9wiRFxgz40o/i/eTBwm+NEbpUeTWhzKrZDSdKm6nplj+z4lKz8ANDgildxHm47Vg8EUia0aicKbawUVVA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 14.14.31 + esbuild: 0.17.19 + postcss: 8.4.23 + rollup: 3.21.7 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitest@0.31.0(happy-dom@9.18.3)(jsdom@21.1.1): + resolution: {integrity: sha512-JwWJS9p3GU9GxkG7eBSmr4Q4x4bvVBSswaCFf1PBNHiPx00obfhHRJfgHcnI0ffn+NMlIh9QGvG75FlaIBdKGA==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.5 + '@types/chai-subset': 1.3.3 + '@types/node': 14.14.31 + '@vitest/expect': 0.31.0 + '@vitest/runner': 0.31.0 + '@vitest/snapshot': 0.31.0 + '@vitest/spy': 0.31.0 + '@vitest/utils': 0.31.0 + acorn: 8.8.2 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + concordance: 5.0.4 + debug: 4.3.4 + happy-dom: 9.18.3 + jsdom: 21.1.1 + local-pkg: 0.4.3 + magic-string: 0.30.0 + pathe: 1.1.0 + picocolors: 1.0.0 + std-env: 3.3.3 + strip-literal: 1.0.1 + tinybench: 2.5.0 + tinypool: 0.5.0 + vite: 4.3.5(@types/node@14.14.31) + vite-node: 0.31.0(@types/node@14.14.31) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} @@ -2802,6 +3222,11 @@ packages: engines: {node: '>=12'} dev: true + /well-known-symbols@2.0.0: + resolution: {integrity: sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==} + engines: {node: '>=6'} + dev: true + /whatwg-encoding@2.0.0: resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==} engines: {node: '>=12'} @@ -2861,24 +3286,20 @@ packages: isexe: 2.0.0 dev: true + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /word-wrap@1.2.3: resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==} engines: {node: '>=0.10.0'} dev: true - /workerpool@6.2.1: - resolution: {integrity: sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==} - dev: true - - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - /wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true @@ -2905,48 +3326,20 @@ packages: resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} dev: true - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - /yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: true - /yargs-parser@20.2.4: - resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} - engines: {node: '>=10'} - dev: true - - /yargs-unparser@2.0.0: - resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} - engines: {node: '>=10'} - dependencies: - camelcase: 6.3.0 - decamelize: 4.0.0 - flat: 5.0.2 - is-plain-obj: 2.1.0 - dev: true - - /yargs@16.2.0: - resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} - engines: {node: '>=10'} - dependencies: - cliui: 7.0.4 - escalade: 3.1.1 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 20.2.4 - dev: true - /yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} dev: true + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641(@typescript-eslint/eslint-plugin@5.58.0)(@typescript-eslint/parser@5.58.0)(eslint-plugin-import@2.27.5)(eslint-plugin-node@11.1.0)(eslint-plugin-svelte3@4.0.0)(eslint@8.35.0)(typescript@5.0.4): resolution: {tarball: https://codeload.github.com/sveltejs/eslint-config/tar.gz/9a7d728e03ac433e5856a6e06775c17ee986d641} id: github.com/sveltejs/eslint-config/9a7d728e03ac433e5856a6e06775c17ee986d641 diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index 22a8b46af6..14f68a08f0 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -8,7 +8,6 @@ import { namespaces, valid_namespaces } from '../utils/namespaces'; import create_module from './create_module'; import { create_scopes, extract_names, Scope, extract_identifiers } from './utils/scope'; import Stylesheet from './css/Stylesheet'; -import { test } from '../config'; import Fragment from './nodes/Fragment'; import internal_exports from './internal_exports'; import { Ast, CompileOptions, Var, Warning, CssResult, Attribute } from '../interfaces'; @@ -413,7 +412,6 @@ export default class Component { } get_unique_name(name: string, scope?: Scope): Identifier { - if (test) name = `${name}$`; let alias = name; for ( let i = 1; @@ -422,8 +420,10 @@ export default class Component { this.used_names.has(alias) || this.globally_used_names.has(alias) || (scope && scope.has(alias)); - alias = `${name}_${i++}` - ); + + ) { + alias = `${name}_${i++}`; + } this.used_names.add(alias); return { type: 'Identifier', name: alias }; } @@ -440,7 +440,6 @@ export default class Component { this.var_lookup.forEach((_value, key) => add(key)); return (name: string): Identifier => { - if (test) name = `${name}$`; let alias = name; for ( let i = 1; diff --git a/src/compiler/compile/utils/__test__.ts b/src/compiler/compile/utils/__test__.ts deleted file mode 100644 index fda8c8ee77..0000000000 --- a/src/compiler/compile/utils/__test__.ts +++ /dev/null @@ -1,88 +0,0 @@ -import * as assert from 'assert'; -import get_name_from_filename from './get_name_from_filename'; -import { - is_contenteditable, - has_contenteditable_attr, - is_name_contenteditable, - get_contenteditable_attr, - CONTENTEDITABLE_BINDINGS -} from './contenteditable'; -import Element from '../nodes/Element'; -import Attribute from '../nodes/Attribute'; - -describe('get_name_from_filename', () => { - it('uses the basename', () => { - assert.equal(get_name_from_filename('path/to/Widget.svelte'), 'Widget'); - }); - - it('uses the directory name, if basename is index', () => { - assert.equal(get_name_from_filename('path/to/Widget/index.svelte'), 'Widget'); - }); - - it('handles Windows filenames', () => { - assert.equal(get_name_from_filename('path\\to\\Widget.svelte'), 'Widget'); - }); - - it('handles special characters in filenames', () => { - assert.equal(get_name_from_filename('@.svelte'), '_'); - assert.equal(get_name_from_filename('&.svelte'), '_'); - assert.equal(get_name_from_filename('~.svelte'), '_'); - }); -}); - -describe('contenteditable', () => { - describe('is_contenteditable', () => { - it('returns false if node is input', () => { - const node = { name: 'input' } as Element; - assert.equal(is_contenteditable(node), false); - }); - it('returns false if node is textarea', () => { - const node = { name: 'textarea' } as Element; - assert.equal(is_contenteditable(node), false); - }); - it('returns false if node is not input or textarea AND it is not contenteditable', () => { - const attr = { name: 'href' } as Attribute; - const node = { name: 'a', attributes: [attr] } as Element; - assert.equal(is_contenteditable(node), false); - }); - it('returns true if node is not input or textarea AND it is contenteditable', () => { - const attr = { name: 'contenteditable' } as Attribute; - const node = { name: 'a', attributes: [attr] } as Element; - assert.equal(is_contenteditable(node), true); - }); - }); - - describe('has_contenteditable_attr', () => { - it('returns true if attribute is contenteditable', () => { - const attr = { name: 'contenteditable' } as Attribute; - const node = { attributes: [attr] } as Element; - assert.equal(has_contenteditable_attr(node), true); - }); - it('returns false if attribute is not contenteditable', () => { - const attr = { name: 'href' } as Attribute; - const node = { attributes: [attr] } as Element; - assert.equal(has_contenteditable_attr(node), false); - }); - }); - - describe('is_name_contenteditable', () => { - it('returns true if name is a contenteditable type', () => { - assert.equal(is_name_contenteditable(CONTENTEDITABLE_BINDINGS[0]), true); - }); - it('returns false if name is not contenteditable type', () => { - assert.equal(is_name_contenteditable('value'), false); - }); - }); - - describe('get_contenteditable_attr', () => { - it('returns the contenteditable Attribute if it exists', () => { - const attr = { name: 'contenteditable' } as Attribute; - const node = { name: 'div', attributes: [attr] } as Element; - assert.equal(get_contenteditable_attr(node), attr); - }); - it('returns undefined if contenteditable attribute cannot be found', () => { - const node = { name: 'div', attributes: [] } as Element; - assert.equal(get_contenteditable_attr(node), undefined); - }); - }); -}); diff --git a/src/compiler/config.ts b/src/compiler/config.ts deleted file mode 100644 index e6d0f65a76..0000000000 --- a/src/compiler/config.ts +++ /dev/null @@ -1 +0,0 @@ -export const test = typeof process !== 'undefined' && process.env.TEST; diff --git a/test/css/index.js b/test/css/css.test.js similarity index 53% rename from test/css/index.js rename to test/css/css.test.js index 8cfa9de16e..2af31550d0 100644 --- a/test/css/index.js +++ b/test/css/css.test.js @@ -1,15 +1,10 @@ -import * as fs from 'fs'; -import { assert, env, svelte, setupHtmlEqual, shouldUpdateExpected } from '../helpers'; +// @vitest-environment happy-dom -function try_require(file) { - try { - const mod = require(file); - return mod.default || mod; - } catch (err) { - if (err.code !== 'MODULE_NOT_FOUND') throw err; - return null; - } -} +import * as fs from 'fs'; +import { assert, describe, it } from 'vitest'; +import * as svelte from '../../compiler.mjs'; +import { create_loader, should_update_expected, try_load_config } from '../helpers.js'; +import { assert_html_equal } from '../html_equal.js'; function normalize_warning(warning) { warning.frame = warning.frame.replace(/^\n/, '').replace(/^\t+/gm, '').replace(/\s+$/gm, ''); @@ -18,25 +13,7 @@ function normalize_warning(warning) { return warning; } -function create(code) { - const fn = new Function('module', 'exports', 'require', code); - - const module = { exports: {} }; - fn(module, module.exports, (id) => { - if (id === 'svelte') return require('../../index.js'); - if (id.startsWith('svelte/')) return require(id.replace('svelte', '../../')); - - return require(id); - }); - - return module.exports.default; -} - describe('css', () => { - before(() => { - setupHtmlEqual(); - }); - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { if (dir[0] === '.') return; @@ -44,12 +21,11 @@ describe('css', () => { const solo = /\.solo/.test(dir); const skip = /\.skip/.test(dir); - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } + const it_fn = solo ? it.only : skip ? it.skip : it; + + it_fn(dir, async () => { + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); - (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = try_require(`./samples/${dir}/_config.js`) || {}; const input = fs .readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8') .replace(/\s+$/, '') @@ -81,13 +57,11 @@ describe('css', () => { css: read(`${__dirname}/samples/${dir}/expected.css`) }; - const actual_css = dom.css.code.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => - $1 ? m : 'svelte-xyz' - ); + const actual_css = replace_css_hash(dom.css.code); try { assert.equal(actual_css, expected.css); } catch (error) { - if (shouldUpdateExpected()) { + if (should_update_expected()) { fs.writeFileSync(`${__dirname}/samples/${dir}/expected.css`, actual_css); console.log(`Updated ${dir}/expected.css.`); } else { @@ -95,20 +69,27 @@ describe('css', () => { } } + const cwd = `${__dirname}/samples/${dir}`; + let ClientComponent; let ServerComponent; // we do this here, rather than in the expected.html !== null // block, to verify that valid code was generated + const load = create_loader({ ...(config.compileOptions || {}), format: 'cjs' }, cwd); try { - ClientComponent = create(dom.js.code); + ClientComponent = (await load('input.svelte')).default; } catch (err) { console.log(dom.js.code); throw err; } + const load_ssr = create_loader( + { ...(config.compileOptions || {}), generate: 'ssr', format: 'cjs' }, + cwd + ); try { - ServerComponent = create(ssr.js.code); + ServerComponent = (await load_ssr('input.svelte')).default; } catch (err) { console.log(dom.js.code); throw err; @@ -116,44 +97,29 @@ describe('css', () => { // verify that the right elements have scoping selectors if (expected.html !== null) { - const window = env(); - - // dom - try { - const target = window.document.querySelector('main'); + const target = window.document.createElement('main'); - new ClientComponent({ target, props: config.props }); - const html = target.innerHTML; + new ClientComponent({ target, props: config.props }); + const html = target.innerHTML; - fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, html); - const actual_html = html.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => - $1 ? m : 'svelte-xyz' - ); - assert.htmlEqual(actual_html, expected.html); + const actual_html = replace_css_hash(html); + assert_html_equal(actual_html, expected.html); - window.document.head.innerHTML = ''; // remove added styles - } catch (err) { - console.log(dom.js.code); - throw err; - } + window.document.head.innerHTML = ''; // remove added styles - // ssr - try { - const actual_ssr = ServerComponent.render(config.props).html.replace( - /svelte(-ref)?-[a-z0-9]+/g, - (m, $1) => ($1 ? m : 'svelte-xyz') - ); - assert.htmlEqual(actual_ssr, expected.html); - } catch (err) { - console.log(ssr.js.code); - throw err; - } + const actual_ssr = replace_css_hash(ServerComponent.render(config.props).html); + assert_html_equal(actual_ssr, expected.html); } }); }); }); +function replace_css_hash(str) { + return str.replace(/svelte(-ref)?-[a-z0-9]+/g, (m, $1) => ($1 ? m : 'svelte-xyz')); +} + function read(file) { try { return fs.readFileSync(file, 'utf-8'); diff --git a/test/custom-elements/custom-elements.test.js b/test/custom-elements/custom-elements.test.js new file mode 100644 index 0000000000..f3f040b4d1 --- /dev/null +++ b/test/custom-elements/custom-elements.test.js @@ -0,0 +1,118 @@ +import { chromium } from '@playwright/test'; +import * as fs from 'fs'; +import * as path from 'path'; +import { rollup } from 'rollup'; +import { try_load_config } from '../helpers.js'; +import * as svelte from '../../compiler.mjs'; +import { beforeAll, describe, afterAll, assert, it } from 'vitest'; + +const internal = path.resolve('internal/index.mjs'); +const index = path.resolve('index.mjs'); + +const browser_assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); + +describe( + 'custom-elements', + () => { + /** @type {import('@playwright/test').Browser} */ + let browser; + + beforeAll(async () => { + browser = await chromium.launch(); + console.log('[custom-elements] Launched browser'); + }, 20000); + + afterAll(async () => { + if (browser) await browser.close(); + }); + + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; + + const solo = /\.solo$/.test(dir); + const skip = /\.skip$/.test(dir); + + const warnings = []; + const it_fn = solo ? it.only : skip ? it.skip : it; + + it_fn(dir, async () => { + // TODO: Vitest currently doesn't register a watcher because the import is hidden + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); + + const expected_warnings = config.warnings || []; + + const bundle = await rollup({ + input: `${__dirname}/samples/${dir}/test.js`, + plugins: [ + { + name: 'plugin-resolve-svelte', + resolveId(importee) { + if (importee === 'svelte/internal' || importee === './internal') { + return internal; + } + + if (importee === 'svelte') { + return index; + } + + if (importee === 'assert') { + return 'assert'; + } + }, + + load(id) { + if (id === 'assert') return browser_assert; + }, + + transform(code, id) { + if (id.endsWith('.svelte')) { + const compiled = svelte.compile(code.replace(/\r/g, ''), { + customElement: true, + dev: config.dev + }); + + compiled.warnings.forEach((w) => warnings.push(w)); + + return compiled.js; + } + } + } + ] + }); + + const generated_bundle = await bundle.generate({ format: 'iife', name: 'test' }); + + function assertWarnings() { + if (expected_warnings) { + assert.deepStrictEqual( + warnings.map((w) => ({ + code: w.code, + message: w.message, + pos: w.pos, + start: w.start, + end: w.end + })), + expected_warnings + ); + } + } + + const page = await browser.newPage(); + page.on('console', (type) => { + console[type.type()](type.text()); + }); + await page.setContent('
'); + await page.evaluate(generated_bundle.output[0].code); + const test_result = await page.evaluate(`test(document.querySelector('main'))`); + + if (test_result) console.log(test_result); + + assertWarnings(); + + await page.close(); + }); + }); + }, + // Browser tests are brittle and slow on CI + { timeout: 20000, retry: process.env.CI ? 1 : 0 } +); diff --git a/test/custom-elements/index.js b/test/custom-elements/index.js deleted file mode 100644 index f034fc14c6..0000000000 --- a/test/custom-elements/index.js +++ /dev/null @@ -1,107 +0,0 @@ -import { chromium } from '@playwright/test'; -import virtual from '@rollup/plugin-virtual'; -import { deepStrictEqual } from 'assert'; -import * as fs from 'fs'; -import * as path from 'path'; -import { rollup } from 'rollup'; -import { loadConfig, loadSvelte } from '../helpers'; - -const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); - -describe('custom-elements', function () { - this.timeout(20000); - - let svelte; - /** @type {import('@playwright/test').Browser} */ - let browser; - - before(async function () { - svelte = loadSvelte(); - console.log('[custom-elements] Loaded Svelte'); - browser = await chromium.launch(); - console.log('[custom-elements] Launched browser'); - }); - - after(async () => { - if (browser) await browser.close(); - }); - - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - const solo = /\.solo$/.test(dir); - const skip = /\.skip$/.test(dir); - const internal = path.resolve('internal/index.mjs'); - const index = path.resolve('index.mjs'); - const warnings = []; - - (solo ? it.only : skip ? it.skip : it)(dir, async () => { - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - const expected_warnings = config.warnings || []; - - const bundle = await rollup({ - input: `${__dirname}/samples/${dir}/test.js`, - plugins: [ - // @ts-ignore -- TODO: fix this - { - resolveId(importee) { - if (importee === 'svelte/internal' || importee === './internal') { - return internal; - } - - if (importee === 'svelte') { - return index; - } - }, - - transform(code, id) { - if (id.endsWith('.svelte')) { - const compiled = svelte.compile(code.replace(/\r/g, ''), { - customElement: true, - dev: config.dev - }); - - compiled.warnings.forEach((w) => warnings.push(w)); - - return compiled.js; - } - } - }, - - virtual({ - assert - }) - ] - }); - - const generated_bundle = await bundle.generate({ format: 'iife', name: 'test' }); - - function assertWarnings() { - if (expected_warnings) { - deepStrictEqual( - warnings.map((w) => ({ - code: w.code, - message: w.message, - pos: w.pos, - start: w.start, - end: w.end - })), - expected_warnings - ); - } - } - - const page = await browser.newPage(); - page.on('console', (type) => { - console[type.type()](type.text()); - }); - await page.setContent('
'); - await page.evaluate(generated_bundle.output[0].code); - const test_result = await page.evaluate(`test(document.querySelector('main'))`); - - if (test_result) console.log(test_result); - assertWarnings(); - page.close(); - }); - }); -}); diff --git a/test/helpers.js b/test/helpers.js index 166d3fafa3..35fffabc6f 100644 --- a/test/helpers.js +++ b/test/helpers.js @@ -1,38 +1,12 @@ -import * as assert$1 from 'assert'; -import * as jsdom from 'jsdom'; -import glob from 'tiny-glob/sync'; -import * as path from 'path'; import * as fs from 'fs'; -import * as colors from 'kleur'; - -/** - * @type {typeof assert$1 & { htmlEqual: (actual: string, expected: string, message?: string) => void, htmlEqualWithOptions: (actual: string, expected: string, options: { preserveComments?: boolean, withoutNormalizeHtml?: boolean }, message?: string) => void }} - */ -export const assert = /** @type {any} */ (assert$1); - -// for coverage purposes, we need to test source files, -// but for sanity purposes, we need to test dist files -export function loadSvelte(test = false) { - process.env.TEST = test ? 'true' : ''; - - const resolved = require.resolve('../compiler.js'); - - delete require.cache[resolved]; - return require(resolved); -} - -export const svelte = loadSvelte(); - -export function exists(path) { - try { - fs.statSync(path); - return true; - } catch (err) { - return false; - } -} +import * as path from 'path'; +import glob from 'tiny-glob/sync'; +import colors from 'kleur'; +import { assert } from 'vitest'; +import { compile } from '../compiler.js'; +import { fileURLToPath } from 'url'; -export function tryToLoadJson(file) { +export function try_load_json(file) { try { return JSON.parse(fs.readFileSync(file, 'utf-8')); } catch (err) { @@ -41,7 +15,7 @@ export function tryToLoadJson(file) { } } -export function tryToReadFile(file) { +export function try_read_file(file) { try { return fs.readFileSync(file, 'utf-8'); } catch (err) { @@ -50,39 +24,138 @@ export function tryToReadFile(file) { } } -export function cleanRequireCache() { - Object.keys(require.cache) - .filter((x) => x.endsWith('.svelte')) - .forEach((file) => delete require.cache[file]); +export async function try_load_config(path) { + if (!fs.existsSync(path)) return {}; + // a whole + + // bunch + const _ = 1; + // of lines + + // cause + const result = await import(path); + // source + + // maps + + // are + + // stupid + + return result.default; } -const virtualConsole = new jsdom.VirtualConsole(); -virtualConsole.sendTo(console); +export function should_update_expected() { + return process.env.SHOULD_UPDATE_EXPECTED === 'true'; +} -const window = new jsdom.JSDOM('
', { virtualConsole }).window; -global.document = window.document; -global.navigator = window.navigator; -global.getComputedStyle = window.getComputedStyle; -global.requestAnimationFrame = null; // placeholder, filled in using set_raf -global.window = window; +export function pretty_print_browser_assertion(message) { + const match = /Error: Expected "(.+)" to equal "(.+)"/.exec(message); -// add missing ecmascript globals to window -for (const key of Object.getOwnPropertyNames(global)) { - if (!(key in window)) window[key] = global[key]; + if (match) { + assert.equal(match[1], match[2]); + } } -// implement mock scroll -window.scrollTo = function (pageXOffset, pageYOffset) { - window.pageXOffset = pageXOffset; - window.pageYOffset = pageYOffset; -}; +export function mkdirp(path) { + if (!fs.existsSync(path)) { + fs.mkdirSync(path, { recursive: true }); + } +} -export function env() { - window.document.title = ''; - window.document.head.innerHTML = ''; - window.document.body.innerHTML = '
'; +export function add_line_numbers(code) { + return code + .split('\n') + .map((line, i) => { + i = String(i + 1); + while (i.length < 3) i = ` ${i}`; - return window; + return ( + colors.gray(` ${i}: `) + line.replace(/^\t+/, (match) => match.split('\t').join(' ')) + ); + }) + .join('\n'); +} + +export function show_output(cwd, options = {}) { + glob('**/*.svelte', { cwd }).forEach((file) => { + if (file[0] === '_') return; + + try { + const { js } = compile( + fs.readFileSync(`${cwd}/${file}`, 'utf-8'), + Object.assign(options, { + filename: file + }) + ); + + console.log( + // eslint-disable-line no-console + `\n>> ${colors.cyan().bold(file)}\n${add_line_numbers(js.code)}\n<< ${colors + .cyan() + .bold(file)}` + ); + } catch (err) { + console.log(`failed to generate output: ${err.message}`); + } + }); +} + +const svelte_path = fileURLToPath(new URL('..', import.meta.url)); + +export function create_loader(compileOptions, cwd) { + const cache = new Map(); + + async function load(file) { + if (cache.has(file)) return cache.get(file); + + if (file.endsWith('.svelte')) { + const compiled = compile( + // Windows/Linux newline conversion + fs.readFileSync(file, 'utf-8').replace(/\r\n/g, '\n'), + { + ...compileOptions, + filename: file + } + ); + + const imports = new Map(); + + for (const match of compiled.js.code.matchAll(/require\("(.+?)"\)/g)) { + const source = match[1]; + let resolved = source; + + if (source.startsWith('.')) { + resolved = path.resolve(path.dirname(file), source); + } + + if (source === 'svelte') { + resolved = `${svelte_path}/index.mjs`; + } + + if (source.startsWith('svelte/')) { + resolved = `${svelte_path}/${source.slice(7)}/index.mjs`; + } + + imports.set(source, await load(resolved)); + } + + function require(id) { + return imports.get(id); + } + + const fn = new Function('require', 'exports', 'module', compiled.js.code); + const module = { exports: {} }; + fn(require, module.exports, module); + + cache.set(file, module.exports); + return module.exports; + } else { + return import(file); + } + } + + return (file) => load(path.resolve(cwd, file)); } function cleanChildren(node) { @@ -181,8 +254,6 @@ export function normalizeNewline(html) { * @param {{ removeDataSvelte?: boolean }} options */ export function setupHtmlEqual(options = {}) { - const window = env(); - // eslint-disable-next-line no-import-assign assert.htmlEqual = (actual, expected, message) => { assert.deepEqual( @@ -223,110 +294,16 @@ export function setupHtmlEqual(options = {}) { }; } -export function loadConfig(file) { - try { - const resolved = require.resolve(file); - delete require.cache[resolved]; - - const config = require(resolved); - return config.default || config; - } catch (err) { - if (err.code === 'MODULE_NOT_FOUND') { - return {}; - } - - throw err; - } -} - -export function addLineNumbers(code) { - return code - .split('\n') - .map((line, i) => { - i = String(i + 1); - while (i.length < 3) i = ` ${i}`; - - return ( - colors.gray(` ${i}: `) + line.replace(/^\t+/, (match) => match.split('\t').join(' ')) - ); - }) - .join('\n'); -} - -export function showOutput(cwd, options = {}, compile = svelte.compile) { - glob('**/*.svelte', { cwd }).forEach((file) => { - if (file[0] === '_') return; - - try { - const { js } = compile( - fs.readFileSync(`${cwd}/${file}`, 'utf-8'), - Object.assign(options, { - filename: file - }) - ); +export function create_deferred() { + /** @type {(value: any) => void} */ + let resolve; + /** @type {(reason: any) => void} */ + let reject; - console.log( - // eslint-disable-line no-console - `\n>> ${colors.cyan().bold(file)}\n${addLineNumbers(js.code)}\n<< ${colors - .cyan() - .bold(file)}` - ); - } catch (err) { - console.log(`failed to generate output: ${err.message}`); - } + const promise = new Promise((f, r) => { + resolve = f; + reject = r; }); -} - -export function shouldUpdateExpected() { - return process.argv.includes('--update'); -} - -export function spaces(i) { - let result = ''; - while (i--) result += ' '; - return result; -} - -// fake timers -const original_set_timeout = global.setTimeout; - -export function useFakeTimers() { - const callbacks = []; - // @ts-ignore - 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; - } - }; -} - -export function mkdirp(dir) { - const parent = path.dirname(dir); - if (parent === dir) return; - - mkdirp(parent); - - try { - fs.mkdirSync(dir); - } catch (err) { - // do nothing - } -} - -export function prettyPrintBrowserAssertionError(message) { - const match = /Error: Expected "(.+)" to equal "(.+)"/.exec(message); - - if (match) { - assert.equal(match[1], match[2]); - } + return { promise, resolve, reject }; } diff --git a/test/html_equal.js b/test/html_equal.js new file mode 100644 index 0000000000..76426da6de --- /dev/null +++ b/test/html_equal.js @@ -0,0 +1,127 @@ +import { assert } from 'vitest'; + +/** @type {HTMLDivElement} */ +let _container; + +/** + * @param {string} html + * @param {{ + * removeDataSvelte?: boolean, + * preserveComments?: boolean, + * }} options + */ +export function normalize_html(html, options = {}) { + const container = (_container ??= document.createElement('div')); + + if (!options.preserveComments) { + html = html.replace(/()/g, ''); + } + + if (options.removeDataSvelte) { + html = html.replace(/(data-svelte-h="[^"]+")/g, ''); + } + + html = html.replace(/>[ \t\n\r\f]+<').trim(); + + container.innerHTML = html; + + clean_children(container); + + return container.innerHTML.replace(/<\/?noscript\/?>/g, ''); +} + +/** @param {any} node */ +function clean_children(node) { + // sort attributes + const attributes = Array.from(node.attributes).sort((a, b) => (a.name < b.name ? -1 : 1)); + + attributes.forEach((attr) => { + node.removeAttribute(attr.name); + }); + + attributes.forEach((attr) => { + node.setAttribute(attr.name, attr.value); + }); + + let previous = null; + // recurse + [...node.childNodes].forEach((child) => { + if (child.nodeType === 3) { + // text + if ( + node.namespaceURI === 'http://www.w3.org/2000/svg' && + node.tagName !== 'text' && + node.tagName !== 'tspan' + ) { + node.removeChild(child); + } + + child.data = child.data.replace(/[ \t\n\r\f]+/g, '\n'); + + if (previous && previous.nodeType === 3) { + previous.data += child.data; + previous.data = previous.data.replace(/[ \t\n\r\f]+/g, '\n'); + + node.removeChild(child); + child = previous; + } + } else if (child.nodeType === 8) { + // comment + // do nothing + } else { + clean_children(child); + } + + previous = child; + }); + + // collapse whitespace + if (node.firstChild && node.firstChild.nodeType === 3) { + node.firstChild.data = node.firstChild.data.replace(/^[ \t\n\r\f]+/, ''); + if (!node.firstChild.data.length) node.removeChild(node.firstChild); + } + + if (node.lastChild && node.lastChild.nodeType === 3) { + node.lastChild.data = node.lastChild.data.replace(/[ \t\n\r\f]+$/, ''); + if (!node.lastChild.data.length) node.removeChild(node.lastChild); + } +} + +/** + * + * @param {string} actual + * @param {string} expected + * @param {{ + * message?: string, + * normalize_html?: { + * removeDataSvelte?: boolean, + * preserveComments?: boolean, + * }, + * without_normalize?: boolean, + * }} options + */ +export function assert_html_equal(actual, expected, options = {}) { + if (options.without_normalize) { + actual = actual.replace(/\r\n/g, '\n'); + expected = expected.replace(/\r\n/g, '\n'); + + if (options.normalize_html.removeDataSvelte) { + actual = actual.replace(/(\sdata-svelte-h="[^"]+")/g, ''); + expected = expected.replace(/(\sdata-svelte-h="[^"]+")/g, ''); + } + } else { + actual = normalize_html(actual, options.normalize_html); + expected = normalize_html(expected, options.normalize_html); + } + + try { + assert.equal(actual, expected, options.message); + } catch (err) { + // Remove this function from the stack trace so that the error is shown in the test file + + if (Error.captureStackTrace) { + Error.captureStackTrace(err, assert_html_equal); + } + throw err; + } +} diff --git a/test/hydration/hydration.test.js b/test/hydration/hydration.test.js new file mode 100644 index 0000000000..cb7fbb17fe --- /dev/null +++ b/test/hydration/hydration.test.js @@ -0,0 +1,107 @@ +// @vitest-environment jsdom +// TODO: https://github.com/capricorn86/happy-dom/issues/916 + +import * as fs from 'fs'; +import * as path from 'path'; +import { assert, describe, it } from 'vitest'; +import { create_loader, should_update_expected, try_load_config } from '../helpers.js'; + +import { assert_html_equal } from '../html_equal.js'; + +describe('hydration', async () => { + async function run_test(dir) { + if (dir[0] === '.') return; + + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); + const solo = config.solo || /\.solo/.test(dir); + + const it_fn = config.skip ? it.skip : solo ? it.only : it; + + it_fn(dir, async () => { + const cwd = path.resolve(`${__dirname}/samples/${dir}`); + + let compileOptions = Object.assign({}, config.compileOptions, { + accessors: 'accessors' in config ? config.accessors : true, + format: 'cjs', + hydratable: true + }); + + const { default: SvelteComponent } = await create_loader(compileOptions, cwd)('main.svelte'); + + const target = window.document.body; + const head = window.document.head; + + target.innerHTML = fs.readFileSync(`${cwd}/_before.html`, 'utf-8'); + + let before_head; + try { + before_head = fs.readFileSync(`${cwd}/_before_head.html`, 'utf-8'); + head.innerHTML = before_head; + } catch (err) { + // continue regardless of error + } + + const snapshot = config.snapshot ? config.snapshot(target) : {}; + + const component = new SvelteComponent({ + target, + hydrate: true, + props: config.props + }); + + try { + assert_html_equal(target.innerHTML, fs.readFileSync(`${cwd}/_after.html`, 'utf-8')); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(`${cwd}/_after.html`, target.innerHTML); + console.log(`Updated ${cwd}/_after.html.`); + } else { + throw error; + } + } + + if (before_head) { + try { + const after_head = fs.readFileSync(`${cwd}/_after_head.html`, 'utf-8'); + assert_html_equal(head.innerHTML, after_head); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(`${cwd}/_after_head.html`, head.innerHTML); + console.log(`Updated ${cwd}/_after_head.html.`); + } else { + throw error; + } + } + } + + if (config.snapshot) { + const snapshot_after = config.snapshot(target); + for (const s in snapshot_after) { + assert.ok( + // Error logger borks because of circular references so use this instead + snapshot_after[s] === snapshot[s], + `Expected snapshot key "${s}" to have same value/reference` + ); + } + } + + if (config.test) { + await config.test( + { + ...assert, + htmlEqual: assert_html_equal + }, + target, + snapshot, + component, + window + ); + } + + component.$destroy(); + assert.equal(target.innerHTML, ''); + }); + } + + await Promise.all(fs.readdirSync(`${__dirname}/samples`).map((dir) => run_test(dir))); +}); diff --git a/test/hydration/index.js b/test/hydration/index.js deleted file mode 100644 index 2eeb42bc5a..0000000000 --- a/test/hydration/index.js +++ /dev/null @@ -1,144 +0,0 @@ -import * as path from 'path'; -import * as fs from 'fs'; - -import { - assert, - showOutput, - loadConfig, - loadSvelte, - env, - setupHtmlEqual, - shouldUpdateExpected -} from '../helpers'; - -let compileOptions = null; - -const sveltePath = process.cwd(); - -describe('hydration', () => { - before(() => { - const svelte = loadSvelte(); - - require.extensions['.svelte'] = function (module, filename) { - const options = Object.assign( - { - filename, - hydratable: true, - format: 'cjs', - sveltePath - }, - compileOptions - ); - - const { js } = svelte.compile(fs.readFileSync(filename, 'utf-8'), options); - - return module._compile(js.code, filename); - }; - - return setupHtmlEqual(); - }); - - function runTest(dir) { - if (dir[0] === '.') return; - - const config = loadConfig(`./hydration/samples/${dir}/_config.js`); - const solo = config.solo || /\.solo/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - (config.skip ? it.skip : solo ? it.only : it)(dir, () => { - const cwd = path.resolve(`${__dirname}/samples/${dir}`); - - compileOptions = config.compileOptions || {}; - compileOptions.accessors = 'accessors' in config ? config.accessors : true; - - const window = env(); - - try { - global.window = window; - - const SvelteComponent = require(`${cwd}/main.svelte`).default; - - const target = window.document.body; - const head = window.document.head; - - target.innerHTML = fs.readFileSync(`${cwd}/_before.html`, 'utf-8'); - - let before_head; - try { - before_head = fs.readFileSync(`${cwd}/_before_head.html`, 'utf-8'); - head.innerHTML = before_head; - } catch (err) { - // continue regardless of error - } - - const snapshot = config.snapshot ? config.snapshot(target) : {}; - - const component = new SvelteComponent({ - target, - hydrate: true, - props: config.props - }); - - try { - assert.htmlEqual(target.innerHTML, fs.readFileSync(`${cwd}/_after.html`, 'utf-8')); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(`${cwd}/_after.html`, target.innerHTML); - console.log(`Updated ${cwd}/_after.html.`); - } else { - throw error; - } - } - - if (before_head) { - try { - assert.htmlEqual(head.innerHTML, fs.readFileSync(`${cwd}/_after_head.html`, 'utf-8')); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(`${cwd}/_after_head.html`, head.innerHTML); - console.log(`Updated ${cwd}/_after_head.html.`); - } else { - throw error; - } - } - } - - if (config.snapshot) { - const snapshot_after = config.snapshot(target); - for (const s in snapshot_after) { - assert.equal( - snapshot_after[s], - snapshot[s], - `Expected snapshot key "${s}" to have same value/reference` - ); - } - } - - if (config.test) { - config.test(assert, target, snapshot, component, window); - } else { - component.$destroy(); - assert.equal(target.innerHTML, ''); - } - } catch (err) { - showOutput(cwd, { - hydratable: true - }); - throw err; - } - - if (config.show) { - showOutput(cwd, { - hydratable: true - }); - } - }); - } - - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - runTest(dir); - }); -}); diff --git a/test/js/index.js b/test/js/index.js deleted file mode 100644 index fce796425c..0000000000 --- a/test/js/index.js +++ /dev/null @@ -1,92 +0,0 @@ -import * as assert from 'assert'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as colors from 'kleur'; -import { loadConfig, svelte, shouldUpdateExpected } from '../helpers'; - -describe('js', () => { - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - // add .solo to a sample directory name to only run that test - const solo = /\.solo/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - const resolved = path.resolve(`${__dirname}/samples`, dir); - - if (!fs.existsSync(`${resolved}/input.svelte`)) { - console.log( - colors - .red() - .bold( - `Missing file ${dir}/input.svelte. If you recently switched branches you may need to delete this directory` - ) - ); - return; - } - - (solo ? it.only : it)(dir, () => { - const config = loadConfig(`${resolved}/_config.js`); - - const input = fs - .readFileSync(`${resolved}/input.svelte`, 'utf-8') - .replace(/\s+$/, '') - .replace(/\r/g, ''); - - let actual; - - try { - const options = Object.assign(config.options || {}); - - actual = svelte - .compile(input, options) - .js.code.replace( - /generated by Svelte v\d+\.\d+\.\d+(-\w+\.\d+)?/, - 'generated by Svelte vX.Y.Z' - ); - } catch (err) { - console.log(err.frame); - throw err; - } - - const output = `${resolved}/_actual.js`; - fs.writeFileSync(output, actual); - - const expectedPath = `${resolved}/expected.js`; - - let expected = ''; - try { - expected = fs.readFileSync(expectedPath, 'utf-8'); - } catch (error) { - console.log(error); - if (error.code === 'ENOENT') { - // missing expected.js - fs.writeFileSync(expectedPath, actual); - } - } - - try { - assert.equal( - actual - .trim() - .replace(/^[ \t]+$/gm, '') - .replace(/\r/g, ''), - expected - .trim() - .replace(/^[ \t]+$/gm, '') - .replace(/\r/g, '') - ); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(expectedPath, actual); - console.log(`Updated ${expectedPath}.`); - } else { - throw error; - } - } - }); - }); -}); diff --git a/test/js/js-output.test.js b/test/js/js-output.test.js new file mode 100644 index 0000000000..367f9b56ec --- /dev/null +++ b/test/js/js-output.test.js @@ -0,0 +1,84 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { describe, it, assert } from 'vitest'; +import { try_load_config, should_update_expected } from '../helpers'; +import * as svelte from '../../compiler'; + +describe('js-output', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; + + // add .solo to a sample directory name to only run that test + const solo = /\.solo/.test(dir); + + const resolved = path.resolve(`${__dirname}/samples`, dir); + + const skip = !fs.existsSync(`${resolved}/input.svelte`); + if (skip) { + console.warn( + `Missing file ${dir}/input.svelte. If you recently switched branches you may need to delete this directory` + ); + } + + const it_fn = solo ? it.only : skip ? it.skip : it; + + it_fn(dir, async () => { + const config = await try_load_config(`${resolved}/_config.js`); + + const input = fs + .readFileSync(`${resolved}/input.svelte`, 'utf-8') + .trimEnd() + .replace(/\r/g, ''); + + let actual; + + try { + const options = Object.assign(config.options || {}); + + actual = svelte + .compile(input, options) + .js.code.replace( + /generated by Svelte v\d+\.\d+\.\d+(-\w+\.\d+)?/, + 'generated by Svelte vX.Y.Z' + ); + } catch (err) { + console.log(err.frame); + throw err; + } + + const output = `${resolved}/_actual.js`; + fs.writeFileSync(output, actual); + + const expected_path = `${resolved}/expected.js`; + + let expected = ''; + try { + expected = fs.readFileSync(expected_path, 'utf-8'); + } catch (error) { + console.log(error); + if (error.code === 'ENOENT') { + // missing expected.js + fs.writeFileSync(expected_path, actual); + } + } + + try { + assert.equal(normalize_output(actual), normalize_output(expected)); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(expected_path, actual); + console.log(`Updated ${expected_path}.`); + } else { + throw error; + } + } + }); + }); +}); + +function normalize_output(str) { + return str + .trim() + .replace(/^[ \t]+$/gm, '') + .replace(/\r/g, ''); +} diff --git a/test/js/samples/component-store-file-invalidate/store.js b/test/js/samples/component-store-file-invalidate/store.js index b799c0ffc4..d432d339ec 100644 --- a/test/js/samples/component-store-file-invalidate/store.js +++ b/test/js/samples/component-store-file-invalidate/store.js @@ -1,3 +1,3 @@ -import { writable } from '../../../../store'; +import { writable } from 'svelte/store'; export const count = writable(0); diff --git a/test/js/samples/reactive-class-optimized/store.js b/test/js/samples/reactive-class-optimized/store.js index bd91f0ece3..f179d1d246 100644 --- a/test/js/samples/reactive-class-optimized/store.js +++ b/test/js/samples/reactive-class-optimized/store.js @@ -1,4 +1,4 @@ -import { writable } from '../../../../store'; +import { writable } from 'svelte/store'; export const reactiveStoreVal = writable(0); export const unreactiveExport = true; diff --git a/test/motion/index.js b/test/motion/motion.test.js similarity index 79% rename from test/motion/index.js rename to test/motion/motion.test.js index 9c28206a06..ea2e5c059e 100644 --- a/test/motion/index.js +++ b/test/motion/motion.test.js @@ -1,6 +1,6 @@ -import * as assert from 'assert'; -import { get } from '../../store'; -import { spring, tweened } from '../../motion'; +import { describe, it, assert } from 'vitest'; +import { get } from 'svelte/store'; +import { spring, tweened } from 'svelte/motion'; describe('motion', () => { describe('spring', () => { diff --git a/test/parser/index.js b/test/parser/parser.test.js similarity index 63% rename from test/parser/index.js rename to test/parser/parser.test.js index 94a4937472..d97eb51a41 100644 --- a/test/parser/index.js +++ b/test/parser/parser.test.js @@ -1,6 +1,7 @@ -import * as assert from 'assert'; import * as fs from 'fs'; -import { svelte, tryToLoadJson } from '../helpers'; +import { assert, describe, it } from 'vitest'; +import * as svelte from '../../compiler'; +import { try_load_json } from '../helpers'; describe('parse', () => { fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { @@ -8,22 +9,25 @@ describe('parse', () => { // add .solo to a sample directory name to only run that test const solo = /\.solo$/.test(dir); - - if (solo && process.env.CI) { - throw new Error(`Forgot to remove '.solo' from test parser/samples/${dir}`); + const skip = !fs.existsSync(`${__dirname}/samples/${dir}/input.svelte`); + if (skip) { + console.warn( + `skipping ${dir} because no input.svelte exists. This could be a leftover folder from a different branch.` + ); } - const skip = !fs.existsSync(`${__dirname}/samples/${dir}/input.svelte`); + const it_fn = skip ? it.skip : solo ? it.only : it; - (skip ? it.skip : solo ? it.only : it)(dir, () => { - const options = tryToLoadJson(`${__dirname}/samples/${dir}/options.json`) || {}; + it_fn(dir, () => { + const options = try_load_json(`${__dirname}/samples/${dir}/options.json`) || {}; const input = fs .readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8') - .replace(/\s+$/, '') + .trimEnd() .replace(/\r/g, ''); - const expectedOutput = tryToLoadJson(`${__dirname}/samples/${dir}/output.json`); - const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); + + const expectedOutput = try_load_json(`${__dirname}/samples/${dir}/output.json`); + const expectedError = try_load_json(`${__dirname}/samples/${dir}/error.json`); try { const { ast } = svelte.compile( @@ -46,12 +50,8 @@ describe('parse', () => { if (err.name !== 'ParseError') throw err; if (!expectedError) throw err; const { code, message, pos, start } = err; - try { - assert.deepEqual({ code, message, pos, start }, expectedError); - } catch (err2) { - const e = err2.code === 'MODULE_NOT_FOUND' ? err : err2; - throw e; - } + + assert.deepEqual({ code, message, pos, start }, expectedError); } }); }); diff --git a/test/preprocess/index.js b/test/preprocess/index.js deleted file mode 100644 index 4d005d36d6..0000000000 --- a/test/preprocess/index.js +++ /dev/null @@ -1,39 +0,0 @@ -import * as fs from 'fs'; -import * as assert from 'assert'; -import { loadConfig, svelte } from '../helpers'; - -describe('preprocess', () => { - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - const solo = config.solo || /\.solo/.test(dir); - const skip = config.skip || /\.skip/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - (skip ? it.skip : solo ? it.only : it)(dir, async () => { - const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8'); - const expected = fs.readFileSync(`${__dirname}/samples/${dir}/output.svelte`, 'utf-8'); - - const result = await svelte.preprocess( - input, - config.preprocess || {}, - config.options || { filename: 'input.svelte' } - ); - fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, result.code); - if (result.map) { - fs.writeFileSync( - `${__dirname}/samples/${dir}/_actual.html.map`, - JSON.stringify(result.map, null, 2) - ); - } - - assert.equal(result.code, expected); - - assert.deepEqual(result.dependencies, config.dependencies || []); - }); - }); -}); diff --git a/test/preprocess/preprocess.test.js b/test/preprocess/preprocess.test.js new file mode 100644 index 0000000000..ab141f9f19 --- /dev/null +++ b/test/preprocess/preprocess.test.js @@ -0,0 +1,42 @@ +import * as fs from 'fs'; +import * as svelte from '../../compiler'; +import { try_load_config } from '../helpers'; +import { describe, it } from 'vitest'; + +const samples = fs.readdirSync(`${__dirname}/samples`); + +describe('preprocess', async () => { + await Promise.all(samples.map((dir) => run(dir))); + + async function run(dir) { + if (dir[0] === '.') return; + + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); + const solo = config.solo || /\.solo/.test(dir); + const skip = config.skip || /\.skip/.test(dir); + + const it_fn = skip ? it.skip : solo ? it.only : it; + + it_fn(dir, async ({ expect }) => { + const input = fs.readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8'); + + const result = await svelte.preprocess( + input, + config.preprocess || {}, + config.options || { filename: 'input.svelte' } + ); + fs.writeFileSync(`${__dirname}/samples/${dir}/_actual.html`, result.code); + + if (result.map) { + fs.writeFileSync( + `${__dirname}/samples/${dir}/_actual.html.map`, + JSON.stringify(result.map, null, 2) + ); + } + + expect(result.code).toMatchFileSnapshot(`${__dirname}/samples/${dir}/output.svelte`); + + expect(result.dependencies).toEqual(config.dependencies || []); + }); + } +}); diff --git a/test/runtime-browser/browser.test.js b/test/runtime-browser/browser.test.js new file mode 100644 index 0000000000..490757b165 --- /dev/null +++ b/test/runtime-browser/browser.test.js @@ -0,0 +1,173 @@ +import { chromium } from '@playwright/test'; +import * as fs from 'fs'; +import * as path from 'path'; +import { rollup } from 'rollup'; +import { pretty_print_browser_assertion, try_load_config } from '../helpers.js'; +import * as svelte from '../../compiler.mjs'; +import { beforeAll, describe, afterAll, assert } from 'vitest'; + +const internal = path.resolve('internal/index.mjs'); +const index = path.resolve('index.mjs'); + +const main = fs.readFileSync(`${__dirname}/driver.js`, 'utf-8'); +const browser_assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); + +describe( + 'runtime (browser)', + async (it) => { + /** @type {import('@playwright/test').Browser} */ + let browser; + + beforeAll(async () => { + browser = await chromium.launch(); + console.log('[runtime-browser] Launched browser'); + }); + + afterAll(async () => { + if (browser) await browser.close(); + }); + + const failed = new Set(); + + async function runTest(dir, hydrate) { + if (dir[0] === '.') return; + + // TODO: Vitest currently doesn't register a watcher because the import is hidden + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); + const solo = config.solo || /\.solo/.test(dir); + const skip = config.skip || /\.skip/.test(dir); + + if (hydrate && config.skip_if_hydrate) return; + + const it_fn = skip ? it.skip : solo ? it.only : it; + + it_fn(`${dir} ${hydrate ? '(with hydration)' : ''}`, async () => { + if (failed.has(dir)) { + // this makes debugging easier, by only printing compiled output once + throw new Error('skipping test, already failed'); + } + + const warnings = []; + + const bundle = await rollup({ + input: 'main', + plugins: [ + { + name: 'testing-runtime-browser', + resolveId(importee) { + if (importee === 'svelte/internal' || importee === './internal') { + return internal; + } + + if (importee === 'svelte') { + return index; + } + + if (importee === 'main') { + return 'main'; + } + + if (importee === 'assert') { + return 'assert'; + } + + if (importee === '__MAIN_DOT_SVELTE__') { + return path.resolve(__dirname, 'samples', dir, 'main.svelte'); + } + + if (importee === '__CONFIG__') { + return path.resolve(__dirname, 'samples', dir, '_config.js'); + } + }, + load(id) { + if (id === 'assert') return browser_assert; + + if (id === 'main') { + return main.replace('__HYDRATE__', hydrate ? 'true' : 'false'); + } + return null; + }, + transform(code, id) { + if (id.endsWith('.svelte')) { + const compiled = svelte.compile(code.replace(/\r/g, ''), { + ...config.compileOptions, + hydratable: hydrate, + immutable: config.immutable, + accessors: 'accessors' in config ? config.accessors : true + }); + + const out_dir = `${__dirname}/samples/${dir}/_output/${ + hydrate ? 'hydratable' : 'normal' + }`; + const out = `${out_dir}/${path.basename(id).replace(/\.svelte$/, '.js')}`; + + if (fs.existsSync(out)) { + fs.unlinkSync(out); + } + if (!fs.existsSync(out_dir)) { + fs.mkdirSync(out_dir, { recursive: true }); + } + + fs.writeFileSync(out, compiled.js.code, 'utf8'); + + compiled.warnings.forEach((w) => warnings.push(w)); + + return compiled.js; + } + } + } + ] + }); + + const generated_bundle = await bundle.generate({ format: 'iife', name: 'test' }); + + function assertWarnings() { + if (config.warnings) { + assert.deepStrictEqual( + warnings.map((w) => ({ + code: w.code, + message: w.message, + pos: w.pos, + start: w.start, + end: w.end + })), + config.warnings + ); + } else if (warnings.length) { + failed.add(dir); + /* eslint-disable no-unsafe-finally */ + throw new Error('Received unexpected warnings'); + } + } + + try { + const page = await browser.newPage(); + page.on('console', (type) => { + console[type.type()](type.text()); + }); + await page.setContent('
'); + await page.evaluate(generated_bundle.output[0].code); + const test_result = await page.evaluate(`test(document.querySelector('main'))`); + + if (test_result) console.log(test_result); + assertWarnings(); + await page.close(); + } catch (err) { + failed.add(dir); + pretty_print_browser_assertion(err.message); + assertWarnings(); + throw err; + } + }); + } + + await Promise.all( + fs.readdirSync(`${__dirname}/samples`).map(async (dir) => { + await runTest(dir, false); + await runTest(dir, true); + }) + ); + }, + // Browser tests are brittle and slow on CI + { timeout: 20000, retry: process.env.CI ? 1 : 0 } +); diff --git a/test/runtime-browser/driver.js b/test/runtime-browser/driver.js new file mode 100644 index 0000000000..3c8b093476 --- /dev/null +++ b/test/runtime-browser/driver.js @@ -0,0 +1,73 @@ +import SvelteComponent from '__MAIN_DOT_SVELTE__'; +import config from '__CONFIG__'; +import * as assert from 'assert'; + +export default async function (target) { + let unhandled_rejection = false; + function unhandled_rejection_handler(event) { + unhandled_rejection = event.reason; + } + window.addEventListener('unhandledrejection', unhandled_rejection_handler); + + try { + if (config.before_test) config.before_test(); + + const options = Object.assign( + {}, + { + target, + hydrate: __HYDRATE__, + props: config.props, + intro: config.intro + }, + config.options || {} + ); + + const component = new SvelteComponent(options); + + const waitUntil = async (fn, ms = 500) => { + const start = new Date().getTime(); + do { + if (fn()) return; + await new Promise((resolve) => window.setTimeout(resolve, 1)); + } while (new Date().getTime() <= start + ms); + }; + + if (config.html) { + assert.htmlEqual(target.innerHTML, config.html); + } + + if (config.test) { + await config.test({ + assert, + component, + target, + window, + waitUntil + }); + + component.$destroy(); + + if (unhandled_rejection) { + throw unhandled_rejection; + } + } else { + component.$destroy(); + assert.htmlEqual(target.innerHTML, ''); + + if (unhandled_rejection) { + throw unhandled_rejection; + } + } + + if (config.after_test) config.after_test(); + } catch (error) { + if (config.error) { + assert.equal(err.message, config.error); + } else { + throw error; + } + } finally { + window.removeEventListener('unhandledrejection', unhandled_rejection_handler); + } +} diff --git a/test/runtime-browser/index.js b/test/runtime-browser/index.js deleted file mode 100644 index d4f7967c54..0000000000 --- a/test/runtime-browser/index.js +++ /dev/null @@ -1,230 +0,0 @@ -import virtual from '@rollup/plugin-virtual'; -import * as fs from 'fs'; -import * as path from 'path'; -import { rollup } from 'rollup'; - -import { chromium } from '@playwright/test'; -import { deepStrictEqual } from 'assert'; -import { loadConfig, loadSvelte, mkdirp, prettyPrintBrowserAssertionError } from '../helpers'; - -const internal = path.resolve('internal/index.mjs'); -const index = path.resolve('index.mjs'); - -const assert = fs.readFileSync(`${__dirname}/assert.js`, 'utf-8'); - -describe('runtime (browser)', function () { - this.timeout(20000); - - let svelte; - let browser; - - before(async () => { - svelte = loadSvelte(false); - console.log('[runtime-browser] Loaded Svelte'); - - browser = await chromium.launch(); - console.log('[runtime-browser] Launched browser'); - }); - - after(async () => { - if (browser) await browser.close(); - }); - - const failed = new Set(); - - function runTest(dir, hydrate) { - if (dir[0] === '.') return; - - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - const solo = config.solo || /\.solo/.test(dir); - const skip = config.skip || /\.skip/.test(dir); - - if (hydrate && config.skip_if_hydrate) return; - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - (skip ? it.skip : solo ? it.only : it)( - `${dir} ${hydrate ? '(with hydration)' : ''}`, - async () => { - if (failed.has(dir)) { - // this makes debugging easier, by only printing compiled output once - throw new Error('skipping test, already failed'); - } - - const warnings = []; - - const bundle = await rollup({ - input: 'main', - plugins: [ - { - name: 'testing-runtime-browser', - resolveId(importee) { - if (importee === 'svelte/internal' || importee === './internal') { - return internal; - } - - if (importee === 'svelte') { - return index; - } - - if (importee === 'main') { - return 'main'; - } - }, - load(id) { - if (id === 'main') { - return ` - import SvelteComponent from ${JSON.stringify(path.join(__dirname, 'samples', dir, 'main.svelte'))}; - import config from ${JSON.stringify(path.join(__dirname, 'samples', dir, '_config.js'))}; - import * as assert from 'assert'; - - export default async function (target) { - let unhandled_rejection = false; - function unhandled_rejection_handler(event) { - unhandled_rejection = event.reason; - } - window.addEventListener('unhandledrejection', unhandled_rejection_handler); - - try { - if (config.before_test) config.before_test(); - - const options = Object.assign({}, { - target, - hydrate: ${String(!!hydrate)}, - props: config.props, - intro: config.intro - }, config.options || {}); - - const component = new SvelteComponent(options); - - const waitUntil = async (fn, ms = 500) => { - const start = new Date().getTime(); - do { - if (fn()) return; - await new Promise(resolve => window.setTimeout(resolve, 1)); - } while (new Date().getTime() <= start + ms); - }; - - if (config.html) { - assert.htmlEqual(target.innerHTML, config.html); - } - - if (config.test) { - await config.test({ - assert, - component, - target, - window, - waitUntil, - }); - - component.$destroy(); - - if (unhandled_rejection) { - throw unhandled_rejection; - } - } else { - component.$destroy(); - assert.htmlEqual(target.innerHTML, ''); - - if (unhandled_rejection) { - throw unhandled_rejection; - } - } - - if (config.after_test) config.after_test(); - } catch (error) { - if (config.error) { - assert.equal(err.message, config.error); - } else { - throw error; - } - } finally { - window.removeEventListener('unhandledrejection', unhandled_rejection_handler); - } - } - `; - } - return null; - }, - transform(code, id) { - if (id.endsWith('.svelte')) { - const compiled = svelte.compile(code.replace(/\r/g, ''), { - ...config.compileOptions, - hydratable: hydrate, - immutable: config.immutable, - accessors: 'accessors' in config ? config.accessors : true - }); - - const out_dir = `${__dirname}/samples/${dir}/_output/${ - hydrate ? 'hydratable' : 'normal' - }`; - const out = `${out_dir}/${path.basename(id).replace(/\.svelte$/, '.js')}`; - - if (fs.existsSync(out)) { - fs.unlinkSync(out); - } - - mkdirp(out_dir); - fs.writeFileSync(out, compiled.js.code, 'utf8'); - - compiled.warnings.forEach((w) => warnings.push(w)); - - return compiled.js; - } - } - }, - virtual({ assert }) - ] - }); - - const generated_bundle = await bundle.generate({ format: 'iife', name: 'test' }); - - function assertWarnings() { - if (config.warnings) { - deepStrictEqual( - warnings.map((w) => ({ - code: w.code, - message: w.message, - pos: w.pos, - start: w.start, - end: w.end - })), - config.warnings - ); - } else if (warnings.length) { - failed.add(dir); - /* eslint-disable no-unsafe-finally */ - throw new Error('Received unexpected warnings'); - } - } - - try { - const page = await browser.newPage(); - page.on('console', (type) => { - console[type.type()](type.text()); - }); - await page.setContent('
'); - await page.evaluate(generated_bundle.output[0].code); - const test_result = await page.evaluate(`test(document.querySelector('main'))`); - - if (test_result) console.log(test_result); - assertWarnings(); - await page.close(); - } catch (err) { - failed.add(dir); - prettyPrintBrowserAssertionError(err.message); - assertWarnings(); - throw err; - } - } - ); - } - - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - runTest(dir, false); - runTest(dir, true); - }); -}); diff --git a/test/runtime/App.svelte b/test/runtime/App.svelte new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/runtime/index.js b/test/runtime/runtime.test.js similarity index 52% rename from test/runtime/index.js rename to test/runtime/runtime.test.js index b693389622..7cfbaac53d 100644 --- a/test/runtime/index.js +++ b/test/runtime/runtime.test.js @@ -1,105 +1,73 @@ -import * as path from 'path'; +// @vitest-environment jsdom + import * as fs from 'fs'; -import { rollup } from 'rollup'; -import virtual from '@rollup/plugin-virtual'; +import * as path from 'path'; import glob from 'tiny-glob/sync.js'; -import { clear_loops, flush, set_now, set_raf } from '../../internal'; - -import { - assert, - showOutput, - loadConfig, - loadSvelte, - cleanRequireCache, - env, - setupHtmlEqual, - mkdirp -} from '../helpers'; - -let svelte$; -let svelte; - -let compileOptions = null; -let compile = null; - -const sveltePath = process.cwd().split('\\').join('/'); +import { beforeAll, afterAll, describe, it, assert } from 'vitest'; +import { compile } from '../../compiler.mjs'; +import { clear_loops, flush, set_now, set_raf } from 'svelte/internal'; +import { show_output, try_load_config, mkdirp, create_loader, setupHtmlEqual } from '../helpers.js'; +import { setTimeout } from 'timers/promises'; let unhandled_rejection = false; function unhandledRejection_handler(err) { unhandled_rejection = err; } -describe('runtime', () => { - before(() => { - process.on('unhandledRejection', unhandledRejection_handler); - svelte = loadSvelte(false); - svelte$ = loadSvelte(true); - - require.extensions['.svelte'] = function (module, filename) { - const options = Object.assign( - { - filename - }, - compileOptions - ); - - const { - js: { code } - } = compile(fs.readFileSync(filename, 'utf-8').replace(/\r/g, ''), options); - - return module._compile(code, filename); - }; +let listeners = process.rawListeners('unhandledRejection'); +describe('runtime', async () => { + beforeAll(() => { + process.prependListener('unhandledRejection', unhandledRejection_handler); return setupHtmlEqual({ removeDataSvelte: true }); }); - after(() => process.removeListener('unhandledRejection', unhandledRejection_handler)); + + afterAll(() => { + process.removeListener('unhandledRejection', unhandledRejection_handler); + }); const failed = new Set(); - function runTest(dir, hydrate, from_ssr_html) { + async function run_test(dir) { if (dir[0] === '.') return; - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); const solo = config.solo || /\.solo/.test(dir); - if (hydrate && config.skip_if_hydrate) return; - if (hydrate && from_ssr_html && config.skip_if_hydrate_from_ssr) return; + const it_fn = config.skip ? it.skip : solo ? it.only : it; - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } + it_fn.each` + hydrate | from_ssr_html + ${false} | ${false} + ${true} | ${false} + ${true} | ${true} + `(`${dir} hydrate: $hydrate, from_ssr: $from_ssr_html`, async ({ hydrate, from_ssr_html }) => { + if (hydrate && config.skip_if_hydrate) return; + if (hydrate && from_ssr_html && config.skip_if_hydrate_from_ssr) return; - const testName = `${dir} ${ - hydrate ? `(with hydration${from_ssr_html ? ' from ssr rendered html' : ''})` : '' - }`; - (config.skip ? it.skip : solo ? it.only : it)(testName, (done) => { if (failed.has(dir)) { // this makes debugging easier, by only printing compiled output once - throw new Error('skipping test, already failed'); + assert.fail(`skipping ${dir}, already failed`); } unhandled_rejection = null; - compile = (config.preserveIdentifiers ? svelte : svelte$).compile; - const cwd = path.resolve(`${__dirname}/samples/${dir}`); - compileOptions = config.compileOptions || {}; - compileOptions.format = 'cjs'; - compileOptions.sveltePath = sveltePath; - compileOptions.hydratable = hydrate; - compileOptions.immutable = config.immutable; - compileOptions.accessors = 'accessors' in config ? config.accessors : true; + const compileOptions = Object.assign(config.compileOptions || {}, { + format: 'cjs', + hydratable: hydrate, + immutable: config.immutable, + accessors: 'accessors' in config ? config.accessors : true + }); - cleanRequireCache(); + const load = create_loader(compileOptions, cwd); let mod; let SvelteComponent; let unintendedError = null; - const window = env(); - glob('**/*.svelte', { cwd }).forEach((file) => { if (file[0] === '_') return; @@ -124,8 +92,14 @@ describe('runtime', () => { } }); - Promise.resolve() - .then(() => { + if (config.expect_unhandled_rejections) { + listeners.forEach((listener) => { + process.removeListener('unhandledRejection', listener); + }); + } + + await Promise.resolve() + .then(async () => { // hack to support transition tests clear_loops(); @@ -147,25 +121,29 @@ describe('runtime', () => { }); try { - mod = require(`./samples/${dir}/main.svelte`); + mod = await load(`./main.svelte`); SvelteComponent = mod.default; } catch (err) { - showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console + show_output(cwd, compileOptions); // eslint-disable-line no-console throw err; } // Put things we need on window for testing window.SvelteComponent = SvelteComponent; + window.location.href = ''; + window.document.title = ''; + window.document.head.innerHTML = ''; + window.document.body.innerHTML = '
'; const target = window.document.querySelector('main'); let snapshot = undefined; if (hydrate && from_ssr_html) { + const load_ssr = create_loader({ ...compileOptions, generate: 'ssr' }, cwd); + // ssr into target - compileOptions.generate = 'ssr'; - cleanRequireCache(); if (config.before_test) config.before_test(); - const SsrSvelteComponent = require(`./samples/${dir}/main.svelte`).default; + const SsrSvelteComponent = (await load_ssr(`./main.svelte`)).default; const { html } = SsrSvelteComponent.render(config.props); target.innerHTML = html; @@ -173,7 +151,6 @@ describe('runtime', () => { snapshot = config.snapshot(target); } - delete compileOptions.generate; if (config.after_test) config.after_test(); } else { target.innerHTML = ''; @@ -204,14 +181,14 @@ describe('runtime', () => { if (config.error) { unintendedError = true; - throw new Error('Expected a runtime error'); + assert.fail('Expected a runtime error'); } if (config.warnings) { assert.deepEqual(warnings, config.warnings); } else if (warnings.length) { unintendedError = true; - throw new Error('Received unexpected warnings'); + assert.fail('Received unexpected warnings'); } if (config.html) { @@ -220,9 +197,9 @@ describe('runtime', () => { }); } - if (config.test) { - return Promise.resolve( - config.test({ + try { + if (config.test) { + await config.test({ assert, component, mod, @@ -230,19 +207,16 @@ describe('runtime', () => { snapshot, window, raf, - compileOptions - }) - ).then(() => { - component.$destroy(); - - if (unhandled_rejection) { - throw unhandled_rejection; - } - }); - } else { + compileOptions, + load + }); + } + } finally { component.$destroy(); assert.htmlEqual(target.innerHTML, ''); + // TODO: This seems useless, unhandledRejection is only triggered on the next task + // by which time the test has already finished and the next test resets it to null above if (unhandled_rejection) { throw unhandled_rejection; } @@ -261,82 +235,48 @@ describe('runtime', () => { }) .catch((err) => { failed.add(dir); - showOutput(cwd, compileOptions, compile); // eslint-disable-line no-console - throw err; - }) - .catch((err) => { // print a clickable link to open the directory err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`; - done(err); + throw err; }) - .then(() => { - if (config.show) { - showOutput(cwd, compileOptions, compile); - } - + .finally(async () => { flush(); if (config.after_test) config.after_test(); - done(); - }); - }); - } - - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - runTest(dir, false); - runTest(dir, true, false); - runTest(dir, true, true); - }); - async function create_component(src = '
') { - const { js } = svelte$.compile(src, { - format: 'esm', - name: 'SvelteComponent', - dev: true - }); + // Free up the microtask queue, so that + // 1. Vitest's test runner which uses setInterval can log progress + // 2. Any expected unhandled rejections are ran before we reattach the listeners + await setTimeout(); - const bundle = await rollup({ - input: 'main.js', - plugins: [ - virtual({ - 'main.js': js.code - }), - { - name: 'svelte-packages', - resolveId: (importee) => { - if (importee.startsWith('svelte/')) { - return importee.replace('svelte', process.cwd()) + '/index.mjs'; - } + if (config.expect_unhandled_rejections) { + listeners.forEach((listener) => { + process.on('unhandledRejection', listener); + }); } - } - ] + }); }); + } - const result = await bundle.generate({ - format: 'iife', - name: 'App' - }); + const samples = fs.readdirSync(`${__dirname}/samples`); + await Promise.all(samples.map((sample) => run_test(sample))); - return eval(`(function () { ${result.output[0].code}; return App; }())`); - } + const load = create_loader({ generate: 'dom', dev: true, format: 'cjs' }, __dirname); + const { default: App } = await load('App.svelte'); it('fails if options.target is missing in dev mode', async () => { - const App = await create_component(); - assert.throws(() => { new App(); }, /'target' is a required option/); }); it('fails if options.hydrate is true but the component is non-hydratable', async () => { - const App = await create_component(); - assert.throws(() => { new App({ target: { childNodes: [] }, hydrate: true }); - }, /options.hydrate only works if the component was compiled with the `hydratable: true` option/); + }, /options\.hydrate only works if the component was compiled with the `hydratable: true` option/); }); }); diff --git a/test/runtime/samples/$$rest-without-props/_config.js b/test/runtime/samples/$$rest-without-props/_config.js index ac49ea2c8c..9412aa99df 100644 --- a/test/runtime/samples/$$rest-without-props/_config.js +++ b/test/runtime/samples/$$rest-without-props/_config.js @@ -1,9 +1,6 @@ export default { - props: { - a: 3, - b: 4, - c: 5, - d: 6 + get props() { + return { a: 3, b: 4, c: 5, d: 6 }; }, html: `
Length: 3
diff --git a/test/runtime/samples/$$rest/_config.js b/test/runtime/samples/$$rest/_config.js index 1d196a7efd..4d050059fc 100644 --- a/test/runtime/samples/$$rest/_config.js +++ b/test/runtime/samples/$$rest/_config.js @@ -1,9 +1,6 @@ export default { - props: { - a: 3, - b: 4, - c: 5, - d: 6 + get props() { + return { a: 3, b: 4, c: 5, d: 6 }; }, html: `
Length: 3
diff --git a/test/runtime/samples/action-receives-element-mounted/_config.js b/test/runtime/samples/action-receives-element-mounted/_config.js index c6f3fcd89f..b5cc3318ce 100644 --- a/test/runtime/samples/action-receives-element-mounted/_config.js +++ b/test/runtime/samples/action-receives-element-mounted/_config.js @@ -1,7 +1,9 @@ const result = {}; export default { - props: { result }, + get props() { + return { result }; + }, async test({ assert }) { assert.notEqual(result.parentElement, null); } diff --git a/test/runtime/samples/action-ternary-template/_config.js b/test/runtime/samples/action-ternary-template/_config.js index 8b7d7ac51a..23dd450409 100644 --- a/test/runtime/samples/action-ternary-template/_config.js +++ b/test/runtime/samples/action-ternary-template/_config.js @@ -1,7 +1,6 @@ export default { - props: { - target: 'World!', - display: true + get props() { + return { target: 'World!', display: true }; }, html: ` diff --git a/test/runtime/samples/after-render-prevents-loop/_config.js b/test/runtime/samples/after-render-prevents-loop/_config.js index 4ccf6b11ba..86bde0c534 100644 --- a/test/runtime/samples/after-render-prevents-loop/_config.js +++ b/test/runtime/samples/after-render-prevents-loop/_config.js @@ -1,8 +1,8 @@ export default { skip_if_ssr: true, - props: { - value: 'hello!' + get props() { + return { value: 'hello!' }; }, html: ` diff --git a/test/runtime/samples/after-render-triggers-update/_config.js b/test/runtime/samples/after-render-triggers-update/_config.js index 4ccf6b11ba..86bde0c534 100644 --- a/test/runtime/samples/after-render-triggers-update/_config.js +++ b/test/runtime/samples/after-render-triggers-update/_config.js @@ -1,8 +1,8 @@ export default { skip_if_ssr: true, - props: { - value: 'hello!' + get props() { + return { value: 'hello!' }; }, html: ` diff --git a/test/runtime/samples/animation-css/_config.js b/test/runtime/samples/animation-css/_config.js index 245c5213af..3e74025208 100644 --- a/test/runtime/samples/animation-css/_config.js +++ b/test/runtime/samples/animation-css/_config.js @@ -1,12 +1,14 @@ export default { - props: { - things: [ - { id: 1, name: 'a' }, - { id: 2, name: 'b' }, - { id: 3, name: 'c' }, - { id: 4, name: 'd' }, - { id: 5, name: 'e' } - ] + get props() { + return { + things: [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ] + }; }, html: ` diff --git a/test/runtime/samples/animation-js-delay/_config.js b/test/runtime/samples/animation-js-delay/_config.js index a0e4d6227e..4ba519672e 100644 --- a/test/runtime/samples/animation-js-delay/_config.js +++ b/test/runtime/samples/animation-js-delay/_config.js @@ -1,12 +1,14 @@ export default { - props: { - things: [ - { id: 1, name: 'a' }, - { id: 2, name: 'b' }, - { id: 3, name: 'c' }, - { id: 4, name: 'd' }, - { id: 5, name: 'e' } - ] + get props() { + return { + things: [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ] + }; }, html: ` diff --git a/test/runtime/samples/animation-js-easing/_config.js b/test/runtime/samples/animation-js-easing/_config.js index 55b7dc4359..ce4f98d537 100644 --- a/test/runtime/samples/animation-js-easing/_config.js +++ b/test/runtime/samples/animation-js-easing/_config.js @@ -1,12 +1,14 @@ export default { - props: { - things: [ - { id: 1, name: 'a' }, - { id: 2, name: 'b' }, - { id: 3, name: 'c' }, - { id: 4, name: 'd' }, - { id: 5, name: 'e' } - ] + get props() { + return { + things: [ + { id: 1, name: 'a' }, + { id: 2, name: 'b' }, + { id: 3, name: 'c' }, + { id: 4, name: 'd' }, + { id: 5, name: 'e' } + ] + }; }, html: ` diff --git a/test/runtime/samples/animation-js-easing/easing.js b/test/runtime/samples/animation-js-easing/easing.js deleted file mode 100644 index 6399b8858c..0000000000 --- a/test/runtime/samples/animation-js-easing/easing.js +++ /dev/null @@ -1,3 +0,0 @@ -export function linear(t) { - return t; -} diff --git a/test/runtime/samples/animation-js-easing/main.svelte b/test/runtime/samples/animation-js-easing/main.svelte index 264ff6d9d6..a9e0c88078 100644 --- a/test/runtime/samples/animation-js-easing/main.svelte +++ b/test/runtime/samples/animation-js-easing/main.svelte @@ -1,7 +1,10 @@ '; const bar = ''; export default { - props: { - condition: false, - foo, - bar + get props() { + return { condition: false, foo, bar }; }, test({ assert, component, window }) { diff --git a/test/runtime/samples/head-raw-dynamic/_config.js b/test/runtime/samples/head-raw-dynamic/_config.js index 61e86727ff..739c9650d8 100644 --- a/test/runtime/samples/head-raw-dynamic/_config.js +++ b/test/runtime/samples/head-raw-dynamic/_config.js @@ -2,10 +2,8 @@ const foo = ''; const bar = ''; export default { - props: { - condition: 1, - foo, - bar + get props() { + return { condition: 1, foo, bar }; }, test({ assert, component, window }) { diff --git a/test/runtime/samples/head-title-dynamic-simple/_config.js b/test/runtime/samples/head-title-dynamic-simple/_config.js index 7a086419ee..88bed8b65d 100644 --- a/test/runtime/samples/head-title-dynamic-simple/_config.js +++ b/test/runtime/samples/head-title-dynamic-simple/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: 'A Title' + get props() { + return { foo: 'A Title' }; }, test({ assert, component, window }) { diff --git a/test/runtime/samples/head-title-dynamic/_config.js b/test/runtime/samples/head-title-dynamic/_config.js index 2555e55b8c..fe16e7ae02 100644 --- a/test/runtime/samples/head-title-dynamic/_config.js +++ b/test/runtime/samples/head-title-dynamic/_config.js @@ -1,6 +1,6 @@ export default { - props: { - adjective: 'custom' + get props() { + return { adjective: 'custom' }; }, test({ assert, component, window }) { diff --git a/test/runtime/samples/hello-world/_config.js b/test/runtime/samples/hello-world/_config.js index d65d690011..59d39ac863 100644 --- a/test/runtime/samples/hello-world/_config.js +++ b/test/runtime/samples/hello-world/_config.js @@ -1,6 +1,6 @@ export default { - props: { - name: 'world' + get props() { + return { name: 'world' }; }, html: '

Hello world!

', diff --git a/test/runtime/samples/if-block-component-without-outro/_config.js b/test/runtime/samples/if-block-component-without-outro/_config.js index fbcc328fa3..ac8abdfba9 100644 --- a/test/runtime/samples/if-block-component-without-outro/_config.js +++ b/test/runtime/samples/if-block-component-without-outro/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: true + get props() { + return { foo: true }; }, html: '
A wild component appears
', diff --git a/test/runtime/samples/if-block-conservative-update/_config.js b/test/runtime/samples/if-block-conservative-update/_config.js index f7ba01a8fd..ca51c1bb6c 100644 --- a/test/runtime/samples/if-block-conservative-update/_config.js +++ b/test/runtime/samples/if-block-conservative-update/_config.js @@ -1,12 +1,14 @@ let count = 0; export default { - props: { - foo: 'potato', - fn: () => { - count += 1; - return true; - } + get props() { + return { + foo: 'potato', + fn: () => { + count += 1; + return true; + } + }; }, html: '

potato

', diff --git a/test/runtime/samples/if-block-else-conservative-update/_config.js b/test/runtime/samples/if-block-else-conservative-update/_config.js index b3db0bca54..d32a336790 100644 --- a/test/runtime/samples/if-block-else-conservative-update/_config.js +++ b/test/runtime/samples/if-block-else-conservative-update/_config.js @@ -3,21 +3,24 @@ let count_a = 0; let count_b = 0; export default { - props: { - foo: 'potato', - fn: () => { - count_a += 1; - return a; - }, - other_fn: () => { - count_b += 1; - return true; - } + get props() { + return { + foo: 'potato', + fn: () => { + count_a += 1; + return a; + }, + other_fn: () => { + count_b += 1; + return true; + } + }; }, html: '

potato

', before_test() { + a = true; count_a = 0; count_b = 0; }, diff --git a/test/runtime/samples/if-block-else-in-each/_config.js b/test/runtime/samples/if-block-else-in-each/_config.js index e75dcb15ee..d8e5528fe6 100644 --- a/test/runtime/samples/if-block-else-in-each/_config.js +++ b/test/runtime/samples/if-block-else-in-each/_config.js @@ -1,6 +1,6 @@ export default { - props: { - array: [true, false] + get props() { + return { array: [true, false] }; }, html: `
foo
diff --git a/test/runtime/samples/if-block-else-partial-outro/_config.js b/test/runtime/samples/if-block-else-partial-outro/_config.js index 22b2d99c9d..ec02f08e46 100644 --- a/test/runtime/samples/if-block-else-partial-outro/_config.js +++ b/test/runtime/samples/if-block-else-partial-outro/_config.js @@ -1,7 +1,6 @@ export default { - props: { - x: 1, - y: false + get props() { + return { x: 1, y: false }; }, html: ` diff --git a/test/runtime/samples/if-block-else/_config.js b/test/runtime/samples/if-block-else/_config.js index 9752480205..56cd41f420 100644 --- a/test/runtime/samples/if-block-else/_config.js +++ b/test/runtime/samples/if-block-else/_config.js @@ -1,7 +1,6 @@ export default { - props: { - foo: true, - bar: false + get props() { + return { foo: true, bar: false }; }, html: ` diff --git a/test/runtime/samples/if-block-elseif-no-else/_config.js b/test/runtime/samples/if-block-elseif-no-else/_config.js index 80f2304bde..50bead7353 100644 --- a/test/runtime/samples/if-block-elseif-no-else/_config.js +++ b/test/runtime/samples/if-block-elseif-no-else/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 11 + get props() { + return { x: 11 }; }, html: ` diff --git a/test/runtime/samples/if-block-elseif-text/_config.js b/test/runtime/samples/if-block-elseif-text/_config.js index 44beed5f93..0933a761c9 100644 --- a/test/runtime/samples/if-block-elseif-text/_config.js +++ b/test/runtime/samples/if-block-elseif-text/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 11 + get props() { + return { x: 11 }; }, html: ` diff --git a/test/runtime/samples/if-block-elseif/_config.js b/test/runtime/samples/if-block-elseif/_config.js index 1592120e72..53a459fd47 100644 --- a/test/runtime/samples/if-block-elseif/_config.js +++ b/test/runtime/samples/if-block-elseif/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 11 + get props() { + return { x: 11 }; }, html: ` diff --git a/test/runtime/samples/if-block-first/_config.js b/test/runtime/samples/if-block-first/_config.js index b2c8c235fd..ff90ed44f1 100644 --- a/test/runtime/samples/if-block-first/_config.js +++ b/test/runtime/samples/if-block-first/_config.js @@ -1,6 +1,6 @@ export default { - props: { - visible: false + get props() { + return { visible: false }; }, html: '
before me
', diff --git a/test/runtime/samples/if-block-or/_config.js b/test/runtime/samples/if-block-or/_config.js index 521a450c29..e51daa7561 100644 --- a/test/runtime/samples/if-block-or/_config.js +++ b/test/runtime/samples/if-block-or/_config.js @@ -1,7 +1,6 @@ export default { - props: { - a: true, - b: false + get props() { + return { a: true, b: false }; }, html: '

i am visible

', diff --git a/test/runtime/samples/if-block-outro-computed-function/_config.js b/test/runtime/samples/if-block-outro-computed-function/_config.js index 69fd95d008..d638dedd4b 100644 --- a/test/runtime/samples/if-block-outro-computed-function/_config.js +++ b/test/runtime/samples/if-block-outro-computed-function/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: true + get props() { + return { foo: true }; }, html: 'foo', diff --git a/test/runtime/samples/if-block-static-with-dynamic-contents/_config.js b/test/runtime/samples/if-block-static-with-dynamic-contents/_config.js index c5c99c5305..082a3613e4 100644 --- a/test/runtime/samples/if-block-static-with-dynamic-contents/_config.js +++ b/test/runtime/samples/if-block-static-with-dynamic-contents/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: 42 + get props() { + return { foo: 42 }; }, html: '

42

', diff --git a/test/runtime/samples/if-block-widget/_config.js b/test/runtime/samples/if-block-widget/_config.js index b0916439b6..39a65d3b22 100644 --- a/test/runtime/samples/if-block-widget/_config.js +++ b/test/runtime/samples/if-block-widget/_config.js @@ -1,6 +1,6 @@ export default { - props: { - visible: true + get props() { + return { visible: true }; }, html: ` diff --git a/test/runtime/samples/if-block/_config.js b/test/runtime/samples/if-block/_config.js index d4b646f9fc..322b0c6d31 100644 --- a/test/runtime/samples/if-block/_config.js +++ b/test/runtime/samples/if-block/_config.js @@ -1,6 +1,6 @@ export default { - props: { - visible: true + get props() { + return { visible: true }; }, html: '

i am visible

', diff --git a/test/runtime/samples/if-in-keyed-each/_config.js b/test/runtime/samples/if-in-keyed-each/_config.js index b4a24202db..3188e724ee 100644 --- a/test/runtime/samples/if-in-keyed-each/_config.js +++ b/test/runtime/samples/if-in-keyed-each/_config.js @@ -1,9 +1,11 @@ export default { - props: { - items: [ - { id: 1, name: 'one' }, - { id: 2, name: 'two' } - ] + get props() { + return { + items: [ + { id: 1, name: 'one' }, + { id: 2, name: 'two' } + ] + }; }, html: ` diff --git a/test/runtime/samples/ignore-unchanged-attribute-compound/_config.js b/test/runtime/samples/ignore-unchanged-attribute-compound/_config.js index 741d5bc780..64b8715f45 100644 --- a/test/runtime/samples/ignore-unchanged-attribute-compound/_config.js +++ b/test/runtime/samples/ignore-unchanged-attribute-compound/_config.js @@ -1,9 +1,8 @@ import counter from './counter.js'; export default { - props: { - x: 1, - y: 2 + get props() { + return { x: 1, y: 2 }; }, html: ` diff --git a/test/runtime/samples/ignore-unchanged-attribute/_config.js b/test/runtime/samples/ignore-unchanged-attribute/_config.js index c1f8b23b98..ef8487122f 100644 --- a/test/runtime/samples/ignore-unchanged-attribute/_config.js +++ b/test/runtime/samples/ignore-unchanged-attribute/_config.js @@ -1,9 +1,8 @@ import counter from './counter.js'; export default { - props: { - x: 1, - y: 2 + get props() { + return { x: 1, y: 2 }; }, html: ` diff --git a/test/runtime/samples/ignore-unchanged-raw/_config.js b/test/runtime/samples/ignore-unchanged-raw/_config.js index 15e9430c30..43fb7fc3ac 100644 --- a/test/runtime/samples/ignore-unchanged-raw/_config.js +++ b/test/runtime/samples/ignore-unchanged-raw/_config.js @@ -1,9 +1,8 @@ import counter from './counter.js'; export default { - props: { - x: 1, - y: 2 + get props() { + return { x: 1, y: 2 }; }, html: ` diff --git a/test/runtime/samples/ignore-unchanged-tag/_config.js b/test/runtime/samples/ignore-unchanged-tag/_config.js index 15e9430c30..43fb7fc3ac 100644 --- a/test/runtime/samples/ignore-unchanged-tag/_config.js +++ b/test/runtime/samples/ignore-unchanged-tag/_config.js @@ -1,9 +1,8 @@ import counter from './counter.js'; export default { - props: { - x: 1, - y: 2 + get props() { + return { x: 1, y: 2 }; }, html: ` diff --git a/test/runtime/samples/initial-state-assign/_config.js b/test/runtime/samples/initial-state-assign/_config.js index 95a1a5e068..26ee07cec6 100644 --- a/test/runtime/samples/initial-state-assign/_config.js +++ b/test/runtime/samples/initial-state-assign/_config.js @@ -1,5 +1,7 @@ export default { - props: { bar: 'bar' }, + get props() { + return { bar: 'bar' }; + }, html: ` "foo" "bar" diff --git a/test/runtime/samples/inline-expressions/_config.js b/test/runtime/samples/inline-expressions/_config.js index 334e2c5d14..4086020c5f 100644 --- a/test/runtime/samples/inline-expressions/_config.js +++ b/test/runtime/samples/inline-expressions/_config.js @@ -1,7 +1,6 @@ export default { - props: { - a: 1, - b: 2 + get props() { + return { a: 1, b: 2 }; }, html: '

1 + 2 = 3

', test({ assert, component, target }) { diff --git a/test/runtime/samples/instrumentation-script-multiple-assignments/_config.js b/test/runtime/samples/instrumentation-script-multiple-assignments/_config.js index e729926aeb..d219920acd 100644 --- a/test/runtime/samples/instrumentation-script-multiple-assignments/_config.js +++ b/test/runtime/samples/instrumentation-script-multiple-assignments/_config.js @@ -1,7 +1,6 @@ export default { - props: { - foo: 0, - bar: 0 + get props() { + return { foo: 0, bar: 0 }; }, html: ` diff --git a/test/runtime/samples/instrumentation-template-multiple-assignments/_config.js b/test/runtime/samples/instrumentation-template-multiple-assignments/_config.js index e729926aeb..d219920acd 100644 --- a/test/runtime/samples/instrumentation-template-multiple-assignments/_config.js +++ b/test/runtime/samples/instrumentation-template-multiple-assignments/_config.js @@ -1,7 +1,6 @@ export default { - props: { - foo: 0, - bar: 0 + get props() { + return { foo: 0, bar: 0 }; }, html: ` diff --git a/test/runtime/samples/key-block-component-slot/_config.js b/test/runtime/samples/key-block-component-slot/_config.js index d08ff11af9..fadd9484ae 100644 --- a/test/runtime/samples/key-block-component-slot/_config.js +++ b/test/runtime/samples/key-block-component-slot/_config.js @@ -1,10 +1,15 @@ -const logs = []; +let logs = []; export default { html: '', - props: { - logs + get props() { + return { logs }; }, + + before_test() { + logs = []; + }, + async test({ assert, target }) { assert.deepEqual(logs, ['mount']); diff --git a/test/runtime/samples/key-block-transition-local/_config.js b/test/runtime/samples/key-block-transition-local/_config.js index 50400e72b6..06cac37a8d 100644 --- a/test/runtime/samples/key-block-transition-local/_config.js +++ b/test/runtime/samples/key-block-transition-local/_config.js @@ -1,7 +1,6 @@ export default { - props: { - x: false, - y: 1 + get props() { + return { x: false, y: 1 }; }, test({ assert, component, target, raf }) { diff --git a/test/runtime/samples/mixed-let-export/_config.js b/test/runtime/samples/mixed-let-export/_config.js index f3da4215d9..23bcf0c454 100644 --- a/test/runtime/samples/mixed-let-export/_config.js +++ b/test/runtime/samples/mixed-let-export/_config.js @@ -1,6 +1,6 @@ export default { - props: { - a: 42 + get props() { + return { a: 42 }; }, html: ` diff --git a/test/runtime/samples/names-deconflicted-nested/_config.js b/test/runtime/samples/names-deconflicted-nested/_config.js index 22793f9032..ce4fd333b6 100644 --- a/test/runtime/samples/names-deconflicted-nested/_config.js +++ b/test/runtime/samples/names-deconflicted-nested/_config.js @@ -1,10 +1,12 @@ export default { - props: { - array: [ - [0, 0, 0], - [0, 0, 0], - [0, 0, 0] - ] + get props() { + return { + array: [ + [0, 0, 0], + [0, 0, 0], + [0, 0, 0] + ] + }; }, html: ` diff --git a/test/runtime/samples/nested-transition-detach-each/_config.js b/test/runtime/samples/nested-transition-detach-each/_config.js index a7dafd16e1..fc69414657 100644 --- a/test/runtime/samples/nested-transition-detach-each/_config.js +++ b/test/runtime/samples/nested-transition-detach-each/_config.js @@ -1,8 +1,10 @@ export default { - props: { - visible: false, - rows: [1, 2, 3], - cols: ['a', 'b', 'c'] + get props() { + return { + visible: false, + rows: [1, 2, 3], + cols: ['a', 'b', 'c'] + }; }, html: '', diff --git a/test/runtime/samples/nested-transition-if-block-not-remounted/_config.js b/test/runtime/samples/nested-transition-if-block-not-remounted/_config.js index 1f2d5561fe..fe8206914f 100644 --- a/test/runtime/samples/nested-transition-if-block-not-remounted/_config.js +++ b/test/runtime/samples/nested-transition-if-block-not-remounted/_config.js @@ -1,7 +1,6 @@ export default { - props: { - x: true, - value: 'one' + get props() { + return { x: true, value: 'one' }; }, html: ` diff --git a/test/runtime/samples/observable-auto-subscribe/_config.js b/test/runtime/samples/observable-auto-subscribe/_config.js index 2bee7c9c9e..5961d21536 100644 --- a/test/runtime/samples/observable-auto-subscribe/_config.js +++ b/test/runtime/samples/observable-auto-subscribe/_config.js @@ -1,8 +1,9 @@ -const subscribers = []; +import { tick } from 'svelte'; let value = 'initial'; -const observable = { +let subscribers = []; +let observable = { subscribe: (fn) => { subscribers.push(fn); @@ -18,9 +19,13 @@ const observable = { }; export default { - props: { - observable, - visible: false + before_test() { + value = 'initial'; + subscribers = []; + }, + + get props() { + return { observable, visible: false }; }, html: '', @@ -31,22 +36,13 @@ export default { component.visible = true; assert.equal(subscribers.length, 1); - assert.htmlEqual( - target.innerHTML, - ` -

value: initial

- ` - ); - + assert.htmlEqual(target.innerHTML, `

value: initial

`); value = 42; - await subscribers.forEach((fn) => fn(value)); - - assert.htmlEqual( - target.innerHTML, - ` -

value: 42

- ` - ); + subscribers.forEach((fn) => { + fn(value); + }); + await tick(); + assert.htmlEqual(target.innerHTML, `

value: 42

`); component.visible = false; diff --git a/test/runtime/samples/option-without-select/_config.js b/test/runtime/samples/option-without-select/_config.js index b1dcf33344..e1d45f03bb 100644 --- a/test/runtime/samples/option-without-select/_config.js +++ b/test/runtime/samples/option-without-select/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: 'hello' + get props() { + return { foo: 'hello' }; }, html: "", diff --git a/test/runtime/samples/paren-wrapped-expressions/_config.js b/test/runtime/samples/paren-wrapped-expressions/_config.js index 8cebf560d1..400c638c07 100644 --- a/test/runtime/samples/paren-wrapped-expressions/_config.js +++ b/test/runtime/samples/paren-wrapped-expressions/_config.js @@ -1,8 +1,6 @@ export default { - props: { - a: 'foo', - b: true, - c: [1, 2, 3] + get props() { + return { a: 'foo', b: true, c: [1, 2, 3] }; }, html: ` diff --git a/test/runtime/samples/prop-accessors/_config.js b/test/runtime/samples/prop-accessors/_config.js index 2f0f0eb442..247d27ecaf 100644 --- a/test/runtime/samples/prop-accessors/_config.js +++ b/test/runtime/samples/prop-accessors/_config.js @@ -4,9 +4,13 @@ export default { assert.equal(component.foo1, 42); assert.equal(component.foo2(), 42); assert.equal(component.bar, undefined); - component.foo1 = null; - component.foo2 = null; - assert.equal(component.foo1, 42); - assert.equal(component.foo2(), 42); + + assert.throws(() => { + component.foo1 = null; + }, /Cannot set property foo1 of/); + + assert.throws(() => { + component.foo2 = null; + }, /Cannot set property foo2 of/); } }; diff --git a/test/runtime/samples/prop-const/_config.js b/test/runtime/samples/prop-const/_config.js index f489a74d68..56e0087cd3 100644 --- a/test/runtime/samples/prop-const/_config.js +++ b/test/runtime/samples/prop-const/_config.js @@ -1,7 +1,6 @@ export default { - props: { - a: 3, - b: 4 + get props() { + return { a: 3, b: 4 }; }, html: ` diff --git a/test/runtime/samples/prop-exports/_config.js b/test/runtime/samples/prop-exports/_config.js index 69a40c4fab..4bcd1c769c 100644 --- a/test/runtime/samples/prop-exports/_config.js +++ b/test/runtime/samples/prop-exports/_config.js @@ -1,16 +1,18 @@ -import { writable } from '../../../../store'; +import { writable } from 'svelte/store'; export default { - props: { - s1: writable(42), - s2: writable(43), - p1: 2, - p3: 3, - a1: writable(1), - a2: 4, - a6: writable(29), - for: 'loop', - continue: '...' + get props() { + return { + s1: writable(42), + s2: writable(43), + p1: 2, + p3: 3, + a1: writable(1), + a2: 4, + a6: writable(29), + for: 'loop', + continue: '...' + }; }, html: ` diff --git a/test/runtime/samples/prop-not-action/_config.js b/test/runtime/samples/prop-not-action/_config.js index 1f4ffa4749..ce036cbd0e 100644 --- a/test/runtime/samples/prop-not-action/_config.js +++ b/test/runtime/samples/prop-not-action/_config.js @@ -1,6 +1,6 @@ export default { - props: { - currentUser: { name: 'world' } + get props() { + return { currentUser: { name: 'world' } }; }, html: ` diff --git a/test/runtime/samples/prop-quoted/_config.js b/test/runtime/samples/prop-quoted/_config.js index aa2f2ec329..99f97961e3 100644 --- a/test/runtime/samples/prop-quoted/_config.js +++ b/test/runtime/samples/prop-quoted/_config.js @@ -1,6 +1,6 @@ export default { - props: { - foo: 1 + get props() { + return { foo: 1 }; }, html: '1', diff --git a/test/runtime/samples/prop-subscribable/_config.js b/test/runtime/samples/prop-subscribable/_config.js index fcd8fb8b07..bbe76c8170 100644 --- a/test/runtime/samples/prop-subscribable/_config.js +++ b/test/runtime/samples/prop-subscribable/_config.js @@ -1,8 +1,8 @@ -import { writable } from '../../../../store'; +import { writable } from 'svelte/store'; export default { - props: { - b: writable(42) + get props() { + return { b: writable(42) }; }, html: ` diff --git a/test/runtime/samples/prop-without-semicolon-b/_config.js b/test/runtime/samples/prop-without-semicolon-b/_config.js index 6cdc86c521..613951c9b8 100644 --- a/test/runtime/samples/prop-without-semicolon-b/_config.js +++ b/test/runtime/samples/prop-without-semicolon-b/_config.js @@ -1,6 +1,6 @@ export default { - props: { - name: 'world' + get props() { + return { name: 'world' }; }, html: '

Hello world!

' diff --git a/test/runtime/samples/props-reactive-b/_config.js b/test/runtime/samples/props-reactive-b/_config.js index 93cad93d22..3bc2d8d672 100644 --- a/test/runtime/samples/props-reactive-b/_config.js +++ b/test/runtime/samples/props-reactive-b/_config.js @@ -1,7 +1,6 @@ export default { - props: { - a: 1, - b: 2 + get props() { + return { a: 1, b: 2 }; }, html: ` diff --git a/test/runtime/samples/props-reactive-only-with-change/_config.js b/test/runtime/samples/props-reactive-only-with-change/_config.js index 8ed82fc8ec..922059adf9 100644 --- a/test/runtime/samples/props-reactive-only-with-change/_config.js +++ b/test/runtime/samples/props-reactive-only-with-change/_config.js @@ -1,10 +1,12 @@ let callbacks = []; export default { - props: { - callback: (value) => callbacks.push(value), - val1: '1', - val2: '2' + get props() { + return { + callback: (value) => callbacks.push(value), + val1: '1', + val2: '2' + }; }, before_test() { diff --git a/test/runtime/samples/props-reactive/_config.js b/test/runtime/samples/props-reactive/_config.js index 2be0b59822..b2190b665f 100644 --- a/test/runtime/samples/props-reactive/_config.js +++ b/test/runtime/samples/props-reactive/_config.js @@ -1,9 +1,6 @@ export default { - props: { - a: 1, - b: 2, - c: 3, - d: 4 + get props() { + return { a: 1, b: 2, c: 3, d: 4 }; }, html: ` diff --git a/test/runtime/samples/props/_config.js b/test/runtime/samples/props/_config.js index 55df3f9f2b..b63cc47441 100644 --- a/test/runtime/samples/props/_config.js +++ b/test/runtime/samples/props/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 1 + get props() { + return { x: 1 }; }, html: ` diff --git a/test/runtime/samples/raw-anchor-first-child/_config.js b/test/runtime/samples/raw-anchor-first-child/_config.js index f9e6b76626..cec7aaba01 100644 --- a/test/runtime/samples/raw-anchor-first-child/_config.js +++ b/test/runtime/samples/raw-anchor-first-child/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-anchor-first-last-child/_config.js b/test/runtime/samples/raw-anchor-first-last-child/_config.js index 30acfe4d0f..7c08b02967 100644 --- a/test/runtime/samples/raw-anchor-first-last-child/_config.js +++ b/test/runtime/samples/raw-anchor-first-last-child/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-anchor-last-child/_config.js b/test/runtime/samples/raw-anchor-last-child/_config.js index dcc8ac6db6..4aa93c686d 100644 --- a/test/runtime/samples/raw-anchor-last-child/_config.js +++ b/test/runtime/samples/raw-anchor-last-child/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js index c6d2b561c0..5f9d6e5ac5 100644 --- a/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-next-previous-sibling/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-anchor-next-sibling/_config.js b/test/runtime/samples/raw-anchor-next-sibling/_config.js index 1982f28da5..6d6c032f71 100644 --- a/test/runtime/samples/raw-anchor-next-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-next-sibling/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-anchor-previous-sibling/_config.js b/test/runtime/samples/raw-anchor-previous-sibling/_config.js index 1982f28da5..6d6c032f71 100644 --- a/test/runtime/samples/raw-anchor-previous-sibling/_config.js +++ b/test/runtime/samples/raw-anchor-previous-sibling/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: 'foo' + get props() { + return { raw: 'foo' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/raw-mustaches-preserved/_config.js b/test/runtime/samples/raw-mustaches-preserved/_config.js index 9bd0afbb6b..d5c4a00641 100644 --- a/test/runtime/samples/raw-mustaches-preserved/_config.js +++ b/test/runtime/samples/raw-mustaches-preserved/_config.js @@ -1,8 +1,8 @@ export default { skip_if_ssr: true, - props: { - raw: '

does not change

' + get props() { + return { raw: '

does not change

' }; }, html: '

does not change

', diff --git a/test/runtime/samples/raw-mustaches-td-tr/_config.js b/test/runtime/samples/raw-mustaches-td-tr/_config.js index 3b165bb65e..c4cc857184 100644 --- a/test/runtime/samples/raw-mustaches-td-tr/_config.js +++ b/test/runtime/samples/raw-mustaches-td-tr/_config.js @@ -1,6 +1,6 @@ export default { - props: { - raw: '12' + get props() { + return { raw: '12' }; }, html: ` diff --git a/test/runtime/samples/raw-mustaches/_config.js b/test/runtime/samples/raw-mustaches/_config.js index 44087edcd8..1102068885 100644 --- a/test/runtime/samples/raw-mustaches/_config.js +++ b/test/runtime/samples/raw-mustaches/_config.js @@ -1,6 +1,8 @@ export default { - props: { - raw: 'raw html!!!\\o/' + get props() { + return { + raw: 'raw html!!!\\o/' + }; }, html: 'beforeraw html!!!\\o/after', diff --git a/test/runtime/samples/reactive-assignment-in-complex-declaration-with-store-3/store.js b/test/runtime/samples/reactive-assignment-in-complex-declaration-with-store-3/store.js index 3917a96b17..262665017e 100644 --- a/test/runtime/samples/reactive-assignment-in-complex-declaration-with-store-3/store.js +++ b/test/runtime/samples/reactive-assignment-in-complex-declaration-with-store-3/store.js @@ -1,4 +1,4 @@ -import { writable } from '../../../../store'; +import { writable } from 'svelte/store'; const _store = writable(0); let count = 0; diff --git a/test/runtime/samples/reactive-function-called-reassigned/_config.js b/test/runtime/samples/reactive-function-called-reassigned/_config.js index 3d0a241c4c..d653adcf97 100644 --- a/test/runtime/samples/reactive-function-called-reassigned/_config.js +++ b/test/runtime/samples/reactive-function-called-reassigned/_config.js @@ -6,8 +6,8 @@ function callback(_value) { } export default { - props: { - callback + get props() { + return { callback }; }, before_test() { called = 0; diff --git a/test/runtime/samples/reactive-import-statement/_config.js b/test/runtime/samples/reactive-import-statement/_config.js index 4635104464..1160919839 100644 --- a/test/runtime/samples/reactive-import-statement/_config.js +++ b/test/runtime/samples/reactive-import-statement/_config.js @@ -1,4 +1,4 @@ -import * as path from 'path'; +import { reset_numbers } from './data'; export default { html: ` @@ -9,7 +9,7 @@ export default { `, before_test() { - delete require.cache[path.resolve(__dirname, 'data.js')]; + reset_numbers(); }, async test({ assert, target, window }) { const btn = target.querySelector('button'); diff --git a/test/runtime/samples/reactive-import-statement/data.js b/test/runtime/samples/reactive-import-statement/data.js index 22f2f374ab..a45bb69310 100644 --- a/test/runtime/samples/reactive-import-statement/data.js +++ b/test/runtime/samples/reactive-import-statement/data.js @@ -1 +1,5 @@ -export const numbers = [1, 2, 3, 4]; +export let numbers = [1, 2, 3, 4]; + +export function reset_numbers() { + numbers = [1, 2, 3, 4]; +} diff --git a/test/runtime/samples/reactive-values-exported/_config.js b/test/runtime/samples/reactive-values-exported/_config.js index 9f782835f3..2a3ea59621 100644 --- a/test/runtime/samples/reactive-values-exported/_config.js +++ b/test/runtime/samples/reactive-values-exported/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 1 + get props() { + return { x: 1 }; }, html: ` diff --git a/test/runtime/samples/reactive-values-implicit-destructured/_config.js b/test/runtime/samples/reactive-values-implicit-destructured/_config.js index 45a5a8108b..7b05808c6a 100644 --- a/test/runtime/samples/reactive-values-implicit-destructured/_config.js +++ b/test/runtime/samples/reactive-values-implicit-destructured/_config.js @@ -1,7 +1,6 @@ export default { - props: { - coords: [0, 0], - numbers: { answer: 42 } + get props() { + return { coords: [0, 0], numbers: { answer: 42 } }; }, html: ` diff --git a/test/runtime/samples/reactive-values-non-cyclical/_config.js b/test/runtime/samples/reactive-values-non-cyclical/_config.js index 24b47df6d4..ad274547dd 100644 --- a/test/runtime/samples/reactive-values-non-cyclical/_config.js +++ b/test/runtime/samples/reactive-values-non-cyclical/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 42 + get props() { + return { x: 42 }; }, html: ` diff --git a/test/runtime/samples/renamed-instance-exports/_config.js b/test/runtime/samples/renamed-instance-exports/_config.js index 965df3b4f3..5bfba0d485 100644 --- a/test/runtime/samples/renamed-instance-exports/_config.js +++ b/test/runtime/samples/renamed-instance-exports/_config.js @@ -2,7 +2,9 @@ export default { test({ assert, component }) { assert.equal(component.bar1, 42); assert.equal(component.bar2, 42); - component.bar1 = 100; + assert.throws(() => { + component.bar1 = 100; + }, /Cannot set property bar1 of/); component.bar2 = 100; assert.equal(component.bar1, 42); assert.equal(component.bar2, 100); diff --git a/test/runtime/samples/select-bind-array/_config.js b/test/runtime/samples/select-bind-array/_config.js index 983b7c9f77..11be3bce34 100644 --- a/test/runtime/samples/select-bind-array/_config.js +++ b/test/runtime/samples/select-bind-array/_config.js @@ -3,9 +3,8 @@ const items = [{ id: 'a' }, { id: 'b' }]; export default { skip_if_ssr: true, - props: { - foo: 'b', - items + get props() { + return { foo: 'b', items }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/select-bind-in-array/_config.js b/test/runtime/samples/select-bind-in-array/_config.js index 5f4507899c..feba32d389 100644 --- a/test/runtime/samples/select-bind-in-array/_config.js +++ b/test/runtime/samples/select-bind-in-array/_config.js @@ -3,8 +3,8 @@ const items = [{ id: 'a' }, { id: 'b' }]; export default { skip_if_ssr: true, - props: { - items + get props() { + return { items }; }, test({ assert, component }) { diff --git a/test/runtime/samples/select-change-handler/_config.js b/test/runtime/samples/select-change-handler/_config.js index 3e5afdbf69..594d23c62d 100644 --- a/test/runtime/samples/select-change-handler/_config.js +++ b/test/runtime/samples/select-change-handler/_config.js @@ -1,7 +1,9 @@ export default { - props: { - options: [{ id: 'a' }, { id: 'b' }, { id: 'c' }], - selected: 'b' + get props() { + return { + options: [{ id: 'a' }, { id: 'b' }, { id: 'c' }], + selected: 'b' + }; }, test({ assert, component, target, window }) { diff --git a/test/runtime/samples/select-one-way-bind-object/_config.js b/test/runtime/samples/select-one-way-bind-object/_config.js index f789b0d0ff..55dbcd82d2 100644 --- a/test/runtime/samples/select-one-way-bind-object/_config.js +++ b/test/runtime/samples/select-one-way-bind-object/_config.js @@ -3,9 +3,8 @@ const items = [{}, {}]; export default { skip_if_ssr: true, - props: { - foo: items[0], - items + get props() { + return { foo: items[0], items }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/select-one-way-bind/_config.js b/test/runtime/samples/select-one-way-bind/_config.js index 5304e99dd6..fb7920c2a1 100644 --- a/test/runtime/samples/select-one-way-bind/_config.js +++ b/test/runtime/samples/select-one-way-bind/_config.js @@ -1,8 +1,8 @@ export default { skip_if_ssr: true, - props: { - foo: 'a' + get props() { + return { foo: 'a' }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/select/_config.js b/test/runtime/samples/select/_config.js index ccda56dab6..777288d534 100644 --- a/test/runtime/samples/select/_config.js +++ b/test/runtime/samples/select/_config.js @@ -1,9 +1,6 @@ export default { - props: { - item: { - name: 'One', - key: 'a' - } + get props() { + return { item: { name: 'One', key: 'a' } }; }, html: ` diff --git a/test/runtime/samples/self-reference-tree/_config.js b/test/runtime/samples/self-reference-tree/_config.js index cfa7528d31..1cebb70598 100644 --- a/test/runtime/samples/self-reference-tree/_config.js +++ b/test/runtime/samples/self-reference-tree/_config.js @@ -1,33 +1,23 @@ export default { - props: { - file: { - name: '/', - type: 'folder', - children: [ - { - name: 'foo.jpg', - type: 'image' - }, - { - name: 'bar.jpg', - type: 'image' - }, - { - name: 'baz', - type: 'folder', - children: [ - { - name: '.DS_Store', - type: 'junk' - }, - { - name: 'README.md', - type: 'markdown' - } - ] - } - ] - } + get props() { + return { + file: { + name: '/', + type: 'folder', + children: [ + { name: 'foo.jpg', type: 'image' }, + { name: 'bar.jpg', type: 'image' }, + { + name: 'baz', + type: 'folder', + children: [ + { name: '.DS_Store', type: 'junk' }, + { name: 'README.md', type: 'markdown' } + ] + } + ] + } + }; }, html: ` diff --git a/test/runtime/samples/self-reference/_config.js b/test/runtime/samples/self-reference/_config.js index 1bcb660621..d8e5ce66d4 100644 --- a/test/runtime/samples/self-reference/_config.js +++ b/test/runtime/samples/self-reference/_config.js @@ -1,6 +1,6 @@ export default { - props: { - depth: 5 + get props() { + return { depth: 5 }; }, html: ` diff --git a/test/runtime/samples/set-after-destroy/_config.js b/test/runtime/samples/set-after-destroy/_config.js index 3411f0cc79..1f13e95c21 100644 --- a/test/runtime/samples/set-after-destroy/_config.js +++ b/test/runtime/samples/set-after-destroy/_config.js @@ -1,6 +1,6 @@ export default { - props: { - x: 1 + get props() { + return { x: 1 }; }, test({ component }) { diff --git a/test/runtime/samples/set-null-text-node/_config.js b/test/runtime/samples/set-null-text-node/_config.js index a106ee9069..1dbfc72631 100644 --- a/test/runtime/samples/set-null-text-node/_config.js +++ b/test/runtime/samples/set-null-text-node/_config.js @@ -1,5 +1,7 @@ export default { - props: { foo: null }, + get props() { + return { foo: null }; + }, html: 'foo is null', diff --git a/test/runtime/samples/sigil-component-prop/_config.js b/test/runtime/samples/sigil-component-prop/_config.js index 105f8b468c..4cc6131cdb 100644 --- a/test/runtime/samples/sigil-component-prop/_config.js +++ b/test/runtime/samples/sigil-component-prop/_config.js @@ -2,6 +2,8 @@ export default { compileOptions: { dev: true }, - props: { foo: 'foo' }, + get props() { + return { foo: 'foo' }; + }, html: '
foo @ foo # foo
' }; diff --git a/test/runtime/samples/sigil-static-@/_config.js b/test/runtime/samples/sigil-static-at/_config.js similarity index 100% rename from test/runtime/samples/sigil-static-@/_config.js rename to test/runtime/samples/sigil-static-at/_config.js diff --git a/test/runtime/samples/sigil-static-@/main.svelte b/test/runtime/samples/sigil-static-at/main.svelte similarity index 100% rename from test/runtime/samples/sigil-static-@/main.svelte rename to test/runtime/samples/sigil-static-at/main.svelte diff --git a/test/runtime/samples/sigil-static-#/_config.js b/test/runtime/samples/sigil-static-hash/_config.js similarity index 100% rename from test/runtime/samples/sigil-static-#/_config.js rename to test/runtime/samples/sigil-static-hash/_config.js diff --git a/test/runtime/samples/sigil-static-#/main.svelte b/test/runtime/samples/sigil-static-hash/main.svelte similarity index 100% rename from test/runtime/samples/sigil-static-#/main.svelte rename to test/runtime/samples/sigil-static-hash/main.svelte diff --git a/test/runtime/samples/spread-component-2/_config.js b/test/runtime/samples/spread-component-2/_config.js index ed961f8fc5..9b1d50a765 100644 --- a/test/runtime/samples/spread-component-2/_config.js +++ b/test/runtime/samples/spread-component-2/_config.js @@ -1,19 +1,21 @@ export default { - props: { - list: [ - { - foo: 'lol', - baz: 40 + 2, - qux: 5, - quux: 'core' - }, - { - foo: 'lolzz', - baz: 50 + 2, - qux: 1, - quux: 'quuxx' - } - ] + get props() { + return { + list: [ + { + foo: 'lol', + baz: 40 + 2, + qux: 5, + quux: 'core' + }, + { + foo: 'lolzz', + baz: 50 + 2, + qux: 1, + quux: 'quuxx' + } + ] + }; }, html: ` diff --git a/test/runtime/samples/spread-component-dynamic-non-object-multiple-dependencies/_config.js b/test/runtime/samples/spread-component-dynamic-non-object-multiple-dependencies/_config.js index 465376f901..5c42c36f91 100644 --- a/test/runtime/samples/spread-component-dynamic-non-object-multiple-dependencies/_config.js +++ b/test/runtime/samples/spread-component-dynamic-non-object-multiple-dependencies/_config.js @@ -1,9 +1,6 @@ export default { - props: { - props: { - foo: 'lol', - baz: 40 + 2 - } + get props() { + return { props: { foo: 'lol', baz: 40 + 2 } }; }, html: ` diff --git a/test/runtime/samples/spread-component-dynamic-non-object/_config.js b/test/runtime/samples/spread-component-dynamic-non-object/_config.js index 45aa610de2..e772649533 100644 --- a/test/runtime/samples/spread-component-dynamic-non-object/_config.js +++ b/test/runtime/samples/spread-component-dynamic-non-object/_config.js @@ -1,9 +1,6 @@ export default { - props: { - props: { - foo: 'lol', - baz: 40 + 2 - } + get props() { + return { props: { foo: 'lol', baz: 40 + 2 } }; }, html: ` diff --git a/test/runtime/samples/spread-component-dynamic-undefined/_config.js b/test/runtime/samples/spread-component-dynamic-undefined/_config.js index ba41df3e54..5a96b04c2e 100644 --- a/test/runtime/samples/spread-component-dynamic-undefined/_config.js +++ b/test/runtime/samples/spread-component-dynamic-undefined/_config.js @@ -1,8 +1,6 @@ export default { - props: { - props: { - a: 1 - } + get props() { + return { props: { a: 1 } }; }, html: '', diff --git a/test/runtime/samples/spread-component-dynamic/_config.js b/test/runtime/samples/spread-component-dynamic/_config.js index 7992a30514..270d8acd47 100644 --- a/test/runtime/samples/spread-component-dynamic/_config.js +++ b/test/runtime/samples/spread-component-dynamic/_config.js @@ -1,8 +1,6 @@ export default { - props: { - props: { - a: 1 - } + get props() { + return { props: { a: 1 } }; }, html: ` diff --git a/test/runtime/samples/spread-component-immutable/_config.js b/test/runtime/samples/spread-component-immutable/_config.js index 634fac24c1..56a564f0bf 100644 --- a/test/runtime/samples/spread-component-immutable/_config.js +++ b/test/runtime/samples/spread-component-immutable/_config.js @@ -5,8 +5,8 @@ const obj = { }; export default { - props: { - obj + get props() { + return { obj }; }, test({ assert }) { diff --git a/test/runtime/samples/spread-component-side-effects/_config.js b/test/runtime/samples/spread-component-side-effects/_config.js index 3c1c607c91..6832650c85 100644 --- a/test/runtime/samples/spread-component-side-effects/_config.js +++ b/test/runtime/samples/spread-component-side-effects/_config.js @@ -1,5 +1,7 @@ export default { - props: {}, + get props() { + return {}; + }, html: `

i: 1

diff --git a/test/runtime/samples/spread-component/_config.js b/test/runtime/samples/spread-component/_config.js index 537860f72c..6e0da44578 100644 --- a/test/runtime/samples/spread-component/_config.js +++ b/test/runtime/samples/spread-component/_config.js @@ -1,11 +1,13 @@ export default { - props: { - props: { - foo: 'lol', - baz: 40 + 2, - qux: `this is a ${'piece of'} string`, - quux: 'core' - } + get props() { + return { + props: { + foo: 'lol', + baz: 40 + 2, + qux: `this is a ${'piece of'} string`, + quux: 'core' + } + }; }, html: ` diff --git a/test/runtime/samples/spread-each-component/_config.js b/test/runtime/samples/spread-each-component/_config.js index 8a484699d0..7090a0038e 100644 --- a/test/runtime/samples/spread-each-component/_config.js +++ b/test/runtime/samples/spread-each-component/_config.js @@ -4,11 +4,13 @@ export default {
`, - props: { - things: [ - { a: 1, b: 2 }, - { a: 3, b: 4 } - ] + get props() { + return { + things: [ + { a: 1, b: 2 }, + { a: 3, b: 4 } + ] + }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/spread-each-element/_config.js b/test/runtime/samples/spread-each-element/_config.js index ccc7a58114..da851efebb 100644 --- a/test/runtime/samples/spread-each-element/_config.js +++ b/test/runtime/samples/spread-each-element/_config.js @@ -4,11 +4,13 @@ export default {
`, - props: { - things: [ - { 'data-a': 1, 'data-b': 2 }, - { 'data-c': 3, 'data-d': 4 } - ] + get props() { + return { + things: [ + { 'data-a': 1, 'data-b': 2 }, + { 'data-c': 3, 'data-d': 4 } + ] + }; }, test({ assert, component, target }) { diff --git a/test/runtime/samples/spread-element-boolean/_config.js b/test/runtime/samples/spread-element-boolean/_config.js index ce1849be3f..f5893d234e 100644 --- a/test/runtime/samples/spread-element-boolean/_config.js +++ b/test/runtime/samples/spread-element-boolean/_config.js @@ -1,8 +1,6 @@ export default { - props: { - props: { - disabled: true - } + get props() { + return { props: { disabled: true } }; }, html: ` diff --git a/test/runtime/samples/spread-element-input-bind-group-with-value-attr/_config.js b/test/runtime/samples/spread-element-input-bind-group-with-value-attr/_config.js index 5176fb976c..589aeb3ad6 100644 --- a/test/runtime/samples/spread-element-input-bind-group-with-value-attr/_config.js +++ b/test/runtime/samples/spread-element-input-bind-group-with-value-attr/_config.js @@ -1,8 +1,6 @@ export default { - props: { - props: { - 'data-foo': 'bar' - } + get props() { + return { props: { 'data-foo': 'bar' } }; }, html: '', diff --git a/test/runtime/samples/spread-element-input/_config.js b/test/runtime/samples/spread-element-input/_config.js index 2e68e9832f..c25c5e9724 100644 --- a/test/runtime/samples/spread-element-input/_config.js +++ b/test/runtime/samples/spread-element-input/_config.js @@ -1,8 +1,6 @@ export default { - props: { - props: { - 'data-foo': 'bar' - } + get props() { + return { props: { 'data-foo': 'bar' } }; }, html: '' diff --git a/test/runtime/samples/spread-element-multiple/_config.js b/test/runtime/samples/spread-element-multiple/_config.js index 38afe2fe8e..e67cf2b937 100644 --- a/test/runtime/samples/spread-element-multiple/_config.js +++ b/test/runtime/samples/spread-element-multiple/_config.js @@ -1,13 +1,10 @@ export default { - props: { - a: { - 'data-one': 1, - 'data-two': 2 - }, - c: { - 'data-b': 'overridden' - }, - d: 'deeeeee' + get props() { + return { + a: { 'data-one': 1, 'data-two': 2 }, + c: { 'data-b': 'overridden' }, + d: 'deeeeee' + }; }, html: ` diff --git a/test/runtime/samples/spread-own-props/_config.js b/test/runtime/samples/spread-own-props/_config.js index 17ddec6546..fe8cafa21d 100644 --- a/test/runtime/samples/spread-own-props/_config.js +++ b/test/runtime/samples/spread-own-props/_config.js @@ -1,9 +1,11 @@ export default { - props: { - foo: 'lol', - baz: 40 + 2, - qux: `this is a ${'piece of'} string`, - quux: 'core' + get props() { + return { + foo: 'lol', + baz: 40 + 2, + qux: `this is a ${'piece of'} string`, + quux: 'core' + }; }, html: ` diff --git a/test/runtime/samples/state-deconflicted/_config.js b/test/runtime/samples/state-deconflicted/_config.js index 1064b8811e..1039c3f470 100644 --- a/test/runtime/samples/state-deconflicted/_config.js +++ b/test/runtime/samples/state-deconflicted/_config.js @@ -1,7 +1,9 @@ export default { - props: { - state: 'deconflicted', - states: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', '...and some others'] + get props() { + return { + state: 'deconflicted', + states: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', '...and some others'] + }; }, html: ` diff --git a/test/runtime/samples/store-assignment-updates-property/main.svelte b/test/runtime/samples/store-assignment-updates-property/main.svelte index c3196a278c..b2c4a3d1c0 100644 --- a/test/runtime/samples/store-assignment-updates-property/main.svelte +++ b/test/runtime/samples/store-assignment-updates-property/main.svelte @@ -1,5 +1,5 @@ diff --git a/test/runtime/samples/store-auto-resubscribe-immediate/main.svelte b/test/runtime/samples/store-auto-resubscribe-immediate/main.svelte index 8dd40ae7ae..3bcab5674a 100644 --- a/test/runtime/samples/store-auto-resubscribe-immediate/main.svelte +++ b/test/runtime/samples/store-auto-resubscribe-immediate/main.svelte @@ -1,5 +1,5 @@ - +
{width}x{height}
\ No newline at end of file diff --git a/test/server-side-rendering/index.js b/test/server-side-rendering/index.js deleted file mode 100644 index 1351bb79d9..0000000000 --- a/test/server-side-rendering/index.js +++ /dev/null @@ -1,247 +0,0 @@ -import * as fs from 'fs'; -import * as path from 'path'; -import glob from 'tiny-glob/sync.js'; - -import { - assert, - showOutput, - loadConfig, - loadSvelte, - setupHtmlEqual, - tryToLoadJson, - cleanRequireCache, - shouldUpdateExpected, - mkdirp -} from '../helpers'; -import { set_current_component } from '../../internal'; - -function tryToReadFile(file) { - try { - return fs.readFileSync(file, 'utf-8'); - } catch (err) { - if (err.code !== 'ENOENT') throw err; - return null; - } -} - -const sveltePath = process.cwd().split('\\').join('/'); -let compile = null; - -describe('ssr', () => { - before(() => { - compile = loadSvelte(true).compile; - - return setupHtmlEqual(); - }); - - let saved_window; - before(() => (saved_window = global.window)); - after(() => (global.window = saved_window)); - - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - - // add .solo to a sample directory name to only run that test, or - // .show to always show the output. or both - const solo = config.solo || /\.solo/.test(dir); - const show = /\.show/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - (solo ? it.only : it)(dir, (done) => { - try { - dir = path.resolve(`${__dirname}/samples`, dir); - - cleanRequireCache(); - - const compileOptions = { - sveltePath, - ...config.compileOptions, - generate: 'ssr', - format: 'cjs' - }; - - require('../../register.js')(compileOptions); - - const Component = require(`${dir}/main.svelte`).default; - - const expectedHtml = tryToReadFile(`${dir}/_expected.html`); - const expectedCss = tryToReadFile(`${dir}/_expected.css`) || ''; - - const props = tryToLoadJson(`${dir}/data.json`) || undefined; - - const rendered = Component.render(props); - const { html, css, head } = rendered; - - fs.writeFileSync(`${dir}/_actual.html`, html); - if (css.code) fs.writeFileSync(`${dir}/_actual.css`, css.code); - - try { - assert.htmlEqualWithOptions(html, expectedHtml, { - preserveComments: compileOptions.preserveComments, - withoutNormalizeHtml: config.withoutNormalizeHtml - }); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(`${dir}/_expected.html`, html); - console.log(`Updated ${dir}/_expected.html.`); - } else { - throw error; - } - } - - try { - assert.equal( - css.code.replace(/^\s+/gm, '').replace(/[\r\n]/g, ''), - expectedCss.replace(/^\s+/gm, '').replace(/[\r\n]/g, '') - ); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(`${dir}/_expected.css`, css.code); - console.log(`Updated ${dir}/_expected.css.`); - } else { - throw error; - } - } - - if (fs.existsSync(`${dir}/_expected-head.html`)) { - fs.writeFileSync(`${dir}/_actual-head.html`, head); - - try { - assert.htmlEqualWithOptions( - head, - fs.readFileSync(`${dir}/_expected-head.html`, 'utf-8'), - { preserveComments: compileOptions.hydratable } - ); - } catch (error) { - if (shouldUpdateExpected()) { - fs.writeFileSync(`${dir}/_expected-head.html`, head); - console.log(`Updated ${dir}/_expected-head.html.`); - } else { - throw error; - } - } - } - - if (show) showOutput(dir, { generate: 'ssr', format: 'cjs' }); - done(); - } catch (err) { - showOutput(dir, { generate: 'ssr', format: 'cjs' }); - err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), dir)}/main.svelte`; - done(err); - } finally { - set_current_component(null); - } - }); - }); - - // duplicate client-side tests, as far as possible - runRuntimeSamples('runtime'); - runRuntimeSamples('runtime-browser'); - - function runRuntimeSamples(suite) { - fs.readdirSync(`test/${suite}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - const config = loadConfig(`./${suite}/samples/${dir}/_config.js`); - const solo = config.solo || /\.solo/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - if (config.skip_if_ssr) return; - - (config.skip ? it.skip : solo ? it.only : it)(dir, () => { - const cwd = path.resolve(`test/${suite}/samples`, dir); - - cleanRequireCache(); - - delete global.window; - - const compileOptions = { - sveltePath, - ...config.compileOptions, - generate: 'ssr', - format: 'cjs' - }; - - require('../../register')(compileOptions); - - glob('**/*.svelte', { cwd }).forEach((file) => { - if (file[0] === '_') return; - - const dir = `${cwd}/_output/ssr`; - const out = `${dir}/${file.replace(/\.svelte$/, '.js')}`; - - if (fs.existsSync(out)) { - fs.unlinkSync(out); - } - - mkdirp(dir); - - try { - const { js } = compile(fs.readFileSync(`${cwd}/${file}`, 'utf-8'), { - ...compileOptions, - filename: file - }); - - fs.writeFileSync(out, js.code); - } catch (err) { - // do nothing - } - }); - - try { - if (config.before_test) config.before_test(); - - const Component = require(`../${suite}/samples/${dir}/main.svelte`).default; - const { html } = Component.render(config.props, { - store: config.store !== true && config.store - }); - - if (config.ssrHtml) { - assert.htmlEqualWithOptions(html, config.ssrHtml, { - preserveComments: compileOptions.preserveComments, - withoutNormalizeHtml: config.withoutNormalizeHtml - }); - } else if (config.html) { - assert.htmlEqualWithOptions(html, config.html, { - preserveComments: compileOptions.preserveComments, - withoutNormalizeHtml: config.withoutNormalizeHtml - }); - } - - if (config.test_ssr) { - config.test_ssr({ assert }); - } - - if (config.after_test) config.after_test(); - - if (config.show) { - showOutput(cwd, compileOptions); - } - } catch (err) { - err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`; - - if (config.error) { - if (typeof config.error === 'function') { - config.error(assert, err); - } else { - assert.equal(err.message, config.error); - } - } else { - showOutput(cwd, compileOptions); - throw err; - } - } finally { - set_current_component(null); - } - }); - }); - } -}); diff --git a/test/server-side-rendering/ssr-1.test.js b/test/server-side-rendering/ssr-1.test.js new file mode 100644 index 0000000000..c0b668fd9e --- /dev/null +++ b/test/server-side-rendering/ssr-1.test.js @@ -0,0 +1,103 @@ +// @vitest-environment jsdom + +// Yes it's an SSR test, but we need the env to compare html +// TODO: Isolate the html comparison +// TODO: happy-dom might be faster but currently replaces quotes which fails assertions + +import * as fs from 'fs'; +import * as path from 'path'; +import { assert, describe, it } from 'vitest'; +import { + create_loader, + should_update_expected, + try_load_config, + try_load_json, + try_read_file +} from '../helpers.js'; +import { assert_html_equal } from '../html_equal.js'; + +describe('ssr', async () => { + async function run_test(dir) { + if (dir[0] === '.') return; + + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); + + const solo = config.solo || /\.solo/.test(dir); + const it_fn = solo ? it.only : it; + + it_fn(dir, async () => { + dir = path.resolve(`${__dirname}/samples`, dir); + + const compileOptions = { + ...config.compileOptions, + generate: 'ssr', + format: 'cjs' + }; + + const load = create_loader(compileOptions, dir); + + const Component = (await load(`${dir}/main.svelte`)).default; + + const expectedHtml = try_read_file(`${dir}/_expected.html`); + const expectedCss = try_read_file(`${dir}/_expected.css`) || ''; + + const props = try_load_json(`${dir}/data.json`) || undefined; + + const rendered = Component.render(props); + const { html, css, head } = rendered; + + fs.writeFileSync(`${dir}/_actual.html`, html); + if (css.code) fs.writeFileSync(`${dir}/_actual.css`, css.code); + + try { + assert_html_equal(html, expectedHtml, { + normalize_html: { preserveComments: compileOptions.preserveComments }, + without_normalize: config.withoutNormalizeHtml + }); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(`${dir}/_expected.html`, html); + console.log(`Updated ${dir}/_expected.html.`); + } else { + error.message += '\n' + `${dir}/main.svelte`; + throw error; + } + } + + try { + assert.equal( + css.code.trim().replace(/[\r\n]/g, ''), + expectedCss.trim().replace(/[\r\n]/g, '') + ); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(`${dir}/_expected.css`, css.code); + console.log(`Updated ${dir}/_expected.css.`); + } else { + error.message += '\n' + `${dir}/main.svelte`; + throw error; + } + } + + if (fs.existsSync(`${dir}/_expected-head.html`)) { + fs.writeFileSync(`${dir}/_actual-head.html`, head); + + try { + assert_html_equal(head, fs.readFileSync(`${dir}/_expected-head.html`, 'utf-8'), { + normalize_html: { preserveComments: compileOptions.hydratable } + }); + } catch (error) { + if (should_update_expected()) { + fs.writeFileSync(`${dir}/_expected-head.html`, head); + console.log(`Updated ${dir}/_expected-head.html.`); + error.message += '\n' + `${dir}/main.svelte`; + } else { + throw error; + } + } + } + }); + } + + await Promise.all(fs.readdirSync(`${__dirname}/samples`).map(run_test)); +}); diff --git a/test/server-side-rendering/ssr-2.test.js b/test/server-side-rendering/ssr-2.test.js new file mode 100644 index 0000000000..b33b4a94b0 --- /dev/null +++ b/test/server-side-rendering/ssr-2.test.js @@ -0,0 +1,118 @@ +// @vitest-environment jsdom + +import * as path from 'path'; +import { describe, it, assert } from 'vitest'; +import * as fs from 'fs'; +import { try_load_config, mkdirp, create_loader } from '../helpers.js'; +import { assert_html_equal } from '../html_equal'; +import glob from 'tiny-glob/sync'; +import { setTimeout } from 'timers/promises'; + +// duplicate client-side tests, as far as possible +run_runtime_samples('runtime'); +run_runtime_samples('runtime-browser'); + +function run_runtime_samples(suite) { + const samples = path.resolve(__dirname, '..', suite, 'samples'); + + describe(`ssr: ${suite}`, async () => { + await Promise.all(fs.readdirSync(samples).map(run_test)); + }); + + async function run_test(dir) { + if (dir[0] === '.') return; + + const cwd = path.resolve(samples, dir); + const config = await try_load_config(`${cwd}/_config.js`); + const solo = config.solo || /\.solo/.test(dir); + + if (config.skip_if_ssr) return; + const it_fn = config.skip ? it.skip : solo ? it.only : it; + + it_fn(dir, async () => { + const compileOptions = { + ...config.compileOptions, + generate: 'ssr', + format: 'cjs' + }; + + const load = create_loader(compileOptions, cwd); + + glob('**/*.svelte', { cwd: cwd }).forEach((file) => { + if (file[0] === '_') return; + + const dir = `${cwd}/_output/ssr`; + const out = `${cwd}/${file.replace(/\.svelte$/, '.js')}`; + + if (fs.existsSync(out)) { + fs.unlinkSync(out); + } + + mkdirp(dir); + + try { + const { js } = compile(fs.readFileSync(`${cwd}/${file}`, 'utf-8'), { + ...compileOptions, + filename: file + }); + + fs.writeFileSync(out, js.code); + } catch (err) { + // do nothing + } + }); + + try { + if (config.before_test) config.before_test(); + + const Component = (await load(`${cwd}/main.svelte`)).default; + const { html } = Component.render(config.props, { + store: config.store !== true && config.store + }); + + if (config.ssrHtml) { + assert_html_equal(html, config.ssrHtml, { + normalize_html: { + preserveComments: compileOptions.preserveComments, + withoutNormalizeHtml: config.withoutNormalizeHtml + } + }); + } else if (config.html) { + assert_html_equal(html, config.html, { + normalize_html: { + preserveComments: compileOptions.preserveComments, + withoutNormalizeHtml: config.withoutNormalizeHtml + } + }); + } + + if (config.test_ssr) { + await config.test_ssr({ + assert: { + ...assert, + htmlEqual: assert_html_equal + }, + load + }); + } + + if (config.after_test) config.after_test(); + } catch (err) { + err.stack += `\n\ncmd-click: ${path.relative(process.cwd(), cwd)}/main.svelte`; + + if (config.error) { + if (typeof config.error === 'function') { + config.error(assert, err); + } else { + assert.equal(err.message, config.error); + } + } else { + throw err; + } + } + + // wait for vitest to report progress + await setTimeout(10); + }); + } +} diff --git a/test/setup.js b/test/setup.js deleted file mode 100644 index b36153c159..0000000000 --- a/test/setup.js +++ /dev/null @@ -1,50 +0,0 @@ -const fs = require('fs'); - -require('source-map-support').install(); - -process.env.TEST = 'true'; - -require.extensions['.js'] = function (module, filename) { - const exports = []; - - let code = fs - .readFileSync(filename, 'utf-8') - .replace(/^import \* as (\S+) from ['"]([^'"]+)['"];?/gm, 'var $1 = require("$2");') - .replace(/^import {([^}]+)} from ['"](.+)['"];?/gm, 'var {$1} = require("$2");') - .replace( - /^import (\S+),\s+?{([^}]+)} from ['"](.+)['"];?/gm, - 'var {default: $1, $2} = require("$3");' - ) - .replace(/^import (\S+) from ['"]([./][^'"]+)['"];?/gm, 'var {default: $1} = require("$2");') - .replace(/^import (\S+) from ['"]([^'"]+)['"];?/gm, 'var $1 = require("$2");') - .replace(/^export default /gm, 'exports.default = ') - .replace( - /^export (const|let|var|class|function|async\s+function) (\w+)/gm, - (match, type, name) => { - exports.push(name); - return `${type} ${name}`; - } - ) - .replace(/^export \{([^}]+)\}(?: from ['"]([^'"]+)['"];?)?/gm, (match, names, source) => { - names - .split(',') - .filter(Boolean) - .forEach((name) => { - exports.push(name); - }); - - return source ? `const { ${names} } = require("${source}");` : ''; - }) - .replace(/^export function (\w+)/gm, 'exports.$1 = function $1'); - - exports.forEach((name) => { - code += `\nexports.${name} = ${name};`; - }); - - try { - return module._compile(code, filename); - } catch (err) { - console.log(code); // eslint-disable-line no-console - throw err; - } -}; diff --git a/test/sourcemaps/samples/compile-option-dev/_config.js b/test/sourcemaps/samples/compile-option-dev/_config.js index cd353dda40..886030acfe 100644 --- a/test/sourcemaps/samples/compile-option-dev/_config.js +++ b/test/sourcemaps/samples/compile-option-dev/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { compile_options: { diff --git a/test/sourcemaps/samples/compile-option-dev/test.js b/test/sourcemaps/samples/compile-option-dev/test.js index 0dbcaf56b8..f0425495d5 100644 --- a/test/sourcemaps/samples/compile-option-dev/test.js +++ b/test/sourcemaps/samples/compile-option-dev/test.js @@ -7,6 +7,7 @@ export async function test({ assert, css, js }) { const match = js.code.match( /\tappend_styles\(target, "svelte-.{6}", "(.*?)(?:\\n\/\*# sourceMappingURL=data:(.*?);charset=(.*?);base64,(.*?) \*\/)?"\);\n/ ); + assert.notEqual(match, null); const [mimeType, encoding, cssMapBase64] = match.slice(2); diff --git a/test/sourcemaps/samples/decoded-sourcemap/_config.js b/test/sourcemaps/samples/decoded-sourcemap/_config.js index 3af6b4a2b1..89e1bc5273 100644 --- a/test/sourcemaps/samples/decoded-sourcemap/_config.js +++ b/test/sourcemaps/samples/decoded-sourcemap/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { js_map_sources: ['input.svelte'], diff --git a/test/sourcemaps/samples/external/_config.js b/test/sourcemaps/samples/external/_config.js index 07a0903482..8738d5cc07 100644 --- a/test/sourcemaps/samples/external/_config.js +++ b/test/sourcemaps/samples/external/_config.js @@ -1,4 +1,4 @@ -import { magic_string_bundle } from '../../helpers'; +import { magic_string_bundle } from '../../helpers.js'; export const COMMON = ':global(html) { height: 100%; }\n'; diff --git a/test/sourcemaps/samples/external/test.js b/test/sourcemaps/samples/external/test.js index 5193b62ba1..b4b14b7cf5 100644 --- a/test/sourcemaps/samples/external/test.js +++ b/test/sourcemaps/samples/external/test.js @@ -1,4 +1,4 @@ -import { assert_mapped } from '../../helpers'; +import { assert_mapped } from '../../helpers.js'; import { COMMON, STYLES } from './_config'; export function test({ input, preprocessed }) { diff --git a/test/sourcemaps/samples/preprocessed-markup/_config.js b/test/sourcemaps/samples/preprocessed-markup/_config.js index 40e73d7f9b..d08411da48 100644 --- a/test/sourcemaps/samples/preprocessed-markup/_config.js +++ b/test/sourcemaps/samples/preprocessed-markup/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { preprocess: { diff --git a/test/sourcemaps/samples/preprocessed-multiple/_config.js b/test/sourcemaps/samples/preprocessed-multiple/_config.js index 39259a02a8..fa2506bf3e 100644 --- a/test/sourcemaps/samples/preprocessed-multiple/_config.js +++ b/test/sourcemaps/samples/preprocessed-multiple/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { preprocess: { diff --git a/test/sourcemaps/samples/preprocessed-no-map/test.js b/test/sourcemaps/samples/preprocessed-no-map/test.js index ef1aba1630..167e5872b7 100644 --- a/test/sourcemaps/samples/preprocessed-no-map/test.js +++ b/test/sourcemaps/samples/preprocessed-no-map/test.js @@ -1,4 +1,4 @@ -import { assert_mapped, assert_not_mapped } from '../../helpers'; +import { assert_mapped, assert_not_mapped } from '../../helpers.js'; export function test({ input, preprocessed }) { // markup (start) diff --git a/test/sourcemaps/samples/preprocessed-script/_config.js b/test/sourcemaps/samples/preprocessed-script/_config.js index 8e129d06a6..0c4455bc5f 100644 --- a/test/sourcemaps/samples/preprocessed-script/_config.js +++ b/test/sourcemaps/samples/preprocessed-script/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { preprocess: { diff --git a/test/sourcemaps/samples/preprocessed-styles/_config.js b/test/sourcemaps/samples/preprocessed-styles/_config.js index d1d681bea7..2851912e80 100644 --- a/test/sourcemaps/samples/preprocessed-styles/_config.js +++ b/test/sourcemaps/samples/preprocessed-styles/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { preprocess: { diff --git a/test/sourcemaps/samples/sourcemap-basename-without-outputname/_config.js b/test/sourcemaps/samples/sourcemap-basename-without-outputname/_config.js index d1656d0680..2f345d3661 100644 --- a/test/sourcemaps/samples/sourcemap-basename-without-outputname/_config.js +++ b/test/sourcemaps/samples/sourcemap-basename-without-outputname/_config.js @@ -1,4 +1,4 @@ -import { magic_string_bundle } from '../../helpers'; +import { magic_string_bundle } from '../../helpers.js'; export const component_filepath = 'src/some/deep/path/input.svelte'; export const component_file_basename = 'input.svelte'; diff --git a/test/sourcemaps/samples/sourcemap-basename/_config.js b/test/sourcemaps/samples/sourcemap-basename/_config.js index 7692b7a6d9..69c87e7a58 100644 --- a/test/sourcemaps/samples/sourcemap-basename/_config.js +++ b/test/sourcemaps/samples/sourcemap-basename/_config.js @@ -1,4 +1,4 @@ -import { magic_string_bundle } from '../../helpers'; +import { magic_string_bundle } from '../../helpers.js'; export const component_filepath = 'src/input.svelte'; diff --git a/test/sourcemaps/samples/sourcemap-concat/_config.js b/test/sourcemaps/samples/sourcemap-concat/_config.js index 835b3c9e48..6790adcec3 100644 --- a/test/sourcemaps/samples/sourcemap-concat/_config.js +++ b/test/sourcemaps/samples/sourcemap-concat/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result } from '../../helpers'; +import { magic_string_preprocessor_result } from '../../helpers.js'; export default { js_map_sources: ['input.svelte'], diff --git a/test/sourcemaps/samples/sourcemap-concat/test.js b/test/sourcemaps/samples/sourcemap-concat/test.js index c68d7175dc..889b290b15 100644 --- a/test/sourcemaps/samples/sourcemap-concat/test.js +++ b/test/sourcemaps/samples/sourcemap-concat/test.js @@ -1,4 +1,4 @@ -import { assert_mapped } from '../../helpers'; +import { assert_mapped } from '../../helpers.js'; export function test({ input, preprocessed }) { assert_mapped({ diff --git a/test/sourcemaps/samples/sourcemap-names/_config.js b/test/sourcemaps/samples/sourcemap-names/_config.js index 5d2207d8c3..1d56d050a7 100644 --- a/test/sourcemaps/samples/sourcemap-names/_config.js +++ b/test/sourcemaps/samples/sourcemap-names/_config.js @@ -1,5 +1,5 @@ import MagicString from 'magic-string'; -import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers'; +import { magic_string_preprocessor_result, magic_string_replace_all } from '../../helpers.js'; export default { preprocess: [ diff --git a/test/sourcemaps/samples/sourcemap-offsets/_config.js b/test/sourcemaps/samples/sourcemap-offsets/_config.js index bbc24c3225..2f0bcdd6cd 100644 --- a/test/sourcemaps/samples/sourcemap-offsets/_config.js +++ b/test/sourcemaps/samples/sourcemap-offsets/_config.js @@ -1,4 +1,4 @@ -import { magic_string_bundle } from '../../helpers'; +import { magic_string_bundle } from '../../helpers.js'; export const EXTERNAL = 'span { --external-var: 1px; }'; diff --git a/test/sourcemaps/samples/sourcemap-offsets/test.js b/test/sourcemaps/samples/sourcemap-offsets/test.js index ccfa34e945..138fc10b57 100644 --- a/test/sourcemaps/samples/sourcemap-offsets/test.js +++ b/test/sourcemaps/samples/sourcemap-offsets/test.js @@ -1,4 +1,4 @@ -import { assert_mapped } from '../../helpers'; +import { assert_mapped } from '../../helpers.js'; import { EXTERNAL } from './_config'; export function test({ input, preprocessed }) { diff --git a/test/sourcemaps/samples/typescript/test.js b/test/sourcemaps/samples/typescript/test.js index 7af24b6e3b..a1ff5c350d 100644 --- a/test/sourcemaps/samples/typescript/test.js +++ b/test/sourcemaps/samples/typescript/test.js @@ -1,4 +1,4 @@ -import { assert_mapped, assert_not_located } from '../../helpers'; +import { assert_mapped, assert_not_located } from '../../helpers.js'; export function test({ input, preprocessed }) { // TS => JS code diff --git a/test/sourcemaps/index.js b/test/sourcemaps/sourcemaps.test.js similarity index 86% rename from test/sourcemaps/index.js rename to test/sourcemaps/sourcemaps.test.js index 5a4235366d..4fd8ad8033 100644 --- a/test/sourcemaps/index.js +++ b/test/sourcemaps/sourcemaps.test.js @@ -1,28 +1,30 @@ import * as fs from 'fs'; import * as path from 'path'; -import * as assert from 'assert'; -import { loadConfig, svelte } from '../helpers'; +import * as svelte from '../../compiler.js'; +import { try_load_config } from '../helpers'; +import { describe, assert, it } from 'vitest'; // keep source-map at version 0.7.x // https://github.com/mozilla/source-map/issues/400 import { getLocator } from 'locate-character'; import { SourceMapConsumer } from 'source-map'; -describe('sourcemaps', () => { - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { +describe('sourcemaps', async () => { + await Promise.all(fs.readdirSync(`${__dirname}/samples`).map((dir) => run(dir))); + + async function run(dir) { if (dir[0] === '.') return; - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); // add .solo to a sample directory name to only run that test const solo = config.solo || /\.solo/.test(dir); const skip = config.skip || /\.skip/.test(dir); - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } + const it_fn = solo ? it.only : skip ? it.skip : it; + + it_fn(dir, async () => { + const { test } = await import(`./samples/${dir}/test.js`); - (solo ? it.only : skip ? it.skip : it)(dir, async () => { - const { test } = require(`./samples/${dir}/test.js`); const inputFile = path.resolve(`${__dirname}/samples/${dir}/input.svelte`); const outputName = '_actual'; const outputBase = path.resolve(`${__dirname}/samples/${dir}/${outputName}`); @@ -33,7 +35,6 @@ describe('sourcemaps', () => { locate: getLocator(inputCode), locate_1: getLocator(inputCode, { offsetLine: 1 }) }; - const preprocessed = await svelte.preprocess( input.code, config.preprocess || {}, @@ -41,7 +42,6 @@ describe('sourcemaps', () => { filename: 'input.svelte' } ); - const { js, css } = svelte.compile(preprocessed.code, { filename: 'input.svelte', // filenames for sourcemaps @@ -103,7 +103,8 @@ describe('sourcemaps', () => { css.mapConsumer = css.map && (await new SourceMapConsumer(css.map)); css.locate = getLocator(css.code || ''); css.locate_1 = getLocator(css.code || '', { offsetLine: 1 }); + await test({ assert, input, preprocessed, js, css }); }); - }); + } }); diff --git a/test/stats/index.js b/test/stats/stats.test.js similarity index 76% rename from test/stats/index.js rename to test/stats/stats.test.js index 22e2ef5be5..3459868c0f 100644 --- a/test/stats/index.js +++ b/test/stats/stats.test.js @@ -1,6 +1,7 @@ import * as fs from 'fs'; -import * as assert from 'assert'; -import { svelte, loadConfig, tryToLoadJson } from '../helpers'; +import { describe, it, assert } from 'vitest'; +import * as svelte from '../../compiler'; +import { try_load_config, try_load_json } from '../helpers'; describe('stats', () => { fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { @@ -9,17 +10,14 @@ describe('stats', () => { // add .solo to a sample directory name to only run that test const solo = /\.solo/.test(dir); const skip = /\.skip/.test(dir); + const it_fn = solo ? it.only : skip ? it.skip : it; - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + it_fn(dir, async () => { + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); const filename = `${__dirname}/samples/${dir}/input.svelte`; const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); - const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); + const expectedError = try_load_json(`${__dirname}/samples/${dir}/error.json`); let result; let error; diff --git a/test/store/index.js b/test/store/store.test.js similarity index 98% rename from test/store/index.js rename to test/store/store.test.js index 9fcda311e8..1f7d90e50c 100644 --- a/test/store/index.js +++ b/test/store/store.test.js @@ -1,5 +1,5 @@ -import * as assert from 'assert'; -import { readable, writable, derived, get, readonly } from '../../store'; +import { describe, it, assert } from 'vitest'; +import { readable, writable, derived, get, readonly } from 'svelte/store'; describe('store', () => { describe('writable', () => { @@ -139,7 +139,9 @@ describe('store', () => { it('passes an optional update function', () => { let running; + /** @type {(value: any) => void} */ let tick; + /** @type {(value: any) => void} */ let add; const store = readable(undefined, (set, update) => { diff --git a/test/test.js b/test/test.js deleted file mode 100644 index b6998943c2..0000000000 --- a/test/test.js +++ /dev/null @@ -1,21 +0,0 @@ -const glob = require('tiny-glob/sync.js'); - -require('./setup.js'); - -// bind internal to jsdom -require('./helpers.js'); -require('../internal'); - -console.clear(); - -const test_folders = glob('*/index.js', { cwd: 'test' }); -const solo_folders = test_folders.filter((folder) => /\.solo/.test(folder)); - -if (solo_folders.length) { - if (process.env.CI) { - throw new Error('Forgot to remove `.solo` from test'); - } - solo_folders.forEach((name) => require('./' + name)); -} else { - test_folders.forEach((name) => require('./' + name)); -} diff --git a/test/tsconfig.json b/test/tsconfig.json index 83eecc51dc..c5f45fe821 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -6,6 +6,10 @@ "compilerOptions": { "allowJs": true, "checkJs": true, - "noEmit": true + "noEmit": true, + "paths": { + "svelte": [".."], + "svelte/*": ["../*"] + } } } diff --git a/test/utils/index.js b/test/utils/index.js deleted file mode 100644 index 37ba63b1c7..0000000000 --- a/test/utils/index.js +++ /dev/null @@ -1,30 +0,0 @@ -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', () => { - it('trim_start', () => { - const value = trim_start(' \r\n\t svelte content \r\n\t '); - assert.equal(value, 'svelte content \r\n\t '); - }); - - it('trim_end', () => { - const value = trim_end(' \r\n\t svelte content \r\n\t '); - 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/utils/utils.test.js b/test/utils/utils.test.js new file mode 100644 index 0000000000..1714ef4d64 --- /dev/null +++ b/test/utils/utils.test.js @@ -0,0 +1,118 @@ +import { assert, describe, it } from 'vitest'; +import { + CONTENTEDITABLE_BINDINGS, + get_contenteditable_attr, + has_contenteditable_attr, + is_contenteditable, + is_name_contenteditable +} from '../../src/compiler/compile/utils/contenteditable'; +import get_name_from_filename from '../../src/compiler/compile/utils/get_name_from_filename'; +import { trim_end, trim_start } from '../../src/compiler/utils/trim'; +import { split_css_unit } from '../../src/runtime/internal/utils'; + +describe('utils', () => { + describe('trim', () => { + it('trim_start', () => { + const value = trim_start(' \r\n\t svelte content \r\n\t '); + assert.equal(value, 'svelte content \r\n\t '); + }); + + it('trim_end', () => { + const value = trim_end(' \r\n\t svelte content \r\n\t '); + 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']); + }); + }); + + describe('get_name_from_filename', () => { + it('uses the basename', () => { + assert.equal(get_name_from_filename('path/to/Widget.svelte'), 'Widget'); + }); + + it('uses the directory name, if basename is index', () => { + assert.equal(get_name_from_filename('path/to/Widget/index.svelte'), 'Widget'); + }); + + it('handles Windows filenames', () => { + assert.equal(get_name_from_filename('path\\to\\Widget.svelte'), 'Widget'); + }); + + it('handles special characters in filenames', () => { + assert.equal(get_name_from_filename('@.svelte'), '_'); + assert.equal(get_name_from_filename('&.svelte'), '_'); + assert.equal(get_name_from_filename('~.svelte'), '_'); + }); + }); + + /** @typedef {import("../../src/compiler/compile/nodes/Element.js").default} Element */ + /** @typedef {import("../../src/compiler/compile/nodes/Attribute.js").default} Attribute */ + + describe('contenteditable', () => { + describe('is_contenteditable', () => { + it('returns false if node is input', () => { + const node = /** @type {Element} */ ({ name: 'input' }); + assert.equal(is_contenteditable(node), false); + }); + it('returns false if node is textarea', () => { + const node = /** @type {Element} */ ({ name: 'textarea' }); + assert.equal(is_contenteditable(node), false); + }); + it('returns false if node is not input or textarea AND it is not contenteditable', () => { + const attr = /** @type {Attribute}*/ ({ name: 'href' }); + const node = /** @type {Element} */ ({ name: 'a', attributes: [attr] }); + assert.equal(is_contenteditable(node), false); + }); + it('returns true if node is not input or textarea AND it is contenteditable', () => { + const attr = /** @type {Attribute}*/ ({ name: 'contenteditable' }); + const node = /** @type {Element} */ ({ name: 'a', attributes: [attr] }); + assert.equal(is_contenteditable(node), true); + }); + }); + + describe('has_contenteditable_attr', () => { + it('returns true if attribute is contenteditable', () => { + const attr = /** @type {Attribute}*/ ({ name: 'contenteditable' }); + const node = /** @type {Element} */ ({ attributes: [attr] }); + assert.equal(has_contenteditable_attr(node), true); + }); + it('returns false if attribute is not contenteditable', () => { + const attr = /** @type {Attribute}*/ ({ name: 'href' }); + const node = /** @type {Element} */ ({ attributes: [attr] }); + assert.equal(has_contenteditable_attr(node), false); + }); + }); + + describe('is_name_contenteditable', () => { + it('returns true if name is a contenteditable type', () => { + assert.equal(is_name_contenteditable(CONTENTEDITABLE_BINDINGS[0]), true); + }); + it('returns false if name is not contenteditable type', () => { + assert.equal(is_name_contenteditable('value'), false); + }); + }); + + describe('get_contenteditable_attr', () => { + it('returns the contenteditable Attribute if it exists', () => { + const attr = /** @type {Attribute}*/ ({ name: 'contenteditable' }); + const node = /** @type {Element} */ ({ name: 'div', attributes: [attr] }); + assert.equal(get_contenteditable_attr(node), attr); + }); + it('returns undefined if contenteditable attribute cannot be found', () => { + const node = /** @type {Element} */ ({ name: 'div', attributes: [] }); + assert.equal(get_contenteditable_attr(node), undefined); + }); + }); + }); +}); diff --git a/test/validator/index.js b/test/validator/validator.test.js similarity index 88% rename from test/validator/index.js rename to test/validator/validator.test.js index 4c15eaf22b..e251259e3b 100644 --- a/test/validator/index.js +++ b/test/validator/validator.test.js @@ -1,6 +1,7 @@ import * as fs from 'fs'; -import * as assert from 'assert'; -import { svelte, loadConfig, tryToLoadJson } from '../helpers'; +import { describe, it, assert } from 'vitest'; +import * as svelte from '../../compiler.mjs'; +import { try_load_json, try_load_config } from '../helpers.js'; describe('validate', () => { fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { @@ -10,20 +11,19 @@ describe('validate', () => { const solo = /\.solo/.test(dir); const skip = /\.skip/.test(dir); - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } + const it_fn = solo ? it.only : skip ? it.skip : it; - (solo ? it.only : skip ? it.skip : it)(dir, () => { - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); + it_fn(dir, async () => { + const config = await try_load_config(`${__dirname}/samples/${dir}/_config.js`); const input = fs .readFileSync(`${__dirname}/samples/${dir}/input.svelte`, 'utf-8') .replace(/\s+$/, '') .replace(/\r/g, ''); - const expected_warnings = tryToLoadJson(`${__dirname}/samples/${dir}/warnings.json`) || []; - const expected_errors = tryToLoadJson(`${__dirname}/samples/${dir}/errors.json`); - const options = tryToLoadJson(`${__dirname}/samples/${dir}/options.json`); + + const expected_warnings = try_load_json(`${__dirname}/samples/${dir}/warnings.json`) || []; + const expected_errors = try_load_json(`${__dirname}/samples/${dir}/errors.json`); + const options = try_load_json(`${__dirname}/samples/${dir}/options.json`); let error; diff --git a/test/vars/index.js b/test/vars/index.js deleted file mode 100644 index bec6f5b727..0000000000 --- a/test/vars/index.js +++ /dev/null @@ -1,52 +0,0 @@ -import * as fs from 'fs'; -import * as assert from 'assert'; -import { svelte, loadConfig, tryToLoadJson } from '../helpers'; - -describe('vars', () => { - fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { - if (dir[0] === '.') return; - - // add .solo to a sample directory name to only run that test - const solo = /\.solo/.test(dir); - const skip = /\.skip/.test(dir); - - if (solo && process.env.CI) { - throw new Error('Forgot to remove `solo: true` from test'); - } - - for (const generate of ['dom', 'ssr', false]) { - (solo ? it.only : skip ? it.skip : it)(`${dir}, generate: ${generate}`, () => { - const config = loadConfig(`${__dirname}/samples/${dir}/_config.js`); - const filename = `${__dirname}/samples/${dir}/input.svelte`; - const input = fs.readFileSync(filename, 'utf-8').replace(/\s+$/, ''); - - const expectedError = tryToLoadJson(`${__dirname}/samples/${dir}/error.json`); - - let result; - let error; - - try { - result = svelte.compile(input, { ...config.options, generate }); - config.test(assert, result.vars); - } catch (e) { - error = e; - } - - if (error || expectedError) { - if (error && !expectedError) { - throw error; - } - - if (expectedError && !error) { - throw new Error(`Expected an error: ${expectedError.message}`); - } - - assert.equal(error.message, expectedError.message); - assert.deepEqual(error.start, expectedError.start); - assert.deepEqual(error.end, expectedError.end); - assert.equal(error.pos, expectedError.pos); - } - }); - } - }); -}); diff --git a/test/vars/samples/$$props-logicless/_config.js b/test/vars/samples/$$props-logicless/_config.js index 05bbe7e0b2..a6dc8d975c 100644 --- a/test/vars/samples/$$props-logicless/_config.js +++ b/test/vars/samples/$$props-logicless/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/$$props/_config.js b/test/vars/samples/$$props/_config.js index 05bbe7e0b2..a6dc8d975c 100644 --- a/test/vars/samples/$$props/_config.js +++ b/test/vars/samples/$$props/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/actions/_config.js b/test/vars/samples/actions/_config.js index 4cc2b9d18c..5d8e2a1855 100644 --- a/test/vars/samples/actions/_config.js +++ b/test/vars/samples/actions/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/animations/_config.js b/test/vars/samples/animations/_config.js index 4cc2b9d18c..5d8e2a1855 100644 --- a/test/vars/samples/animations/_config.js +++ b/test/vars/samples/animations/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/assumed-global/_config.js b/test/vars/samples/assumed-global/_config.js index 782760bb8b..8be1bf443c 100644 --- a/test/vars/samples/assumed-global/_config.js +++ b/test/vars/samples/assumed-global/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, []); } diff --git a/test/vars/samples/component-namespaced/_config.js b/test/vars/samples/component-namespaced/_config.js index 8ed4872355..33687f75cd 100644 --- a/test/vars/samples/component-namespaced/_config.js +++ b/test/vars/samples/component-namespaced/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/duplicate-globals/_config.js b/test/vars/samples/duplicate-globals/_config.js index 782760bb8b..8be1bf443c 100644 --- a/test/vars/samples/duplicate-globals/_config.js +++ b/test/vars/samples/duplicate-globals/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, []); } diff --git a/test/vars/samples/duplicate-non-hoistable/_config.js b/test/vars/samples/duplicate-non-hoistable/_config.js index bd70e29800..3b7a03fdd6 100644 --- a/test/vars/samples/duplicate-non-hoistable/_config.js +++ b/test/vars/samples/duplicate-non-hoistable/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/duplicate-vars/_config.js b/test/vars/samples/duplicate-vars/_config.js index 749a0c59af..5234a9c6fc 100644 --- a/test/vars/samples/duplicate-vars/_config.js +++ b/test/vars/samples/duplicate-vars/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/implicit-reactive/_config.js b/test/vars/samples/implicit-reactive/_config.js index a8bf261d4f..847cb329e6 100644 --- a/test/vars/samples/implicit-reactive/_config.js +++ b/test/vars/samples/implicit-reactive/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/imports/_config.js b/test/vars/samples/imports/_config.js index 47e8c0c5d9..b2cd77a0e6 100644 --- a/test/vars/samples/imports/_config.js +++ b/test/vars/samples/imports/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/modules-vars/_config.js b/test/vars/samples/modules-vars/_config.js index 52b8940148..3c7be58f57 100644 --- a/test/vars/samples/modules-vars/_config.js +++ b/test/vars/samples/modules-vars/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/mutated-vs-reassigned-bindings/_config.js b/test/vars/samples/mutated-vs-reassigned-bindings/_config.js index 65c7d5cc90..13f15e6b3e 100644 --- a/test/vars/samples/mutated-vs-reassigned-bindings/_config.js +++ b/test/vars/samples/mutated-vs-reassigned-bindings/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/mutated-vs-reassigned/_config.js b/test/vars/samples/mutated-vs-reassigned/_config.js index 65c7d5cc90..13f15e6b3e 100644 --- a/test/vars/samples/mutated-vs-reassigned/_config.js +++ b/test/vars/samples/mutated-vs-reassigned/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/props/_config.js b/test/vars/samples/props/_config.js index d5f5d1cc2b..8d987ee948 100644 --- a/test/vars/samples/props/_config.js +++ b/test/vars/samples/props/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/referenced-from-script/_config.js b/test/vars/samples/referenced-from-script/_config.js index ec068cb1d7..d0fb2b423c 100644 --- a/test/vars/samples/referenced-from-script/_config.js +++ b/test/vars/samples/referenced-from-script/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/store-referenced/_config.js b/test/vars/samples/store-referenced/_config.js index bac35d1dba..5e9df4dbbe 100644 --- a/test/vars/samples/store-referenced/_config.js +++ b/test/vars/samples/store-referenced/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/store-unreferenced/_config.js b/test/vars/samples/store-unreferenced/_config.js index 4965e52fec..601ab07d65 100644 --- a/test/vars/samples/store-unreferenced/_config.js +++ b/test/vars/samples/store-unreferenced/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/template-references/_config.js b/test/vars/samples/template-references/_config.js index 34c07e19e0..563573d71c 100644 --- a/test/vars/samples/template-references/_config.js +++ b/test/vars/samples/template-references/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/transitions/_config.js b/test/vars/samples/transitions/_config.js index 29a99b16cc..79dcc694f6 100644 --- a/test/vars/samples/transitions/_config.js +++ b/test/vars/samples/transitions/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/undeclared/_config.js b/test/vars/samples/undeclared/_config.js index 782760bb8b..8be1bf443c 100644 --- a/test/vars/samples/undeclared/_config.js +++ b/test/vars/samples/undeclared/_config.js @@ -1,4 +1,7 @@ export default { + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, []); } diff --git a/test/vars/samples/vars-report-false/_config.js b/test/vars/samples/vars-report-false/_config.js index f4709f09c8..9967a7f20f 100644 --- a/test/vars/samples/vars-report-false/_config.js +++ b/test/vars/samples/vars-report-false/_config.js @@ -3,6 +3,9 @@ export default { varsReport: false }, + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, []); } diff --git a/test/vars/samples/vars-report-full-noscript/_config.js b/test/vars/samples/vars-report-full-noscript/_config.js index 9dc875cd83..04310f4bb5 100644 --- a/test/vars/samples/vars-report-full-noscript/_config.js +++ b/test/vars/samples/vars-report-full-noscript/_config.js @@ -3,6 +3,9 @@ export default { varsReport: 'full' }, + /** + * @param {import("vitest").assert} assert + */ test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/vars-report-full-script/_config.js b/test/vars/samples/vars-report-full-script/_config.js index 1b267e5b22..ea8b6c3a36 100644 --- a/test/vars/samples/vars-report-full-script/_config.js +++ b/test/vars/samples/vars-report-full-script/_config.js @@ -3,6 +3,10 @@ export default { varsReport: 'full' }, + /** + * @param {import("vitest").assert} assert + */ + test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/samples/vars-report-full/_config.js b/test/vars/samples/vars-report-full/_config.js index 9dc875cd83..2332dd3e76 100644 --- a/test/vars/samples/vars-report-full/_config.js +++ b/test/vars/samples/vars-report-full/_config.js @@ -3,6 +3,10 @@ export default { varsReport: 'full' }, + /** + * @param {import("vitest").assert} assert + */ + test(assert, vars) { assert.deepEqual(vars, [ { diff --git a/test/vars/vars.test.js b/test/vars/vars.test.js new file mode 100644 index 0000000000..de3ed11c09 --- /dev/null +++ b/test/vars/vars.test.js @@ -0,0 +1,48 @@ +import * as fs from 'fs'; +import { assert, describe, it } from 'vitest'; +import { compile } from '../../compiler.mjs'; +import { try_load_json } from '../helpers.js'; + +describe('vars', () => { + fs.readdirSync(`${__dirname}/samples`).forEach((dir) => { + if (dir[0] === '.') return; + + // add .solo to a sample directory name to only run that test + const solo = /\.solo/.test(dir); + const skip = /\.skip/.test(dir); + + const it_fn = solo ? it.only : skip ? it.skip : it; + + it_fn.each(['dom', 'ssr', false])(`${dir} generate: %s`, async (generate) => { + const input = (await import(`./samples/${dir}/input.svelte?raw`)).default + .trimEnd() + .replace(/\r/g, ''); + + const expectedError = try_load_json(`${__dirname}/samples/${dir}/error.json`); + + /** + * @type {{ options: any, test: (assert: typeof import('vitest').assert, vars: any[]) => void }}} + */ + const { options, test } = (await import(`./samples/${dir}/_config.js`)).default; + + try { + const { vars } = compile(input, { ...options, generate }); + test(assert, vars); + } catch (error) { + if (expectedError) { + assert.equal(error.message, expectedError.message); + assert.deepEqual(error.start, expectedError.start); + assert.deepEqual(error.end, expectedError.end); + assert.equal(error.pos, expectedError.pos); + return; + } else { + throw error; + } + } + + if (expectedError) { + assert.fail(`Expected an error: ${JSON.stringify(expectedError)}`); + } + }); + }); +}); diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 0000000000..c38baca71c --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,19 @@ +import { defineConfig, configDefaults } from 'vitest/config'; + +export default defineConfig({ + plugins: [ + { + name: 'resolve-svelte', + resolveId(id) { + if (id.startsWith('svelte')) { + return id.replace(/^svelte(.*)\/?$/, `${__dirname}$1/index.mjs`); + } + } + } + ], + test: { + dir: 'test', + reporters: ['dot'], + exclude: [...configDefaults.exclude, '**/samples/**'] + } +});