Skip to content

Commit fea1cb7

Browse files
committed
add e2e test app
1 parent af5d322 commit fea1cb7

21 files changed

+298
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
.DS_Store
2+
node_modules
3+
/build
4+
/.svelte-kit
5+
/package
6+
.env
7+
.env.*
8+
!.env.example
9+
vite.config.js.timestamp-*
10+
vite.config.ts.timestamp-*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@sentry:registry=http://127.0.0.1:4873
2+
@sentry-internal:registry=http://127.0.0.1:4873
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SvelteKit 2 TwP
2+
3+
E2E test app for testing Tracing Without Performance in a (SvelteKit) meta framework scenario
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{
2+
"name": "sveltekit-2-svelte-5",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"dev": "vite dev",
7+
"build": "vite build",
8+
"preview": "vite preview",
9+
"proxy": "node start-event-proxy.mjs",
10+
"clean": "npx rimraf node_modules pnpm-lock.yaml",
11+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
12+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
13+
"test:prod": "TEST_ENV=production playwright test",
14+
"test:build": "pnpm install && npx playwright install && pnpm build",
15+
"test:assert": "pnpm test:prod"
16+
},
17+
"dependencies": {
18+
"@sentry/sveltekit": "latest || *"
19+
},
20+
"devDependencies": {
21+
"@playwright/test": "^1.44.1",
22+
"@sentry-internal/test-utils": "link:../../../test-utils",
23+
"@sentry/types": "latest || *",
24+
"@sentry/utils": "latest || *",
25+
"@sveltejs/adapter-auto": "^3.0.0",
26+
"@sveltejs/kit": "^2.0.0",
27+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
28+
"svelte": "^5.0.0-next.115",
29+
"svelte-check": "^3.6.0",
30+
"tslib": "^2.4.1",
31+
"typescript": "^5.0.0",
32+
"vite": "^5.0.3"
33+
},
34+
"type": "module"
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const config = getPlaywrightConfig({
4+
startCommand: 'pnpm preview --port 3030',
5+
port: 3030,
6+
});
7+
8+
export default config;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// See https://kit.svelte.dev/docs/types#app
2+
// for information about these interfaces
3+
declare global {
4+
namespace App {
5+
// interface Error {}
6+
// interface Locals {}
7+
// interface PageData {}
8+
// interface PageState {}
9+
// interface Platform {}
10+
}
11+
}
12+
13+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1" />
7+
%sveltekit.head%
8+
</head>
9+
<body data-sveltekit-preload-data="hover">
10+
<div style="display: contents">%sveltekit.body%</div>
11+
</body>
12+
</html>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { env } from '$env/dynamic/public';
2+
import * as Sentry from '@sentry/sveltekit';
3+
4+
console.log('dsn', env.PUBLIC_E2E_TEST_DSN);
5+
6+
Sentry.init({
7+
environment: 'qa', // dynamic sampling bias to keep transactions
8+
dsn: env.PUBLIC_E2E_TEST_DSN,
9+
release: '1.0.0',
10+
tunnel: `http://localhost:3031/`, // proxy server
11+
});
12+
13+
const myErrorHandler = ({ error, event }: any) => {
14+
console.error('An error occurred on the client side:', error, event);
15+
};
16+
17+
export const handleError = Sentry.handleErrorWithSentry(myErrorHandler);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { E2E_TEST_DSN } from '$env/static/private';
2+
import * as Sentry from '@sentry/sveltekit';
3+
4+
Sentry.init({
5+
environment: 'qa', // dynamic sampling bias to keep transactions
6+
dsn: E2E_TEST_DSN,
7+
tunnel: `http://localhost:3031/`, // proxy server
8+
});
9+
10+
// not logging anything to console to avoid noise in the test output
11+
export const handleError = Sentry.handleErrorWithSentry(() => {});
12+
13+
export const handle = Sentry.sentryHandle();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<script lang="ts">
2+
import { onMount } from "svelte";
3+
4+
onMount(() => {
5+
// Indicate that the SvelteKit app was hydrated
6+
document.body.classList.add("hydrated");
7+
});
8+
9+
10+
</script>
11+
12+
<h1>Sveltekit E2E Test app</h1>
13+
<div data-sveltekit-preload-data="off">
14+
<slot></slot>
15+
</div>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<h1>Welcome to SvelteKit 2 with Svelte 5!</h1>
2+
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
3+
4+
<ul>
5+
<li>
6+
<a href="/errors">error route</a>
7+
</li>
8+
</ul>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as Sentry from '@sentry/sveltekit';
2+
3+
export const load = async ({ url }) => {
4+
if (!url.search) {
5+
Sentry.captureException(new Error('No search query provided'));
6+
return {
7+
error: 'No search query provided',
8+
};
9+
}
10+
return {
11+
message: 'hi',
12+
};
13+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<script lang="ts">
2+
import { onMount } from 'svelte';
3+
4+
export let data;
5+
6+
onMount(() => {
7+
8+
if (data.error) {
9+
throw new Error('Client Error');
10+
}
11+
});
12+
</script>
13+
14+
<p>
15+
Data:
16+
{data.message || 'nothing'}
17+
</p>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { startEventProxyServer } from '@sentry-internal/test-utils';
2+
3+
startEventProxyServer({
4+
port: 3031,
5+
proxyServerName: 'sveltekit-2-twp',
6+
});
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import adapter from '@sveltejs/adapter-auto';
2+
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
3+
4+
/** @type {import('@sveltejs/kit').Config} */
5+
const config = {
6+
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
7+
// for more information about preprocessors
8+
preprocess: vitePreprocess(),
9+
10+
kit: {
11+
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
12+
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
13+
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
14+
adapter: adapter(),
15+
},
16+
};
17+
18+
export default config;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { test } from '@playwright/test';
2+
3+
test.skip('errors on frontend and backend are connected by the same trace', async ({ page }) => {
4+
// TODO once browserTracingIntegration is always added
5+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect, test } from '@playwright/test';
2+
3+
test('Initially loaded page contains trace meta tags from backend trace', async ({ page }) => {
4+
await page.goto('/');
5+
6+
const sentryTraceMetaTag = page.locator('meta[name="sentry-trace"]').first();
7+
const sentryTraceContent = await sentryTraceMetaTag.getAttribute('content');
8+
9+
const baggageMetaTag = page.locator('meta[name="baggage"]').first();
10+
const baggageContent = await baggageMetaTag.getAttribute('content');
11+
12+
// ensure that we do not pass a sampled -1 or -0 flag at the end:
13+
expect(sentryTraceContent).toMatch(/^[a-f0-9]{32}-[a-f0-9]{16}$/);
14+
15+
expect(baggageContent?.length).toBeGreaterThan(0);
16+
17+
const traceId = sentryTraceContent!.split('-')[0];
18+
19+
expect(baggageContent).toContain('sentry-environment=qa');
20+
expect(baggageContent).toContain(`sentry-trace_id=${traceId}`);
21+
// ensure baggage also doesn't contain a sampled flag
22+
expect(baggageContent).not.toContain('sentry-sampled=');
23+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Page } from '@playwright/test';
2+
import { waitForTransaction } from '@sentry-internal/test-utils';
3+
4+
/**
5+
* Helper function that waits for the initial pageload to complete.
6+
*
7+
* This function
8+
* - loads the given route ("/" by default)
9+
* - waits for SvelteKit's hydration
10+
* - waits for the pageload transaction to be sent (doesn't assert on it though)
11+
*
12+
* Useful for tests that test outcomes of _navigations_ after an initial pageload.
13+
* Waiting on the pageload transaction excludes edge cases where navigations occur
14+
* so quickly that the pageload idle transaction is still active. This might lead
15+
* to cases where the routing span would be attached to the pageload transaction
16+
* and hence eliminates a lot of flakiness.
17+
*
18+
*/
19+
export async function waitForInitialPageload(
20+
page: Page,
21+
opts?: { route?: string; parameterizedRoute?: string; debug?: boolean },
22+
) {
23+
const route = opts?.route ?? '/';
24+
const txnName = opts?.parameterizedRoute ?? route;
25+
const debug = opts?.debug ?? false;
26+
27+
const clientPageloadTxnEventPromise = waitForTransaction('sveltekit-2-svelte-5', txnEvent => {
28+
debug &&
29+
console.log({
30+
txn: txnEvent?.transaction,
31+
op: txnEvent.contexts?.trace?.op,
32+
trace: txnEvent.contexts?.trace?.trace_id,
33+
span: txnEvent.contexts?.trace?.span_id,
34+
parent: txnEvent.contexts?.trace?.parent_span_id,
35+
});
36+
37+
return txnEvent?.transaction === txnName && txnEvent.contexts?.trace?.op === 'pageload';
38+
});
39+
40+
await Promise.all([
41+
page.goto(route),
42+
// the test app adds the "hydrated" class to the body when hydrating
43+
page.waitForSelector('body.hydrated'),
44+
// also waiting for the initial pageload txn so that later navigations don't interfere
45+
clientPageloadTxnEventPromise,
46+
]);
47+
48+
debug && console.log('hydrated');
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"extends": "./.svelte-kit/tsconfig.json",
3+
"compilerOptions": {
4+
"allowJs": true,
5+
"checkJs": true,
6+
"esModuleInterop": true,
7+
"forceConsistentCasingInFileNames": true,
8+
"resolveJsonModule": true,
9+
"skipLibCheck": true,
10+
"sourceMap": true,
11+
"strict": true,
12+
"moduleResolution": "bundler"
13+
},
14+
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
15+
// except $lib which is handled by https://kit.svelte.dev/docs/configuration#files
16+
//
17+
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
18+
// from the referenced tsconfig.json - TypeScript does not merge them in
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { sentrySvelteKit } from '@sentry/sveltekit';
2+
import { sveltekit } from '@sveltejs/kit/vite';
3+
import { defineConfig } from 'vite';
4+
5+
export default defineConfig({
6+
plugins: [
7+
sentrySvelteKit({
8+
autoUploadSourceMaps: false,
9+
}),
10+
sveltekit(),
11+
],
12+
});

0 commit comments

Comments
 (0)