Repository: sveltejs/kit
Pull request #8410 (2023-01-09 15:31:20)

[chore] use locator to click during playwright tests
this is what playwright recommends

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 138720c9edd40386e90069b3f3a2d01e0891d3d8

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/sveltejs/kit/new/locator-click?filename=.changeset/serious-needles-begin.md&value=---%0A%22%40sveltejs%2Fkit%22%3A%20patch%0A%22test-basics%22%3A%20patch%0A---%0A%0A%5Bchore%5D%20use%20locator%20to%20click%20during%20playwright%20tests%0A)



Patch:
diff --git a/.changeset/big-cobras-sip.md b/.changeset/big-cobras-sip.md
deleted file mode 100644
index 4436c6b4e..000000000
--- a/.changeset/big-cobras-sip.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-Disable illegal import detection when running unit tests
diff --git a/.changeset/breezy-walls-provide.md b/.changeset/breezy-walls-provide.md
deleted file mode 100644
index 28b778bfa..000000000
--- a/.changeset/breezy-walls-provide.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-only prerender a given dependency once
diff --git a/.changeset/cuddly-cows-vanish.md b/.changeset/cuddly-cows-vanish.md
deleted file mode 100644
index e4b8aa589..000000000
--- a/.changeset/cuddly-cows-vanish.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-Error in dev mode if global `fetch` is used with relative URL
diff --git a/.changeset/dry-poets-crash.md b/.changeset/dry-poets-crash.md
deleted file mode 100644
index 58eeacb84..000000000
--- a/.changeset/dry-poets-crash.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-`updated.check()` type changed to Promise<boolean>
diff --git a/.changeset/stupid-actors-look.md b/.changeset/stupid-actors-look.md
deleted file mode 100644
index b31e235a4..000000000
--- a/.changeset/stupid-actors-look.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-[fix] quote 'script' in CSP directives
diff --git a/.changeset/sweet-hairs-roll.md b/.changeset/sweet-hairs-roll.md
deleted file mode 100644
index fb969c331..000000000
--- a/.changeset/sweet-hairs-roll.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-fix route id comparison for load change detection
diff --git a/.changeset/wet-feet-lie.md b/.changeset/wet-feet-lie.md
deleted file mode 100644
index 6cee5b7d2..000000000
--- a/.changeset/wet-feet-lie.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-'@sveltejs/kit': patch
----
-
-[fix] don't add nonce attribute to `<link>` elements
diff --git a/documentation/docs/30-advanced/25-errors.md b/documentation/docs/30-advanced/25-errors.md
index 40ac2269e..249aa8dd2 100644
--- a/documentation/docs/30-advanced/25-errors.md
+++ b/documentation/docs/30-advanced/25-errors.md
@@ -106,6 +106,8 @@ export function handleError({ error, event }) {
 }
 ```
 
+> Make sure that `handleError` _never_ throws an error
+
 ## Responses
 
 If an error occurs inside `handle` or inside a [`+server.js`](/docs/routing#server) request handler, SvelteKit will respond with either a fallback error page or a JSON representation of the error object, depending on the request's `Accept` headers.
diff --git a/documentation/docs/60-appendix/10-migrating.md b/documentation/docs/60-appendix/10-migrating.md
index 759bec196..2e04f115c 100644
--- a/documentation/docs/60-appendix/10-migrating.md
+++ b/documentation/docs/60-appendix/10-migrating.md
@@ -150,7 +150,7 @@ See [the FAQ](/faq#integrations) for detailed information about integrations.
 
 ### HTML minifier
 
-Sapper includes `html-minifier` by default. SvelteKit does not include this, but it can be added as a [hook](/docs/hooks#server-hooks-handle):
+Sapper includes `html-minifier` by default. SvelteKit does not include this, but you can add it as a prod dependency and then use it through a [hook](/docs/hooks#server-hooks-handle):
 
 ```js
 // @filename: ambient.d.ts
diff --git a/package.json b/package.json
index 1b4e7d3ac..18698ed23 100644
--- a/package.json
+++ b/package.json
@@ -28,7 +28,7 @@
 		"@rollup/plugin-json": "^6.0.0",
 		"@rollup/plugin-node-resolve": "^15.0.1",
 		"@svitejs/changesets-changelog-github-compact": "^1.0.0",
-		"playwright": "^1.28.1",
+		"playwright": "^1.29.2",
 		"prettier": "^2.8.0",
 		"rollup": "^3.7.0",
 		"svelte": "^3.55.0",
diff --git a/packages/kit/CHANGELOG.md b/packages/kit/CHANGELOG.md
index 3a5baa878..d7d9d110a 100644
--- a/packages/kit/CHANGELOG.md
+++ b/packages/kit/CHANGELOG.md
@@ -1,5 +1,31 @@
 # @sveltejs/kit
 
+## 1.0.9
+
+### Patch Changes
+
+- Add `assetFileNames` to worker rollup options ([#8384](https://github.com/sveltejs/kit/pull/8384))
+
+## 1.0.8
+
+### Patch Changes
+
+- Disable illegal import detection when running unit tests ([#8365](https://github.com/sveltejs/kit/pull/8365))
+
+- only prerender a given dependency once ([#8376](https://github.com/sveltejs/kit/pull/8376))
+
+- Error in dev mode if global `fetch` is used with relative URL ([#8370](https://github.com/sveltejs/kit/pull/8370))
+
+- `updated.check()` type changed to Promise<boolean> ([#8400](https://github.com/sveltejs/kit/pull/8400))
+
+- Don't strip body in no-cors mode on the server ([#8412](https://github.com/sveltejs/kit/pull/8412))
+
+- [fix] quote 'script' in CSP directives ([#8372](https://github.com/sveltejs/kit/pull/8372))
+
+- fix route id comparison for load change detection ([#8399](https://github.com/sveltejs/kit/pull/8399))
+
+- [fix] don't add nonce attribute to `<link>` elements ([#8369](https://github.com/sveltejs/kit/pull/8369))
+
 ## 1.0.7
 
 ### Patch Changes
diff --git a/packages/kit/package.json b/packages/kit/package.json
index 18e143eff..2e246b3bc 100644
--- a/packages/kit/package.json
+++ b/packages/kit/package.json
@@ -1,6 +1,6 @@
 {
 	"name": "@sveltejs/kit",
-	"version": "1.0.7",
+	"version": "1.0.9",
 	"repository": {
 		"type": "git",
 		"url": "https://github.com/sveltejs/kit",
@@ -25,7 +25,7 @@
 		"undici": "5.14.0"
 	},
 	"devDependencies": {
-		"@playwright/test": "^1.28.1",
+		"@playwright/test": "^1.29.2",
 		"@types/connect": "^3.4.35",
 		"@types/marked": "^4.0.7",
 		"@types/mime": "^3.0.1",
diff --git a/packages/kit/src/exports/vite/build/utils.js b/packages/kit/src/exports/vite/build/utils.js
index 1198053c5..c5bf6e448 100644
--- a/packages/kit/src/exports/vite/build/utils.js
+++ b/packages/kit/src/exports/vite/build/utils.js
@@ -189,6 +189,7 @@ export function get_build_compile_config({ config, input, ssr, outDir }) {
 				output: {
 					entryFileNames: `${prefix}/workers/[name]-[hash].js`,
 					chunkFileNames: `${prefix}/workers/chunks/[name]-[hash].js`,
+					assetFileNames: `${prefix}/workers/assets/[name]-[hash][extname]`,
 					hoistTransitiveImports: false
 				}
 			}
diff --git a/packages/kit/src/runtime/server/fetch.js b/packages/kit/src/runtime/server/fetch.js
index 89f21b5db..8ff06eec8 100644
--- a/packages/kit/src/runtime/server/fetch.js
+++ b/packages/kit/src/runtime/server/fetch.js
@@ -63,17 +63,7 @@ export function create_fetch({ event, options, state, get_cookie_header }) {
 						if (cookie) request.headers.set('cookie', cookie);
 					}
 
-					let response = await fetch(request);
-
-					if (mode === 'no-cors') {
-						response = new Response('', {
-							status: response.status,
-							statusText: response.statusText,
-							headers: response.headers
-						});
-					}
-
-					return response;
+					return fetch(request);
 				}
 
 				/** @type {Response} */
diff --git a/packages/kit/src/runtime/server/page/load_data.js b/packages/kit/src/runtime/server/page/load_data.js
index f5f20c44f..7b12a46fe 100644
--- a/packages/kit/src/runtime/server/page/load_data.js
+++ b/packages/kit/src/runtime/server/page/load_data.js
@@ -107,129 +107,150 @@ export async function load_data({
 		params: event.params,
 		data: server_data_node?.data ?? null,
 		route: event.route,
-		fetch: async (input, init) => {
-			const cloned_body = input instanceof Request && input.body ? input.clone().body : null;
-			const response = await event.fetch(input, init);
+		fetch: create_universal_fetch(event, state, fetched, csr, resolve_opts),
+		setHeaders: event.setHeaders,
+		depends: () => {},
+		parent
+	});
 
-			const url = new URL(input instanceof Request ? input.url : input, event.url);
-			const same_origin = url.origin === event.url.origin;
+	const data = result ? await unwrap_promises(result) : null;
+	validate_load_response(data, /** @type {string} */ (event.route.id));
+	return data;
+}
+
+/**
+ * @param {Pick<import('types').RequestEvent, 'fetch' | 'url' | 'request' | 'route'>} event
+ * @param {import("types").SSRState} state
+ * @param {import("./types").Fetched[]} fetched
+ * @param {boolean} csr
+ * @param {Pick<Required<import("types").ResolveOptions>, 'filterSerializedResponseHeaders'>} resolve_opts
+ */
+export function create_universal_fetch(event, state, fetched, csr, resolve_opts) {
+	/**
+	 * @param {URL | RequestInfo} input
+	 * @param {RequestInit} [init]
+	 */
+	return async (input, init) => {
+		const cloned_body = input instanceof Request && input.body ? input.clone().body : null;
+		let response = await event.fetch(input, init);
 
-			/** @type {import('types').PrerenderDependency} */
-			let dependency;
+		const url = new URL(input instanceof Request ? input.url : input, event.url);
+		const same_origin = url.origin === event.url.origin;
 
-			if (same_origin) {
-				if (state.prerendering) {
-					dependency = { response, body: null };
-					state.prerendering.dependencies.set(url.pathname, dependency);
-				}
+		/** @type {import('types').PrerenderDependency} */
+		let dependency;
+
+		if (same_origin) {
+			if (state.prerendering) {
+				dependency = { response, body: null };
+				state.prerendering.dependencies.set(url.pathname, dependency);
+			}
+		} else {
+			// simulate CORS errors and "no access to body in no-cors mode" server-side for consistency with client-side behaviour
+			const mode = input instanceof Request ? input.mode : init?.mode ?? 'cors';
+			if (mode === 'no-cors') {
+				response = new Response('', {
+					status: response.status,
+					statusText: response.statusText,
+					headers: response.headers
+				});
 			} else {
-				// simulate CORS errors server-side for consistency with client-side behaviour
-				const mode = input instanceof Request ? input.mode : init?.mode ?? 'cors';
-				if (mode !== 'no-cors') {
-					const acao = response.headers.get('access-control-allow-origin');
-					if (!acao || (acao !== event.url.origin && acao !== '*')) {
-						throw new Error(
-							`CORS error: ${
-								acao ? 'Incorrect' : 'No'
-							} 'Access-Control-Allow-Origin' header is present on the requested resource`
-						);
-					}
+				const acao = response.headers.get('access-control-allow-origin');
+				if (!acao || (acao !== event.url.origin && acao !== '*')) {
+					throw new Error(
+						`CORS error: ${
+							acao ? 'Incorrect' : 'No'
+						} 'Access-Control-Allow-Origin' header is present on the requested resource`
+					);
 				}
 			}
+		}
 
-			const proxy = new Proxy(response, {
-				get(response, key, _receiver) {
-					async function text() {
-						const body = await response.text();
-
-						if (!body || typeof body === 'string') {
-							const status_number = Number(response.status);
-							if (isNaN(status_number)) {
-								throw new Error(
-									`response.status is not a number. value: "${
-										response.status
-									}" type: ${typeof response.status}`
-								);
-							}
-
-							fetched.push({
-								url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
-								method: event.request.method,
-								request_body: /** @type {string | ArrayBufferView | undefined} */ (
-									input instanceof Request && cloned_body
-										? await stream_to_string(cloned_body)
-										: init?.body
-								),
-								response_body: body,
-								response: response
-							});
-						}
+		const proxy = new Proxy(response, {
+			get(response, key, _receiver) {
+				async function text() {
+					const body = await response.text();
 
-						if (dependency) {
-							dependency.body = body;
+					if (!body || typeof body === 'string') {
+						const status_number = Number(response.status);
+						if (isNaN(status_number)) {
+							throw new Error(
+								`response.status is not a number. value: "${
+									response.status
+								}" type: ${typeof response.status}`
+							);
 						}
 
-						return body;
+						fetched.push({
+							url: same_origin ? url.href.slice(event.url.origin.length) : url.href,
+							method: event.request.method,
+							request_body: /** @type {string | ArrayBufferView | undefined} */ (
+								input instanceof Request && cloned_body
+									? await stream_to_string(cloned_body)
+									: init?.body
+							),
+							response_body: body,
+							response: response
+						});
 					}
 
-					if (key === 'arrayBuffer') {
-						return async () => {
-							const buffer = await response.arrayBuffer();
+					if (dependency) {
+						dependency.body = body;
+					}
 
-							if (dependency) {
-								dependency.body = new Uint8Array(buffer);
-							}
+					return body;
+				}
 
-							// TODO should buffer be inlined into the page (albeit base64'd)?
-							// any conditions in which it shouldn't be?
+				if (key === 'arrayBuffer') {
+					return async () => {
+						const buffer = await response.arrayBuffer();
 
-							return buffer;
-						};
-					}
+						if (dependency) {
+							dependency.body = new Uint8Array(buffer);
+						}
 
-					if (key === 'text') {
-						return text;
-					}
+						// TODO should buffer be inlined into the page (albeit base64'd)?
+						// any conditions in which it shouldn't be?
 
-					if (key === 'json') {
-						return async () => {
-							return JSON.parse(await text());
-						};
-					}
+						return buffer;
+					};
+				}
 
-					return Reflect.get(response, key, response);
+				if (key === 'text') {
+					return text;
+				}
+
+				if (key === 'json') {
+					return async () => {
+						return JSON.parse(await text());
+					};
 				}
-			});
-
-			if (csr) {
-				// ensure that excluded headers can't be read
-				const get = response.headers.get;
-				response.headers.get = (key) => {
-					const lower = key.toLowerCase();
-					const value = get.call(response.headers, lower);
-					if (value && !lower.startsWith('x-sveltekit-')) {
-						const included = resolve_opts.filterSerializedResponseHeaders(lower, value);
-						if (!included) {
-							throw new Error(
-								`Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://kit.svelte.dev/docs/hooks#server-hooks-handle (at ${event.route.id})`
-							);
-						}
-					}
 
-					return value;
-				};
+				return Reflect.get(response, key, response);
 			}
+		});
 
-			return proxy;
-		},
-		setHeaders: event.setHeaders,
-		depends: () => {},
-		parent
-	});
+		if (csr) {
+			// ensure that excluded headers can't be read
+			const get = response.headers.get;
+			response.headers.get = (key) => {
+				const lower = key.toLowerCase();
+				const value = get.call(response.headers, lower);
+				if (value && !lower.startsWith('x-sveltekit-')) {
+					const included = resolve_opts.filterSerializedResponseHeaders(lower, value);
+					if (!included) {
+						throw new Error(
+							`Failed to get response header "${lower}" — it must be included by the \`filterSerializedResponseHeaders\` option: https://kit.svelte.dev/docs/hooks#server-hooks-handle (at ${event.route.id})`
+						);
+					}
+				}
 
-	const data = result ? await unwrap_promises(result) : null;
-	validate_load_response(data, /** @type {string} */ (event.route.id));
-	return data;
+				return value;
+			};
+		}
+
+		return proxy;
+	};
 }
 
 /**
diff --git a/packages/kit/src/runtime/server/page/load_data.spec.js b/packages/kit/src/runtime/server/page/load_data.spec.js
new file mode 100644
index 000000000..493cbcfc5
--- /dev/null
+++ b/packages/kit/src/runtime/server/page/load_data.spec.js
@@ -0,0 +1,73 @@
+import { test } from 'uvu';
+import * as assert from 'uvu/assert';
+import { create_universal_fetch } from './load_data.js';
+
+/**
+ * @param {Partial<Pick<import('types').RequestEvent, 'fetch' | 'url' | 'request' | 'route'>>} event
+ */
+function create_fetch(event) {
+	event.fetch = event.fetch || (async () => new Response('foo'));
+	event.request = event.request || new Request('doesnt:matter');
+	event.route = event.route || { id: 'foo' };
+	event.url = event.url || new URL('https://domain-a.com');
+	return create_universal_fetch(
+		/** @type {Pick<import('types').RequestEvent, 'fetch' | 'url' | 'request' | 'route'>} */ (
+			event
+		),
+		{ getClientAddress: () => '' },
+		[],
+		true,
+		{
+			filterSerializedResponseHeaders: () => false
+		}
+	);
+}
+
+test('sets body to empty when mode is no-cors', async () => {
+	const fetch = create_fetch({});
+	const response = await fetch('https://domain-b.com', { mode: 'no-cors' });
+	const text = await response.text();
+	assert.equal(text, '');
+});
+
+test('keeps body when mode isnt no-cors on same domain', async () => {
+	const fetch = create_fetch({});
+	const response = await fetch('https://domain-a.com');
+	const text = await response.text();
+	assert.equal(text, 'foo');
+});
+
+test('succeeds when acao header present on cors', async () => {
+	const fetch = create_fetch({
+		fetch: async () => new Response('foo', { headers: { 'access-control-allow-origin': '*' } })
+	});
+	const response = await fetch('https://domain-a.com');
+	const text = await response.text();
+	assert.equal(text, 'foo');
+});
+
+test('errors when no acao header present on cors', async () => {
+	const fetch = create_fetch({});
+	try {
+		const response = await fetch('https://domain-b.com');
+		await response.text();
+		assert.unreachable('should have thrown cors error');
+	} catch (e) {
+		assert.ok(e instanceof Error);
+		assert.match(
+			e.message,
+			/CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource/
+		);
+	}
+});
+
+test('errors when trying to access non-serialized request headers on the server', async () => {
+	const fetch = create_fetch({});
+	const response = await fetch('https://domain-a.com');
+	assert.throws(
+		() => response.headers.get('content-type'),
+		/Failed to get response header "content-type" — it must be included by the `filterSerializedResponseHeaders` option/
+	);
+});
+
+test.run();
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0ced942b4..6ce043ba1 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -9,7 +9,7 @@ importers:
       '@rollup/plugin-json': ^6.0.0
       '@rollup/plugin-node-resolve': ^15.0.1
       '@svitejs/changesets-changelog-github-compact': ^1.0.0
-      playwright: ^1.28.1
+      playwright: ^1.29.2
       prettier: ^2.8.0
       rollup: ^3.7.0
       svelte: ^3.55.0
@@ -22,7 +22,7 @@ importers:
       '@rollup/plugin-json': 6.0.0_rollup@3.7.0
       '@rollup/plugin-node-resolve': 15.0.1_rollup@3.7.0
       '@svitejs/changesets-changelog-github-compact': 1.0.0
-      playwright: 1.28.1
+      playwright: 1.29.2
       prettier: 2.8.0
       rollup: 3.7.0
       svelte: 3.55.0
@@ -218,7 +218,7 @@ importers:
       kleur: 4.1.5
       prompts: 2.4.2
     devDependencies:
-      '@playwright/test': 1.28.1
+      '@playwright/test': 1.29.2
       '@types/gitignore-parser': 0.0.0
       '@types/prettier': 2.7.1
       '@types/prompts': 2.4.1
@@ -257,7 +257,7 @@ importers:
 
   packages/kit:
     specifiers:
-      '@playwright/test': ^1.28.1
+      '@playwright/test': ^1.29.2
       '@sveltejs/vite-plugin-svelte': ^2.0.0
       '@types/connect': ^3.4.35
       '@types/cookie': ^0.5.1
@@ -299,7 +299,7 @@ importers:
       tiny-glob: 0.2.9
       undici: 5.14.0
     devDependencies:
-      '@playwright/test': 1.28.1
+      '@playwright/test': 1.29.2
       '@types/connect': 3.4.35
       '@types/marked': 4.0.7
       '@types/mime': 3.0.1
@@ -1239,13 +1239,13 @@ packages:
       fastq: 1.13.0
     dev: true
 
-  /@playwright/test/1.28.1:
-    resolution: {integrity: sha512-xN6spdqrNlwSn9KabIhqfZR7IWjPpFK1835tFNgjrlysaSezuX8PYUwaz38V/yI8TJLG9PkAMEXoHRXYXlpTPQ==}
+  /@playwright/test/1.29.2:
+    resolution: {integrity: sha512-+3/GPwOgcoF0xLz/opTnahel1/y42PdcgZ4hs+BZGIUjtmEFSXGg+nFoaH3NSmuc7a6GSFwXDJ5L7VXpqzigNg==}
     engines: {node: '>=14'}
     hasBin: true
     dependencies:
       '@types/node': 18.11.11
-      playwright-core: 1.28.1
+      playwright-core: 1.29.2
     dev: true
 
   /@polka/url/1.0.0-next.21:
@@ -3457,19 +3457,19 @@ packages:
       find-up: 4.1.0
     dev: true
 
-  /playwright-core/1.28.1:
-    resolution: {integrity: sha512-3PixLnGPno0E8rSBJjtwqTwJe3Yw72QwBBBxNoukIj3lEeBNXwbNiKrNuB1oyQgTBw5QHUhNO3SteEtHaMK6ag==}
+  /playwright-core/1.29.2:
+    resolution: {integrity: sha512-94QXm4PMgFoHAhlCuoWyaBYKb92yOcGVHdQLoxQ7Wjlc7Flg4aC/jbFW7xMR52OfXMVkWicue4WXE7QEegbIRA==}
     engines: {node: '>=14'}
     hasBin: true
     dev: true
 
-  /playwright/1.28.1:
-    resolution: {integrity: sha512-92Sz6XBlfHlb9tK5UCDzIFAuIkHHpemA9zwUaqvo+w7sFMSmVMGmvKcbptof/eJObq63PGnMhM75x7qxhTR78Q==}
+  /playwright/1.29.2:
+    resolution: {integrity: sha512-hKBYJUtdmYzcjdhYDkP9WGtORwwZBBKAW8+Lz7sr0ZMxtJr04ASXVzH5eBWtDkdb0c3LLFsehfPBTRfvlfKJOA==}
     engines: {node: '>=14'}
     hasBin: true
     requiresBuild: true
     dependencies:
-      playwright-core: 1.28.1
+      playwright-core: 1.29.2
     dev: true
 
   /polka/1.0.0-next.22:


Test patch:
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/+page.js b/packages/kit/test/apps/basics/src/routes/load/cors/+page.js
deleted file mode 100644
index 9fbd10aed..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/+page.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/** @type {import('./$types').PageLoad} */
-export async function load({ fetch, url }) {
-	const res = await fetch(`http://localhost:${url.searchParams.get('port')}`);
-
-	return {
-		text: await res.text()
-	};
-}
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/cors/+page.svelte
deleted file mode 100644
index a5063216b..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/+page.svelte
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-	/** @type {import('./$types').PageData} */
-	export let data;
-</script>
-
-<h1>{@html data.text}</h1>
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.js b/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.js
deleted file mode 100644
index 70614cbbc..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.js
+++ /dev/null
@@ -1,10 +0,0 @@
-/** @type {import('./$types').PageLoad} */
-export async function load({ fetch, url }) {
-	const res = await fetch(`http://localhost:${url.searchParams.get('port')}`, {
-		mode: 'no-cors'
-	});
-
-	return {
-		text: await res.text()
-	};
-}
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.svelte
deleted file mode 100644
index d689a601e..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/no-cors/+page.svelte
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-	/** @type {import('./$types').PageData} */
-	export let data;
-</script>
-
-<h1>result: {@html data.text}</h1>
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.server.js b/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.server.js
deleted file mode 100644
index 7a412012c..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.server.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/** @type {import('./$types').PageServerLoad} */
-export async function load({ fetch, url }) {
-	const res = await fetch(`http://localhost:${url.searchParams.get('port')}`);
-
-	return {
-		text: await res.text()
-	};
-}
diff --git a/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.svelte
deleted file mode 100644
index a5063216b..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/cors/server-only/+page.svelte
+++ /dev/null
@@ -1,6 +0,0 @@
-<script>
-	/** @type {import('./$types').PageData} */
-	export let data;
-</script>
-
-<h1>{@html data.text}</h1>
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request-headers-invalid-access/+page.js b/packages/kit/test/apps/basics/src/routes/load/fetch-request-headers-invalid-access/+page.js
deleted file mode 100644
index eda2a2da8..000000000
--- a/packages/kit/test/apps/basics/src/routes/load/fetch-request-headers-invalid-access/+page.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/** @type {import('./$types').PageLoad} */
-export async function load({ fetch }) {
-	let res = await fetch('./irrelevant', {
-		method: 'GET'
-	});
-
-	res.headers.get('content-type');
-}
diff --git a/packages/kit/test/apps/basics/src/routes/load/fetch-request-headers-invalid-access/+page.svelte b/packages/kit/test/apps/basics/src/routes/load/fetch-request-headers-invalid-access/+page.svelte
deleted file mode 100644
index e69de29bb..000000000
diff --git a/packages/kit/test/apps/basics/test/test.js b/packages/kit/test/apps/basics/test/test.js
index 0a8476d16..b6a8c844c 100644
--- a/packages/kit/test/apps/basics/test/test.js
+++ b/packages/kit/test/apps/basics/test/test.js
@@ -838,8 +838,7 @@ test.describe('Load', () => {
 		baseURL,
 		page,
 		clicknav,
-		javaScriptEnabled,
-		browserName
+		javaScriptEnabled
 	}) => {
 		await page.goto('/load');
 		await clicknav('[href="/load/fetch-request-headers"]');
@@ -856,8 +855,8 @@ test.describe('Load', () => {
 				referer: `${baseURL}/load`,
 				// these headers aren't particularly useful, but they allow us to verify
 				// that page headers are being forwarded
-				'sec-fetch-dest': browserName === 'webkit' ? undefined : 'empty',
-				'sec-fetch-mode': browserName === 'webkit' ? undefined : 'cors',
+				'sec-fetch-dest': 'empty',
+				'sec-fetch-mode': 'cors',
 				connection: 'keep-alive'
 			});
 		} else {
@@ -868,17 +867,6 @@ test.describe('Load', () => {
 		}
 	});
 
-	test('errors when trying to access non-serialized request headers on the server', async ({
-		page,
-		read_errors
-	}) => {
-		await page.goto('/load/fetch-request-headers-invalid-access');
-
-		expect(read_errors(`/load/fetch-request-headers-invalid-access`).message).toContain(
-			'Failed to get response header "content-type" — it must be included by the `filterSerializedResponseHeaders` option'
-		);
-	});
-
 	test('exposes rawBody as a DataView to endpoints', async ({ page, clicknav }) => {
 		await page.goto('/load/raw-body');
 		await clicknav('[href="/load/raw-body/dataview"]');
@@ -952,37 +940,6 @@ test.describe('Load', () => {
 
 		expect(await page.textContent('h1')).toBe('true');
 	});
-
-	test('CORS errors are simulated server-side for shared load functions', async ({
-		page,
-		read_errors
-	}) => {
-		const { port, close } = await start_server(async (req, res) => {
-			res.end('hello');
-		});
-
-		await page.goto(`/load/cors?port=${port}`);
-		expect(await page.textContent('h1')).toBe('500');
-		expect(read_errors(`/load/cors`).message).toContain(
-			`CORS error: No 'Access-Control-Allow-Origin' header is present on the requested resource`
-		);
-
-		await page.goto(`/load/cors/no-cors?port=${port}`);
-		expect(await page.textContent('h1')).toBe('result: ');
-
-		await close();
-	});
-
-	test('CORS errors are skipped for server-only load functions', async ({ page }) => {
-		const { port, close } = await start_server(async (req, res) => {
-			res.end('hello');
-		});
-
-		await page.goto(`/load/cors/server-only?port=${port}`);
-		expect(await page.textContent('h1')).toBe('hello');
-
-		await close();
-	});
 });
 
 test.describe('Nested layouts', () => {
@@ -1297,7 +1254,7 @@ test.describe('Redirects', () => {
 	test('prevents redirect loops', async ({ baseURL, page, javaScriptEnabled, browserName }) => {
 		await page.goto('/redirect');
 
-		await page.click('[href="/redirect/loopy/a"]');
+		await page.locator('[href="/redirect/loopy/a"]').click();
 
 		if (javaScriptEnabled) {
 			await page.waitForSelector('#message');
@@ -1490,8 +1447,7 @@ test.describe('Routing', () => {
 
 	test('does not attempt client-side navigation to server routes', async ({ page }) => {
 		await page.goto('/routing');
-		await page.click('[href="/routing/ambiguous/ok.json"]');
-		await page.waitForLoadState('networkidle');
+		await page.locator('[href="/routing/ambiguous/ok.json"]').click();
 		expect(await page.textContent('body')).toBe('ok');
 	});
 
@@ -1560,7 +1516,7 @@ test.describe('Routing', () => {
 	}) => {
 		await page.goto('/routing/hashes/a');
 
-		await page.click('[href="#hash-target"]');
+		await page.locator('[href="#hash-target"]').click();
 		await clicknav('[href="/routing/hashes/b"]');
 
 		await page.goBack();
@@ -1624,6 +1580,7 @@ test.describe('Routing', () => {
 			await page.goto(`/routing?port=${port}`);
 			await Promise.all([
 				page.click(`[href="http://localhost:${port}"]`),
+				// assert that the app can visit a URL not owned by the app without crashing
 				page.waitForURL(`http://localhost:${port}/`)
 			]);
 		} finally {
@@ -1656,7 +1613,7 @@ test.describe('Routing', () => {
 		expect(await page.textContent('h1')).toBe('xyz/abc');
 		expect(await page.textContent('h2')).toBe('xyz/abc');
 
-		await page.click('[href="/routing/rest/xyz/abc/qwe/deep.json"]');
+		await page.locator('[href="/routing/rest/xyz/abc/qwe/deep.json"]').click();
 		expect(await page.textContent('body')).toBe('xyz/abc/qwe');
 	});
 
@@ -1711,7 +1668,7 @@ test.describe('Routing', () => {
 		clicknav
 	}) => {
 		await page.goto('/routing/cancellation');
-		await page.click('[href="/routing/cancellation/a"]');
+		await page.locator('[href="/routing/cancellation/a"]').click();
 		await clicknav('[href="/routing/cancellation/b"]');
 
 		expect(await page.url()).toBe(`${baseURL}/routing/cancellation/b`);
@@ -1892,10 +1849,10 @@ test.describe('Actions', () => {
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
 
 		if (javaScriptEnabled) {
-			await page.click('button.increment-success');
+			await page.locator('button.increment-success').click();
 			await expect(page.locator('pre')).toHaveText(JSON.stringify({ count: 0 }));
 
-			await page.click('button.increment-invalid');
+			await page.locator('button.increment-invalid').click();
 			await expect(page.locator('pre')).toHaveText(JSON.stringify({ count: 1 }));
 		}
 	});
@@ -1909,10 +1866,10 @@ test.describe('Actions', () => {
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
 
 		if (javaScriptEnabled) {
-			await page.click('button.increment-success');
+			await page.locator('button.increment-success').click();
 			await expect(page.locator('pre')).toHaveText(JSON.stringify({ count: 0 }));
 
-			await page.click('button.invalidateAll');
+			await page.locator('button.invalidateAll').click();
 			await page.waitForTimeout(500);
 			await expect(page.locator('pre')).toHaveText(JSON.stringify({ count: 0 }));
 			await app.goto('/actions/enhance');
@@ -1928,7 +1885,7 @@ test.describe('Actions', () => {
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
 
 		if (javaScriptEnabled) {
-			await page.click('button.redirect');
+			await page.locator('button.redirect').click();
 			await expect(page.locator('footer')).toHaveText('Custom layout');
 		}
 	});
@@ -1938,7 +1895,7 @@ test.describe('Actions', () => {
 		expect(await page.textContent('pre')).toBe(JSON.stringify(null));
 
 		if (javaScriptEnabled) {
-			await page.click('button.error');
+			await page.locator('button.error').click();
 			await expect(page.locator('p')).toHaveText(
 				'This is your custom error page saying: "Unexpected Form Error"'
 			);
@@ -2103,11 +2060,11 @@ test.describe.serial('Cookies API', () => {
 		let span = page.locator('#cookie-value');
 		expect(await span.innerText()).toContain('undefined');
 
-		await page.click('button#teapot');
+		await page.locator('button#teapot').click();
 		await expect(page.locator('#cookie-value')).toHaveText('teapot');
 
 		// setting a different value...
-		await page.click('button#janeAusten');
+		await page.locator('button#janeAusten').click();
 		await expect(page.locator('#cookie-value')).toHaveText('Jane Austen');
 	});