diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74096f3a7f..1b8f5a05a2 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 55c39c257c..ea3ec75537 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 568cb5e6bb..a51cd60c5c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -88,6 +88,9 @@ importers: 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 @@ -103,9 +106,6 @@ importers: 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 @@ -136,6 +136,9 @@ importers: 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) sites/svelte.dev: dependencies: @@ -598,7 +601,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 @@ -620,7 +623,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 @@ -1627,7 +1630,7 @@ packages: svelte: ^3.54.0 vite: ^4.0.0 dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 deepmerge: 4.3.1 kleur: 4.1.5 magic-string: 0.30.0 @@ -1660,6 +1663,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/cookie@0.5.1: resolution: {integrity: sha512-COUnqfB2+ckwXXSFInsFdOAWQzCCx+a5hq2ruyj+Vjund94RJQd4LG2u9hnvJrTgunKAaax7ancBYlDrNYxA0g==} @@ -1735,7 +1748,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 @@ -1760,7 +1773,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: @@ -1787,7 +1800,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 @@ -1811,7 +1824,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 @@ -1853,7 +1866,7 @@ packages: resolution: {integrity: sha512-kTwMUQ8xtAZaC4wb2XuLkPqFVBj2dNBueMQ89NWEuw87k2nLBbuafeG5cob/QEr6YduxIdTVUjix0MtC7mPlmg==} dependencies: '@typescript/vfs': 1.3.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 lz-string: 1.5.0 transitivePeerDependencies: - supports-color @@ -1862,7 +1875,7 @@ packages: /@typescript/vfs@1.3.4: resolution: {integrity: sha512-RbyJiaAGQPIcAGWFa3jAXSuAexU4BFiDRF1g3hy7LmRqfNpYlTQWGXjcrOaVZjJ8YkkpuwG0FcsYvtWQpd9igQ==} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -1870,7 +1883,7 @@ packages: /@typescript/vfs@1.3.5: resolution: {integrity: sha512-pI8Saqjupf9MfLw7w2+og+fmb0fZS0J6vsKXXrp4/PDXEFvntgzXmChCXC/KefZZS0YGS6AT8e0hGAJcTsdJlg==} dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4 transitivePeerDependencies: - supports-color dev: true @@ -1896,6 +1909,45 @@ packages: - supports-color 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 @@ -1942,7 +1994,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 @@ -1956,11 +2008,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'} @@ -1985,6 +2032,11 @@ packages: color-convert: 2.0.1 dev: true + /ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + dev: true + /any-base@1.1.0: resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==} dev: true @@ -2065,6 +2117,10 @@ packages: es-shim-unscopables: 1.0.0 dev: true + /assertion-error@1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /async-sema@3.1.1: resolution: {integrity: sha512-tLRNUXati5MFePdAk8dw7Qt7DpxPB60ofAgn8WRhW6a2rcimZnYBP9oxHiv0OHy+Wz7kPMG+t4LGdt31+4EmGg==} dev: true @@ -2115,6 +2171,10 @@ packages: readable-stream: 3.6.2 dev: true + /blueimp-md5@2.19.0: + resolution: {integrity: sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==} + dev: true + /bmp-js@0.1.0: resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==} dev: true @@ -2138,10 +2198,6 @@ packages: dependencies: fill-range: 7.0.1 - /browser-stdout@1.3.1: - resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} - dev: true - /buffer-crc32@0.2.13: resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} dev: true @@ -2181,6 +2237,11 @@ packages: dependencies: streamsearch: 1.1.0 + /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: @@ -2193,15 +2254,23 @@ packages: engines: {node: '>=6'} dev: true - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - dev: true - /camelize@1.0.1: resolution: {integrity: sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==} dev: true + /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@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2220,6 +2289,10 @@ packages: supports-color: 7.2.0 dev: true + /check-error@1.0.2: + resolution: {integrity: sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==} + dev: true + /chokidar@3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} @@ -2243,14 +2316,6 @@ packages: engines: {node: '>=10'} 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 - dev: true - /code-block-writer@12.0.0: resolution: {integrity: sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==} dev: true @@ -2342,6 +2407,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.5.1 + well-known-symbols: 2.0.0 + dev: true + /console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} dev: true @@ -2400,6 +2479,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'} @@ -2432,6 +2515,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@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2454,7 +2544,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: @@ -2464,12 +2554,6 @@ packages: optional: true dependencies: ms: 2.1.2 - supports-color: 8.1.1 - - /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==} @@ -2482,6 +2566,13 @@ packages: mimic-response: 3.1.0 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: @@ -2553,11 +2644,6 @@ packages: /devalue@4.3.1: resolution: {integrity: sha512-Kc0TSP9IUU9eg55au5Q3YtqaYI2cgntVpunJV9Exbm9nvlBeTE5p2NqYHfpuXK6+VF2hF5PI+BPFPUti7e2N1g==} - /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'} @@ -2613,8 +2699,8 @@ packages: once: 1.4.0 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 @@ -2754,11 +2840,6 @@ packages: '@esbuild/win32-ia32': 0.17.19 '@esbuild/win32-x64': 0.17.19 - /escalade@3.1.1: - resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} - engines: {node: '>=6'} - dev: true - /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -2961,7 +3042,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 @@ -3072,6 +3153,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'} @@ -3156,11 +3241,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 @@ -3251,9 +3331,8 @@ packages: wide-align: 1.1.5 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: @@ -3307,17 +3386,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: @@ -3393,6 +3461,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 @@ -3406,6 +3485,7 @@ packages: /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} + dev: true /has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} @@ -3441,11 +3521,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 - /hex-rgb@4.3.0: resolution: {integrity: sha512-Ox1pJVrDCyGHMG9CFg1tmrRUMRPRsAWYc/PinY0XzJU4K7y7vjNoLKIQ7BR5UJMCxNN8EM1MNDmHWA/B3aZUuw==} engines: {node: '>=6'} @@ -3464,7 +3539,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 @@ -3474,7 +3549,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 @@ -3673,11 +3748,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 @@ -3741,11 +3811,6 @@ packages: resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} dev: false - /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 @@ -3799,6 +3864,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-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true @@ -3915,6 +3985,11 @@ packages: xtend: 4.0.2 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 @@ -3930,12 +4005,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: @@ -3975,6 +4052,13 @@ packages: engines: {node: '>= 18'} hasBin: 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 @@ -4037,13 +4121,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'} @@ -4105,32 +4182,13 @@ packages: hasBin: true 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 /mri@1.2.0: @@ -4160,12 +4218,6 @@ packages: thenify-all: 1.6.0 dev: true - /nanoid@3.3.3: - resolution: {integrity: sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - dev: true - /nanoid@3.3.6: resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -4352,6 +4404,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'} @@ -4403,7 +4462,7 @@ packages: /parse5@7.1.2: resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} dependencies: - entities: 4.4.0 + entities: 4.5.0 dev: true /path-browserify@1.0.1: @@ -4434,6 +4493,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 + /peek-readable@4.1.0: resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} engines: {node: '>=8'} @@ -4470,6 +4537,14 @@ packages: pngjs: 3.4.0 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'} @@ -4553,6 +4628,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 + /process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -4582,12 +4666,6 @@ 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 - /rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true @@ -4598,6 +4676,10 @@ packages: strip-json-comments: 2.0.1 dev: true + /react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + dev: true + /readable-stream@3.6.2: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} @@ -4645,11 +4727,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 @@ -4829,12 +4906,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 - /set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} dev: true @@ -4918,6 +4989,10 @@ packages: object-inspect: 1.12.3 dev: true + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} dev: true @@ -4984,6 +5059,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'} @@ -5068,6 +5151,12 @@ packages: engines: {node: '>=8'} dev: true + /strip-literal@1.0.1: + resolution: {integrity: sha512-QZTsipNpa2Ppr6v1AmJHESqJ3Uz247MUS0OjrnnZjFAvEoWqxuyFuXn2xLgMtRnijJShAa1HL0gtJyUs7u7n3Q==} + dependencies: + acorn: 8.8.2 + dev: true + /strtok3@6.3.0: resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} engines: {node: '>=10'} @@ -5109,12 +5198,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 - /supports-preserve-symlinks-flag@1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -5273,6 +5356,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 + /timm@1.7.1: resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==} dev: true @@ -5287,10 +5375,24 @@ packages: resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} dev: true + /tinybench@2.5.0: + resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true + /tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} 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'} @@ -5387,6 +5489,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'} @@ -5420,6 +5527,10 @@ packages: hasBin: true dev: true + /ufo@1.1.2: + resolution: {integrity: sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==} + dev: true + /ultrahtml@1.2.0: resolution: {integrity: sha512-vxZM2yNvajRmCj/SknRYGNXk2tqiy6kRNvZjJLaleG3zJbSh/aNkOqD1/CVzypw8tyHyhpzYuwQgMMhUB4ZVNQ==} dev: true @@ -5502,6 +5613,27 @@ packages: - rollup dev: true + /vite-node@0.31.0(@types/node@20.1.7): + 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.7(@types/node@20.1.7)(sass@1.62.1) + transitivePeerDependencies: + - '@types/node' + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vite@4.3.7(@types/node@20.1.7)(sass@1.62.1): resolution: {integrity: sha512-MTIFpbIm9v7Hh5b0wSBgkcWzSBz7SAa6K/cBTwS4kUiQJfQLFlZZRJRQgqunCVzhTPCk674tW+0Qaqh3Q00dBg==} engines: {node: ^14.18.0 || >=16.0.0} @@ -5545,6 +5677,73 @@ packages: dependencies: vite: 4.3.7(@types/node@20.1.7)(sass@1.62.1) + /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': 20.1.7 + '@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.7(@types/node@20.1.7)(sass@1.62.1) + vite-node: 0.31.0(@types/node@20.1.7) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: true @@ -5595,6 +5794,11 @@ packages: - supports-color dev: false + /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'} @@ -5664,6 +5868,15 @@ 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 + /wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} dependencies: @@ -5675,19 +5888,6 @@ packages: 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 @@ -5745,11 +5945,6 @@ packages: engines: {node: '>=0.4'} dev: true - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true - /yaeti@0.0.6: resolution: {integrity: sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==} engines: {node: '>=0.10.32'} @@ -5759,39 +5954,16 @@ packages: 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 + /yoga-wasm-web@0.3.3: resolution: {integrity: sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA==} dev: true 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: `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: `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: 'does not change
' + get props() { + return { raw: 'does not change
' }; }, html: 'does not change
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 @@ -