Skip to content

Commit 7acc2c8

Browse files
logaretmJPeer264
authored andcommitted
fix: use match instead and export fn for testing
1 parent 8919cc7 commit 7acc2c8

2 files changed

Lines changed: 44 additions & 45 deletions

File tree

packages/nextjs/src/config/withSentryConfig/getFinalConfigObjectUtils.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,16 @@ export function maybeCreateRouteManifest(
115115

116116
// Apply route exclusion filter if configured
117117
const excludeFilter = userSentryOptions.routeManifestInjection?.exclude;
118+
return filterRouteManifest(manifest, excludeFilter);
119+
}
120+
121+
type ExcludeFilter = ((route: string) => boolean) | (string | RegExp)[] | undefined;
122+
123+
/**
124+
* Filters routes from the manifest based on the exclude filter.
125+
* (Exported only for testing)
126+
*/
127+
export function filterRouteManifest(manifest: RouteManifest, excludeFilter: ExcludeFilter): RouteManifest {
118128
if (!excludeFilter) {
119129
return manifest;
120130
}
@@ -128,7 +138,8 @@ export function maybeCreateRouteManifest(
128138
if (typeof pattern === 'string') {
129139
return route === pattern;
130140
}
131-
return pattern.test(route);
141+
142+
return !!route.match(pattern);
132143
});
133144
};
134145

packages/nextjs/test/config/manifest/excludeRoutesFromManifest.test.ts

Lines changed: 32 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,6 @@
11
import { describe, expect, it } from 'vitest';
22
import type { RouteManifest } from '../../../src/config/manifest/types';
3-
import type { SentryBuildOptions } from '../../../src/config/types';
4-
5-
type RouteManifestInjectionOptions = Exclude<SentryBuildOptions['routeManifestInjection'], false | undefined>;
6-
type ExcludeFilter = RouteManifestInjectionOptions['exclude'];
7-
8-
// Inline the filtering logic for unit testing
9-
// This mirrors what maybeCreateRouteManifest does internally
10-
function filterManifest(manifest: RouteManifest, excludeFilter: ExcludeFilter): RouteManifest {
11-
if (!excludeFilter) {
12-
return manifest;
13-
}
14-
15-
const shouldExclude = (route: string): boolean => {
16-
if (typeof excludeFilter === 'function') {
17-
return excludeFilter(route);
18-
}
19-
20-
return excludeFilter.some((pattern: string | RegExp) => {
21-
if (typeof pattern === 'string') {
22-
return route === pattern;
23-
}
24-
return pattern.test(route);
25-
});
26-
};
27-
28-
return {
29-
staticRoutes: manifest.staticRoutes.filter(r => !shouldExclude(r.path)),
30-
dynamicRoutes: manifest.dynamicRoutes.filter(r => !shouldExclude(r.path)),
31-
isrRoutes: manifest.isrRoutes.filter(r => !shouldExclude(r)),
32-
};
33-
}
3+
import { filterRouteManifest } from '../../../src/config/withSentryConfig/getFinalConfigObjectUtils';
344

355
describe('routeManifestInjection.exclude', () => {
366
const mockManifest: RouteManifest = {
@@ -52,14 +22,14 @@ describe('routeManifestInjection.exclude', () => {
5222

5323
describe('with no filter', () => {
5424
it('should return manifest unchanged', () => {
55-
const result = filterManifest(mockManifest, undefined);
25+
const result = filterRouteManifest(mockManifest, undefined);
5626
expect(result).toEqual(mockManifest);
5727
});
5828
});
5929

6030
describe('with string patterns', () => {
6131
it('should exclude exact string matches', () => {
62-
const result = filterManifest(mockManifest, ['/admin']);
32+
const result = filterRouteManifest(mockManifest, ['/admin']);
6333

6434
expect(result.staticRoutes.map(r => r.path)).toEqual([
6535
'/',
@@ -71,7 +41,7 @@ describe('routeManifestInjection.exclude', () => {
7141
});
7242

7343
it('should exclude multiple exact matches', () => {
74-
const result = filterManifest(mockManifest, ['/admin', '/about', '/blog']);
44+
const result = filterRouteManifest(mockManifest, ['/admin', '/about', '/blog']);
7545

7646
expect(result.staticRoutes.map(r => r.path)).toEqual([
7747
'/',
@@ -85,22 +55,22 @@ describe('routeManifestInjection.exclude', () => {
8555

8656
describe('with regex patterns', () => {
8757
it('should exclude routes matching regex', () => {
88-
const result = filterManifest(mockManifest, [/^\/admin/]);
58+
const result = filterRouteManifest(mockManifest, [/^\/admin/]);
8959

9060
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/about', '/internal/secret', '/public/page']);
9161
expect(result.dynamicRoutes.map(r => r.path)).toEqual(['/users/:id', '/secret-feature/:id']);
9262
expect(result.isrRoutes).toEqual(['/blog', '/internal/stats']);
9363
});
9464

9565
it('should support multiple regex patterns', () => {
96-
const result = filterManifest(mockManifest, [/^\/admin/, /^\/internal/]);
66+
const result = filterRouteManifest(mockManifest, [/^\/admin/, /^\/internal/]);
9767

9868
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/about', '/public/page']);
9969
expect(result.isrRoutes).toEqual(['/blog']);
10070
});
10171

10272
it('should support partial regex matches', () => {
103-
const result = filterManifest(mockManifest, [/secret/]);
73+
const result = filterRouteManifest(mockManifest, [/secret/]);
10474

10575
expect(result.staticRoutes.map(r => r.path)).toEqual([
10676
'/',
@@ -111,27 +81,45 @@ describe('routeManifestInjection.exclude', () => {
11181
]);
11282
expect(result.dynamicRoutes.map(r => r.path)).toEqual(['/users/:id', '/admin/users/:id']);
11383
});
84+
85+
it('should handle regex with global flag consistently across multiple routes', () => {
86+
// Regex with `g` flag has stateful lastIndex - ensure it works correctly
87+
const globalRegex = /admin/g;
88+
const result = filterRouteManifest(mockManifest, [globalRegex]);
89+
90+
// All admin routes should be excluded, not just every other one
91+
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/about', '/internal/secret', '/public/page']);
92+
expect(result.dynamicRoutes.map(r => r.path)).toEqual(['/users/:id', '/secret-feature/:id']);
93+
expect(result.isrRoutes).toEqual(['/blog', '/internal/stats']);
94+
});
95+
96+
it('should handle regex with global and case-insensitive flags', () => {
97+
const result = filterRouteManifest(mockManifest, [/ADMIN/gi]);
98+
99+
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/about', '/internal/secret', '/public/page']);
100+
expect(result.dynamicRoutes.map(r => r.path)).toEqual(['/users/:id', '/secret-feature/:id']);
101+
});
114102
});
115103

116104
describe('with mixed patterns', () => {
117105
it('should support both strings and regex', () => {
118-
const result = filterManifest(mockManifest, ['/about', /^\/admin/]);
106+
const result = filterRouteManifest(mockManifest, ['/about', /^\/admin/]);
119107

120108
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/internal/secret', '/public/page']);
121109
});
122110
});
123111

124112
describe('with function filter', () => {
125113
it('should exclude routes where function returns true', () => {
126-
const result = filterManifest(mockManifest, (route: string) => route.includes('admin'));
114+
const result = filterRouteManifest(mockManifest, (route: string) => route.includes('admin'));
127115

128116
expect(result.staticRoutes.map(r => r.path)).toEqual(['/', '/about', '/internal/secret', '/public/page']);
129117
expect(result.dynamicRoutes.map(r => r.path)).toEqual(['/users/:id', '/secret-feature/:id']);
130118
expect(result.isrRoutes).toEqual(['/blog', '/internal/stats']);
131119
});
132120

133121
it('should support complex filter logic', () => {
134-
const result = filterManifest(mockManifest, (route: string) => {
122+
const result = filterRouteManifest(mockManifest, (route: string) => {
135123
// Exclude anything with "secret" or "internal" or admin routes
136124
return route.includes('secret') || route.includes('internal') || route.startsWith('/admin');
137125
});
@@ -150,25 +138,25 @@ describe('routeManifestInjection.exclude', () => {
150138
isrRoutes: [],
151139
};
152140

153-
const result = filterManifest(emptyManifest, [/admin/]);
141+
const result = filterRouteManifest(emptyManifest, [/admin/]);
154142
expect(result).toEqual(emptyManifest);
155143
});
156144

157145
it('should handle filter that excludes everything', () => {
158-
const result = filterManifest(mockManifest, () => true);
146+
const result = filterRouteManifest(mockManifest, () => true);
159147

160148
expect(result.staticRoutes).toEqual([]);
161149
expect(result.dynamicRoutes).toEqual([]);
162150
expect(result.isrRoutes).toEqual([]);
163151
});
164152

165153
it('should handle filter that excludes nothing', () => {
166-
const result = filterManifest(mockManifest, () => false);
154+
const result = filterRouteManifest(mockManifest, () => false);
167155
expect(result).toEqual(mockManifest);
168156
});
169157

170158
it('should handle empty filter array', () => {
171-
const result = filterManifest(mockManifest, []);
159+
const result = filterRouteManifest(mockManifest, []);
172160
expect(result).toEqual(mockManifest);
173161
});
174162
});

0 commit comments

Comments
 (0)