Skip to content

Commit 549370c

Browse files
authored
test(svelte): Add Svelte Testing Library and trackComponent tests (#5686)
1 parent f13fed6 commit 549370c

File tree

6 files changed

+238
-1
lines changed

6 files changed

+238
-1
lines changed

packages/svelte/jest.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@ const baseConfig = require('../../jest/jest.config.js');
33
module.exports = {
44
...baseConfig,
55
testEnvironment: 'jsdom',
6+
transform: {
7+
'^.+\\.svelte$': 'svelte-jester',
8+
...baseConfig.transform,
9+
},
610
};

packages/svelte/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
"svelte": "3.x"
2727
},
2828
"devDependencies": {
29-
"svelte": "3.49.0"
29+
"@testing-library/svelte": "^3.2.1",
30+
"svelte": "3.49.0",
31+
"svelte-jester": "^2.3.2"
3032
},
3133
"scripts": {
3234
"build": "run-p build:rollup build:types",
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<script>
2+
import { onMount, beforeUpdate, afterUpdate } from 'svelte';
3+
import * as Sentry from '../../src/index';
4+
5+
// Pass options to trackComponent as props of this component
6+
export let options;
7+
8+
Sentry.trackComponent(options);
9+
</script>
10+
11+
<h1>Hi, I'm a dummy component for testing</h1>
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
import { Scope } from '@sentry/hub';
2+
import { act, render } from '@testing-library/svelte';
3+
4+
// linter doesn't like Svelte component imports
5+
// eslint-disable-next-line import/no-unresolved
6+
import DummyComponent from './components/Dummy.svelte';
7+
8+
let returnUndefinedTransaction = false;
9+
10+
const testTransaction: { spans: any[]; startChild: jest.Mock; finish: jest.Mock } = {
11+
spans: [],
12+
startChild: jest.fn(),
13+
finish: jest.fn(),
14+
};
15+
const testUpdateSpan = { finish: jest.fn() };
16+
const testInitSpan: any = {
17+
transaction: testTransaction,
18+
finish: jest.fn(),
19+
startChild: jest.fn(),
20+
};
21+
22+
jest.mock('@sentry/hub', () => {
23+
const original = jest.requireActual('@sentry/hub');
24+
return {
25+
...original,
26+
getCurrentHub(): {
27+
getScope(): Scope;
28+
} {
29+
return {
30+
getScope(): any {
31+
return {
32+
getTransaction: () => {
33+
return returnUndefinedTransaction ? undefined : testTransaction;
34+
},
35+
};
36+
},
37+
};
38+
},
39+
};
40+
});
41+
42+
describe('Sentry.trackComponent()', () => {
43+
beforeEach(() => {
44+
jest.resetAllMocks();
45+
testTransaction.spans = [];
46+
47+
testTransaction.startChild.mockImplementation(spanCtx => {
48+
testTransaction.spans.push(spanCtx);
49+
return testInitSpan;
50+
});
51+
52+
testInitSpan.startChild.mockImplementation((spanCtx: any) => {
53+
testTransaction.spans.push(spanCtx);
54+
return testUpdateSpan;
55+
});
56+
57+
testInitSpan.finish = jest.fn();
58+
testInitSpan.endTimestamp = undefined;
59+
returnUndefinedTransaction = false;
60+
});
61+
62+
it('creates nested init and update spans on component initialization', () => {
63+
render(DummyComponent, { props: { options: {} } });
64+
65+
expect(testTransaction.startChild).toHaveBeenCalledWith({
66+
description: '<Dummy>',
67+
op: 'ui.svelte.init',
68+
});
69+
70+
expect(testInitSpan.startChild).toHaveBeenCalledWith({
71+
description: '<Dummy>',
72+
op: 'ui.svelte.update',
73+
});
74+
75+
expect(testInitSpan.finish).toHaveBeenCalledTimes(1);
76+
expect(testUpdateSpan.finish).toHaveBeenCalledTimes(1);
77+
expect(testTransaction.spans.length).toEqual(2);
78+
});
79+
80+
it('creates an update span, when the component is updated', async () => {
81+
// Make the finish() function actually end the initSpan
82+
testInitSpan.finish.mockImplementation(() => {
83+
testInitSpan.endTimestamp = new Date().getTime();
84+
});
85+
86+
// first we create the component
87+
const { component } = render(DummyComponent, { props: { options: {} } });
88+
89+
// then trigger an update
90+
// (just changing the trackUpdates prop so that we trigger an update. #
91+
// The value doesn't do anything here)
92+
await act(() => component.$set({ options: { trackUpdates: true } }));
93+
94+
// once for init (unimportant here), once for starting the update span
95+
expect(testTransaction.startChild).toHaveBeenCalledTimes(2);
96+
expect(testTransaction.startChild).toHaveBeenLastCalledWith({
97+
description: '<Dummy>',
98+
op: 'ui.svelte.update',
99+
});
100+
expect(testTransaction.spans.length).toEqual(3);
101+
});
102+
103+
it('only creates init spans if trackUpdates is deactivated', () => {
104+
render(DummyComponent, { props: { options: { trackUpdates: false } } });
105+
106+
expect(testTransaction.startChild).toHaveBeenCalledWith({
107+
description: '<Dummy>',
108+
op: 'ui.svelte.init',
109+
});
110+
111+
expect(testInitSpan.startChild).not.toHaveBeenCalled();
112+
113+
expect(testInitSpan.finish).toHaveBeenCalledTimes(1);
114+
expect(testTransaction.spans.length).toEqual(1);
115+
});
116+
117+
it('only creates update spans if trackInit is deactivated', () => {
118+
render(DummyComponent, { props: { options: { trackInit: false } } });
119+
120+
expect(testTransaction.startChild).toHaveBeenCalledWith({
121+
description: '<Dummy>',
122+
op: 'ui.svelte.update',
123+
});
124+
125+
expect(testInitSpan.startChild).not.toHaveBeenCalled();
126+
127+
expect(testInitSpan.finish).toHaveBeenCalledTimes(1);
128+
expect(testTransaction.spans.length).toEqual(1);
129+
});
130+
131+
it('creates no spans if trackInit and trackUpdates are deactivated', () => {
132+
render(DummyComponent, { props: { options: { trackInit: false, trackUpdates: false } } });
133+
134+
expect(testTransaction.startChild).not.toHaveBeenCalled();
135+
expect(testInitSpan.startChild).not.toHaveBeenCalled();
136+
expect(testTransaction.spans.length).toEqual(0);
137+
});
138+
139+
it('sets a custom component name as a span description if `componentName` is provided', async () => {
140+
render(DummyComponent, {
141+
props: { options: { componentName: 'CustomComponentName' } },
142+
});
143+
144+
expect(testTransaction.startChild).toHaveBeenCalledWith({
145+
description: '<CustomComponentName>',
146+
op: 'ui.svelte.init',
147+
});
148+
149+
expect(testInitSpan.startChild).toHaveBeenCalledWith({
150+
description: '<CustomComponentName>',
151+
op: 'ui.svelte.update',
152+
});
153+
154+
expect(testInitSpan.finish).toHaveBeenCalledTimes(1);
155+
expect(testUpdateSpan.finish).toHaveBeenCalledTimes(1);
156+
expect(testTransaction.spans.length).toEqual(2);
157+
});
158+
159+
it("doesn't do anything, if there's no ongoing transaction", async () => {
160+
returnUndefinedTransaction = true;
161+
162+
render(DummyComponent, {
163+
props: { options: { componentName: 'CustomComponentName' } },
164+
});
165+
166+
expect(testInitSpan.finish).toHaveBeenCalledTimes(0);
167+
expect(testUpdateSpan.finish).toHaveBeenCalledTimes(0);
168+
expect(testTransaction.spans.length).toEqual(0);
169+
});
170+
171+
it("doesn't record update spans, if there's no ongoing transaction at that time", async () => {
172+
// Make the finish() function actually end the initSpan
173+
testInitSpan.finish.mockImplementation(() => {
174+
testInitSpan.endTimestamp = new Date().getTime();
175+
});
176+
177+
// first we create the component
178+
const { component } = render(DummyComponent, { props: { options: {} } });
179+
180+
// then clear the current transaction and trigger an update
181+
returnUndefinedTransaction = true;
182+
await act(() => component.$set({ options: { trackUpdates: true } }));
183+
184+
// we should only record the init spans (including the initial update)
185+
// but not the second update
186+
expect(testTransaction.startChild).toHaveBeenCalledTimes(1);
187+
expect(testTransaction.startChild).toHaveBeenLastCalledWith({
188+
description: '<Dummy>',
189+
op: 'ui.svelte.init',
190+
});
191+
expect(testTransaction.spans.length).toEqual(2);
192+
});
193+
});

scripts/test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const NODE_8_SKIP_TESTS_PACKAGES = [
1717
'@sentry/nextjs',
1818
'@sentry/angular',
1919
'@sentry/remix',
20+
'@sentry/svelte', // svelte testing library requires Node >= 10
2021
];
2122

2223
// We have to downgrade some of our dependencies in order to run tests in Node 8 and 10.

yarn.lock

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4979,6 +4979,20 @@
49794979
dependencies:
49804980
defer-to-connect "^1.0.1"
49814981

4982+
"@testing-library/dom@^8.1.0":
4983+
version "8.17.1"
4984+
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.17.1.tgz#2d7af4ff6dad8d837630fecd08835aee08320ad7"
4985+
integrity sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ==
4986+
dependencies:
4987+
"@babel/code-frame" "^7.10.4"
4988+
"@babel/runtime" "^7.12.5"
4989+
"@types/aria-query" "^4.2.0"
4990+
aria-query "^5.0.0"
4991+
chalk "^4.1.0"
4992+
dom-accessibility-api "^0.5.9"
4993+
lz-string "^1.4.4"
4994+
pretty-format "^27.0.2"
4995+
49824996
"@testing-library/dom@^8.5.0":
49834997
version "8.12.0"
49844998
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.12.0.tgz#fef5e545533fb084175dda6509ee71d7d2f72e23"
@@ -5013,6 +5027,13 @@
50135027
"@testing-library/dom" "^8.5.0"
50145028
"@types/react-dom" "*"
50155029

5030+
"@testing-library/svelte@^3.2.1":
5031+
version "3.2.1"
5032+
resolved "https://registry.yarnpkg.com/@testing-library/svelte/-/svelte-3.2.1.tgz#c63bd2b7df7907f26e91b4ce0c50c77d8e7c4745"
5033+
integrity sha512-qP5nMAx78zt+a3y9Sws9BNQYP30cOQ/LXDYuAj7wNtw86b7AtB7TFAz6/Av9hFsW3IJHPBBIGff6utVNyq+F1g==
5034+
dependencies:
5035+
"@testing-library/dom" "^8.1.0"
5036+
50165037
"@tootallnate/once@1":
50175038
version "1.1.2"
50185039
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@@ -24945,6 +24966,11 @@ supports-preserve-symlinks-flag@^1.0.0:
2494524966
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
2494624967
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
2494724968

24969+
svelte-jester@^2.3.2:
24970+
version "2.3.2"
24971+
resolved "https://registry.yarnpkg.com/svelte-jester/-/svelte-jester-2.3.2.tgz#9eb818da30807bbcc940b6130d15b2c34408d64f"
24972+
integrity sha512-JtxSz4FWAaCRBXbPsh4LcDs4Ua7zdXgLC0TZvT1R56hRV0dymmNP+abw67DTPF7sQPyNxWsOKd0Sl7Q8SnP8kg==
24973+
2494824974
2494924975
version "3.49.0"
2495024976
resolved "https://registry.yarnpkg.com/svelte/-/svelte-3.49.0.tgz#5baee3c672306de1070c3b7888fc2204e36a4029"

0 commit comments

Comments
 (0)