Skip to content

Commit 5c08d03

Browse files
authored
test(e2e): Add Astro 4 E2E test app (#13375)
1 parent 7aebf94 commit 5c08d03

26 files changed

+673
-1
lines changed

.github/workflows/build.yml

+1
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,7 @@ jobs:
847847
[
848848
'angular-17',
849849
'angular-18',
850+
'astro-4',
850851
'aws-lambda-layer-cjs',
851852
'aws-serverless-esm',
852853
'node-express',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# build output
2+
dist/
3+
4+
# generated types
5+
.astro/
6+
7+
# dependencies
8+
node_modules/
9+
10+
# logs
11+
npm-debug.log*
12+
yarn-debug.log*
13+
yarn-error.log*
14+
pnpm-debug.log*
15+
16+
# environment variables
17+
.env
18+
.env.production
19+
20+
# macOS-specific files
21+
.DS_Store
22+
23+
# jetbrains setting folder
24+
.idea/
25+
26+
test-results
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,6 @@
1+
# Astro 4 E2E test app
2+
3+
- Astro 4.x
4+
- Output mode `hybrid` (== opt into SSR routes)
5+
- Node adapter
6+
- Configured for Tracing and Performance
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import node from '@astrojs/node';
2+
import sentry from '@sentry/astro';
3+
import { defineConfig } from 'astro/config';
4+
5+
import spotlightjs from '@spotlightjs/astro';
6+
7+
// https://astro.build/config
8+
export default defineConfig({
9+
output: 'hybrid',
10+
integrations: [
11+
sentry({
12+
debug: true,
13+
sourceMapsUploadOptions: {
14+
enabled: false,
15+
},
16+
}),
17+
spotlightjs(),
18+
],
19+
adapter: node({
20+
mode: 'standalone',
21+
}),
22+
vite: {
23+
build: {
24+
rollupOptions: {
25+
external: ['https'],
26+
},
27+
},
28+
},
29+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"name": "astro-hybrid-with-static-routes",
3+
"type": "module",
4+
"version": "0.0.1",
5+
"scripts": {
6+
"dev": "astro dev --force",
7+
"start": "astro dev",
8+
"build": "astro check && astro build",
9+
"preview": "astro preview",
10+
"astro": "astro",
11+
"test:build": "pnpm install && npx playwright install && pnpm build",
12+
"test:assert": "TEST_ENV=production playwright test"
13+
},
14+
"dependencies": {
15+
"@astrojs/check": "^0.9.2",
16+
"@astrojs/node": "^8.3.2",
17+
"@playwright/test": "^1.46.0",
18+
"@sentry/astro": "* || latest",
19+
"@sentry-internal/test-utils": "link:../../../test-utils",
20+
"@spotlightjs/astro": "^2.1.6",
21+
"astro": "^4.13.3",
22+
"typescript": "^5.5.4"
23+
},
24+
"devDependencies": {
25+
"@astrojs/internal-helpers": "^0.4.1"
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { getPlaywrightConfig } from '@sentry-internal/test-utils';
2+
3+
const testEnv = process.env.TEST_ENV;
4+
5+
if (!testEnv) {
6+
throw new Error('No test env defined');
7+
}
8+
9+
const config = getPlaywrightConfig({
10+
startCommand: 'node ./dist/server/entry.mjs',
11+
});
12+
13+
export default config;
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import * as Sentry from '@sentry/astro';
2+
3+
Sentry.init({
4+
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
5+
environment: 'qa',
6+
tracesSampleRate: 1.0,
7+
tunnel: 'http://localhost:3031/', // proxy server
8+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as Sentry from '@sentry/astro';
2+
3+
Sentry.init({
4+
dsn: import.meta.env.PUBLIC_E2E_TEST_DSN,
5+
environment: 'qa',
6+
tracesSampleRate: 1.0,
7+
spotlight: true,
8+
tunnel: 'http://localhost:3031/', // proxy server
9+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="astro/client" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
interface Props {
3+
title: string;
4+
}
5+
6+
const { title } = Astro.props;
7+
---
8+
9+
<!doctype html>
10+
<html lang="en">
11+
<head>
12+
<meta charset="UTF-8" />
13+
<meta name="description" content="Astro description" />
14+
<meta name="viewport" content="width=device-width" />
15+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
16+
<meta name="generator" content={Astro.generator} />
17+
<title>{title}</title>
18+
</head>
19+
<body>
20+
<slot />
21+
</body>
22+
</html>
23+
<style is:global>
24+
:root {
25+
--accent: 136, 58, 234;
26+
--accent-light: 224, 204, 250;
27+
--accent-dark: 49, 10, 101;
28+
--accent-gradient: linear-gradient(
29+
45deg,
30+
rgb(var(--accent)),
31+
rgb(var(--accent-light)) 30%,
32+
white 60%
33+
);
34+
}
35+
html {
36+
font-family: system-ui, sans-serif;
37+
background: #b7b7b7;
38+
}
39+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
import Layout from "../../layouts/Layout.astro";
3+
---
4+
5+
<Layout title="Client Error">
6+
7+
<button id="#errBtn" onclick="throw new Error('client error')">
8+
Throw Error
9+
</button>
10+
11+
</Layout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type { APIRoute } from 'astro';
2+
3+
export const prerender = false;
4+
5+
export const GET: APIRoute = ({ request, url }) => {
6+
if (url.searchParams.has('error')) {
7+
throw new Error('Endpoint Error');
8+
}
9+
return new Response(
10+
JSON.stringify({
11+
search: url.search,
12+
sp: url.searchParams,
13+
}),
14+
);
15+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
import Layout from "../../layouts/Layout.astro";
3+
4+
export const prerender = false;
5+
---
6+
7+
<Layout title="Endpoint Error">
8+
<button onclick="fetch('endpoint-error/api?error=1')">Get Data</button>
9+
</Layout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
import Layout from '../layouts/Layout.astro';
3+
---
4+
5+
<Layout title="Welcome to Astro.">
6+
<main>
7+
<h1>Astro E2E Test App</h1>
8+
<ul role="list" style="display: flex; flex-direction: column;">
9+
<a href="/ssr-error">SSR Error</a>
10+
<a href="/endpoint-error">Endpoint Error</a>
11+
<a href="/client-error">Cliennt Error</a>
12+
<a href="/test-ssr">SSR page</a>
13+
<a href="/test-static" title="static page">Static Page</a>
14+
</ul>
15+
</main>
16+
</Layout>
17+
18+
<style>
19+
main {
20+
margin: auto;
21+
padding: 1rem;
22+
width: 800px;
23+
max-width: calc(100% - 2rem);
24+
color: white;
25+
font-size: 20px;
26+
line-height: 1.6;
27+
}
28+
29+
h1 {
30+
font-size: 4rem;
31+
font-weight: 700;
32+
line-height: 1;
33+
text-align: center;
34+
margin-bottom: 1em;
35+
}
36+
</style>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
import Layout from "../../layouts/Layout.astro";
3+
4+
const a = {} as any;
5+
console.log(a.foo.x);
6+
export const prerender = false;
7+
---
8+
9+
<Layout title="SSR Error">
10+
11+
<h1>Page with SSR error</h1>
12+
13+
</Layout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
import Layout from "../../layouts/Layout.astro"
3+
4+
export const prerender = false
5+
---
6+
7+
<Layout title="Dynamic SSR page">
8+
9+
<h1>
10+
This is a server page
11+
</h1>
12+
13+
<button onclick="fetch('/test-server')">fetch</button>
14+
15+
</Layout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
import Layout from "../../layouts/Layout.astro";
3+
4+
export const prerender = true;
5+
---
6+
7+
<Layout title="Static Page">
8+
9+
<h1>
10+
This is a static page
11+
</h1>
12+
13+
<button onclick="fetch('/test-static')">fetch</button>
14+
15+
</Layout>
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: 'astro-4',
6+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError } from '@sentry-internal/test-utils';
3+
4+
test.describe('client-side errors', () => {
5+
test('captures error thrown on click', async ({ page }) => {
6+
const errorEventPromise = waitForError('astro-4', errorEvent => {
7+
return errorEvent?.exception?.values?.[0]?.value === 'client error';
8+
});
9+
10+
await page.goto('/client-error');
11+
12+
await page.getByText('Throw Error').click();
13+
14+
const errorEvent = await errorEventPromise;
15+
16+
const errorEventFrames = errorEvent.exception?.values?.[0]?.stacktrace?.frames;
17+
18+
expect(errorEventFrames?.[errorEventFrames?.length - 1]).toEqual(
19+
expect.objectContaining({
20+
colno: expect.any(Number),
21+
lineno: expect.any(Number),
22+
filename: expect.stringContaining('/client-error'),
23+
function: 'HTMLButtonElement.onclick',
24+
in_app: true,
25+
}),
26+
);
27+
28+
expect(errorEvent).toMatchObject({
29+
exception: {
30+
values: [
31+
{
32+
mechanism: {
33+
handled: false,
34+
type: 'onerror',
35+
},
36+
type: 'Error',
37+
value: 'client error',
38+
stacktrace: expect.any(Object), // detailed check above
39+
},
40+
],
41+
},
42+
level: 'error',
43+
platform: 'javascript',
44+
request: {
45+
url: expect.stringContaining('/client-error'),
46+
headers: {
47+
'User-Agent': expect.any(String),
48+
},
49+
},
50+
event_id: expect.stringMatching(/[a-f0-9]{32}/),
51+
timestamp: expect.any(Number),
52+
sdk: {
53+
integrations: expect.arrayContaining([
54+
'InboundFilters',
55+
'FunctionToString',
56+
'BrowserApiErrors',
57+
'Breadcrumbs',
58+
'GlobalHandlers',
59+
'LinkedErrors',
60+
'Dedupe',
61+
'HttpContext',
62+
'BrowserTracing',
63+
]),
64+
name: 'sentry.javascript.astro',
65+
version: expect.any(String),
66+
packages: expect.any(Array),
67+
},
68+
transaction: '/client-error',
69+
contexts: {
70+
trace: {
71+
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
72+
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
73+
span_id: expect.stringMatching(/[a-f0-9]{16}/),
74+
},
75+
},
76+
environment: 'qa',
77+
});
78+
});
79+
});

0 commit comments

Comments
 (0)