Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add route support #127

Merged
merged 16 commits into from
Feb 16, 2024
2 changes: 1 addition & 1 deletion .github/composite-actions/install/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ runs:
using: composite
steps:
- name: Set up Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
registry-url: 'https://registry.npmjs.org'
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
18.16.1
18.19.0
11 changes: 11 additions & 0 deletions apps/nextjs/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Link from 'next/link';

export default function BlogPage({ params }: { params: { slug: string } }) {
return (
<div>
<h2>{params.slug}</h2>

<Link href="/blog">Back to blog</Link>
</div>
);
}
11 changes: 11 additions & 0 deletions apps/nextjs/app/blog/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Link from 'next/link';

export default function Layout({ children }: { children: React.ReactNode }) {
return (
<div>
<Link href="/blog/my-first-blogpost">My first blog post</Link>
<Link href="/blog/new-feature-release">Feature just got released</Link>
<div>{children}</div>
</div>
);
}
3 changes: 3 additions & 0 deletions apps/nextjs/app/blog/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Blog() {
return <div>Welcome on the blog</div>;
}
7 changes: 6 additions & 1 deletion apps/nextjs/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Analytics } from '@vercel/analytics/next';

export const metadata = {
title: 'Next.js',
description: 'Generated by Next.js',
Expand All @@ -10,7 +12,10 @@ export default function RootLayout({
}) {
return (
<html lang="en">
<body>{children}</body>
<body>
<Analytics />
{children}
</body>
</html>
);
}
25 changes: 0 additions & 25 deletions apps/nextjs/components/withAnalytics.tsx

This file was deleted.

20 changes: 19 additions & 1 deletion apps/nextjs/e2e/development/beforeSend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ test.describe('beforeSend', () => {
page,
}) => {
const messages: string[] = [];
await useMockForProductionScript({
page,
onPageView: () => {},
debug: true,
});

page.on('console', (msg) => {
const message = msg.text();
Expand All @@ -28,6 +33,19 @@ test.describe('beforeSend', () => {

await page.waitForLoadState('networkidle');

expect(messages).toHaveLength(5);
expect(
messages.find((m) =>
m.includes('[pageview] http://localhost:3000/before-send/first')
)
).toBeDefined();
expect(
messages.find((m) =>
m.includes(
'[pageview] http://localhost:3000/before-send/second?secret=REDACTED'
)
)
).toBeDefined();

expect(messages.find((m) => m.includes('secret=vercel'))).toBeUndefined();
});
});
21 changes: 17 additions & 4 deletions apps/nextjs/e2e/development/pageview.spec.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import { test, expect } from '@playwright/test';
import { useMockForProductionScript } from '../utils';

test.describe('pageview', () => {
test('should track page views when navigating between pages', async ({
page,
}) => {
const messages: string[] = [];
await useMockForProductionScript({
page,
onPageView: () => {},
debug: true,
});

page.on('console', (msg) => {
const message = msg.text();

console.log(message);

if (
message.includes('[Vercel Web Analytics]') ||
message.includes('[Vercel Analytics]')
Expand All @@ -20,7 +24,7 @@ test.describe('pageview', () => {
});

await page.goto('/navigation/first');
await page.waitForTimeout(200);
await page.waitForTimeout(800);

await page.click('text=Next');

Expand All @@ -29,6 +33,15 @@ test.describe('pageview', () => {

await page.waitForTimeout(200);

expect(messages).toHaveLength(3);
expect(
messages.find((m) =>
m.includes('[pageview] http://localhost:3000/navigation/first')
)
).toBeDefined();
expect(
messages.find((m) =>
m.includes('[pageview] http://localhost:3000/navigation/second')
)
).toBeDefined();
});
});
4 changes: 2 additions & 2 deletions apps/nextjs/e2e/production/beforeSend.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ test.describe('beforeSend', () => {
payload: {
o: 'http://localhost:3000/before-send/first',
sv: expect.any(String),
sdkn: '@vercel/analytics',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
ts: expect.any(Number),
r: '',
Expand All @@ -42,7 +42,7 @@ test.describe('beforeSend', () => {
o: 'http://localhost:3000/before-send/second?secret=REDACTED',
ts: expect.any(Number),
sv: expect.any(String),
sdkn: '@vercel/analytics',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
},
},
Expand Down
130 changes: 128 additions & 2 deletions apps/nextjs/e2e/production/pageview.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ test.describe('pageview', () => {
ts: expect.any(Number),
r: '',
sv: expect.any(String),
sdkn: '@vercel/analytics',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
dp: '/navigation/first',
},
},
{
Expand All @@ -42,8 +43,133 @@ test.describe('pageview', () => {
o: 'http://localhost:3000/navigation/second',
ts: expect.any(Number),
sv: expect.any(String),
sdkn: '@vercel/analytics',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
dp: '/navigation/second',
},
},
]);
});

test('should properly send dynamic route', async ({ page }) => {
const payloads: { page: string; payload: Object }[] = [];

await useMockForProductionScript({
page,
onPageView: (page, payload) => {
payloads.push({ page, payload });
},
});

await page.goto('/blog');
await page.waitForLoadState('networkidle');

await page.click('text=My first blog post');

await expect(page).toHaveURL('/blog/my-first-blogpost');
await expect(page.locator('h2')).toContainText('my-first-blogpost');

await page.waitForLoadState('networkidle');

await page.click('text=Back to blog');

await page.waitForLoadState('networkidle');
await expect(page).toHaveURL('/blog');

await page.click('text=Feature just got released');

await expect(page.locator('h2')).toContainText('new-feature-release');

expect(payloads).toEqual([
{
page: 'http://localhost:3000/blog',
payload: {
dp: '/blog',
o: 'http://localhost:3000/blog',
r: '',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
},
},
{
page: 'http://localhost:3000/blog/my-first-blogpost',
payload: {
dp: '/blog/[slug]',
o: 'http://localhost:3000/blog/my-first-blogpost',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
},
},
{
page: 'http://localhost:3000/blog',
payload: {
dp: '/blog',
o: 'http://localhost:3000/blog',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
},
},
{
page: 'http://localhost:3000/blog/new-feature-release',
payload: {
dp: '/blog/[slug]',
o: 'http://localhost:3000/blog/new-feature-release',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
},
},
]);
});

test('should send pageviews when route doesnt change but path does', async ({
page,
}) => {
tobiaslins marked this conversation as resolved.
Show resolved Hide resolved
const payloads: { page: string; payload: Object }[] = [];

await useMockForProductionScript({
page,
onPageView: (page, payload) => {
payloads.push({ page, payload });
},
});

await page.goto('/blog/my-first-blogpost');
await page.waitForLoadState('networkidle');

await page.click('text=Feature just got released');

await expect(page.locator('h2')).toContainText('new-feature-release');

expect(payloads).toEqual([
{
page: 'http://localhost:3000/blog/my-first-blogpost',
payload: {
dp: '/blog/[slug]',
o: 'http://localhost:3000/blog/my-first-blogpost',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
r: '',
feugy marked this conversation as resolved.
Show resolved Hide resolved
},
},
{
page: 'http://localhost:3000/blog/new-feature-release',
payload: {
dp: '/blog/[slug]',
o: 'http://localhost:3000/blog/new-feature-release',
sdkn: '@vercel/analytics/next',
sdkv: expect.any(String),
sv: expect.any(String),
ts: expect.any(Number),
},
},
]);
Expand Down
7 changes: 6 additions & 1 deletion apps/nextjs/e2e/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import { Page } from '@playwright/test';
export async function useMockForProductionScript(props: {
page: Page;
onPageView: (page: string, payload: Object) => void;
debug?: boolean;
}) {
await props.page.route('**/_vercel/insights/script.js', async (route, _) => {
return route.fulfill({
status: 301,
headers: { location: 'https://cdn.vercel-insights.com/v1/script.js' },
headers: {
location: props.debug
? 'https://cdn.vercel-insights.com/v1/script.debug.js'
: 'https://cdn.vercel-insights.com/v1/script.js',
},
});
});

Expand Down
1 change: 1 addition & 0 deletions apps/nextjs/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const nextConfig = {
experimental: {
serverActions: true,
},
// reactStrictMode: false,
};

module.exports = nextConfig;
2 changes: 1 addition & 1 deletion apps/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"dependencies": {
"@vercel/analytics": "workspace:*",
"next": "13.5.4",
"next": "14.1.0",
"react": "18.2.0",
"react-dom": "18.2.0"
},
Expand Down
22 changes: 22 additions & 0 deletions apps/nextjs/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Analytics } from '@vercel/analytics/next';
import type { AppProps } from 'next/app';

export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Analytics
beforeSend={(event) => {
const url = new URL(event.url);
if (url.searchParams.has('secret')) {
url.searchParams.set('secret', 'REDACTED');
}
return {
...event,
url: url.toString(),
};
}}
/>
<Component {...pageProps} />
</>
);
}
Loading
Loading