Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
24 changes: 24 additions & 0 deletions dev-packages/e2e-tests/test-applications/nuxt-4-static/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Nuxt dev/build outputs
.output
.data
.nuxt
.nitro
.cache
dist

# Node dependencies
node_modules

# Logs
logs
*.log

# Misc
.DS_Store
.fleet
.idea

# Local env files
.env
.env.*
!.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup>
import { defineProps } from 'vue';

const props = defineProps({
errorText: {
type: String,
required: true,
},
id: {
type: String,
required: true,
},
});

const triggerError = () => {
throw new Error(props.errorText);
};
</script>

<template>
<button :id="props.id" @click="triggerError">Trigger Error</button>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// fixme: this needs to be imported from @sentry/core, not @sentry/nuxt in dev mode (because of import-in-the-middle error)
// This could also be a problem with the specific setup of the pnpm E2E test setup, because this could not be reproduced outside of the E2E test.
// Related to this: https://github.com/getsentry/sentry-javascript/issues/15204#issuecomment-2948908130
import { setTag } from '@sentry/nuxt';

export default function useSentryTestTag(): void {
setTag('test-tag', null);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup>
import ErrorButton from '../components/ErrorButton.vue';

const catchErr = () => {
console.log('Additional functionality in NuxtErrorBoundary');
};
</script>

<template>
<ErrorButton id="errorBtn" error-text="Error thrown from Nuxt-4 E2E test app" />
<ErrorButton id="errorBtn2" error-text="Another Error thrown from Nuxt-4 E2E test app" />

<NuxtErrorBoundary @error="catchErr">
<ErrorButton id="error-in-error-boundary" error-text="Error thrown in Error Boundary" />
</NuxtErrorBoundary>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<div>
<button @click="fetchError">Fetch Server API Error</button>
<button @click="fetchNitroFetch">Fetch Nitro $fetch</button>
</div>
</template>

<script setup lang="ts">
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
@@ -0,0 +1,20 @@
<template>
<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,65 @@
<script setup lang="ts">
import { ref } from '#imports';
import { useCartStore } from '~~/stores/cart';

const cart = useCartStore();

const itemName = ref('');

function addItemToCart() {
if (!itemName.value) return;
cart.addItem(itemName.value);
itemName.value = '';
}

function throwError() {
throw new Error('This is an error');
}

function clearCart() {
if (window.confirm('Are you sure you want to clear the cart?')) {
cart.rawItems = [];
}
}
</script>

<template>
<Layout>
<div>
<div style="margin: 1rem 0">
<PiniaLogo />
</div>

<form @submit.prevent="addItemToCart" data-testid="add-items">
<input id="item-input" type="text" v-model="itemName" />
<button id="item-add">Add</button>
<button id="throw-error" @click="throwError">Throw error</button>
</form>

<form>
<ul data-testid="items">
<li v-for="item in cart.items" :key="item.name">
{{ item.name }} ({{ item.amount }})
<button @click="cart.removeItem(item.name)" type="button">X</button>
</li>
</ul>

<button :disabled="!cart.items.length" @click="clearCart" type="button" data-testid="clear">
Clear the cart
</button>
</form>
</div>
</Layout>
</template>

<style scoped>
img {
width: 200px;
}

button,
input {
margin-right: 0.5rem;
margin-bottom: 0.5rem;
}
</style>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>Client Side Only Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>ISR 1h Cached Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>ISR Cached Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>Pre-Rendered Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>SWR 1h Cached Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<template><p>SWR Cached Page</p></template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script setup lang="ts">
import { useRoute, useFetch } from '#imports';

const route = useRoute();
const param = route.params.param;

const fetchError = async () => {
await useFetch(`/api/param-error/${param}`);
};

const fetchData = async () => {
await useFetch(`/api/test-param/${param}`);
};
</script>

<template>
<p>Param: {{ $route.params.param }}</p>

<ErrorButton id="errorBtn" errorText="Error thrown from Param Route Button" />
<button @click="fetchData">Fetch Server Data</button>
<button @click="fetchError">Fetch Server API Error</button>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script setup lang="ts">
import { useFetch, useRoute } from '#imports';

const route = useRoute();
const userId = route.params.userId as string;

const { data } = await useFetch(`/api/user/${userId}`, {
server: false, // Don't fetch during SSR, only client-side
});
</script>

<template>
<div>
<p v-if="data">User ID: {{ data }}</p>
</div>
</template>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
services:
db:
image: mysql:8.0
restart: always
container_name: e2e-tests-nuxt-4-mysql

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Bug: The nuxt-4-static test application uses the same Docker container names as the nuxt-4 application, which will cause CI failures due to name conflicts.
Severity: HIGH

Suggested Fix

Update the container_name values in dev-packages/e2e-tests/test-applications/nuxt-4-static/docker-compose.yml to be unique. For instance, rename e2e-tests-nuxt-4-mysql to e2e-tests-nuxt-4-static-mysql and e2e-tests-nuxt-4-redis to e2e-tests-nuxt-4-static-redis to prevent conflicts in the CI environment.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: dev-packages/e2e-tests/test-applications/nuxt-4-static/docker-compose.yml#L5

Potential issue: The `nuxt-4-static` test application's `docker-compose.yml` file
defines container names, `e2e-tests-nuxt-4-mysql` and `e2e-tests-nuxt-4-redis`, that are
identical to those used by the existing `nuxt-4` test application. The CI workflow runs
E2E tests for all applications on the same runner without cleaning up Docker containers
between jobs. This will cause a name conflict when the CI attempts to run tests for both
applications. The second test suite to run will fail during its setup phase because
`docker compose up` will be unable to create containers with names that are already in
use, crashing the test run.

Also affects:

  • dev-packages/e2e-tests/test-applications/nuxt-4-static/docker-compose.yml:23~23

Did we get this right? 👍 / 👎 to inform future reviews.

# The `mysql` 2.x driver doesn't speak MySQL 8's default
# `caching_sha2_password` auth, so force the legacy plugin.
command: ['--default-authentication-plugin=mysql_native_password']
ports:
- '3306:3306'
environment:
MYSQL_ROOT_PASSWORD: docker
healthcheck:
test: ['CMD-SHELL', 'mysqladmin ping -h 127.0.0.1 -uroot -pdocker']
interval: 2s
timeout: 3s
retries: 30
start_period: 10s

redis:
image: redis:7
restart: always
container_name: e2e-tests-nuxt-4-redis
ports:
- '6379:6379'
healthcheck:
test: ['CMD', 'redis-cli', 'ping']
interval: 2s
timeout: 3s
retries: 30
start_period: 5s
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalSetup() {
// Start MySQL + Redis via Docker Compose. `--wait` blocks until the
// healthchecks in docker-compose.yml pass, so the app can connect immediately.
execSync('docker compose up -d --wait', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { execSync } from 'child_process';
import { dirname } from 'path';
import { fileURLToPath } from 'url';

const __dirname = dirname(fileURLToPath(import.meta.url));

export default async function globalTeardown() {
execSync('docker compose down --volumes', {
cwd: __dirname,
stdio: 'inherit',
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { defineNuxtModule } from 'nuxt/kit';

// Just a fake module to check if the SDK works alongside other local Nuxt modules without breaking the build
export default defineNuxtModule({
meta: { name: 'another-module' },
setup() {
console.log('another-module setup called');
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#!/bin/bash
# To enable Sentry in Nuxt dev, it needs the sentry.server.config.mjs file from the .nuxt folder.
# First, we need to start 'nuxt dev' to generate the file, and then start 'nuxt dev' again with the NODE_OPTIONS to have Sentry enabled.

# Using a different port to avoid playwright already starting with the tests for port 3030
TEMP_PORT=3035

# 1. Start dev in background - this generates .nuxt folder
pnpm dev -p $TEMP_PORT &
DEV_PID=$!

# 2. Wait for the sentry.server.config.mjs file to appear
echo "Waiting for .nuxt/dev/sentry.server.config.mjs file..."
COUNTER=0
while [ ! -f ".nuxt/dev/sentry.server.config.mjs" ] && [ $COUNTER -lt 30 ]; do
sleep 1
((COUNTER++))
done

if [ ! -f ".nuxt/dev/sentry.server.config.mjs" ]; then
echo "ERROR: .nuxt/dev/sentry.server.config.mjs file never appeared!"
echo "This usually means the Nuxt dev server failed to start or generate the file. Try to rerun the test."
pkill -P $DEV_PID || kill $DEV_PID
exit 1
fi

# 3. Cleanup
# `pkill -P` only kills direct children, so the grandchild dev server holding the
# port survives; newer Nuxt's directory-scoped dev lock then blocks the real start.
echo "Found .nuxt/dev/sentry.server.config.mjs, stopping 'nuxt dev' process..."
pkill -P $DEV_PID 2>/dev/null
kill $DEV_PID 2>/dev/null

# Wait for port to be released
echo "Waiting for port $TEMP_PORT to be released..."
COUNTER=0
# Check if port is still in use
while lsof -i :$TEMP_PORT > /dev/null 2>&1 && [ $COUNTER -lt 10 ]; do
sleep 1
((COUNTER++))
done

if lsof -i :$TEMP_PORT > /dev/null 2>&1; then
echo "Port $TEMP_PORT still in use, killing remaining processes bound to it..."
lsof -t -i :$TEMP_PORT | xargs -r kill -9 2>/dev/null
sleep 1
fi

if lsof -i :$TEMP_PORT > /dev/null 2>&1; then
echo "WARNING: Port $TEMP_PORT still in use, proceeding anyway..."
else
echo "Port $TEMP_PORT released successfully"
fi

echo "Nuxt dev server can now be started with '--import ./.nuxt/dev/sentry.server.config.mjs'"
Loading
Loading