Skip to content

Commit ba16ef7

Browse files
author
Luca Forstner
authored
Merge branch 'develop' into lforst-set-headers-instead-of-append
2 parents 28ace04 + 41515a6 commit ba16ef7

File tree

17 files changed

+469
-86
lines changed

17 files changed

+469
-86
lines changed

dev-packages/e2e-tests/test-applications/vue-3/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
},
1717
"dependencies": {
1818
"@sentry/vue": "latest || *",
19+
"pinia": "^2.2.3",
1920
"vue": "^3.4.15",
2021
"vue-router": "^4.2.5"
2122
},

dev-packages/e2e-tests/test-applications/vue-3/src/main.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import { createApp } from 'vue';
44
import App from './App.vue';
55
import router from './router';
66

7+
import { createPinia } from 'pinia';
8+
79
import * as Sentry from '@sentry/vue';
810
import { browserTracingIntegration } from '@sentry/vue';
911

1012
const app = createApp(App);
13+
const pinia = createPinia();
1114

1215
Sentry.init({
1316
app,
@@ -22,5 +25,16 @@ Sentry.init({
2225
trackComponents: ['ComponentMainView', '<ComponentOneView>'],
2326
});
2427

28+
pinia.use(
29+
Sentry.createSentryPiniaPlugin({
30+
actionTransformer: action => `Transformed: ${action}`,
31+
stateTransformer: state => ({
32+
transformed: true,
33+
...state,
34+
}),
35+
}),
36+
);
37+
38+
app.use(pinia);
2539
app.use(router);
2640
app.mount('#app');

dev-packages/e2e-tests/test-applications/vue-3/src/router/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ const router = createRouter({
3434
path: '/components',
3535
component: () => import('../views/ComponentMainView.vue'),
3636
},
37+
{
38+
path: '/cart',
39+
component: () => import('../views/CartView.vue'),
40+
},
3741
],
3842
});
3943

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { acceptHMRUpdate, defineStore } from 'pinia';
2+
3+
export const useCartStore = defineStore({
4+
id: 'cart',
5+
state: () => ({
6+
rawItems: [] as string[],
7+
}),
8+
getters: {
9+
items: (state): Array<{ name: string; amount: number }> =>
10+
state.rawItems.reduce(
11+
(items, item) => {
12+
const existingItem = items.find(it => it.name === item);
13+
14+
if (!existingItem) {
15+
items.push({ name: item, amount: 1 });
16+
} else {
17+
existingItem.amount++;
18+
}
19+
20+
return items;
21+
},
22+
[] as Array<{ name: string; amount: number }>,
23+
),
24+
},
25+
actions: {
26+
addItem(name: string) {
27+
this.rawItems.push(name);
28+
},
29+
30+
removeItem(name: string) {
31+
const i = this.rawItems.lastIndexOf(name);
32+
if (i > -1) this.rawItems.splice(i, 1);
33+
},
34+
35+
throwError() {
36+
throw new Error('error');
37+
},
38+
},
39+
});
40+
41+
if (import.meta.hot) {
42+
import.meta.hot.accept(acceptHMRUpdate(useCartStore, import.meta.hot));
43+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<template>
2+
<Layout>
3+
<div>
4+
<div style="margin: 1rem 0;">
5+
<PiniaLogo />
6+
</div>
7+
8+
<form @submit.prevent="addItemToCart" data-testid="add-items">
9+
<input id="item-input" type="text" v-model="itemName" />
10+
<button id="item-add">Add</button>
11+
<button id="throw-error" @click="throwError">Throw error</button>
12+
</form>
13+
14+
<form>
15+
<ul data-testid="items">
16+
<li v-for="item in cart.items" :key="item.name">
17+
{{ item.name }} ({{ item.amount }})
18+
<button
19+
@click="cart.removeItem(item.name)"
20+
type="button"
21+
>X</button>
22+
</li>
23+
</ul>
24+
25+
<button
26+
:disabled="!cart.items.length"
27+
@click="clearCart"
28+
type="button"
29+
data-testid="clear"
30+
>Clear the cart</button>
31+
</form>
32+
</div>
33+
</Layout>
34+
</template>
35+
36+
<script lang="ts">
37+
import { defineComponent, ref } from 'vue'
38+
import { useCartStore } from '../stores/cart'
39+
40+
41+
export default defineComponent({
42+
setup() {
43+
const cart = useCartStore()
44+
45+
const itemName = ref('')
46+
47+
function addItemToCart() {
48+
if (!itemName.value) return
49+
cart.addItem(itemName.value)
50+
itemName.value = ''
51+
}
52+
53+
function throwError() {
54+
throw new Error('This is an error')
55+
}
56+
57+
function clearCart() {
58+
if (window.confirm('Are you sure you want to clear the cart?')) {
59+
cart.rawItems = []
60+
}
61+
}
62+
63+
// @ts-ignore
64+
window.stores = { cart }
65+
66+
return {
67+
itemName,
68+
addItemToCart,
69+
cart,
70+
71+
throwError,
72+
clearCart,
73+
}
74+
},
75+
})
76+
</script>
77+
78+
<style scoped>
79+
img {
80+
width: 200px;
81+
}
82+
83+
button,
84+
input {
85+
margin-right: 0.5rem;
86+
margin-bottom: 0.5rem;
87+
}
88+
</style>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { expect, test } from '@playwright/test';
2+
import { waitForError } from '@sentry-internal/test-utils';
3+
4+
test('sends pinia action breadcrumbs and state context', async ({ page }) => {
5+
await page.goto('/cart');
6+
7+
await page.locator('#item-input').fill('item');
8+
await page.locator('#item-add').click();
9+
10+
const errorPromise = waitForError('vue-3', async errorEvent => {
11+
return errorEvent?.exception?.values?.[0].value === 'This is an error';
12+
});
13+
14+
await page.locator('#throw-error').click();
15+
16+
const error = await errorPromise;
17+
18+
expect(error).toBeTruthy();
19+
expect(error.breadcrumbs?.length).toBeGreaterThan(0);
20+
21+
const actionBreadcrumb = error.breadcrumbs?.find(breadcrumb => breadcrumb.category === 'action');
22+
23+
expect(actionBreadcrumb).toBeDefined();
24+
expect(actionBreadcrumb?.message).toBe('Transformed: addItem');
25+
expect(actionBreadcrumb?.level).toBe('info');
26+
27+
const stateContext = error.contexts?.state?.state;
28+
29+
expect(stateContext).toBeDefined();
30+
expect(stateContext?.type).toBe('pinia');
31+
expect(stateContext?.value).toEqual({
32+
transformed: true,
33+
rawItems: ['item'],
34+
});
35+
});

dev-packages/node-integration-tests/suites/tracing/httpIntegration/server-ignoreOutgoingRequests.js

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ Sentry.init({
1111
integrations: [
1212
Sentry.httpIntegration({
1313
ignoreOutgoingRequests: (url, request) => {
14-
if (url.includes('example.com')) {
14+
if (url === 'http://example.com/blockUrl') {
1515
return true;
1616
}
17-
if (request.method === 'POST' && request.path === '/path') {
17+
18+
if (request.hostname === 'example.com' && request.path === '/blockRequest') {
1819
return true;
1920
}
2021
return false;
@@ -32,28 +33,37 @@ const app = express();
3233

3334
app.use(cors());
3435

35-
app.get('/test', (_req, response) => {
36-
http
37-
.request('http://example.com/', res => {
38-
res.on('data', () => {});
39-
res.on('end', () => {
40-
response.send({ response: 'done' });
41-
});
42-
})
43-
.end();
36+
app.get('/testUrl', (_req, response) => {
37+
makeHttpRequest('http://example.com/blockUrl').then(() => {
38+
makeHttpRequest('http://example.com/pass').then(() => {
39+
response.send({ response: 'done' });
40+
});
41+
});
4442
});
4543

46-
app.post('/testPath', (_req, response) => {
47-
http
48-
.request('http://example.com/path', res => {
49-
res.on('data', () => {});
50-
res.on('end', () => {
51-
response.send({ response: 'done' });
52-
});
53-
})
54-
.end();
44+
app.get('/testRequest', (_req, response) => {
45+
makeHttpRequest('http://example.com/blockRequest').then(() => {
46+
makeHttpRequest('http://example.com/pass').then(() => {
47+
response.send({ response: 'done' });
48+
});
49+
});
5550
});
5651

5752
Sentry.setupExpressErrorHandler(app);
5853

5954
startExpressServerAndSendPortToRunner(app);
55+
56+
function makeHttpRequest(url) {
57+
return new Promise((resolve, reject) => {
58+
http
59+
.get(url, res => {
60+
res.on('data', () => {});
61+
res.on('end', () => {
62+
resolve();
63+
});
64+
})
65+
.on('error', error => {
66+
reject(error);
67+
});
68+
});
69+
}

dev-packages/node-integration-tests/suites/tracing/httpIntegration/test.ts

Lines changed: 23 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -128,65 +128,45 @@ describe('httpIntegration', () => {
128128
});
129129
});
130130

131-
describe("doesn't create child spans for outgoing requests ignored via `ignoreOutgoingRequests`", () => {
131+
describe("doesn't create child spans or breadcrumbs for outgoing requests ignored via `ignoreOutgoingRequests`", () => {
132132
test('via the url param', done => {
133133
const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js')
134134
.expect({
135-
transaction: {
136-
contexts: {
137-
trace: {
138-
span_id: expect.any(String),
139-
trace_id: expect.any(String),
140-
data: {
141-
url: expect.stringMatching(/\/test$/),
142-
'http.response.status_code': 200,
143-
},
144-
op: 'http.server',
145-
status: 'ok',
146-
},
147-
},
148-
transaction: 'GET /test',
149-
spans: [
150-
expect.objectContaining({ op: 'middleware.express', description: 'query' }),
151-
expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }),
152-
expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }),
153-
expect.objectContaining({ op: 'request_handler.express', description: '/test' }),
154-
],
135+
transaction: event => {
136+
expect(event.transaction).toBe('GET /testUrl');
137+
138+
const requestSpans = event.spans?.filter(span => span.op === 'http.client');
139+
expect(requestSpans).toHaveLength(1);
140+
expect(requestSpans![0]?.description).toBe('GET http://example.com/pass');
141+
142+
const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http');
143+
expect(breadcrumbs).toHaveLength(1);
144+
expect(breadcrumbs![0]?.data?.url).toEqual('http://example.com/pass');
155145
},
156146
})
157147
.start(done);
158148

159-
runner.makeRequest('get', '/test');
149+
runner.makeRequest('get', '/testUrl');
160150
});
161151

162152
test('via the request param', done => {
163153
const runner = createRunner(__dirname, 'server-ignoreOutgoingRequests.js')
164154
.expect({
165-
transaction: {
166-
contexts: {
167-
trace: {
168-
span_id: expect.any(String),
169-
trace_id: expect.any(String),
170-
data: {
171-
url: expect.stringMatching(/\/testPath$/),
172-
'http.response.status_code': 200,
173-
},
174-
op: 'http.server',
175-
status: 'ok',
176-
},
177-
},
178-
transaction: 'POST /testPath',
179-
spans: [
180-
expect.objectContaining({ op: 'middleware.express', description: 'query' }),
181-
expect.objectContaining({ op: 'middleware.express', description: 'expressInit' }),
182-
expect.objectContaining({ op: 'middleware.express', description: 'corsMiddleware' }),
183-
expect.objectContaining({ op: 'request_handler.express', description: '/testPath' }),
184-
],
155+
transaction: event => {
156+
expect(event.transaction).toBe('GET /testRequest');
157+
158+
const requestSpans = event.spans?.filter(span => span.op === 'http.client');
159+
expect(requestSpans).toHaveLength(1);
160+
expect(requestSpans![0]?.description).toBe('GET http://example.com/pass');
161+
162+
const breadcrumbs = event.breadcrumbs?.filter(b => b.category === 'http');
163+
expect(breadcrumbs).toHaveLength(1);
164+
expect(breadcrumbs![0]?.data?.url).toEqual('http://example.com/pass');
185165
},
186166
})
187167
.start(done);
188168

189-
runner.makeRequest('post', '/testPath');
169+
runner.makeRequest('get', '/testRequest');
190170
});
191171
});
192172
});

0 commit comments

Comments
 (0)