From 3a60d55ff04e83caa55bc68546387cb40775efb5 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Feb 2024 15:26:01 +0100 Subject: [PATCH 1/4] test(e2e): Add Vue 3 E2E tests --- .../test-applications/vue-3/.gitignore | 30 ++ .../e2e-tests/test-applications/vue-3/.npmrc | 2 + .../test-applications/vue-3/README.md | 3 + .../test-applications/vue-3/env.d.ts | 1 + .../vue-3/event-proxy-server.ts | 253 +++++++++++ .../test-applications/vue-3/index.html | 13 + .../test-applications/vue-3/package.json | 42 ++ .../vue-3/playwright.config.ts | 77 ++++ .../vue-3/public/favicon.ico | Bin 0 -> 4286 bytes .../test-applications/vue-3/src/App.vue | 84 ++++ .../vue-3/src/assets/base.css | 86 ++++ .../vue-3/src/assets/logo.svg | 1 + .../vue-3/src/assets/main.css | 35 ++ .../test-applications/vue-3/src/main.ts | 25 ++ .../vue-3/src/router/index.ts | 23 + .../vue-3/src/views/AboutView.vue | 3 + .../vue-3/src/views/HomeView.vue | 14 + .../vue-3/src/views/UserIdView.vue | 3 + .../vue-3/start-event-proxy.ts | 6 + .../vue-3/tests/errors.test.ts | 29 ++ .../vue-3/tests/performance.test.ts | 102 +++++ .../test-applications/vue-3/tests/tmp.json | 393 ++++++++++++++++++ .../test-applications/vue-3/tsconfig.app.json | 14 + .../test-applications/vue-3/tsconfig.json | 11 + .../vue-3/tsconfig.node.json | 19 + .../vue-3/tsconfig.proxy.json | 11 + .../test-applications/vue-3/vite.config.ts | 16 + 27 files changed, 1296 insertions(+) create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/.gitignore create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/.npmrc create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/README.md create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/env.d.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/event-proxy-server.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/index.html create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/package.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/playwright.config.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/public/favicon.ico create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/App.vue create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/assets/base.css create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/assets/logo.svg create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/assets/main.css create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/main.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/views/AboutView.vue create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/views/HomeView.vue create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdView.vue create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/start-event-proxy.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tsconfig.app.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tsconfig.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tsconfig.proxy.json create mode 100644 dev-packages/e2e-tests/test-applications/vue-3/vite.config.ts diff --git a/dev-packages/e2e-tests/test-applications/vue-3/.gitignore b/dev-packages/e2e-tests/test-applications/vue-3/.gitignore new file mode 100644 index 000000000000..8ee54e8d343e --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/.gitignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +.DS_Store +dist +dist-ssr +coverage +*.local + +/cypress/videos/ +/cypress/screenshots/ + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +*.tsbuildinfo diff --git a/dev-packages/e2e-tests/test-applications/vue-3/.npmrc b/dev-packages/e2e-tests/test-applications/vue-3/.npmrc new file mode 100644 index 000000000000..070f80f05092 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/.npmrc @@ -0,0 +1,2 @@ +@sentry:registry=http://127.0.0.1:4873 +@sentry-internal:registry=http://127.0.0.1:4873 diff --git a/dev-packages/e2e-tests/test-applications/vue-3/README.md b/dev-packages/e2e-tests/test-applications/vue-3/README.md new file mode 100644 index 000000000000..6af7bb60b866 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/README.md @@ -0,0 +1,3 @@ +# Vue 3 E2E Test App + +E2E test app for Vue 3 and `@sentry/vue`. diff --git a/dev-packages/e2e-tests/test-applications/vue-3/env.d.ts b/dev-packages/e2e-tests/test-applications/vue-3/env.d.ts new file mode 100644 index 000000000000..11f02fe2a006 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/env.d.ts @@ -0,0 +1 @@ +/// diff --git a/dev-packages/e2e-tests/test-applications/vue-3/event-proxy-server.ts b/dev-packages/e2e-tests/test-applications/vue-3/event-proxy-server.ts new file mode 100644 index 000000000000..4c2df32399f0 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/event-proxy-server.ts @@ -0,0 +1,253 @@ +import * as fs from 'fs'; +import * as http from 'http'; +import * as https from 'https'; +import type { AddressInfo } from 'net'; +import * as os from 'os'; +import * as path from 'path'; +import * as util from 'util'; +import * as zlib from 'zlib'; +import type { Envelope, EnvelopeItem, SerializedEvent } from '@sentry/types'; +import { parseEnvelope } from '@sentry/utils'; + +const readFile = util.promisify(fs.readFile); +const writeFile = util.promisify(fs.writeFile); + +interface EventProxyServerOptions { + /** Port to start the event proxy server at. */ + port: number; + /** The name for the proxy server used for referencing it with listener functions */ + proxyServerName: string; +} + +interface SentryRequestCallbackData { + envelope: Envelope; + rawProxyRequestBody: string; + rawSentryResponseBody: string; + sentryResponseStatusCode?: number; +} + +/** + * Starts an event proxy server that will proxy events to sentry when the `tunnel` option is used. Point the `tunnel` + * option to this server (like this `tunnel: http://localhost:${port option}/`). + */ +export async function startEventProxyServer(options: EventProxyServerOptions): Promise { + const eventCallbackListeners: Set<(data: string) => void> = new Set(); + + const proxyServer = http.createServer((proxyRequest, proxyResponse) => { + const proxyRequestChunks: Uint8Array[] = []; + + proxyRequest.addListener('data', (chunk: Buffer) => { + proxyRequestChunks.push(chunk); + }); + + proxyRequest.addListener('error', err => { + throw err; + }); + + proxyRequest.addListener('end', () => { + const proxyRequestBody = + proxyRequest.headers['content-encoding'] === 'gzip' + ? zlib.gunzipSync(Buffer.concat(proxyRequestChunks)).toString() + : Buffer.concat(proxyRequestChunks).toString(); + + let envelopeHeader = JSON.parse(proxyRequestBody.split('\n')[0]); + + if (!envelopeHeader.dsn) { + throw new Error('[event-proxy-server] No dsn on envelope header. Please set tunnel option.'); + } + + const { origin, pathname, host } = new URL(envelopeHeader.dsn); + + const projectId = pathname.substring(1); + const sentryIngestUrl = `${origin}/api/${projectId}/envelope/`; + + proxyRequest.headers.host = host; + + const sentryResponseChunks: Uint8Array[] = []; + + const sentryRequest = https.request( + sentryIngestUrl, + { headers: proxyRequest.headers, method: proxyRequest.method }, + sentryResponse => { + sentryResponse.addListener('data', (chunk: Buffer) => { + proxyResponse.write(chunk, 'binary'); + sentryResponseChunks.push(chunk); + }); + + sentryResponse.addListener('end', () => { + eventCallbackListeners.forEach(listener => { + const rawSentryResponseBody = Buffer.concat(sentryResponseChunks).toString(); + + const data: SentryRequestCallbackData = { + envelope: parseEnvelope(proxyRequestBody, new TextEncoder(), new TextDecoder()), + rawProxyRequestBody: proxyRequestBody, + rawSentryResponseBody, + sentryResponseStatusCode: sentryResponse.statusCode, + }; + + listener(Buffer.from(JSON.stringify(data)).toString('base64')); + }); + proxyResponse.end(); + }); + + sentryResponse.addListener('error', err => { + throw err; + }); + + proxyResponse.writeHead(sentryResponse.statusCode || 500, sentryResponse.headers); + }, + ); + + sentryRequest.write(Buffer.concat(proxyRequestChunks), 'binary'); + sentryRequest.end(); + }); + }); + + const proxyServerStartupPromise = new Promise(resolve => { + proxyServer.listen(options.port, () => { + resolve(); + }); + }); + + const eventCallbackServer = http.createServer((eventCallbackRequest, eventCallbackResponse) => { + eventCallbackResponse.statusCode = 200; + eventCallbackResponse.setHeader('connection', 'keep-alive'); + + const callbackListener = (data: string): void => { + eventCallbackResponse.write(data.concat('\n'), 'utf8'); + }; + + eventCallbackListeners.add(callbackListener); + + eventCallbackRequest.on('close', () => { + eventCallbackListeners.delete(callbackListener); + }); + + eventCallbackRequest.on('error', () => { + eventCallbackListeners.delete(callbackListener); + }); + }); + + const eventCallbackServerStartupPromise = new Promise(resolve => { + eventCallbackServer.listen(0, () => { + const port = String((eventCallbackServer.address() as AddressInfo).port); + void registerCallbackServerPort(options.proxyServerName, port).then(resolve); + }); + }); + + await eventCallbackServerStartupPromise; + await proxyServerStartupPromise; + return; +} + +export async function waitForRequest( + proxyServerName: string, + callback: (eventData: SentryRequestCallbackData) => Promise | boolean, +): Promise { + const eventCallbackServerPort = await retrieveCallbackServerPort(proxyServerName); + + return new Promise((resolve, reject) => { + const request = http.request(`http://localhost:${eventCallbackServerPort}/`, {}, response => { + let eventContents = ''; + + response.on('error', err => { + reject(err); + }); + + response.on('data', (chunk: Buffer) => { + const chunkString = chunk.toString('utf8'); + chunkString.split('').forEach(char => { + if (char === '\n') { + const eventCallbackData: SentryRequestCallbackData = JSON.parse( + Buffer.from(eventContents, 'base64').toString('utf8'), + ); + const callbackResult = callback(eventCallbackData); + if (typeof callbackResult !== 'boolean') { + callbackResult.then( + match => { + if (match) { + response.destroy(); + resolve(eventCallbackData); + } + }, + err => { + throw err; + }, + ); + } else if (callbackResult) { + response.destroy(); + resolve(eventCallbackData); + } + eventContents = ''; + } else { + eventContents = eventContents.concat(char); + } + }); + }); + }); + + request.end(); + }); +} + +export function waitForEnvelopeItem( + proxyServerName: string, + callback: (envelopeItem: EnvelopeItem) => Promise | boolean, +): Promise { + return new Promise((resolve, reject) => { + waitForRequest(proxyServerName, async eventData => { + const envelopeItems = eventData.envelope[1]; + for (const envelopeItem of envelopeItems) { + if (await callback(envelopeItem)) { + resolve(envelopeItem); + return true; + } + } + return false; + }).catch(reject); + }); +} + +export function waitForError( + proxyServerName: string, + callback: (transactionEvent: SerializedEvent) => Promise | boolean, +): Promise { + return new Promise((resolve, reject) => { + waitForEnvelopeItem(proxyServerName, async envelopeItem => { + const [envelopeItemHeader, envelopeItemBody] = envelopeItem; + if (envelopeItemHeader.type === 'event' && (await callback(envelopeItemBody as SerializedEvent))) { + resolve(envelopeItemBody as SerializedEvent); + return true; + } + return false; + }).catch(reject); + }); +} + +export function waitForTransaction( + proxyServerName: string, + callback: (transactionEvent: SerializedEvent) => Promise | boolean, +): Promise { + return new Promise((resolve, reject) => { + waitForEnvelopeItem(proxyServerName, async envelopeItem => { + const [envelopeItemHeader, envelopeItemBody] = envelopeItem; + if (envelopeItemHeader.type === 'transaction' && (await callback(envelopeItemBody as SerializedEvent))) { + resolve(envelopeItemBody as SerializedEvent); + return true; + } + return false; + }).catch(reject); + }); +} + +const TEMP_FILE_PREFIX = 'event-proxy-server-'; + +async function registerCallbackServerPort(serverName: string, port: string): Promise { + const tmpFilePath = path.join(os.tmpdir(), `${TEMP_FILE_PREFIX}${serverName}`); + await writeFile(tmpFilePath, port, { encoding: 'utf8' }); +} + +function retrieveCallbackServerPort(serverName: string): Promise { + const tmpFilePath = path.join(os.tmpdir(), `${TEMP_FILE_PREFIX}${serverName}`); + return readFile(tmpFilePath, 'utf8'); +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/index.html b/dev-packages/e2e-tests/test-applications/vue-3/index.html new file mode 100644 index 000000000000..a888544898a5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite App + + +
+ + + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/package.json b/dev-packages/e2e-tests/test-applications/vue-3/package.json new file mode 100644 index 000000000000..1fa4cbcf3882 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/package.json @@ -0,0 +1,42 @@ +{ + "name": "vue-3-tmp", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "clean": "npx rimraf node_modules,pnpm-lock.yaml,dist", + "dev": "vite", + "build": "run-p type-check \"build-only {@}\" --", + "preview": "vite preview", + "build-only": "vite build", + "type-check": "vue-tsc --build --force", + "test": "playwright test", + "test:build": "pnpm install && npx playwright install && pnpm build", + "test:assert": "playwright test" + }, + "dependencies": { + "@sentry/vue": "latest || *", + "vue": "^3.4.15", + "vue-router": "^4.2.5" + }, + "devDependencies": { + "@playwright/test": "^1.41.1", + "@sentry/types": "^7.99.0", + "@sentry/utils": "^7.99.0", + "@tsconfig/node20": "^20.1.2", + "@types/node": "^20.11.10", + "@vitejs/plugin-vue": "^5.0.3", + "@vitejs/plugin-vue-jsx": "^3.1.0", + "@vue/tsconfig": "^0.5.1", + "http-server": "^14.1.1", + "npm-run-all2": "^6.1.1", + "ts-node": "10.9.1", + "typescript": "~5.3.0", + "vite": "^5.0.11", + "vue-tsc": "^1.8.27", + "wait-port": "1.0.4" + }, + "volta": { + "extends": "../../package.json" + } +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/playwright.config.ts b/dev-packages/e2e-tests/test-applications/vue-3/playwright.config.ts new file mode 100644 index 000000000000..16dd640e58ef --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/playwright.config.ts @@ -0,0 +1,77 @@ +import type { PlaywrightTestConfig } from '@playwright/test'; +import { devices } from '@playwright/test'; + +// Fix urls not resolving to localhost on Node v17+ +// See: https://github.com/axios/axios/issues/3821#issuecomment-1413727575 +import { setDefaultResultOrder } from 'dns'; +setDefaultResultOrder('ipv4first'); + +const testEnv = process.env['TEST_ENV'] || 'production'; + +if (!testEnv) { + throw new Error('No test env defined'); +} + +const vuePort = 4173; +const eventProxyPort = 3031; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +const config: PlaywrightTestConfig = { + testDir: './tests', + /* Maximum time one test can run for. */ + timeout: 150_000, + expect: { + /** + * Maximum time expect() should wait for the condition to be met. + * For example in `await expect(locator).toHaveText();` + */ + timeout: 10000, + }, + fullyParallel: false, + workers: 1, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* `next dev` is incredibly buggy with the app dir */ + retries: testEnv === 'development' ? 3 : 0, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'list', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */ + actionTimeout: 0, + /* Base URL to use in actions like `await page.goto('/')`. */ + baseURL: `http://localhost:${vuePort}`, + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: { + ...devices['Desktop Chrome'], + }, + }, + ], + + /* Run your local dev server before starting the tests */ + webServer: [ + { + command: 'pnpm ts-node-script --project tsconfig.proxy.json start-event-proxy.ts', + port: eventProxyPort, + }, + { + command: + testEnv === 'development' + ? `pnpm wait-port ${eventProxyPort} && pnpm preview --port ${vuePort}` + : `pnpm wait-port ${eventProxyPort} && pnpm preview --port ${vuePort}`, + port: vuePort, + }, + ], +}; + +export default config; diff --git a/dev-packages/e2e-tests/test-applications/vue-3/public/favicon.ico b/dev-packages/e2e-tests/test-applications/vue-3/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2 GIT binary patch literal 4286 zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56 zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp= zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8 zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3 z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6 z={Xwx{TKxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S literal 0 HcmV?d00001 diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/App.vue b/dev-packages/e2e-tests/test-applications/vue-3/src/App.vue new file mode 100644 index 000000000000..08c38cecfda9 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/App.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/assets/base.css b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/base.css new file mode 100644 index 000000000000..8816868a41b6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/base.css @@ -0,0 +1,86 @@ +/* color palette from */ +:root { + --vt-c-white: #ffffff; + --vt-c-white-soft: #f8f8f8; + --vt-c-white-mute: #f2f2f2; + + --vt-c-black: #181818; + --vt-c-black-soft: #222222; + --vt-c-black-mute: #282828; + + --vt-c-indigo: #2c3e50; + + --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); + --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); + --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); + --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); + + --vt-c-text-light-1: var(--vt-c-indigo); + --vt-c-text-light-2: rgba(60, 60, 60, 0.66); + --vt-c-text-dark-1: var(--vt-c-white); + --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); +} + +/* semantic color variables for this project */ +:root { + --color-background: var(--vt-c-white); + --color-background-soft: var(--vt-c-white-soft); + --color-background-mute: var(--vt-c-white-mute); + + --color-border: var(--vt-c-divider-light-2); + --color-border-hover: var(--vt-c-divider-light-1); + + --color-heading: var(--vt-c-text-light-1); + --color-text: var(--vt-c-text-light-1); + + --section-gap: 160px; +} + +@media (prefers-color-scheme: dark) { + :root { + --color-background: var(--vt-c-black); + --color-background-soft: var(--vt-c-black-soft); + --color-background-mute: var(--vt-c-black-mute); + + --color-border: var(--vt-c-divider-dark-2); + --color-border-hover: var(--vt-c-divider-dark-1); + + --color-heading: var(--vt-c-text-dark-1); + --color-text: var(--vt-c-text-dark-2); + } +} + +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + font-weight: normal; +} + +body { + min-height: 100vh; + color: var(--color-text); + background: var(--color-background); + transition: + color 0.5s, + background-color 0.5s; + line-height: 1.6; + font-family: + Inter, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + Roboto, + Oxygen, + Ubuntu, + Cantarell, + 'Fira Sans', + 'Droid Sans', + 'Helvetica Neue', + sans-serif; + font-size: 15px; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/assets/logo.svg b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/logo.svg new file mode 100644 index 000000000000..7565660356e5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/logo.svg @@ -0,0 +1 @@ + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/assets/main.css b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/main.css new file mode 100644 index 000000000000..36fb845b5232 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/assets/main.css @@ -0,0 +1,35 @@ +@import './base.css'; + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + font-weight: normal; +} + +a, +.green { + text-decoration: none; + color: hsla(160, 100%, 37%, 1); + transition: 0.4s; + padding: 3px; +} + +@media (hover: hover) { + a:hover { + background-color: hsla(160, 100%, 37%, 0.2); + } +} + +@media (min-width: 1024px) { + body { + display: flex; + place-items: center; + } + + #app { + display: grid; + grid-template-columns: 1fr 1fr; + padding: 0 2rem; + } +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/main.ts b/dev-packages/e2e-tests/test-applications/vue-3/src/main.ts new file mode 100644 index 000000000000..503a9e44d14f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/main.ts @@ -0,0 +1,25 @@ +import './assets/main.css'; + +import { createApp } from 'vue'; +import App from './App.vue'; +import router from './router'; + +import * as Sentry from '@sentry/vue'; + +const app = createApp(App); + +Sentry.init({ + app, + dsn: import.meta.env.PUBLIC_E2E_TEST_DSN, + tracesSampleRate: 1.0, + integrations: [ + new Sentry.BrowserTracing({ + routingInstrumentation: Sentry.vueRouterInstrumentation(router), + }), + ], + tunnel: `http://localhost:3031/`, // proxy server + debug: true, +}); + +app.use(router); +app.mount('#app'); diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts new file mode 100644 index 000000000000..a17208711eff --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts @@ -0,0 +1,23 @@ +import { createRouter, createWebHistory } from 'vue-router'; +import HomeView from '../views/HomeView.vue'; + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes: [ + { + path: '/', + component: HomeView, + }, + { + path: '/about', + name: 'AboutView', + component: () => import('../views/AboutView.vue'), + }, + { + path: '/users/:id', + component: () => import('../views/UserIdView.vue'), + }, + ], +}); + +export default router; diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/views/AboutView.vue b/dev-packages/e2e-tests/test-applications/vue-3/src/views/AboutView.vue new file mode 100644 index 000000000000..8c706352120a --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/views/AboutView.vue @@ -0,0 +1,3 @@ + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/views/HomeView.vue b/dev-packages/e2e-tests/test-applications/vue-3/src/views/HomeView.vue new file mode 100644 index 000000000000..92b38c308a6d --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/views/HomeView.vue @@ -0,0 +1,14 @@ + + + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdView.vue b/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdView.vue new file mode 100644 index 000000000000..a6c973ef6e35 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/src/views/UserIdView.vue @@ -0,0 +1,3 @@ + diff --git a/dev-packages/e2e-tests/test-applications/vue-3/start-event-proxy.ts b/dev-packages/e2e-tests/test-applications/vue-3/start-event-proxy.ts new file mode 100644 index 000000000000..6435984ad069 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/start-event-proxy.ts @@ -0,0 +1,6 @@ +import { startEventProxyServer } from './event-proxy-server'; + +startEventProxyServer({ + port: 3031, + proxyServerName: 'vue-3', +}); diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts new file mode 100644 index 000000000000..508fe738bbc5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/errors.test.ts @@ -0,0 +1,29 @@ +import { expect, test } from '@playwright/test'; +import { waitForError } from '../event-proxy-server'; + +test('sends an error', async ({ page }) => { + const errorPromise = waitForError('vue-3', async errorEvent => { + return !errorEvent?.transaction; + }); + + await page.goto(`/`); + + await page.locator('#errorBtn').click(); + + const error = await errorPromise; + + expect(error).toMatchObject({ + exception: { + values: [ + { + type: 'Error', + value: 'This is a Vue test error', + mechanism: { + type: 'generic', + handled: false, + }, + }, + ], + }, + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts b/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts new file mode 100644 index 000000000000..732ec98a54f4 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/performance.test.ts @@ -0,0 +1,102 @@ +import { expect, test } from '@playwright/test'; +import { waitForTransaction } from '../event-proxy-server'; + +test('sends a pageload transaction with a parameterized URL', async ({ page }) => { + const transactionPromise = waitForTransaction('vue-3', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto(`/users/456`); + + const rootSpan = await transactionPromise; + + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + data: { + params: { + id: '456', + }, + 'sentry.source': 'route', + 'sentry.origin': 'auto.pageload.vue', + 'sentry.op': 'pageload', + }, + op: 'pageload', + origin: 'auto.pageload.vue', + }, + }, + transaction: '/users/:id', + transaction_info: { + source: 'route', + }, + }); +}); + +test('sends a navigation transaction with a parameterized URL', async ({ page }) => { + const pageloadTxnPromise = waitForTransaction('vue-3', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + const navigationTxnPromise = waitForTransaction('vue-3', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation'; + }); + + await page.goto(`/`); + await pageloadTxnPromise; + + await page.waitForTimeout(5000); + + const [_, navigationTxn] = await Promise.all([page.locator('#navLink').click(), navigationTxnPromise]); + + expect(navigationTxn).toMatchObject({ + contexts: { + trace: { + data: { + params: { + id: '123', + }, + 'sentry.source': 'route', + 'sentry.origin': 'auto.navigation.vue', + 'sentry.op': 'navigation', + }, + op: 'navigation', + origin: 'auto.navigation.vue', + }, + }, + transaction: '/users/:id', + transaction_info: { + // So this is weird. The source is set to custom although the route doesn't have a name. + // This also only happens during a navigation. A pageload will set the source as 'route'. + // TODO: Figure out what's going on here. + source: 'custom', + }, + }); +}); + +test('sends a pageload transaction with a route name as transaction name if available', async ({ page }) => { + const transactionPromise = waitForTransaction('vue-3', async transactionEvent => { + return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload'; + }); + + await page.goto(`/about`); + + const rootSpan = await transactionPromise; + + expect(rootSpan).toMatchObject({ + contexts: { + trace: { + data: { + 'sentry.source': 'custom', + 'sentry.origin': 'auto.pageload.vue', + 'sentry.op': 'pageload', + }, + op: 'pageload', + origin: 'auto.pageload.vue', + }, + }, + transaction: 'AboutView', + transaction_info: { + source: 'custom', + }, + }); +}); diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json b/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json new file mode 100644 index 000000000000..de130d4c90b6 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json @@ -0,0 +1,393 @@ +[ + { + "event_id": "fa4845fd85844a7f8f0538d75c2104a6", + "sent_at": "2024-02-02T13:50:22.830Z", + "sdk": { "name": "sentry.javascript.vue", "version": "7.99.0" }, + "dsn": "https://3b6c388182fb435097f41d181be2b2ba@o4504321058471936.ingest.sentry.io/4504321066008576", + "trace": { + "environment": "production", + "public_key": "3b6c388182fb435097f41d181be2b2ba", + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "sample_rate": "1", + "transaction": "/users/:id", + "sampled": "true" + } + }, + { "type": "transaction" }, + { + "contexts": { + "trace": { + "data": { + "sentry.source": "route", + "params": { "id": "123" }, + "query": {}, + "sentry.origin": "auto.pageload.vue", + "sentry.op": "pageload", + "sentry.sample_rate": 1 + }, + "op": "pageload", + "span_id": "b0f32f97f27cee02", + "tags": { + "routing.instrumentation": "vue-router", + "effectiveConnectionType": "4g", + "deviceMemory": "8 GB", + "hardwareConcurrency": "10", + "lcp.element": "body > div#app > header > img.logo[alt=\"Vue logo\"]", + "lcp.url": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20261.76%20226.69'%3", + "lcp.size": 15625 + }, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.pageload.vue" + } + }, + "spans": [ + { + "data": { "sentry.origin": "auto.ui.vue", "sentry.op": "ui.vue.render" }, + "description": "Application Render", + "op": "ui.vue.render", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "95b99e90f5ccc67b", + "start_timestamp": 1706881819.812, + "timestamp": 1706881819.8151999, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.ui.vue" + }, + { + "data": { "sentry.origin": "auto.ui.vue", "sentry.op": "ui.vue.mount" }, + "description": "Vue <>", + "op": "ui.vue.mount", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "822bedda46901c70", + "start_timestamp": 1706881819.8122997, + "timestamp": 1706881819.8151999, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.ui.vue" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "domContentLoadedEvent", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "aefe038e1fd0e83f", + "start_timestamp": 1706881819.8172, + "timestamp": 1706881819.8172, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "loadEvent", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "a2549097e3136451", + "start_timestamp": 1706881819.8356, + "timestamp": 1706881819.8356, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "connect", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "8f354b202c3b2bad", + "start_timestamp": 1706881819.7175, + "timestamp": 1706881819.7178, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "cache", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "ae9f00b237410a1f", + "start_timestamp": 1706881819.7124, + "timestamp": 1706881819.7173998, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "DNS", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "8a813a57702d0681", + "start_timestamp": 1706881819.7173998, + "timestamp": 1706881819.7175, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "request", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "9d828bcc58cebd6b", + "start_timestamp": 1706881819.7178998, + "timestamp": 1706881819.7387998, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, + "description": "response", + "op": "browser", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "9000380e602ec269", + "start_timestamp": 1706881819.7384999, + "timestamp": 1706881819.7387998, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.browser.browser.metrics" + }, + { + "data": { + "http.response_transfer_size": 69155, + "http.response_content_length": 68855, + "http.decoded_response_content_length": 192738, + "resource.render_blocking_status": "non-blocking", + "url.scheme": "http", + "server.address": "localhost:4173", + "url.same_origin": true, + "sentry.origin": "auto.resource.browser.metrics", + "sentry.op": "resource.script" + }, + "description": "/assets/index-9iZGNg-C.js", + "op": "resource.script", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "9e592a1690d8f8cd", + "start_timestamp": 1706881819.7445, + "timestamp": 1706881819.7954, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { + "http.response_transfer_size": 1307, + "http.response_content_length": 1007, + "http.decoded_response_content_length": 2846, + "resource.render_blocking_status": "blocking", + "url.scheme": "http", + "server.address": "localhost:4173", + "url.same_origin": true, + "sentry.origin": "auto.resource.browser.metrics", + "sentry.op": "resource.link" + }, + "description": "/assets/index-Kq1Mc0UN.css", + "op": "resource.link", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "87f489ec7f318373", + "start_timestamp": 1706881819.7447999, + "timestamp": 1706881819.7824, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "mark" }, + "description": "sentry-tracing-init", + "op": "mark", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "aded3749b2bee1eb", + "start_timestamp": 1706881819.8042998, + "timestamp": 1706881819.8042998, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { + "http.response_transfer_size": 498, + "http.response_content_length": 198, + "http.decoded_response_content_length": 198, + "resource.render_blocking_status": "non-blocking", + "url.scheme": "http", + "server.address": "localhost:4173", + "url.same_origin": true, + "sentry.origin": "auto.resource.browser.metrics", + "sentry.op": "resource.other" + }, + "description": "/assets/UserIdView-EIppvVhr.js", + "op": "resource.other", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "8e2957ca4a10f94a", + "start_timestamp": 1706881819.8170998, + "timestamp": 1706881819.8228998, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "paint" }, + "description": "first-paint", + "op": "paint", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "b80abcccddd51709", + "start_timestamp": 1706881819.8483999, + "timestamp": 1706881819.8483999, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "paint" }, + "description": "first-contentful-paint", + "op": "paint", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "87fbdbce2fc61b63", + "start_timestamp": 1706881819.8483999, + "timestamp": 1706881819.8483999, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.resource.browser.metrics" + }, + { + "data": { "sentry.origin": "auto.ui.browser.metrics", "sentry.op": "ui.action" }, + "description": "first input delay", + "op": "ui.action", + "parent_span_id": "b0f32f97f27cee02", + "span_id": "a8159d20c06f8443", + "start_timestamp": 1706881821.4106, + "timestamp": 1706881821.4119, + "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", + "origin": "auto.ui.browser.metrics" + } + ], + "start_timestamp": 1706881819.7124, + "tags": { + "routing.instrumentation": "vue-router", + "effectiveConnectionType": "4g", + "deviceMemory": "8 GB", + "hardwareConcurrency": "10", + "lcp.element": "body > div#app > header > img.logo[alt=\"Vue logo\"]", + "lcp.url": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20261.76%20226.69'%3", + "lcp.size": 15625 + }, + "timestamp": 1706881821.4119, + "transaction": "/users/:id", + "type": "transaction", + "transaction_info": { "source": "route" }, + "measurements": { + "lcp": { "value": 40.10009765625, "unit": "millisecond" }, + "fid": { "value": 1.300000011920929, "unit": "millisecond" }, + "fp": { "value": 40.10009765625, "unit": "millisecond" }, + "fcp": { "value": 40.10009765625, "unit": "millisecond" }, + "connection.rtt": { "value": 50, "unit": "millisecond" }, + "ttfb": { "value": 0, "unit": "millisecond" }, + "ttfb.requestTime": { "value": 20.600080490112305, "unit": "millisecond" } + }, + "platform": "javascript", + "request": { + "url": "http://localhost:4173/users/123", + "headers": { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" + } + }, + "event_id": "fa4845fd85844a7f8f0538d75c2104a6", + "environment": "production", + "sdk": { + "integrations": [ + "InboundFilters", + "FunctionToString", + "TryCatch", + "Breadcrumbs", + "GlobalHandlers", + "LinkedErrors", + "Dedupe", + "HttpContext", + "Vue", + "BrowserTracing" + ], + "name": "sentry.javascript.vue", + "version": "7.99.0", + "packages": [{ "name": "npm:@sentry/vue", "version": "7.99.0" }] + }, + "breadcrumbs": [ + { "timestamp": 1706881819.837, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, + { "timestamp": 1706881821.487, "category": "ui.click", "message": "body" } + ] + }, + { + "contexts": { + "trace": { + "data": { + "params": { "id": "123" }, + "query": {}, + "sentry.source": "route", + "sentry.origin": "auto.navigation.vue", + "sentry.op": "navigation", + "sentry.sample_rate": 1 + }, + "op": "navigation", + "span_id": "8782ddee1e6ee8ae", + "tags": { + "routing.instrumentation": "vue-router", + "effectiveConnectionType": "4g", + "deviceMemory": "8 GB", + "hardwareConcurrency": "10" + }, + "trace_id": "a393aa7ea8da4c95b970a3e19ff708da", + "origin": "auto.navigation.vue" + } + }, + "spans": [], + "start_timestamp": 1706881988.431, + "tags": { + "routing.instrumentation": "vue-router", + "effectiveConnectionType": "4g", + "deviceMemory": "8 GB", + "hardwareConcurrency": "10" + }, + "timestamp": 1706881989.4382, + "transaction": "/users/:id", + "type": "transaction", + "transaction_info": { "source": "custom" }, + "platform": "javascript", + "request": { + "url": "http://localhost:4173/users/123", + "headers": { + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" + } + }, + "event_id": "32c30040542b46dbb234e3cb30a49899", + "environment": "production", + "sdk": { + "integrations": [ + "InboundFilters", + "FunctionToString", + "TryCatch", + "Breadcrumbs", + "GlobalHandlers", + "LinkedErrors", + "Dedupe", + "HttpContext", + "Vue", + "BrowserTracing" + ], + "name": "sentry.javascript.vue", + "version": "7.99.0", + "packages": [{ "name": "npm:@sentry/vue", "version": "7.99.0" }] + }, + "breadcrumbs": [ + { "timestamp": 1706881819.837, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, + { "timestamp": 1706881821.487, "category": "ui.click", "message": "body" }, + { + "timestamp": 1706881822.829, + "category": "sentry.transaction", + "event_id": "fa4845fd85844a7f8f0538d75c2104a6", + "message": "fa4845fd85844a7f8f0538d75c2104a6" + }, + { "timestamp": 1706881982.286, "category": "ui.click", "message": "div#app > header > div.wrapper > nav > a" }, + { "timestamp": 1706881982.295, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, + { "timestamp": 1706881982.295, "category": "navigation", "data": { "from": "/users/123", "to": "/about" } }, + { + "timestamp": 1706881983.292, + "category": "sentry.transaction", + "event_id": "0b3d30081dd64b588e5df245acd1ff69", + "message": "0b3d30081dd64b588e5df245acd1ff69" + }, + { + "timestamp": 1706881988.429, + "category": "ui.click", + "message": "div#app > header > div.wrapper > nav > a#navLink" + }, + { "timestamp": 1706881988.43, "category": "navigation", "data": { "from": "/about", "to": "/about" } }, + { "timestamp": 1706881988.43, "category": "navigation", "data": { "from": "/about", "to": "/users/123" } } + ] + } +] diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.app.json b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.app.json new file mode 100644 index 000000000000..e14c754d3ae5 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.app.json @@ -0,0 +1,14 @@ +{ + "extends": "@vue/tsconfig/tsconfig.dom.json", + "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], + "exclude": ["src/**/__tests__/*"], + "compilerOptions": { + "composite": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.json b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.json new file mode 100644 index 000000000000..78f134a16dca --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.json @@ -0,0 +1,11 @@ +{ + "files": [], + "references": [ + { + "path": "./tsconfig.node.json" + }, + { + "path": "./tsconfig.app.json" + } + ], +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json new file mode 100644 index 000000000000..f0940630302f --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json @@ -0,0 +1,19 @@ +{ + "extends": "@tsconfig/node20/tsconfig.json", + "include": [ + "vite.config.*", + "vitest.config.*", + "cypress.config.*", + "nightwatch.conf.*", + "playwright.config.*" + ], + "compilerOptions": { + "composite": true, + "noEmit": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["node"] + } +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.proxy.json b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.proxy.json new file mode 100644 index 000000000000..7ccdde196a3b --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.proxy.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "moduleResolution": "Node", + "target": "ES2022", + "module": "ES2022", + }, + "ts-node": { + "esm": true, + "experimentalSpecifierResolution": "node", + } +} diff --git a/dev-packages/e2e-tests/test-applications/vue-3/vite.config.ts b/dev-packages/e2e-tests/test-applications/vue-3/vite.config.ts new file mode 100644 index 000000000000..72a15caeae52 --- /dev/null +++ b/dev-packages/e2e-tests/test-applications/vue-3/vite.config.ts @@ -0,0 +1,16 @@ +import { URL, fileURLToPath } from 'node:url'; + +import vue from '@vitejs/plugin-vue'; +import vueJsx from '@vitejs/plugin-vue-jsx'; +import { defineConfig } from 'vite'; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue(), vueJsx()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)), + }, + }, + envPrefix: 'PUBLIC_', +}); From 3191654aefc90c3a3ff74bdf439a9563d0f3133a Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Feb 2024 15:36:09 +0100 Subject: [PATCH 2/4] formatting --- biome.json | 8 ++++++-- .../e2e-tests/test-applications/vue-3/tsconfig.node.json | 8 +------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/biome.json b/biome.json index c18c0720b6d1..ccb69e4746db 100644 --- a/biome.json +++ b/biome.json @@ -41,7 +41,9 @@ ".next/**", ".svelte-kit/**", ".angular/**", - "angular.json" + "angular.json", + "ember/instance-initializers/**", + "ember/types.d.ts" ] }, "files": { @@ -65,7 +67,9 @@ ".svelte-kit/**", ".angular/**", "angular.json", - "**/profiling-node/lib/**" + "**/profiling-node/lib/**", + "ember/instance-initializers/**", + "ember/types.d.ts" ] }, "javascript": { diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json index f0940630302f..2c669eeb8e8a 100644 --- a/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json +++ b/dev-packages/e2e-tests/test-applications/vue-3/tsconfig.node.json @@ -1,12 +1,6 @@ { "extends": "@tsconfig/node20/tsconfig.json", - "include": [ - "vite.config.*", - "vitest.config.*", - "cypress.config.*", - "nightwatch.conf.*", - "playwright.config.*" - ], + "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*"], "compilerOptions": { "composite": true, "noEmit": true, From 053b3ecf9540ab71f64e3fdb6fbeba2c4ee0a691 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Feb 2024 15:38:06 +0100 Subject: [PATCH 3/4] add to CI --- .github/workflows/build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 74ccf23dc669..c87ef1782865 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1031,7 +1031,8 @@ jobs: 'node-experimental-fastify-app', 'node-hapi-app', 'node-exports-test-app', - 'node-profiling' + 'node-profiling', + 'vue-3' ] build-command: - false From cd4d104abf9cd4ec46a4c7cb344266d2e8fde976 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Fri, 2 Feb 2024 16:14:54 +0100 Subject: [PATCH 4/4] cleanup --- .../test-applications/vue-3/tests/tmp.json | 393 ------------------ 1 file changed, 393 deletions(-) delete mode 100644 dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json diff --git a/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json b/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json deleted file mode 100644 index de130d4c90b6..000000000000 --- a/dev-packages/e2e-tests/test-applications/vue-3/tests/tmp.json +++ /dev/null @@ -1,393 +0,0 @@ -[ - { - "event_id": "fa4845fd85844a7f8f0538d75c2104a6", - "sent_at": "2024-02-02T13:50:22.830Z", - "sdk": { "name": "sentry.javascript.vue", "version": "7.99.0" }, - "dsn": "https://3b6c388182fb435097f41d181be2b2ba@o4504321058471936.ingest.sentry.io/4504321066008576", - "trace": { - "environment": "production", - "public_key": "3b6c388182fb435097f41d181be2b2ba", - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "sample_rate": "1", - "transaction": "/users/:id", - "sampled": "true" - } - }, - { "type": "transaction" }, - { - "contexts": { - "trace": { - "data": { - "sentry.source": "route", - "params": { "id": "123" }, - "query": {}, - "sentry.origin": "auto.pageload.vue", - "sentry.op": "pageload", - "sentry.sample_rate": 1 - }, - "op": "pageload", - "span_id": "b0f32f97f27cee02", - "tags": { - "routing.instrumentation": "vue-router", - "effectiveConnectionType": "4g", - "deviceMemory": "8 GB", - "hardwareConcurrency": "10", - "lcp.element": "body > div#app > header > img.logo[alt=\"Vue logo\"]", - "lcp.url": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20261.76%20226.69'%3", - "lcp.size": 15625 - }, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.pageload.vue" - } - }, - "spans": [ - { - "data": { "sentry.origin": "auto.ui.vue", "sentry.op": "ui.vue.render" }, - "description": "Application Render", - "op": "ui.vue.render", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "95b99e90f5ccc67b", - "start_timestamp": 1706881819.812, - "timestamp": 1706881819.8151999, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.ui.vue" - }, - { - "data": { "sentry.origin": "auto.ui.vue", "sentry.op": "ui.vue.mount" }, - "description": "Vue <>", - "op": "ui.vue.mount", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "822bedda46901c70", - "start_timestamp": 1706881819.8122997, - "timestamp": 1706881819.8151999, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.ui.vue" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "domContentLoadedEvent", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "aefe038e1fd0e83f", - "start_timestamp": 1706881819.8172, - "timestamp": 1706881819.8172, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "loadEvent", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "a2549097e3136451", - "start_timestamp": 1706881819.8356, - "timestamp": 1706881819.8356, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "connect", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "8f354b202c3b2bad", - "start_timestamp": 1706881819.7175, - "timestamp": 1706881819.7178, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "cache", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "ae9f00b237410a1f", - "start_timestamp": 1706881819.7124, - "timestamp": 1706881819.7173998, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "DNS", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "8a813a57702d0681", - "start_timestamp": 1706881819.7173998, - "timestamp": 1706881819.7175, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "request", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "9d828bcc58cebd6b", - "start_timestamp": 1706881819.7178998, - "timestamp": 1706881819.7387998, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.browser.browser.metrics", "sentry.op": "browser" }, - "description": "response", - "op": "browser", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "9000380e602ec269", - "start_timestamp": 1706881819.7384999, - "timestamp": 1706881819.7387998, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.browser.browser.metrics" - }, - { - "data": { - "http.response_transfer_size": 69155, - "http.response_content_length": 68855, - "http.decoded_response_content_length": 192738, - "resource.render_blocking_status": "non-blocking", - "url.scheme": "http", - "server.address": "localhost:4173", - "url.same_origin": true, - "sentry.origin": "auto.resource.browser.metrics", - "sentry.op": "resource.script" - }, - "description": "/assets/index-9iZGNg-C.js", - "op": "resource.script", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "9e592a1690d8f8cd", - "start_timestamp": 1706881819.7445, - "timestamp": 1706881819.7954, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { - "http.response_transfer_size": 1307, - "http.response_content_length": 1007, - "http.decoded_response_content_length": 2846, - "resource.render_blocking_status": "blocking", - "url.scheme": "http", - "server.address": "localhost:4173", - "url.same_origin": true, - "sentry.origin": "auto.resource.browser.metrics", - "sentry.op": "resource.link" - }, - "description": "/assets/index-Kq1Mc0UN.css", - "op": "resource.link", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "87f489ec7f318373", - "start_timestamp": 1706881819.7447999, - "timestamp": 1706881819.7824, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "mark" }, - "description": "sentry-tracing-init", - "op": "mark", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "aded3749b2bee1eb", - "start_timestamp": 1706881819.8042998, - "timestamp": 1706881819.8042998, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { - "http.response_transfer_size": 498, - "http.response_content_length": 198, - "http.decoded_response_content_length": 198, - "resource.render_blocking_status": "non-blocking", - "url.scheme": "http", - "server.address": "localhost:4173", - "url.same_origin": true, - "sentry.origin": "auto.resource.browser.metrics", - "sentry.op": "resource.other" - }, - "description": "/assets/UserIdView-EIppvVhr.js", - "op": "resource.other", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "8e2957ca4a10f94a", - "start_timestamp": 1706881819.8170998, - "timestamp": 1706881819.8228998, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "paint" }, - "description": "first-paint", - "op": "paint", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "b80abcccddd51709", - "start_timestamp": 1706881819.8483999, - "timestamp": 1706881819.8483999, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.resource.browser.metrics", "sentry.op": "paint" }, - "description": "first-contentful-paint", - "op": "paint", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "87fbdbce2fc61b63", - "start_timestamp": 1706881819.8483999, - "timestamp": 1706881819.8483999, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.resource.browser.metrics" - }, - { - "data": { "sentry.origin": "auto.ui.browser.metrics", "sentry.op": "ui.action" }, - "description": "first input delay", - "op": "ui.action", - "parent_span_id": "b0f32f97f27cee02", - "span_id": "a8159d20c06f8443", - "start_timestamp": 1706881821.4106, - "timestamp": 1706881821.4119, - "trace_id": "fd0e792e3a974d9484f447cd6e5b1245", - "origin": "auto.ui.browser.metrics" - } - ], - "start_timestamp": 1706881819.7124, - "tags": { - "routing.instrumentation": "vue-router", - "effectiveConnectionType": "4g", - "deviceMemory": "8 GB", - "hardwareConcurrency": "10", - "lcp.element": "body > div#app > header > img.logo[alt=\"Vue logo\"]", - "lcp.url": "data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20261.76%20226.69'%3", - "lcp.size": 15625 - }, - "timestamp": 1706881821.4119, - "transaction": "/users/:id", - "type": "transaction", - "transaction_info": { "source": "route" }, - "measurements": { - "lcp": { "value": 40.10009765625, "unit": "millisecond" }, - "fid": { "value": 1.300000011920929, "unit": "millisecond" }, - "fp": { "value": 40.10009765625, "unit": "millisecond" }, - "fcp": { "value": 40.10009765625, "unit": "millisecond" }, - "connection.rtt": { "value": 50, "unit": "millisecond" }, - "ttfb": { "value": 0, "unit": "millisecond" }, - "ttfb.requestTime": { "value": 20.600080490112305, "unit": "millisecond" } - }, - "platform": "javascript", - "request": { - "url": "http://localhost:4173/users/123", - "headers": { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" - } - }, - "event_id": "fa4845fd85844a7f8f0538d75c2104a6", - "environment": "production", - "sdk": { - "integrations": [ - "InboundFilters", - "FunctionToString", - "TryCatch", - "Breadcrumbs", - "GlobalHandlers", - "LinkedErrors", - "Dedupe", - "HttpContext", - "Vue", - "BrowserTracing" - ], - "name": "sentry.javascript.vue", - "version": "7.99.0", - "packages": [{ "name": "npm:@sentry/vue", "version": "7.99.0" }] - }, - "breadcrumbs": [ - { "timestamp": 1706881819.837, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, - { "timestamp": 1706881821.487, "category": "ui.click", "message": "body" } - ] - }, - { - "contexts": { - "trace": { - "data": { - "params": { "id": "123" }, - "query": {}, - "sentry.source": "route", - "sentry.origin": "auto.navigation.vue", - "sentry.op": "navigation", - "sentry.sample_rate": 1 - }, - "op": "navigation", - "span_id": "8782ddee1e6ee8ae", - "tags": { - "routing.instrumentation": "vue-router", - "effectiveConnectionType": "4g", - "deviceMemory": "8 GB", - "hardwareConcurrency": "10" - }, - "trace_id": "a393aa7ea8da4c95b970a3e19ff708da", - "origin": "auto.navigation.vue" - } - }, - "spans": [], - "start_timestamp": 1706881988.431, - "tags": { - "routing.instrumentation": "vue-router", - "effectiveConnectionType": "4g", - "deviceMemory": "8 GB", - "hardwareConcurrency": "10" - }, - "timestamp": 1706881989.4382, - "transaction": "/users/:id", - "type": "transaction", - "transaction_info": { "source": "custom" }, - "platform": "javascript", - "request": { - "url": "http://localhost:4173/users/123", - "headers": { - "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36" - } - }, - "event_id": "32c30040542b46dbb234e3cb30a49899", - "environment": "production", - "sdk": { - "integrations": [ - "InboundFilters", - "FunctionToString", - "TryCatch", - "Breadcrumbs", - "GlobalHandlers", - "LinkedErrors", - "Dedupe", - "HttpContext", - "Vue", - "BrowserTracing" - ], - "name": "sentry.javascript.vue", - "version": "7.99.0", - "packages": [{ "name": "npm:@sentry/vue", "version": "7.99.0" }] - }, - "breadcrumbs": [ - { "timestamp": 1706881819.837, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, - { "timestamp": 1706881821.487, "category": "ui.click", "message": "body" }, - { - "timestamp": 1706881822.829, - "category": "sentry.transaction", - "event_id": "fa4845fd85844a7f8f0538d75c2104a6", - "message": "fa4845fd85844a7f8f0538d75c2104a6" - }, - { "timestamp": 1706881982.286, "category": "ui.click", "message": "div#app > header > div.wrapper > nav > a" }, - { "timestamp": 1706881982.295, "category": "navigation", "data": { "from": "/users/123", "to": "/users/123" } }, - { "timestamp": 1706881982.295, "category": "navigation", "data": { "from": "/users/123", "to": "/about" } }, - { - "timestamp": 1706881983.292, - "category": "sentry.transaction", - "event_id": "0b3d30081dd64b588e5df245acd1ff69", - "message": "0b3d30081dd64b588e5df245acd1ff69" - }, - { - "timestamp": 1706881988.429, - "category": "ui.click", - "message": "div#app > header > div.wrapper > nav > a#navLink" - }, - { "timestamp": 1706881988.43, "category": "navigation", "data": { "from": "/about", "to": "/about" } }, - { "timestamp": 1706881988.43, "category": "navigation", "data": { "from": "/about", "to": "/users/123" } } - ] - } -]