Skip to content

Commit 8fff1ab

Browse files
committed
Add bundler tests
1 parent 83b3890 commit 8fff1ab

File tree

11 files changed

+408
-50
lines changed

11 files changed

+408
-50
lines changed

.github/workflows/build.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,28 @@ jobs:
551551
files: packages/**/*.junit.xml
552552
token: ${{ secrets.CODECOV_TOKEN }}
553553

554+
job_browser_bundler_tests:
555+
name: Browser Bundler Tests
556+
needs: [job_get_metadata, job_build]
557+
timeout-minutes: 5
558+
runs-on: ubuntu-24.04
559+
steps:
560+
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})
561+
uses: actions/checkout@v5
562+
with:
563+
ref: ${{ env.HEAD_COMMIT }}
564+
- name: Set up Node
565+
uses: actions/setup-node@v4
566+
with:
567+
node-version-file: 'package.json'
568+
- name: Restore caches
569+
uses: ./.github/actions/restore-cache
570+
with:
571+
dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }}
572+
573+
- name: Run bundler tests
574+
run: yarn test:bundler
575+
554576
job_browser_playwright_tests:
555577
name:
556578
Playwright ${{ matrix.bundle }}${{ matrix.project && matrix.project != 'chromium' && format(' {0}',
@@ -1160,6 +1182,7 @@ jobs:
11601182
job_node_unit_tests,
11611183
job_node_integration_tests,
11621184
job_cloudflare_integration_tests,
1185+
job_browser_bundler_tests,
11631186
job_browser_playwright_tests,
11641187
job_browser_loader_tests,
11651188
job_remix_integration_tests,
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module.exports = {
2+
extends: ['../../.eslintrc.js'],
3+
parserOptions: {
4+
sourceType: 'module',
5+
},
6+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<body>
4+
<script type="module" src="/index.js"></script>
5+
</body>
6+
</html>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { init } from '@sentry/browser';
2+
3+
init({
4+
dsn: 'https://00000000000000000000000000000000@o000000.ingest.sentry.io/0000000',
5+
});
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "@sentry-internal/bundler-tests",
3+
"version": "10.22.0",
4+
"description": "Bundler tests for Sentry Browser SDK",
5+
"repository": "git://github.com/getsentry/sentry-javascript.git",
6+
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/bundler-tests",
7+
"author": "Sentry",
8+
"license": "MIT",
9+
"private": true,
10+
"main": "./index.mjs",
11+
"scripts": {
12+
"test": "vitest run"
13+
},
14+
"dependencies": {
15+
"@sentry/browser": "10.22.0",
16+
"webpack": "^5.0.0",
17+
"rollup": "^4.0.0",
18+
"vite": "^7.0.0",
19+
"@rollup/plugin-node-resolve": "^15.2.3",
20+
"vitest": "^3.2.4"
21+
},
22+
"volta": {
23+
"extends": "../../package.json"
24+
},
25+
"type": "module"
26+
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { describe, expect, beforeAll, test } from 'vitest';
2+
import * as path from 'node:path';
3+
import * as fs from 'node:fs';
4+
import { fileURLToPath } from 'node:url';
5+
6+
import webpack from 'webpack';
7+
import { rollup } from 'rollup';
8+
import { build as viteBuild } from 'vite';
9+
import nodeResolve from '@rollup/plugin-node-resolve';
10+
11+
// Helper functions
12+
const __filename = fileURLToPath(import.meta.url);
13+
const __dirname = path.dirname(__filename);
14+
15+
function distDir(name: string): string {
16+
const dir = path.join(__dirname, '..', 'dist', name);
17+
if (!fs.existsSync(dir)) {
18+
fs.mkdirSync(dir, { recursive: true });
19+
}
20+
return dir;
21+
}
22+
23+
function rimraf(dir: string): void {
24+
if (fs.existsSync(dir)) {
25+
fs.rmSync(dir, { recursive: true, force: true });
26+
}
27+
}
28+
29+
function readAllJs(outDir: string): string {
30+
let contents = '';
31+
const stack = [outDir];
32+
while (stack.length) {
33+
const current = stack.pop()!;
34+
for (const entry of fs.readdirSync(current)) {
35+
const full = path.join(current, entry);
36+
const stat = fs.statSync(full);
37+
if (stat.isDirectory()) {
38+
stack.push(full);
39+
} else if (entry.endsWith('.js') || entry.endsWith('.mjs')) {
40+
contents += fs.readFileSync(full, 'utf8');
41+
}
42+
}
43+
}
44+
return contents;
45+
}
46+
47+
function fixtureEntry(name: string): string {
48+
return path.resolve(__dirname, '..', 'fixtures', name, 'index.js');
49+
}
50+
51+
function rootDir(): string {
52+
return path.join(__dirname, '../../..');
53+
}
54+
55+
const SPOTLIGHT_URL = 'localhost:8969';
56+
57+
type BundleMode = 'development' | 'production';
58+
59+
function bundleWithWebpack(mode: BundleMode): Promise<string> {
60+
return new Promise((resolve, reject) => {
61+
const outDir = distDir(`webpack-${mode}`);
62+
rimraf(outDir);
63+
const compiler = webpack({
64+
mode,
65+
entry: fixtureEntry('basic'),
66+
output: { path: outDir, filename: 'bundle.js' },
67+
});
68+
compiler?.run((err: Error | null | undefined, stats: webpack.Stats | undefined) => {
69+
try {
70+
if (err) throw err;
71+
if (stats?.hasErrors()) {
72+
throw new Error(stats.toString('errors-only'));
73+
}
74+
resolve(readAllJs(outDir));
75+
} catch (e) {
76+
reject(e);
77+
} finally {
78+
compiler.close(() => {});
79+
}
80+
});
81+
});
82+
}
83+
84+
async function bundleWithRollup(mode: BundleMode): Promise<string> {
85+
const outDir = distDir(`rollup-${mode}`);
86+
rimraf(outDir);
87+
88+
const bundle = await rollup({
89+
input: fixtureEntry('basic'),
90+
plugins: [
91+
nodeResolve({
92+
// There should really be a default where these get specified automatically
93+
exportConditions: [mode === 'production' ? 'production' : 'development'],
94+
}),
95+
],
96+
});
97+
await bundle.write({ dir: outDir, format: 'esm' });
98+
await bundle.close();
99+
return readAllJs(outDir);
100+
}
101+
102+
async function bundleWithVite(mode: BundleMode): Promise<string> {
103+
const outDir = distDir(`vite-${mode}`);
104+
rimraf(outDir);
105+
106+
// In Vitest, NODE_ENV is always 'test', so we need to override it here
107+
const prev = process.env.NODE_ENV;
108+
process.env.NODE_ENV = mode;
109+
110+
await viteBuild({
111+
mode,
112+
root: path.dirname(fixtureEntry('basic')),
113+
build: { outDir, minify: mode === 'production' },
114+
});
115+
116+
process.env.NODE_ENV = prev;
117+
118+
return readAllJs(outDir);
119+
}
120+
121+
describe('spotlight', () => {
122+
beforeAll(() => {
123+
const distRoot = path.join(rootDir(), 'dist');
124+
rimraf(distRoot);
125+
});
126+
127+
const cases: [string, (mode: BundleMode) => Promise<string>][] = [
128+
['webpack', bundleWithWebpack],
129+
['rollup', bundleWithRollup],
130+
['vite', bundleWithVite],
131+
];
132+
133+
for (const [name, bundler] of cases) {
134+
test(`${name} development bundle contains spotlight`, async () => {
135+
const code = await bundler('development');
136+
expect(code).includes(SPOTLIGHT_URL);
137+
});
138+
139+
test(`${name} production bundle does not contain spotlight`, async () => {
140+
const code = await bundler('production');
141+
expect(code).not.includes(SPOTLIGHT_URL);
142+
});
143+
}
144+
});
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { defineConfig } from 'vitest/config';
2+
3+
export default defineConfig({
4+
test: {
5+
include: ['tests/**/*.test.*s'],
6+
timeout: 10000,
7+
hookTimeout: 10000,
8+
},
9+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const path = require('path');
2+
3+
module.exports = (env, argv) => ({
4+
mode: argv.mode || 'development',
5+
entry: path.resolve(__dirname, 'fixtures/basic/index.js'),
6+
output: {
7+
path: path.resolve(__dirname, 'dist/webpack-' + (argv.mode || 'development')),
8+
filename: 'bundle.js',
9+
},
10+
});

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"test": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,node-core-integration-tests,cloudflare-integration-tests}\" test",
3535
"test:unit": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,node-core-integration-tests,cloudflare-integration-tests}\" test:unit",
3636
"test:update-snapshots": "lerna run test:update-snapshots",
37+
"test:bundler": "lerna run test --scope @sentry-internal/bundler-tests",
3738
"test:pr": "nx affected -t test --exclude \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,node-core-integration-tests,cloudflare-integration-tests}\"",
3839
"test:pr:browser": "UNIT_TEST_ENV=browser ts-node ./scripts/ci-unit-tests.ts --affected",
3940
"test:pr:node": "UNIT_TEST_ENV=node ts-node ./scripts/ci-unit-tests.ts --affected",
@@ -100,7 +101,8 @@
100101
"dev-packages/clear-cache-gh-action",
101102
"dev-packages/external-contributor-gh-action",
102103
"dev-packages/rollup-utils",
103-
"dev-packages/node-overhead-gh-action"
104+
"dev-packages/node-overhead-gh-action",
105+
"dev-packages/bundler-tests"
104106
],
105107
"devDependencies": {
106108
"@rollup/plugin-commonjs": "^25.0.7",

packages/browser/package.json

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,18 @@
1818
"exports": {
1919
"./package.json": "./package.json",
2020
".": {
21-
"import": {
22-
"types": "./build/npm/types/index.d.ts",
23-
"development": "./build/npm/esm/dev/index.js",
24-
"production": "./build/npm/esm/prod/index.js",
25-
"default": "./build/npm/esm/prod/index.js"
21+
"types": "./build/npm/types/index.d.ts",
22+
"development": {
23+
"import": "./build/npm/esm/dev/index.js",
24+
"require": "./build/npm/cjs/dev/index.js"
2625
},
27-
"require": {
28-
"types": "./build/npm/types/index.d.ts",
29-
"development": "./build/npm/cjs/dev/index.js",
30-
"production": "./build/npm/cjs/prod/index.js",
31-
"default": "./build/npm/cjs/prod/index.js"
26+
"production": {
27+
"import": "./build/npm/esm/prod/index.js",
28+
"require": "./build/npm/cjs/prod/index.js"
29+
},
30+
"default": {
31+
"import": "./build/npm/esm/prod/index.js",
32+
"require": "./build/npm/cjs/prod/index.js"
3233
}
3334
}
3435
},

0 commit comments

Comments
 (0)