diff --git a/packages/kit/test/apps/async/.env b/packages/kit/test/apps/async/.env new file mode 100644 index 000000000000..b12773447030 --- /dev/null +++ b/packages/kit/test/apps/async/.env @@ -0,0 +1,5 @@ +PRIVATE_STATIC="accessible to server-side code/replaced at build time" +PRIVATE_DYNAMIC="accessible to server-side code/evaluated at run time" + +PUBLIC_STATIC="accessible anywhere/replaced at build time" +PUBLIC_DYNAMIC="accessible anywhere/evaluated at run time" \ No newline at end of file diff --git a/packages/kit/test/apps/async/.gitignore b/packages/kit/test/apps/async/.gitignore new file mode 100644 index 000000000000..d5868cb0ca8a --- /dev/null +++ b/packages/kit/test/apps/async/.gitignore @@ -0,0 +1 @@ +!/.env \ No newline at end of file diff --git a/packages/kit/test/apps/async/README.md b/packages/kit/test/apps/async/README.md new file mode 100644 index 000000000000..75842c404de0 --- /dev/null +++ b/packages/kit/test/apps/async/README.md @@ -0,0 +1,38 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```sh +# create a new project in the current directory +npx sv create + +# create a new project in my-app +npx sv create my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```sh +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```sh +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/packages/kit/test/apps/async/package.json b/packages/kit/test/apps/async/package.json new file mode 100644 index 000000000000..7afccae4847a --- /dev/null +++ b/packages/kit/test/apps/async/package.json @@ -0,0 +1,25 @@ +{ + "name": "test-async", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && tsc && svelte-check", + "test": "pnpm test:dev && pnpm test:build", + "test:dev": "DEV=true playwright test", + "test:build": "playwright test" + }, + "devDependencies": { + "@sveltejs/kit": "workspace:^", + "@sveltejs/vite-plugin-svelte": "catalog:", + "svelte": "catalog:", + "svelte-check": "catalog:", + "typescript": "^5.5.4", + "valibot": "catalog:", + "vite": "catalog:" + } +} diff --git a/packages/kit/test/apps/async/playwright.config.js b/packages/kit/test/apps/async/playwright.config.js new file mode 100644 index 000000000000..f3ef088b1b6f --- /dev/null +++ b/packages/kit/test/apps/async/playwright.config.js @@ -0,0 +1,11 @@ +import process from 'node:process'; +import { config } from '../../utils.js'; +import { defineConfig } from '@playwright/test'; + +export default defineConfig({ + ...config, + webServer: { + command: process.env.DEV ? `pnpm dev` : `pnpm build && pnpm preview`, + port: process.env.DEV ? 5173 : 4173 + } +}); diff --git a/packages/kit/test/apps/async/src/app.html b/packages/kit/test/apps/async/src/app.html new file mode 100644 index 000000000000..f273cc58f7eb --- /dev/null +++ b/packages/kit/test/apps/async/src/app.html @@ -0,0 +1,11 @@ + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/packages/kit/test/apps/async/src/hooks.js b/packages/kit/test/apps/async/src/hooks.js new file mode 100644 index 000000000000..238cc7744f2c --- /dev/null +++ b/packages/kit/test/apps/async/src/hooks.js @@ -0,0 +1,9 @@ +import { Foo } from '$lib'; + +/** @type {import("@sveltejs/kit").Transport} */ +export const transport = { + Foo: { + encode: (value) => value instanceof Foo && [value.message], + decode: ([message]) => new Foo(message) + } +}; diff --git a/packages/kit/test/apps/async/src/hooks.server.js b/packages/kit/test/apps/async/src/hooks.server.js new file mode 100644 index 000000000000..9851e98922eb --- /dev/null +++ b/packages/kit/test/apps/async/src/hooks.server.js @@ -0,0 +1,21 @@ +/** @type {import('@sveltejs/kit').HandleValidationError} */ +export const handleValidationError = ({ issues }) => { + return { message: issues[0].message }; +}; + +/** @type {import('@sveltejs/kit').HandleServerError} */ +export const handleError = ({ error: e, status, message }) => { + const error = /** @type {Error} */ (e); + + return { message: `${error.message} (${status} ${message})` }; +}; + +// @ts-ignore this doesn't exist in old Node TODO remove SvelteKit 3 (same in test-basics) +Promise.withResolvers ??= () => { + const d = {}; + d.promise = new Promise((resolve, reject) => { + d.resolve = resolve; + d.reject = reject; + }); + return d; +}; diff --git a/packages/kit/test/apps/async/src/lib/assets/favicon.svg b/packages/kit/test/apps/async/src/lib/assets/favicon.svg new file mode 100644 index 000000000000..cc5dc66a3a52 --- /dev/null +++ b/packages/kit/test/apps/async/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/packages/kit/test/apps/async/src/lib/index.js b/packages/kit/test/apps/async/src/lib/index.js new file mode 100644 index 000000000000..f9917fd877e2 --- /dev/null +++ b/packages/kit/test/apps/async/src/lib/index.js @@ -0,0 +1,9 @@ +export class Foo { + constructor(message) { + this.message = message; + } + + bar() { + return this.message + '!'; + } +} diff --git a/packages/kit/test/apps/async/src/routes/+error.svelte b/packages/kit/test/apps/async/src/routes/+error.svelte new file mode 100644 index 000000000000..61ad908c0b25 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/+error.svelte @@ -0,0 +1,35 @@ + + + + Custom error page: {page.error.message} + + +

{page.status}

+ +

This is your custom error page saying: "{page.error.message}"

+ + diff --git a/packages/kit/test/apps/async/src/routes/+layout.svelte b/packages/kit/test/apps/async/src/routes/+layout.svelte new file mode 100644 index 000000000000..969491327885 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/+layout.svelte @@ -0,0 +1,9 @@ + + +{@render children?.()} diff --git a/packages/kit/test/apps/basics/src/routes/remote/+page.js b/packages/kit/test/apps/async/src/routes/remote/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/+page.js rename to packages/kit/test/apps/async/src/routes/remote/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/+page.svelte similarity index 89% rename from packages/kit/test/apps/basics/src/routes/remote/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/+page.svelte index a7dbab7bf6ab..c7efb3edc63b 100644 --- a/packages/kit/test/apps/basics/src/routes/remote/+page.svelte +++ b/packages/kit/test/apps/async/src/routes/remote/+page.svelte @@ -23,13 +23,12 @@

{data.echo_result}

- {#if browser}

- {#await count then result}{result}{/await} / {count.current} ({count.loading}) + {await count} / {count.current} ({count.loading})

- {#await add({ a: 2, b: 2 }) then result}{result}{/await} + {await add({ a: 2, b: 2 })} {/if}

{command_result}

@@ -110,4 +109,8 @@ -/remote/event + +/remote/event diff --git a/packages/kit/test/apps/basics/src/routes/remote/accessing-env.remote.js b/packages/kit/test/apps/async/src/routes/remote/accessing-env.remote.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/accessing-env.remote.js rename to packages/kit/test/apps/async/src/routes/remote/accessing-env.remote.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/batch/+page.js b/packages/kit/test/apps/async/src/routes/remote/batch/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/batch/+page.js rename to packages/kit/test/apps/async/src/routes/remote/batch/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/batch/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/batch/+page.svelte similarity index 73% rename from packages/kit/test/apps/basics/src/routes/remote/batch/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/batch/+page.svelte index 0d801a2dde21..9c4ab7aa73cc 100644 --- a/packages/kit/test/apps/basics/src/routes/remote/batch/+page.svelte +++ b/packages/kit/test/apps/async/src/routes/remote/batch/+page.svelte @@ -1,4 +1,4 @@ - + +

route: {event.route.id}

+

pathname: {event.url.pathname}

diff --git a/packages/kit/test/apps/basics/src/routes/remote/event/data.remote.ts b/packages/kit/test/apps/async/src/routes/remote/event/data.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/event/data.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/event/data.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/[test_name]/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/[test_name]/+page.svelte similarity index 95% rename from packages/kit/test/apps/basics/src/routes/remote/form/[test_name]/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/[test_name]/+page.svelte index 9607ab161ce5..485cfab34a11 100644 --- a/packages/kit/test/apps/basics/src/routes/remote/form/[test_name]/+page.svelte +++ b/packages/kit/test/apps/async/src/routes/remote/form/[test_name]/+page.svelte @@ -16,10 +16,7 @@

message.current: {message.current}

- -{#await message then m} -

await get_message(): {m}

-{/await} +

await get_message(): {await message}


diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/[test_name]/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/[test_name]/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/[test_name]/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/[test_name]/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/file-upload/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/file-upload/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/file-upload/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/file-upload/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/file-upload/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/file-upload/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/file-upload/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/file-upload/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/imperative/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/imperative/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/imperative/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/imperative/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/preflight-only/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/preflight-only/+page.svelte similarity index 82% rename from packages/kit/test/apps/basics/src/routes/remote/form/preflight-only/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/preflight-only/+page.svelte index 6de838b61e63..0b2cb866102e 100644 --- a/packages/kit/test/apps/basics/src/routes/remote/form/preflight-only/+page.svelte +++ b/packages/kit/test/apps/async/src/routes/remote/form/preflight-only/+page.svelte @@ -11,12 +11,9 @@ }); - -{#await data then { a, b, c }} -

a: {a}

-

b: {b}

-

c: {c}

-{/await} +

a: {(await data).a}

+

b: {(await data).b}

+

c: {(await data).c}


diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/preflight-only/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/preflight-only/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/preflight-only/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/preflight-only/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/preflight/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/preflight/+page.svelte similarity index 91% rename from packages/kit/test/apps/basics/src/routes/remote/form/preflight/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/preflight/+page.svelte index 38d1248d4515..85269d87cff4 100644 --- a/packages/kit/test/apps/basics/src/routes/remote/form/preflight/+page.svelte +++ b/packages/kit/test/apps/async/src/routes/remote/form/preflight/+page.svelte @@ -13,10 +13,7 @@

number.current: {number.current}

- -{#await number then n} -

await get_number(): {n}

-{/await} +

await get_number(): {await number}


diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/preflight/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/preflight/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/preflight/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/preflight/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/select-untouched/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/select-untouched/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/select-untouched/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/select-untouched/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/select-untouched/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/select-untouched/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/select-untouched/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/select-untouched/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/submitter/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/submitter/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/submitter/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/submitter/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/submitter/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/submitter/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/submitter/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/submitter/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/underscore/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/underscore/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/underscore/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/underscore/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/underscore/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/underscore/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/underscore/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/underscore/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/validate/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/validate/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/validate/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/validate/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/validate/form.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/validate/form.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/validate/form.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/validate/form.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/value/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/form/value/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/value/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/form/value/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/form/value/value.remote.ts b/packages/kit/test/apps/async/src/routes/remote/form/value/value.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/form/value/value.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/form/value/value.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/prerender/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/prerender/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/functions-only/+page.js b/packages/kit/test/apps/async/src/routes/remote/prerender/functions-only/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/functions-only/+page.js rename to packages/kit/test/apps/async/src/routes/remote/prerender/functions-only/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/functions-only/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/prerender/functions-only/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/functions-only/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/prerender/functions-only/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/prerender.remote.js b/packages/kit/test/apps/async/src/routes/remote/prerender/prerender.remote.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/prerender.remote.js rename to packages/kit/test/apps/async/src/routes/remote/prerender/prerender.remote.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/test.txt b/packages/kit/test/apps/async/src/routes/remote/prerender/test.txt similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/test.txt rename to packages/kit/test/apps/async/src/routes/remote/prerender/test.txt diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/whole-page/+page.js b/packages/kit/test/apps/async/src/routes/remote/prerender/whole-page/+page.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/whole-page/+page.js rename to packages/kit/test/apps/async/src/routes/remote/prerender/whole-page/+page.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/prerender/whole-page/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/prerender/whole-page/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/prerender/whole-page/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/prerender/whole-page/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-command.remote.js b/packages/kit/test/apps/async/src/routes/remote/query-command.remote.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-command.remote.js rename to packages/kit/test/apps/async/src/routes/remote/query-command.remote.js diff --git a/packages/kit/test/apps/async/src/routes/remote/query-non-exported/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-non-exported/+page.svelte new file mode 100644 index 000000000000..d69be7f2c775 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-non-exported/+page.svelte @@ -0,0 +1,5 @@ + + +

{await total()}

diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-non-exported/data.remote.ts b/packages/kit/test/apps/async/src/routes/remote/query-non-exported/data.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-non-exported/data.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/query-non-exported/data.remote.ts diff --git a/packages/kit/test/apps/async/src/routes/remote/query-redirect/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/+page.svelte new file mode 100644 index 000000000000..efc180ae5bd7 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-redirect/+page.svelte @@ -0,0 +1,4 @@ + +from page +from layout diff --git a/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/+layout.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/+layout.svelte new file mode 100644 index 000000000000..1eebf5f10c74 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/+layout.svelte @@ -0,0 +1,12 @@ + + +

+ on page {await layoutRedirect(page.url.pathname)} (== {page.url.pathname}) +

+ +{@render children()} diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/redirected/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/redirected/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/redirected/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/query-redirect/from-common-layout/redirected/+page.svelte diff --git a/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-page/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-page/+page.svelte new file mode 100644 index 000000000000..8cad914e4f02 --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/query-redirect/from-page/+page.svelte @@ -0,0 +1,8 @@ + + + + {await pageRedirect()} +

should never see this

+
diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/redirect.remote.js b/packages/kit/test/apps/async/src/routes/remote/query-redirect/redirect.remote.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-redirect/redirect.remote.js rename to packages/kit/test/apps/async/src/routes/remote/query-redirect/redirect.remote.js diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/redirected/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/query-redirect/redirected/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/query-redirect/redirected/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/query-redirect/redirected/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/server-endpoint/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/server-endpoint/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/server-endpoint/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts b/packages/kit/test/apps/async/src/routes/remote/server-endpoint/api/+server.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/server-endpoint/api/+server.ts rename to packages/kit/test/apps/async/src/routes/remote/server-endpoint/api/+server.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts b/packages/kit/test/apps/async/src/routes/remote/server-endpoint/internal.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/server-endpoint/internal.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/server-endpoint/internal.remote.ts diff --git a/packages/kit/test/apps/async/src/routes/remote/transport/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/transport/+page.svelte new file mode 100644 index 000000000000..43a5205219ac --- /dev/null +++ b/packages/kit/test/apps/async/src/routes/remote/transport/+page.svelte @@ -0,0 +1,5 @@ + + +

{(await greeting()).bar()}

diff --git a/packages/kit/test/apps/basics/src/routes/remote/transport/data.remote.ts b/packages/kit/test/apps/async/src/routes/remote/transport/data.remote.ts similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/transport/data.remote.ts rename to packages/kit/test/apps/async/src/routes/remote/transport/data.remote.ts diff --git a/packages/kit/test/apps/basics/src/routes/remote/validation/+page.svelte b/packages/kit/test/apps/async/src/routes/remote/validation/+page.svelte similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/validation/+page.svelte rename to packages/kit/test/apps/async/src/routes/remote/validation/+page.svelte diff --git a/packages/kit/test/apps/basics/src/routes/remote/validation/validation.remote.js b/packages/kit/test/apps/async/src/routes/remote/validation/validation.remote.js similarity index 100% rename from packages/kit/test/apps/basics/src/routes/remote/validation/validation.remote.js rename to packages/kit/test/apps/async/src/routes/remote/validation/validation.remote.js diff --git a/packages/kit/test/apps/async/static/robots.txt b/packages/kit/test/apps/async/static/robots.txt new file mode 100644 index 000000000000..b6dd6670cbb0 --- /dev/null +++ b/packages/kit/test/apps/async/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/packages/kit/test/apps/async/svelte.config.js b/packages/kit/test/apps/async/svelte.config.js new file mode 100644 index 000000000000..d5098a971446 --- /dev/null +++ b/packages/kit/test/apps/async/svelte.config.js @@ -0,0 +1,16 @@ +/** @type {import('@sveltejs/kit').Config} */ +const config = { + compilerOptions: { + experimental: { + async: true + } + }, + + kit: { + experimental: { + remoteFunctions: true + } + } +}; + +export default config; diff --git a/packages/kit/test/apps/async/test/client.test.js b/packages/kit/test/apps/async/test/client.test.js new file mode 100644 index 000000000000..8266b5ac8338 --- /dev/null +++ b/packages/kit/test/apps/async/test/client.test.js @@ -0,0 +1,310 @@ +import process from 'node:process'; +import { expect } from '@playwright/test'; +import { test } from '../../../utils.js'; + +test.skip(({ javaScriptEnabled }) => !javaScriptEnabled); + +test.describe('remote functions', () => { + test('preloading data works when the page component and server load both import a remote function', async ({ + page + }) => { + test.skip(!process.env.DEV, 'remote functions are only analysed in dev mode'); + await page.goto('/remote/dev'); + await page.locator('a[href="/remote/dev/preload"]').hover(); + await Promise.all([ + page.waitForTimeout(100), // wait for preloading to start + page.waitForLoadState('networkidle') // wait for preloading to finish + ]); + await page.click('a[href="/remote/dev/preload"]'); + await expect(page.locator('p')).toHaveText('foobar'); + }); +}); + +// have to run in serial because commands mutate in-memory data on the server (should fix this at some point) +test.describe('remote function mutations', () => { + test.afterEach(async ({ page }) => { + if (page.url().endsWith('/remote')) { + await page.click('#reset-btn'); + } + }); + + test('query.set works', async ({ page }) => { + await page.goto('/remote'); + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#set-btn'); + await expect(page.locator('#count-result')).toHaveText('999 / 999 (false)'); + await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) + expect(request_count).toBe(0); + }); + + test('hydrated data is reused', async ({ page }) => { + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + // only the calls in the template are done, not the one in the load function + expect(request_count).toBe(2); + }); + + test('command returns correct sum but does not refresh data by default', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#multiply-btn'); + await expect(page.locator('#command-result')).toHaveText('2'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + await page.waitForTimeout(100); // allow all requests to finish + expect(request_count).toBe(1); // 1 for the command, no refreshes + }); + + test('command returns correct sum and does client-initiated single flight mutation', async ({ + page + }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#multiply-refresh-btn'); + await expect(page.locator('#command-result')).toHaveText('3'); + await expect(page.locator('#count-result')).toHaveText('3 / 3 (false)'); + await page.waitForTimeout(100); // allow all requests to finish + expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response + }); + + test('command does server-initiated single flight mutation (refresh)', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#multiply-server-refresh-btn'); + await expect(page.locator('#command-result')).toHaveText('4'); + await expect(page.locator('#count-result')).toHaveText('4 / 4 (false)'); + await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) + expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response + }); + + test('command refresh after reading query reruns the query', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#multiply-server-refresh-after-read-btn'); + await expect(page.locator('#command-result')).toHaveText('6'); + await expect(page.locator('#count-result')).toHaveText('6 / 6 (false)'); + await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) + expect(request_count).toBe(1); + }); + + test('command does server-initiated single flight mutation (set)', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#multiply-server-set-btn'); + await expect(page.locator('#command-result')).toHaveText('8'); + await expect(page.locator('#count-result')).toHaveText('8 / 8 (false)'); + await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) + expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response + }); + + test('command does client-initiated single flight mutation with override', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + page.click('#multiply-override-refresh-btn'); + await expect(page.locator('#count-result')).toHaveText('6 / 6 (false)'); + await expect(page.locator('#command-result')).toHaveText('5'); + await expect(page.locator('#count-result')).toHaveText('5 / 5 (false)'); + await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) + expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response + }); + + test('query/command inside endpoint works', async ({ page }) => { + await page.goto('/remote/server-endpoint'); + + await page.getByRole('button', { name: 'get' }).click(); + await expect(page.locator('p')).toHaveText('get'); + + await page.getByRole('button', { name: 'post' }).click(); + await expect(page.locator('p')).toHaveText('post'); + }); + + test('prerendered entries not called in prod', async ({ page }) => { + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + await page.goto('/remote/prerender'); + + await page.click('#fetch-prerendered'); + await expect(page.locator('#fetch-prerendered')).toHaveText('yes'); + + await page.click('#fetch-not-prerendered'); + await expect(page.locator('#fetch-not-prerendered')).toHaveText('d'); + }); + + test('refreshAll reloads remote functions and load functions', async ({ page }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#refresh-all'); + await page.waitForTimeout(100); // allow things to rerun + expect(request_count).toBe(3); + }); + + test('refreshAll({ includeLoadFunctions: false }) reloads remote functions only', async ({ + page + }) => { + await page.goto('/remote'); + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('#refresh-remote-only'); + await page.waitForTimeout(100); // allow things to rerun + expect(request_count).toBe(2); + }); + + test('command tracks pending state', async ({ page }) => { + await page.goto('/remote'); + + // Initial pending should be 0 + await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); + + // Start a slow command - this will hang until we resolve it + await page.click('#command-deferred-btn'); + + // Check that pending has incremented to 1 + await expect(page.locator('#command-pending')).toHaveText('Command pending: 1'); + + // Resolve the deferred command + await page.click('#resolve-deferreds'); + + // Wait for the command to complete and pending to go back to 0 + await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); + }); + + test('validation works', async ({ page }) => { + await page.goto('/remote/validation'); + await expect(page.locator('p')).toHaveText('pending'); + + await page.click('button:nth-of-type(1)'); + await expect(page.locator('p')).toHaveText('success'); + + await page.click('button:nth-of-type(2)'); + await expect(page.locator('p')).toHaveText('success'); + + await page.click('button:nth-of-type(3)'); + await expect(page.locator('p')).toHaveText('success'); + + await page.click('button:nth-of-type(4)'); + await expect(page.locator('p')).toHaveText('success'); + }); + + test('fields.set updates DOM before validate', async ({ page }) => { + await page.goto('/remote/form/imperative'); + + const input = page.locator('input[name="message"]'); + await input.fill('123'); + + await page.locator('#set-and-validate').click(); + + await expect(input).toHaveValue('hello'); + await expect(page.locator('#issue')).toHaveText('ok'); + }); + + test('command pending state is tracked correctly', async ({ page }) => { + await page.goto('/remote'); + + // Initially no pending commands + await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); + + // Start a slow command - this will hang until we resolve it + await page.click('#command-deferred-btn'); + + // Check that pending has incremented to 1 + await expect(page.locator('#command-pending')).toHaveText('Command pending: 1'); + + // Resolve the deferred command + await page.click('#resolve-deferreds'); + + // Wait for the command to complete and verify results + await expect(page.locator('#command-result')).toHaveText('7'); + + // Verify pending count returns to 0 + await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); + }); + + // TODO once we have async SSR adjust the test and move this into test.js + test('query.batch works', async ({ page }) => { + await page.goto('/remote/batch'); + + await expect(page.locator('#batch-result-1')).toHaveText('Buy groceries'); + await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog'); + await expect(page.locator('#batch-result-3')).toHaveText('Buy groceries'); + await expect(page.locator('#batch-result-4')).toHaveText('Error loading todo error: Not found'); + + let request_count = 0; + page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); + + await page.click('button'); + await page.waitForTimeout(100); // allow all requests to finish + expect(request_count).toBe(1); + }); + + test('query.batch set updates cache without extra request', async ({ page }) => { + await page.goto('/remote/batch'); + await page.click('#batch-reset-btn'); + await expect(page.locator('#batch-result-1')).toHaveText('Buy groceries'); + + let request_count = 0; + const handler = (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0); + page.on('request', handler); + + await page.click('#batch-set-btn'); + await expect(page.locator('#batch-result-1')).toHaveText('Buy cat food'); + await page.waitForTimeout(100); // allow all requests to finish + expect(request_count).toBe(1); // only the command request + }); + + test('query.batch refresh in command reuses single flight', async ({ page }) => { + await page.goto('/remote/batch'); + await page.click('#batch-reset-btn'); + await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog'); + + let request_count = 0; + const handler = (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0); + page.on('request', handler); + + await page.click('#batch-refresh-btn'); + await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog (refreshed)'); + await page.waitForTimeout(100); // allow all requests to finish + expect(request_count).toBe(1); // only the command request + }); + + // TODO ditto + test('query works with transport', async ({ page }) => { + await page.goto('/remote/transport'); + + await expect(page.locator('h1')).toHaveText('hello from remote function!'); + }); +}); diff --git a/packages/kit/test/apps/async/test/server.test.js b/packages/kit/test/apps/async/test/server.test.js new file mode 100644 index 000000000000..69412eadbcbe --- /dev/null +++ b/packages/kit/test/apps/async/test/server.test.js @@ -0,0 +1,17 @@ +import process from 'node:process'; +import { expect } from '@playwright/test'; +import { test } from '../../../utils.js'; +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +test.skip(({ javaScriptEnabled }) => javaScriptEnabled); + +const root = path.resolve(fileURLToPath(import.meta.url), '..', '..'); + +test.describe('remote functions', () => { + test("doesn't write bundle to disk when treeshaking prerendered remote functions", () => { + test.skip(!!process.env.DEV, 'skip when in dev mode'); + expect(fs.existsSync(path.join(root, 'dist'))).toBe(false); + }); +}); diff --git a/packages/kit/test/apps/async/test/test.js b/packages/kit/test/apps/async/test/test.js new file mode 100644 index 000000000000..bf647f6e4e38 --- /dev/null +++ b/packages/kit/test/apps/async/test/test.js @@ -0,0 +1,486 @@ +import { expect } from '@playwright/test'; +import { test } from '../../../utils.js'; + +test.describe('remote functions', () => { + test('query returns correct data', async ({ page, javaScriptEnabled }) => { + await page.goto('/remote'); + await expect(page.locator('#echo-result')).toHaveText('Hello world'); + if (javaScriptEnabled) { + await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); + } + }); + + test('query redirects on page load (query in common layout)', async ({ page }) => { + await page.goto('/remote/query-redirect'); + await page.click('a[href="/remote/query-redirect/from-common-layout"]'); + await expect(page.locator('#redirected')).toHaveText('redirected'); + await expect(page.locator('#layout-query')).toHaveText( + 'on page /remote/query-redirect/from-common-layout/redirected (== /remote/query-redirect/from-common-layout/redirected)' + ); + }); + + test('query redirects on page load (query on page)', async ({ page }) => { + await page.goto('/remote/query-redirect'); + await page.click('a[href="/remote/query-redirect/from-page"]'); + await expect(page.locator('#redirected')).toHaveText('redirected'); + }); + + test('non-exported queries do not clobber each other', async ({ page }) => { + await page.goto('/remote/query-non-exported'); + + await expect(page.locator('h1')).toHaveText('3'); + }); + + test('queries can access the route/url of the page they were called from', async ({ + page, + clicknav + }) => { + await page.goto('/remote'); + + await clicknav('[href="/remote/event"]'); + + await expect(page.locator('[data-id="route"]')).toHaveText('route: /remote/event'); + await expect(page.locator('[data-id="pathname"]')).toHaveText('pathname: /remote/event'); + }); + + test('form works', async ({ page, javaScriptEnabled }) => { + await page.goto(`/remote/form/basic-${javaScriptEnabled}`); + + if (javaScriptEnabled) { + await expect(page.getByText('message.current:')).toHaveText('message.current: initial'); + } + await expect(page.getByText('await get_message():')).toHaveText('await get_message(): initial'); + + await page.fill('[data-unscoped] input', 'hello'); + await page.getByText('set message').click(); + + if (javaScriptEnabled) { + await expect(page.getByText('set_message.pending:')).toHaveText('set_message.pending: 1'); + await page.getByText('resolve deferreds').click(); + await expect(page.getByText('set_message.pending:')).toHaveText('set_message.pending: 0'); + await expect(page.getByText('message.current:')).toHaveText('message.current: hello'); + } + + await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); + + await expect(page.getByText('set_message.result')).toHaveText('set_message.result: hello'); + await expect(page.locator('[data-unscoped] input[name="message"]')).toHaveValue(''); + }); + + test('form submitters work', async ({ page }) => { + await page.goto('/remote/form/submitter'); + + await page.locator('button').click(); + + await expect(page.locator('#result')).toHaveText('hello'); + }); + + test('form updates inputs live', async ({ page, javaScriptEnabled }) => { + await page.goto('/remote/form/live-update'); + + await page.fill('input', 'hello'); + + if (javaScriptEnabled) { + await expect(page.getByText('set_message.input.message:')).toHaveText( + 'set_message.input.message: hello' + ); + } + + await page.getByText('set message').click(); + + if (javaScriptEnabled) { + await page.getByText('resolve deferreds').click(); + } + + await expect(page.getByText('set_message.input.message:')).toHaveText( + 'set_message.input.message:' + ); + }); + + test('form reports validation issues', async ({ page }) => { + await page.goto('/remote/form/validation-issues'); + + await page.fill('input', 'invalid'); + await page.getByText('set message').click(); + + await page.getByText('message is invalid').waitFor(); + }); + + test('form handles unexpected error', async ({ page }) => { + await page.goto('/remote/form/unexpected-error'); + + await page.fill('input', 'unexpected error'); + await page.getByText('set message').click(); + + await page + .getByText('This is your custom error page saying: "oops (500 Internal Error)"') + .waitFor(); + }); + + test('form handles expected error', async ({ page }) => { + await page.goto('/remote/form/expected-error'); + + await page.fill('input', 'expected error'); + await page.getByText('set message').click(); + + await page.getByText('This is your custom error page saying: "oops"').waitFor(); + }); + + test('form redirects', async ({ page }) => { + await page.goto('/remote/form/redirect'); + + await page.fill('input', 'redirect'); + await page.getByText('set message').click(); + + await page.waitForURL('/remote'); + }); + + test('form.buttonProps works', async ({ page, javaScriptEnabled }) => { + await page.goto('/remote/form/button-props'); + + await page.fill('[data-unscoped] input', 'backwards'); + await page.getByText('set reverse message').click(); + + if (javaScriptEnabled) { + await page.getByText('message.current: sdrawkcab').waitFor(); + await expect(page.getByText('await get_message():')).toHaveText( + 'await get_message(): sdrawkcab' + ); + } + + await expect(page.getByText('set_reverse_message.result')).toHaveText( + 'set_reverse_message.result: sdrawkcab' + ); + }); + + test('form scoping with for(...) works', async ({ page, javaScriptEnabled }) => { + await page.goto('/remote/form/form-scoped'); + + await page.fill('[data-scoped] input', 'hello'); + await page.getByText('set scoped message').click(); + + if (javaScriptEnabled) { + await expect(page.getByText('scoped.pending:')).toHaveText('scoped.pending: 1'); + await page.getByText('resolve deferreds').click(); + await expect(page.getByText('scoped.pending:')).toHaveText('scoped.pending: 0'); + + await page.getByText('message.current: hello').waitFor(); + await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); + } + + await expect(page.getByText('scoped.result')).toHaveText( + 'scoped.result: hello (from: scoped:form-scoped)' + ); + await expect(page.locator('[data-scoped] input[name="message"]')).toHaveValue(''); + }); + + test('form enhance(...) works', async ({ page, javaScriptEnabled }) => { + await page.goto('/remote/form/enhanced'); + + await page.fill('[data-enhanced] input', 'hello'); + + // Click on the span inside the button to test the event.target vs event.currentTarget issue (#14159) + await page.locator('[data-enhanced] span').click(); + + if (javaScriptEnabled) { + await expect(page.getByText('enhanced.pending:')).toHaveText('enhanced.pending: 1'); + + await page.getByText('message.current: hello (override)').waitFor(); + + await page.getByText('resolve deferreds').click(); + await expect(page.getByText('enhanced.pending:')).toHaveText('enhanced.pending: 0'); + await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); + + // enhanced submission should not clear the input; the developer must do that at the appropriate time + await expect(page.locator('[data-enhanced] input[name="message"]')).toHaveValue('hello'); + } else { + await expect(page.locator('[data-enhanced] input[name="message"]')).toHaveValue(''); + } + + await expect(page.getByText('enhanced.result')).toHaveText( + 'enhanced.result: hello (from: enhanced:enhanced)' + ); + }); + + test('form preflight works', async ({ page, javaScriptEnabled }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/preflight'); + + for (const enhanced of [true, false]) { + const input = page.locator(enhanced ? '[data-enhanced] input' : '[data-default] input'); + const button = page.getByText(enhanced ? 'set enhanced number' : 'set number'); + + await input.fill('21'); + await button.click(); + await page.getByText('too big').waitFor(); + + await input.fill('9'); + await button.click(); + await page.getByText('too small').waitFor(); + + await input.fill('15'); + await button.click(); + await expect(page.getByText('number.current')).toHaveText('number.current: 15'); + } + }); + + test('form preflight-only validation works', async ({ page, javaScriptEnabled }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/preflight-only'); + + const a = page.locator('[name="a"]'); + const button = page.locator('button'); + const issues = page.locator('.issues'); + + await button.click(); + await expect(issues).toContainText('a is too short'); + await expect(issues).toContainText('b is too short'); + await expect(issues).toContainText('c is too short'); + + await a.fill('aaaaaaaa'); + await expect(issues).toContainText('a is too long'); + + // server issues should be preserved... + await expect(issues).toContainText('b is too short'); + await expect(issues).toContainText('c is too short'); + + // ...unless overridden by client issues + await expect(issues).not.toContainText('a is too short'); + }); + + test('form validate works', async ({ page, javaScriptEnabled }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/validate'); + + const myForm = page.locator('form#my-form'); + const foo = page.locator('input[name="foo"]'); + const bar = page.locator('input[name="bar"]'); + const submit = page.locator('button:has-text("imperative validation")'); + + await foo.fill('a'); + await expect(myForm).not.toContainText('Invalid type: Expected'); + + await bar.fill('g'); + await expect(myForm).toContainText('Invalid type: Expected ("d" | "e") but received "g"'); + + await bar.fill('d'); + await expect(myForm).not.toContainText('Invalid type: Expected'); + + await page.locator('#trigger-validate').click(); + await expect(myForm).toContainText( + 'Invalid type: Expected "submitter" but received "incorrect_value"' + ); + + // Test imperative validation + await foo.fill('c'); + await bar.fill('d'); + await submit.click(); + await expect(myForm).toContainText('Imperative: foo cannot be c'); + + const nestedValue = page.locator('input[name="nested.value"]'); + const validate = page.locator('button#validate'); + const allIssues = page.locator('#allIssues'); + + await nestedValue.fill('in'); + await validate.click(); + await expect(allIssues).toContainText('"path":["nested","value"]'); + }); + + test('form validation issues cleared', async ({ page, javaScriptEnabled }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/validate'); + + const baz = page.locator('input[name="baz"]'); + const submit = page.locator('#my-form-2 button'); + + await baz.fill('c'); + await submit.click(); + await expect(page.locator('#my-form-2')).toContainText('Invalid type: Expected'); + + await baz.fill('a'); + await submit.click(); + await expect(page.locator('#my-form-2')).not.toContainText('Invalid type: Expected'); + await expect(page.locator('[data-error]')).toHaveText('An error occurred'); + + await baz.fill('c'); + await submit.click(); + await expect(page.locator('#my-form-2')).toContainText('Invalid type: Expected'); + + await baz.fill('b'); + await submit.click(); + await expect(page.locator('#my-form-2')).not.toContainText('Invalid type: Expected'); + await expect(page.locator('[data-error]')).toHaveText('No error'); + }); + + test('form inputs excludes underscore-prefixed fields', async ({ page, javaScriptEnabled }) => { + if (javaScriptEnabled) return; + + await page.goto('/remote/form/underscore'); + + await page.fill('input[name="username"]', 'abcdefg'); + await page.fill('input[name="_password"]', 'pqrstuv'); + await page.locator('button').click(); + + await expect(page.locator('input[name="username"]')).toHaveValue('abcdefg'); + await expect(page.locator('input[name="_password"]')).toHaveValue(''); + }); + + test('prerendered entries not called in prod', async ({ page, clicknav }) => { + await page.goto('/remote/prerender'); + await clicknav('[href="/remote/prerender/whole-page"]'); + await expect(page.locator('#prerendered-data')).toHaveText('a c 中文 yes'); + + await page.goto('/remote/prerender'); + await clicknav('[href="/remote/prerender/functions-only"]'); + await expect(page.locator('#prerendered-data')).toHaveText('a c 中文 yes'); + }); + + test('form.fields.value() returns correct nested object structure', async ({ + page, + javaScriptEnabled + }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/value'); + + // Initially should be empty object or undefined values + const initialValue = await page.locator('#full-value').textContent(); + expect(JSON.parse(initialValue)).toEqual({}); + + // Fill leaf field + await page.fill('input[name="leaf"]', 'leaf-value'); + const afterLeaf = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterLeaf)).toEqual({ + leaf: 'leaf-value' + }); + + // Fill object.leaf field + await page.fill('input[name="object.leaf"]', 'object-leaf-value'); + const afterObjectLeaf = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterObjectLeaf)).toEqual({ + leaf: 'leaf-value', + object: { + leaf: 'object-leaf-value' + } + }); + + // Fill object.array fields + await page.fill('input[name="object.array[0]"]', 'array-item-1'); + const afterArrayItem1 = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterArrayItem1)).toEqual({ + leaf: 'leaf-value', + object: { + leaf: 'object-leaf-value', + array: ['array-item-1'] + } + }); + + await page.fill('input[name="object.array[1]"]', 'array-item-2'); + const afterArrayItem2 = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterArrayItem2)).toEqual({ + leaf: 'leaf-value', + object: { + leaf: 'object-leaf-value', + array: ['array-item-1', 'array-item-2'] + } + }); + + // Fill array[0].leaf field + await page.fill('input[name="array[0].leaf"]', 'array-0-leaf'); + const afterArray0 = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterArray0)).toEqual({ + leaf: 'leaf-value', + object: { + leaf: 'object-leaf-value', + array: ['array-item-1', 'array-item-2'] + }, + array: [{ leaf: 'array-0-leaf' }] + }); + + // Fill array[1].leaf field + await page.fill('input[name="array[1].leaf"]', 'array-1-leaf'); + const afterArray1 = await page.locator('#full-value').textContent(); + expect(JSON.parse(afterArray1)).toEqual({ + leaf: 'leaf-value', + object: { + leaf: 'object-leaf-value', + array: ['array-item-1', 'array-item-2'] + }, + array: [{ leaf: 'array-0-leaf' }, { leaf: 'array-1-leaf' }] + }); + + // Test nested object value access + const objectValue = await page.locator('#object-value').textContent(); + expect(JSON.parse(objectValue)).toEqual({ + leaf: 'object-leaf-value', + array: ['array-item-1', 'array-item-2'] + }); + + // Test array value access + const arrayValue = await page.locator('#array-value').textContent(); + expect(JSON.parse(arrayValue)).toEqual([{ leaf: 'array-0-leaf' }, { leaf: 'array-1-leaf' }]); + }); + + test('selects are not nuked when unrelated controls change', async ({ + page, + javaScriptEnabled + }) => { + if (!javaScriptEnabled) return; + + await page.goto('/remote/form/select-untouched'); + + await page.fill('input', 'hello'); + await expect(page.locator('select')).toHaveValue('one'); + }); + test('file uploads work', async ({ page }) => { + await page.goto('/remote/form/file-upload'); + + await page.locator('input[name="file1"]').setInputFiles({ + name: 'a.txt', + mimeType: 'text/plain', + buffer: Buffer.from('a') + }); + await page.locator('input[name="file2"]').setInputFiles({ + name: 'b.txt', + mimeType: 'text/plain', + buffer: Buffer.from('b') + }); + await page.locator('input[type="checkbox"]').check(); + await page.locator('button').click(); + + await expect(page.locator('pre')).toHaveText( + JSON.stringify({ + text: 'Hello world', + file1: 'a', + file2: 'b' + }) + ); + }); + test('large file uploads work', async ({ page }) => { + await page.goto('/remote/form/file-upload'); + + await page.locator('input[name="file1"]').setInputFiles({ + name: 'a.txt', + mimeType: 'text/plain', + buffer: Buffer.alloc(1024 * 1024 * 10) + }); + await page.locator('input[name="file2"]').setInputFiles({ + name: 'b.txt', + mimeType: 'text/plain', + buffer: Buffer.from('b') + }); + await page.locator('button').click(); + + await expect(page.locator('pre')).toHaveText( + JSON.stringify({ + text: 'Hello world', + file1: 1024 * 1024 * 10, + file2: 1 + }) + ); + }); +}); diff --git a/packages/kit/test/apps/async/tsconfig.json b/packages/kit/test/apps/async/tsconfig.json new file mode 100644 index 000000000000..1d665886266b --- /dev/null +++ b/packages/kit/test/apps/async/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "noEmit": true, + "resolveJsonModule": true + }, + "extends": "./.svelte-kit/tsconfig.json" +} diff --git a/packages/kit/test/apps/async/vite.config.js b/packages/kit/test/apps/async/vite.config.js new file mode 100644 index 000000000000..69200cdb7cd8 --- /dev/null +++ b/packages/kit/test/apps/async/vite.config.js @@ -0,0 +1,18 @@ +import * as path from 'node:path'; +import { sveltekit } from '@sveltejs/kit/vite'; + +/** @type {import('vite').UserConfig} */ +const config = { + build: { + minify: false + }, + clearScreen: false, + plugins: [sveltekit()], + server: { + fs: { + allow: [path.resolve('../../../src')] + } + } +}; + +export default config; diff --git a/packages/kit/test/apps/basics/src/hooks.server.js b/packages/kit/test/apps/basics/src/hooks.server.js index 34d0e9cba874..c988355d8ece 100644 --- a/packages/kit/test/apps/basics/src/hooks.server.js +++ b/packages/kit/test/apps/basics/src/hooks.server.js @@ -62,11 +62,6 @@ export const handleError = ({ event, error: e, status, message }) => { : { message: `${error.message} (${status} ${message})` }; }; -/** @type {import('@sveltejs/kit').HandleValidationError} */ -export const handleValidationError = ({ issues }) => { - return { message: issues[0].message }; -}; - export const handle = sequence( // eslint-disable-next-line prefer-arrow-callback -- this needs a name for tests function set_tracing_test_id({ event, resolve }) { diff --git a/packages/kit/test/apps/basics/src/routes/remote/event/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/event/+page.svelte deleted file mode 100644 index 3aa05f322ddc..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/event/+page.svelte +++ /dev/null @@ -1,9 +0,0 @@ - - - -{#await get_event() then event} -

route: {event.route.id}

-

pathname: {event.url.pathname}

-{/await} diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-non-exported/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/query-non-exported/+page.svelte deleted file mode 100644 index 5fc45d97d451..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/query-non-exported/+page.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - -{#await total() then t} -

{t}

-{/await} diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/query-redirect/+page.svelte deleted file mode 100644 index 2cf96c388267..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/+page.svelte +++ /dev/null @@ -1,2 +0,0 @@ -from page -from layout diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/+layout.svelte b/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/+layout.svelte deleted file mode 100644 index dbfd19f02d29..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-common-layout/+layout.svelte +++ /dev/null @@ -1,13 +0,0 @@ - - - -{#await layoutRedirect(page.url.pathname) then path} -

on page {path} (== {page.url.pathname})

-{/await} - -{@render children()} diff --git a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-page/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-page/+page.svelte deleted file mode 100644 index fa4783f66c60..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/query-redirect/from-page/+page.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - -{#await pageRedirect() then _} -

should never see this

-{/await} diff --git a/packages/kit/test/apps/basics/src/routes/remote/transport/+page.svelte b/packages/kit/test/apps/basics/src/routes/remote/transport/+page.svelte deleted file mode 100644 index 023c95be9909..000000000000 --- a/packages/kit/test/apps/basics/src/routes/remote/transport/+page.svelte +++ /dev/null @@ -1,8 +0,0 @@ - - - -{#await greeting() then x} -

{x.bar()}

-{/await} diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js index 350d91d0fbb3..a6730dd5c1c4 100644 --- a/packages/kit/test/apps/basics/test/client.test.js +++ b/packages/kit/test/apps/basics/test/client.test.js @@ -1800,312 +1800,3 @@ test.describe('routing', () => { await expect(page).toHaveURL((url) => url.pathname === '/routing'); }); }); - -test.describe('remote functions', () => { - test('preloading data works when the page component and server load both import a remote function', async ({ - page - }) => { - test.skip(!process.env.DEV, 'remote functions are only analysed in dev mode'); - await page.goto('/remote/dev'); - await page.locator('a[href="/remote/dev/preload"]').hover(); - await Promise.all([ - page.waitForTimeout(100), // wait for preloading to start - page.waitForLoadState('networkidle') // wait for preloading to finish - ]); - await page.click('a[href="/remote/dev/preload"]'); - await expect(page.locator('p')).toHaveText('foobar'); - }); -}); - -// have to run in serial because commands mutate in-memory data on the server -test.describe('remote function mutations', () => { - test.describe.configure({ mode: 'default' }); - test.afterEach(async ({ page }) => { - if (page.url().endsWith('/remote')) { - await page.click('#reset-btn'); - } - }); - - test('query.set works', async ({ page }) => { - await page.goto('/remote'); - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#set-btn'); - await expect(page.locator('#count-result')).toHaveText('999 / 999 (false)'); - await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) - expect(request_count).toBe(0); - }); - - test('hydrated data is reused', async ({ page }) => { - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - // only the calls in the template are done, not the one in the load function - expect(request_count).toBe(2); - }); - - test('command returns correct sum but does not refresh data by default', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#multiply-btn'); - await expect(page.locator('#command-result')).toHaveText('2'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - await page.waitForTimeout(100); // allow all requests to finish - expect(request_count).toBe(1); // 1 for the command, no refreshes - }); - - test('command returns correct sum and does client-initiated single flight mutation', async ({ - page - }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#multiply-refresh-btn'); - await expect(page.locator('#command-result')).toHaveText('3'); - await expect(page.locator('#count-result')).toHaveText('3 / 3 (false)'); - await page.waitForTimeout(100); // allow all requests to finish - expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response - }); - - test('command does server-initiated single flight mutation (refresh)', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#multiply-server-refresh-btn'); - await expect(page.locator('#command-result')).toHaveText('4'); - await expect(page.locator('#count-result')).toHaveText('4 / 4 (false)'); - await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) - expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response - }); - - test('command refresh after reading query reruns the query', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#multiply-server-refresh-after-read-btn'); - await expect(page.locator('#command-result')).toHaveText('6'); - await expect(page.locator('#count-result')).toHaveText('6 / 6 (false)'); - await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) - expect(request_count).toBe(1); - }); - - test('command does server-initiated single flight mutation (set)', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#multiply-server-set-btn'); - await expect(page.locator('#command-result')).toHaveText('8'); - await expect(page.locator('#count-result')).toHaveText('8 / 8 (false)'); - await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) - expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response - }); - - test('command does client-initiated single flight mutation with override', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - page.click('#multiply-override-refresh-btn'); - await expect(page.locator('#count-result')).toHaveText('6 / 6 (false)'); - await expect(page.locator('#command-result')).toHaveText('5'); - await expect(page.locator('#count-result')).toHaveText('5 / 5 (false)'); - await page.waitForTimeout(100); // allow all requests to finish (in case there are query refreshes which shouldn't happen) - expect(request_count).toBe(1); // no query refreshes, since that happens as part of the command response - }); - - test('query/command inside endpoint works', async ({ page }) => { - await page.goto('/remote/server-endpoint'); - - await page.getByRole('button', { name: 'get' }).click(); - await expect(page.locator('p')).toHaveText('get'); - - await page.getByRole('button', { name: 'post' }).click(); - await expect(page.locator('p')).toHaveText('post'); - }); - - test('prerendered entries not called in prod', async ({ page }) => { - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - await page.goto('/remote/prerender'); - - await page.click('#fetch-prerendered'); - await expect(page.locator('#fetch-prerendered')).toHaveText('yes'); - - await page.click('#fetch-not-prerendered'); - await expect(page.locator('#fetch-not-prerendered')).toHaveText('d'); - }); - - test('refreshAll reloads remote functions and load functions', async ({ page }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#refresh-all'); - await page.waitForTimeout(100); // allow things to rerun - expect(request_count).toBe(3); - }); - - test('refreshAll({ includeLoadFunctions: false }) reloads remote functions only', async ({ - page - }) => { - await page.goto('/remote'); - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('#refresh-remote-only'); - await page.waitForTimeout(100); // allow things to rerun - expect(request_count).toBe(2); - }); - - test('command tracks pending state', async ({ page }) => { - await page.goto('/remote'); - - // Initial pending should be 0 - await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); - - // Start a slow command - this will hang until we resolve it - await page.click('#command-deferred-btn'); - - // Check that pending has incremented to 1 - await expect(page.locator('#command-pending')).toHaveText('Command pending: 1'); - - // Resolve the deferred command - await page.click('#resolve-deferreds'); - - // Wait for the command to complete and pending to go back to 0 - await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); - }); - - test('validation works', async ({ page }) => { - await page.goto('/remote/validation'); - await expect(page.locator('p')).toHaveText('pending'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('button:nth-of-type(1)'); - await expect(page.locator('p')).toHaveText('success'); - - await page.click('button:nth-of-type(2)'); - await expect(page.locator('p')).toHaveText('success'); - - await page.click('button:nth-of-type(3)'); - await expect(page.locator('p')).toHaveText('success'); - - await page.click('button:nth-of-type(4)'); - await expect(page.locator('p')).toHaveText('success'); - }); - - test('fields.set updates DOM before validate', async ({ page }) => { - await page.goto('/remote/form/imperative'); - - const input = page.locator('input[name="message"]'); - await input.fill('123'); - - await page.locator('#set-and-validate').click(); - - await expect(input).toHaveValue('hello'); - await expect(page.locator('#issue')).toHaveText('ok'); - }); - - test('command pending state is tracked correctly', async ({ page }) => { - await page.goto('/remote'); - - // Initially no pending commands - await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); - - // Start a slow command - this will hang until we resolve it - await page.click('#command-deferred-btn'); - - // Check that pending has incremented to 1 - await expect(page.locator('#command-pending')).toHaveText('Command pending: 1'); - - // Resolve the deferred command - await page.click('#resolve-deferreds'); - - // Wait for the command to complete and verify results - await expect(page.locator('#command-result')).toHaveText('7'); - - // Verify pending count returns to 0 - await expect(page.locator('#command-pending')).toHaveText('Command pending: 0'); - }); - - // TODO once we have async SSR adjust the test and move this into test.js - test('query.batch works', async ({ page }) => { - await page.goto('/remote/batch'); - - await expect(page.locator('#batch-result-1')).toHaveText('Buy groceries'); - await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog'); - await expect(page.locator('#batch-result-3')).toHaveText('Buy groceries'); - await expect(page.locator('#batch-result-4')).toHaveText('Error loading todo error: Not found'); - - let request_count = 0; - page.on('request', (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0)); - - await page.click('button'); - await page.waitForTimeout(100); // allow all requests to finish - expect(request_count).toBe(1); - }); - - test('query.batch set updates cache without extra request', async ({ page }) => { - await page.goto('/remote/batch'); - await page.click('#batch-reset-btn'); - await expect(page.locator('#batch-result-1')).toHaveText('Buy groceries'); - - let request_count = 0; - const handler = (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0); - page.on('request', handler); - - await page.click('#batch-set-btn'); - await expect(page.locator('#batch-result-1')).toHaveText('Buy cat food'); - await page.waitForTimeout(100); // allow all requests to finish - expect(request_count).toBe(1); // only the command request - }); - - test('query.batch refresh in command reuses single flight', async ({ page }) => { - await page.goto('/remote/batch'); - await page.click('#batch-reset-btn'); - await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog'); - - let request_count = 0; - const handler = (r) => (request_count += r.url().includes('/_app/remote') ? 1 : 0); - page.on('request', handler); - - await page.click('#batch-refresh-btn'); - await expect(page.locator('#batch-result-2')).toHaveText('Walk the dog (refreshed)'); - await page.waitForTimeout(100); // allow all requests to finish - expect(request_count).toBe(1); // only the command request - }); - - // TODO ditto - test('query works with transport', async ({ page }) => { - await page.goto('/remote/transport'); - - await expect(page.locator('h1')).toHaveText('hello from remote function!'); - }); -}); diff --git a/packages/kit/test/apps/basics/test/server.test.js b/packages/kit/test/apps/basics/test/server.test.js index eea785051107..b8a502d92d45 100644 --- a/packages/kit/test/apps/basics/test/server.test.js +++ b/packages/kit/test/apps/basics/test/server.test.js @@ -1364,13 +1364,6 @@ test.describe('tracing', () => { }); }); -test.describe('remote functions', () => { - test("doesn't write bundle to disk when treeshaking prerendered remote functions", () => { - test.skip(!!process.env.DEV, 'skip when in dev mode'); - expect(fs.existsSync(path.join(root, 'dist'))).toBe(false); - }); -}); - test.describe('asset preload', () => { if (!process.env.DEV) { test('injects Link headers', async ({ request }) => { diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js index 554a4d8ba59d..b29ab7d3447c 100644 --- a/packages/kit/test/apps/basics/test/test.js +++ b/packages/kit/test/apps/basics/test/test.js @@ -1600,509 +1600,6 @@ test.describe('getRequestEvent', () => { }); }); -test.describe('remote functions', () => { - test('query returns correct data', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote'); - await expect(page.locator('#echo-result')).toHaveText('Hello world'); - if (javaScriptEnabled) { - await expect(page.locator('#count-result')).toHaveText('0 / 0 (false)'); - } - }); - - test('query redirects on page load (query in common layout)', async ({ - page, - javaScriptEnabled - }) => { - // TODO remove once async SSR exists - if (!javaScriptEnabled) return; - - await page.goto('/remote/query-redirect'); - await page.click('a[href="/remote/query-redirect/from-common-layout"]'); - await expect(page.locator('#redirected')).toHaveText('redirected'); - await expect(page.locator('#layout-query')).toHaveText( - 'on page /remote/query-redirect/from-common-layout/redirected (== /remote/query-redirect/from-common-layout/redirected)' - ); - }); - - test('query redirects on page load (query on page)', async ({ page, javaScriptEnabled }) => { - // TODO remove once async SSR exists - if (!javaScriptEnabled) return; - - await page.goto('/remote/query-redirect'); - await page.click('a[href="/remote/query-redirect/from-page"]'); - await expect(page.locator('#redirected')).toHaveText('redirected'); - }); - - test('non-exported queries do not clobber each other', async ({ page, javaScriptEnabled }) => { - // TODO remove once async SSR exists - if (!javaScriptEnabled) return; - - await page.goto('/remote/query-non-exported'); - - await expect(page.locator('h1')).toHaveText('3'); - }); - - test('queries can access the route/url of the page they were called from', async ({ - page, - javaScriptEnabled, - clicknav - }) => { - // TODO remove once async SSR exists - if (!javaScriptEnabled) return; - - await page.goto('/remote'); - - await clicknav('[href="/remote/event"]'); - - await expect(page.locator('[data-id="route"]')).toHaveText('route: /remote/event'); - await expect(page.locator('[data-id="pathname"]')).toHaveText('pathname: /remote/event'); - }); - - test('form works', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote/form/basic'); - - if (javaScriptEnabled) { - // TODO remove the `if` — once async SSR lands these assertions should always succeed - await expect(page.getByText('message.current:')).toHaveText('message.current: initial'); - await expect(page.getByText('await get_message():')).toHaveText( - 'await get_message(): initial' - ); - } - - await page.fill('[data-unscoped] input', 'hello'); - await page.getByText('set message').click(); - - if (javaScriptEnabled) { - await expect(page.getByText('set_message.pending:')).toHaveText('set_message.pending: 1'); - await page.getByText('resolve deferreds').click(); - await expect(page.getByText('set_message.pending:')).toHaveText('set_message.pending: 0'); - - await expect(page.getByText('message.current:')).toHaveText('message.current: hello'); - await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); - } - - await expect(page.getByText('set_message.result')).toHaveText('set_message.result: hello'); - await expect(page.locator('[data-unscoped] input[name="message"]')).toHaveValue(''); - }); - - test('form submitters work', async ({ page }) => { - await page.goto('/remote/form/submitter'); - - await page.locator('button').click(); - - await expect(page.locator('#result')).toHaveText('hello'); - }); - - test('form updates inputs live', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote/form/live-update'); - - await page.fill('input', 'hello'); - - if (javaScriptEnabled) { - await expect(page.getByText('set_message.input.message:')).toHaveText( - 'set_message.input.message: hello' - ); - } - - await page.getByText('set message').click(); - - if (javaScriptEnabled) { - await page.getByText('resolve deferreds').click(); - } - - await expect(page.getByText('set_message.input.message:')).toHaveText( - 'set_message.input.message:' - ); - }); - - test('form reports validation issues', async ({ page }) => { - await page.goto('/remote/form/validation-issues'); - - await page.fill('input', 'invalid'); - await page.getByText('set message').click(); - - await page.getByText('message is invalid').waitFor(); - }); - - test('form handles unexpected error', async ({ page }) => { - await page.goto('/remote/form/unexpected-error'); - - await page.fill('input', 'unexpected error'); - await page.getByText('set message').click(); - - await page - .getByText('This is your custom error page saying: "oops (500 Internal Error)"') - .waitFor(); - }); - - test('form handles expected error', async ({ page }) => { - await page.goto('/remote/form/expected-error'); - - await page.fill('input', 'expected error'); - await page.getByText('set message').click(); - - await page.getByText('This is your custom error page saying: "oops"').waitFor(); - }); - - test('form redirects', async ({ page }) => { - await page.goto('/remote/form/redirect'); - - await page.fill('input', 'redirect'); - await page.getByText('set message').click(); - - await page.waitForURL('/remote'); - }); - - test('form.buttonProps works', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote/form/button-props'); - - await page.fill('[data-unscoped] input', 'backwards'); - await page.getByText('set reverse message').click(); - - if (javaScriptEnabled) { - await page.getByText('message.current: sdrawkcab').waitFor(); - await expect(page.getByText('await get_message():')).toHaveText( - 'await get_message(): sdrawkcab' - ); - } - - await expect(page.getByText('set_reverse_message.result')).toHaveText( - 'set_reverse_message.result: sdrawkcab' - ); - }); - - test('form scoping with for(...) works', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote/form/form-scoped'); - - await page.fill('[data-scoped] input', 'hello'); - await page.getByText('set scoped message').click(); - - if (javaScriptEnabled) { - await expect(page.getByText('scoped.pending:')).toHaveText('scoped.pending: 1'); - await page.getByText('resolve deferreds').click(); - await expect(page.getByText('scoped.pending:')).toHaveText('scoped.pending: 0'); - - await page.getByText('message.current: hello').waitFor(); - await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); - } - - await expect(page.getByText('scoped.result')).toHaveText( - 'scoped.result: hello (from: scoped:form-scoped)' - ); - await expect(page.locator('[data-scoped] input[name="message"]')).toHaveValue(''); - }); - - test('form enhance(...) works', async ({ page, javaScriptEnabled }) => { - await page.goto('/remote/form/enhanced'); - - await page.fill('[data-enhanced] input', 'hello'); - - // Click on the span inside the button to test the event.target vs event.currentTarget issue (#14159) - await page.locator('[data-enhanced] span').click(); - - if (javaScriptEnabled) { - await expect(page.getByText('enhanced.pending:')).toHaveText('enhanced.pending: 1'); - - await page.getByText('message.current: hello (override)').waitFor(); - - await page.getByText('resolve deferreds').click(); - await expect(page.getByText('enhanced.pending:')).toHaveText('enhanced.pending: 0'); - await expect(page.getByText('await get_message():')).toHaveText('await get_message(): hello'); - - // enhanced submission should not clear the input; the developer must do that at the appropriate time - await expect(page.locator('[data-enhanced] input[name="message"]')).toHaveValue('hello'); - } else { - await expect(page.locator('[data-enhanced] input[name="message"]')).toHaveValue(''); - } - - await expect(page.getByText('enhanced.result')).toHaveText( - 'enhanced.result: hello (from: enhanced:enhanced)' - ); - }); - - test('form preflight works', async ({ page, javaScriptEnabled }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/preflight'); - - for (const enhanced of [true, false]) { - const input = page.locator(enhanced ? '[data-enhanced] input' : '[data-default] input'); - const button = page.getByText(enhanced ? 'set enhanced number' : 'set number'); - - await input.fill('21'); - await button.click(); - await page.getByText('too big').waitFor(); - - await input.fill('9'); - await button.click(); - await page.getByText('too small').waitFor(); - - await input.fill('15'); - await button.click(); - await expect(page.getByText('number.current')).toHaveText('number.current: 15'); - } - }); - - test('form preflight-only validation works', async ({ page, javaScriptEnabled }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/preflight-only'); - - const a = page.locator('[name="a"]'); - const button = page.locator('button'); - const issues = page.locator('.issues'); - - await button.click(); - await expect(issues).toContainText('a is too short'); - await expect(issues).toContainText('b is too short'); - await expect(issues).toContainText('c is too short'); - - await a.fill('aaaaaaaa'); - await expect(issues).toContainText('a is too long'); - - // server issues should be preserved... - await expect(issues).toContainText('b is too short'); - await expect(issues).toContainText('c is too short'); - - // ...unless overridden by client issues - await expect(issues).not.toContainText('a is too short'); - }); - - test('form validate works', async ({ page, javaScriptEnabled }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/validate'); - - const myForm = page.locator('form#my-form'); - const foo = page.locator('input[name="foo"]'); - const bar = page.locator('input[name="bar"]'); - const submit = page.locator('button:has-text("imperative validation")'); - - await foo.fill('a'); - await expect(myForm).not.toContainText('Invalid type: Expected'); - - await bar.fill('g'); - await expect(myForm).toContainText('Invalid type: Expected ("d" | "e") but received "g"'); - - await bar.fill('d'); - await expect(myForm).not.toContainText('Invalid type: Expected'); - - await page.locator('#trigger-validate').click(); - await expect(myForm).toContainText( - 'Invalid type: Expected "submitter" but received "incorrect_value"' - ); - - // Test imperative validation - await foo.fill('c'); - await bar.fill('d'); - await submit.click(); - await expect(myForm).toContainText('Imperative: foo cannot be c'); - - const nestedValue = page.locator('input[name="nested.value"]'); - const validate = page.locator('button#validate'); - const allIssues = page.locator('#allIssues'); - - await nestedValue.fill('in'); - await validate.click(); - await expect(allIssues).toContainText('"path":["nested","value"]'); - }); - - test('form validation issues cleared', async ({ page, javaScriptEnabled }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/validate'); - - const baz = page.locator('input[name="baz"]'); - const submit = page.locator('#my-form-2 button'); - - await baz.fill('c'); - await submit.click(); - await expect(page.locator('#my-form-2')).toContainText('Invalid type: Expected'); - - await baz.fill('a'); - await submit.click(); - await expect(page.locator('#my-form-2')).not.toContainText('Invalid type: Expected'); - await expect(page.locator('[data-error]')).toHaveText('An error occurred'); - - await baz.fill('c'); - await submit.click(); - await expect(page.locator('#my-form-2')).toContainText('Invalid type: Expected'); - - await baz.fill('b'); - await submit.click(); - await expect(page.locator('#my-form-2')).not.toContainText('Invalid type: Expected'); - await expect(page.locator('[data-error]')).toHaveText('No error'); - }); - - test('form inputs excludes underscore-prefixed fields', async ({ page, javaScriptEnabled }) => { - if (javaScriptEnabled) return; - - await page.goto('/remote/form/underscore'); - - await page.fill('input[name="username"]', 'abcdefg'); - await page.fill('input[name="_password"]', 'pqrstuv'); - await page.locator('button').click(); - - await expect(page.locator('input[name="username"]')).toHaveValue('abcdefg'); - await expect(page.locator('input[name="_password"]')).toHaveValue(''); - }); - - test('prerendered entries not called in prod', async ({ page, clicknav }) => { - await page.goto('/remote/prerender'); - await clicknav('[href="/remote/prerender/whole-page"]'); - await expect(page.locator('#prerendered-data')).toHaveText('a c 中文 yes'); - - await page.goto('/remote/prerender'); - await clicknav('[href="/remote/prerender/functions-only"]'); - await expect(page.locator('#prerendered-data')).toHaveText('a c 中文 yes'); - }); - - test('form.fields.value() returns correct nested object structure', async ({ - page, - javaScriptEnabled - }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/value'); - - // Initially should be empty object or undefined values - const initialValue = await page.locator('#full-value').textContent(); - expect(JSON.parse(initialValue)).toEqual({}); - - // Fill leaf field - await page.fill('input[name="leaf"]', 'leaf-value'); - const afterLeaf = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterLeaf)).toEqual({ - leaf: 'leaf-value' - }); - - // Fill object.leaf field - await page.fill('input[name="object.leaf"]', 'object-leaf-value'); - const afterObjectLeaf = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterObjectLeaf)).toEqual({ - leaf: 'leaf-value', - object: { - leaf: 'object-leaf-value' - } - }); - - // Fill object.array fields - await page.fill('input[name="object.array[0]"]', 'array-item-1'); - const afterArrayItem1 = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterArrayItem1)).toEqual({ - leaf: 'leaf-value', - object: { - leaf: 'object-leaf-value', - array: ['array-item-1'] - } - }); - - await page.fill('input[name="object.array[1]"]', 'array-item-2'); - const afterArrayItem2 = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterArrayItem2)).toEqual({ - leaf: 'leaf-value', - object: { - leaf: 'object-leaf-value', - array: ['array-item-1', 'array-item-2'] - } - }); - - // Fill array[0].leaf field - await page.fill('input[name="array[0].leaf"]', 'array-0-leaf'); - const afterArray0 = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterArray0)).toEqual({ - leaf: 'leaf-value', - object: { - leaf: 'object-leaf-value', - array: ['array-item-1', 'array-item-2'] - }, - array: [{ leaf: 'array-0-leaf' }] - }); - - // Fill array[1].leaf field - await page.fill('input[name="array[1].leaf"]', 'array-1-leaf'); - const afterArray1 = await page.locator('#full-value').textContent(); - expect(JSON.parse(afterArray1)).toEqual({ - leaf: 'leaf-value', - object: { - leaf: 'object-leaf-value', - array: ['array-item-1', 'array-item-2'] - }, - array: [{ leaf: 'array-0-leaf' }, { leaf: 'array-1-leaf' }] - }); - - // Test nested object value access - const objectValue = await page.locator('#object-value').textContent(); - expect(JSON.parse(objectValue)).toEqual({ - leaf: 'object-leaf-value', - array: ['array-item-1', 'array-item-2'] - }); - - // Test array value access - const arrayValue = await page.locator('#array-value').textContent(); - expect(JSON.parse(arrayValue)).toEqual([{ leaf: 'array-0-leaf' }, { leaf: 'array-1-leaf' }]); - }); - - test('selects are not nuked when unrelated controls change', async ({ - page, - javaScriptEnabled - }) => { - if (!javaScriptEnabled) return; - - await page.goto('/remote/form/select-untouched'); - - await page.fill('input', 'hello'); - await expect(page.locator('select')).toHaveValue('one'); - }); - test('file uploads work', async ({ page }) => { - await page.goto('/remote/form/file-upload'); - - await page.locator('input[name="file1"]').setInputFiles({ - name: 'a.txt', - mimeType: 'text/plain', - buffer: Buffer.from('a') - }); - await page.locator('input[name="file2"]').setInputFiles({ - name: 'b.txt', - mimeType: 'text/plain', - buffer: Buffer.from('b') - }); - await page.locator('input[type="checkbox"]').check(); - await page.locator('button').click(); - - await expect(page.locator('pre')).toHaveText( - JSON.stringify({ - text: 'Hello world', - file1: 'a', - file2: 'b' - }) - ); - }); - test('large file uploads work', async ({ page }) => { - await page.goto('/remote/form/file-upload'); - - await page.locator('input[name="file1"]').setInputFiles({ - name: 'a.txt', - mimeType: 'text/plain', - buffer: Buffer.alloc(1024 * 1024 * 10) - }); - await page.locator('input[name="file2"]').setInputFiles({ - name: 'b.txt', - mimeType: 'text/plain', - buffer: Buffer.from('b') - }); - await page.locator('button').click(); - - await expect(page.locator('pre')).toHaveText( - JSON.stringify({ - text: 'Hello world', - file1: 1024 * 1024 * 10, - file2: 1 - }) - ); - }); -}); - test.describe('params prop', () => { test('params prop is passed to the page', async ({ page, clicknav }) => { await page.goto('/params-prop'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 911efa0a043c..53780fb1c006 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -94,8 +94,8 @@ catalogs: specifier: ^5.42.1 version: 5.42.2 svelte-check: - specifier: ^4.1.1 - version: 4.1.1 + specifier: ^4.3.4 + version: 4.3.4 svelte-preprocess: specifier: ^6.0.0 version: 6.0.0 @@ -625,7 +625,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -633,6 +633,30 @@ importers: specifier: 'catalog:' version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/apps/async: + devDependencies: + '@sveltejs/kit': + specifier: workspace:^ + version: link:../../.. + '@sveltejs/vite-plugin-svelte': + specifier: 'catalog:' + version: 6.0.0-next.3(svelte@5.42.2)(vite@6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0)) + svelte: + specifier: 'catalog:' + version: 5.42.2 + svelte-check: + specifier: 'catalog:' + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + typescript: + specifier: ^5.5.4 + version: 5.8.3 + valibot: + specifier: 'catalog:' + version: 1.2.0(typescript@5.8.3) + vite: + specifier: 'catalog:' + version: 6.3.6(@types/node@18.19.119)(jiti@2.4.2)(lightningcss@1.30.1)(yaml@2.8.0) + packages/kit/test/apps/basics: devDependencies: '@opentelemetry/api': @@ -658,7 +682,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) test-redirect-importer: specifier: workspace:* version: link:../../../../test-redirect-importer @@ -718,7 +742,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -739,7 +763,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -760,7 +784,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -781,7 +805,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -805,7 +829,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -829,7 +853,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -856,7 +880,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -877,7 +901,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -898,7 +922,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -928,7 +952,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -952,7 +976,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -976,7 +1000,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1000,7 +1024,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1021,7 +1045,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1042,7 +1066,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1063,7 +1087,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1084,7 +1108,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1105,7 +1129,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1126,7 +1150,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1147,7 +1171,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1168,7 +1192,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1189,7 +1213,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1210,7 +1234,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1231,7 +1255,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1252,7 +1276,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1276,7 +1300,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1300,7 +1324,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.4 version: 5.8.3 @@ -1409,7 +1433,7 @@ importers: version: 5.42.2 svelte-check: specifier: 'catalog:' - version: 4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) + version: 4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3) typescript: specifier: ^5.5.0 version: 5.8.3 @@ -6616,8 +6640,8 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.1.1: - resolution: {integrity: sha512-NfaX+6Qtc8W/CyVGS/F7/XdiSSyXz+WGYA9ZWV3z8tso14V2vzjfXviKaTFEzB7g8TqfgO2FOzP6XT4ApSTUTw==} + svelte-check@4.3.4: + resolution: {integrity: sha512-DVWvxhBrDsd+0hHWKfjP99lsSXASeOhHJYyuKOFYJcP7ThfSCKgjVarE8XfuMWpS5JV3AlDf+iK1YGGo2TACdw==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: @@ -12892,7 +12916,7 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.1.1(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3): + svelte-check@4.3.4(picomatch@4.0.3)(svelte@5.42.2)(typescript@5.8.3): dependencies: '@jridgewell/trace-mapping': 0.3.25 chokidar: 4.0.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6228fbf48785..52556b76f569 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -41,7 +41,7 @@ catalog: semver: ^7.5.4 sirv-cli: ^3.0.0 svelte: ^5.42.1 - svelte-check: ^4.1.1 + svelte-check: ^4.3.4 svelte-preprocess: ^6.0.0 typescript-eslint: ^8.43.0 valibot: ^1.1.0