Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions dev-packages/e2e-tests/test-applications/nuxt-4/app/app.vue

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<div>
<button @click="fetchError">Fetch Server API Error</button>
<button @click="fetchNitroFetch">Fetch Nitro $fetch</button>
</div>
</template>

Expand All @@ -10,4 +11,8 @@ import { useFetch } from '#imports';
const fetchError = async () => {
await useFetch('/api/server-error');
};

const fetchNitroFetch = async () => {
await useFetch('/api/nitro-fetch');
};
</script>
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
<template>
<h1>Hello!</h1>
<NuxtLayout>
<header>
<nav>
<ul>
<li><NuxtLink to="/fetch-server-routes">Fetch Server Routes</NuxtLink></li>
<li><NuxtLink to="/test-param/1234">Fetch Param</NuxtLink></li>
<li><NuxtLink to="/client-error">Client Error</NuxtLink></li>
</ul>
</nav>
</header>
<NuxtPage />
</NuxtLayout>
</template>

<script setup lang="ts">
import { useSentryTestTag } from '#imports';

useSentryTestTag();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineEventHandler } from '#imports';

export default defineEventHandler(async () => {
const data = await $fetch('https://ungh.cc/orgs/unjs/repos');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l/m: Never heard of ungh.cc, do we want to depend on this in the e2e tests? Wonder if example.com is not better.

I remember we've had issues with example.com too tho, so not sure.

Copy link
Member

@Lms24 Lms24 Sep 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd recommend avoiding external requests entirely and just intercept the request in playwright. Examaple:

await page.route('**/foo', route => {
return route.fulfill({
status: 200,
body: JSON.stringify({
userNames: ['John', 'Jane'],
}),
headers: {
'Content-Type': 'application/json',
},
});
});

(not sure how consistent we are with this in e2e tests but this is what we do in the browser integration tests)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's the endpoint that Nitro uses in their examples: https://nitro.build/guide/fetch

But intercepting sounds like a good idea 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out it's not that straightforward to use page.route here. As the request is not made by the browser but the nitro server, it is not intercepted. And I cannot just call another Nitro server route as Nitro does not create a separate requests when it's within the server. For the test however, I want to specifically test calling an external API and example.com is okay for this case as the test does not fail if the endpoint cannot be reached. It's just about adding the span.


return data;
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test.describe('server-side errors', async () => {
return errorEvent?.exception?.values?.[0]?.value === 'Nuxt 4 Server error';
});

await page.goto(`/fetch-server-error`);
await page.goto(`/fetch-server-routes`);
await page.getByText('Fetch Server API Error', { exact: true }).click();

const error = await errorPromise;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from '@playwright/test';
import { waitForTransaction } from '@sentry-internal/test-utils';
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/nuxt';

test('sends a server action transaction on pageload', async ({ page }) => {
Expand Down Expand Up @@ -43,3 +43,25 @@ test('does not send transactions for build asset folder "_nuxt"', async ({ page

expect(transactionEvent.transaction).toBe('GET /test-param/:param()');
});

test('captures server API calls made with Nitro $fetch', async ({ page }) => {
const transactionPromise = waitForTransaction('nuxt-4', async transactionEvent => {
return transactionEvent.transaction === 'GET /api/nitro-fetch';
});

await page.goto(`/fetch-server-routes`);
await page.getByText('Fetch Nitro $fetch', { exact: true }).click();

const httpServerFetchSpan = await transactionPromise;
const httpClientSpan = httpServerFetchSpan.spans.find(
span => span.description === 'GET https://ungh.cc/orgs/unjs/repos',
);

const error = await transactionPromise;

expect(httpServerFetchSpan.transaction).toEqual('GET /api/nitro-fetch');
expect(httpServerFetchSpan.contexts.trace.op).toEqual('http.server');

expect(httpClientSpan.parent_span_id).toEqual(httpServerFetchSpan.contexts.trace.span_id);
expect(httpClientSpan.op).toEqual('http.client');
});