-
-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathmoduleMetadataInjectionLoader.test.ts
More file actions
160 lines (122 loc) · 6.25 KB
/
moduleMetadataInjectionLoader.test.ts
File metadata and controls
160 lines (122 loc) · 6.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import { describe, expect, it } from 'vitest';
import type { ModuleMetadataInjectionLoaderOptions } from '../../src/config/loaders/moduleMetadataInjectionLoader';
import moduleMetadataInjectionLoader from '../../src/config/loaders/moduleMetadataInjectionLoader';
import type { LoaderThis } from '../../src/config/loaders/types';
function createLoaderThis(
applicationKey: string,
useGetOptions = true,
): LoaderThis<ModuleMetadataInjectionLoaderOptions> {
const base = {
addDependency: () => undefined,
async: () => undefined,
cacheable: () => undefined,
callback: () => undefined,
resourcePath: './app/page.tsx',
};
if (useGetOptions) {
return { ...base, getOptions: () => ({ applicationKey }) } as LoaderThis<ModuleMetadataInjectionLoaderOptions>;
}
return { ...base, query: { applicationKey } } as LoaderThis<ModuleMetadataInjectionLoaderOptions>;
}
describe('moduleMetadataInjectionLoader', () => {
it('should inject metadata snippet into simple code', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = "import * as Sentry from '@sentry/nextjs';\nSentry.init();";
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
expect(result).toContain('_sentryModuleMetadata');
expect(result).toContain('_sentryBundlerPluginAppKey:my-app');
// Wrapped in try-catch IIFE
expect(result).toContain('!function(){try{');
});
it('should inject after "use strict" directive', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '"use strict";\nconsole.log("hello");';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
const metadataIndex = result.indexOf('_sentryModuleMetadata');
const directiveIndex = result.indexOf('"use strict"');
expect(metadataIndex).toBeGreaterThan(directiveIndex);
});
it('should inject after "use client" directive', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '"use client";\nimport React from \'react\';';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
const metadataIndex = result.indexOf('_sentryModuleMetadata');
const directiveIndex = result.indexOf('"use client"');
expect(metadataIndex).toBeGreaterThan(directiveIndex);
});
it('should handle code with leading comments before directives', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '// some comment\n"use client";\nimport React from \'react\';';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
expect(result).toContain('_sentryBundlerPluginAppKey:my-app');
const metadataIndex = result.indexOf('_sentryModuleMetadata');
const directiveIndex = result.indexOf('"use client"');
expect(metadataIndex).toBeGreaterThan(directiveIndex);
});
it('should handle code with block comments before directives', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '/* block comment */\n"use client";\nimport React from \'react\';';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
expect(result).toContain('_sentryBundlerPluginAppKey:my-app');
});
it('should set cacheable to false', () => {
let cacheableValue: boolean | undefined;
const loaderThis = {
addDependency: () => undefined,
async: () => undefined,
cacheable: (flag: boolean) => {
cacheableValue = flag;
},
callback: () => undefined,
resourcePath: './app/page.tsx',
getOptions: () => ({ applicationKey: 'my-app' }),
} as LoaderThis<ModuleMetadataInjectionLoaderOptions>;
moduleMetadataInjectionLoader.call(loaderThis, 'const x = 1;');
expect(cacheableValue).toBe(false);
});
it('should work with webpack 4 query API', () => {
const loaderThis = createLoaderThis('my-app', false);
const userCode = 'const x = 1;';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
expect(result).toContain('_sentryBundlerPluginAppKey:my-app');
});
it('should use try-catch IIFE pattern matching the webpack plugin', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = 'const x = 1;';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
// Should be wrapped in a try-catch IIFE so injection failures never break the module
expect(result).toContain('!function(){try{');
expect(result).toContain('}catch(e){}}();');
// Should resolve the global object like the webpack plugin does
expect(result).toContain('typeof window');
expect(result).toContain('typeof globalThis');
// Should key by stack trace like the webpack plugin does
expect(result).toContain('e._sentryModuleMetadata[(new e.Error).stack]');
// Should use Object.assign to merge metadata
expect(result).toContain('Object.assign({}');
});
it('should contain the correct app key format in output', () => {
const loaderThis = createLoaderThis('test-key-123');
const userCode = 'export default function Page() {}';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
expect(result).toContain('"_sentryBundlerPluginAppKey:test-key-123":true');
});
it('should inject after multiple directives', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '"use strict";\n"use client";\nimport React from \'react\';';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
const metadataIndex = result.indexOf('_sentryModuleMetadata');
const clientDirectiveIndex = result.indexOf('"use client"');
const importIndex = result.indexOf("import React from 'react';");
expect(metadataIndex).toBeGreaterThan(clientDirectiveIndex);
expect(metadataIndex).toBeLessThan(importIndex);
});
it('should inject after comments between multiple directives', () => {
const loaderThis = createLoaderThis('my-app');
const userCode = '"use strict";\n/* keep */\n"use client";\nimport React from \'react\';';
const result = moduleMetadataInjectionLoader.call(loaderThis, userCode);
const metadataIndex = result.indexOf('_sentryModuleMetadata');
const clientDirectiveIndex = result.indexOf('"use client"');
expect(metadataIndex).toBeGreaterThan(clientDirectiveIndex);
});
});