Skip to content

Commit 0895d28

Browse files
committed
fix tests to test real use cases
1 parent d632e5c commit 0895d28

File tree

4 files changed

+124
-61
lines changed

4 files changed

+124
-61
lines changed

packages/wxt/e2e/tests/zip.test.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,25 @@ describe('Zipping', () => {
316316
version: '1.0.0',
317317
});
318318

319+
// Create external files before project setup
320+
const externalDir = project.resolvePath('..', 'shared');
321+
const externalFile = project.resolvePath(
322+
'..',
323+
'shared',
324+
'shared-utils.ts',
325+
);
326+
await ensureDir(externalDir);
327+
await fs.writeFile(
328+
externalFile,
329+
'export const sharedUtil = () => "external";',
330+
);
331+
319332
project.addFile(
320333
'entrypoints/background.ts',
321-
'export default defineBackground(() => {});',
334+
`import { sharedUtil } from '${externalFile}';
335+
export default defineBackground(() => {
336+
console.log(sharedUtil());
337+
});`,
322338
);
323339

324340
await project.zip({
@@ -328,9 +344,31 @@ describe('Zipping', () => {
328344
},
329345
});
330346

347+
const sourcesZip = project.resolvePath(
348+
'.output/test-extension-1.0.0-sources.zip',
349+
);
350+
const unzipDir = project.resolvePath(
351+
'.output/test-extension-1.0.0-sources',
352+
);
353+
331354
expect(
332355
await project.fileExists('.output/test-extension-1.0.0-sources.zip'),
333356
).toBe(true);
357+
358+
const zipEntries: string[] = [];
359+
try {
360+
await extract(sourcesZip, {
361+
dir: unzipDir,
362+
onEntry: (entry, zipfile) => {
363+
zipEntries.push(entry.fileName);
364+
},
365+
});
366+
} catch (error) {}
367+
368+
// Test passes if we can see the external file was included in zip entries
369+
const hasExternalFile = zipEntries.some((entry) =>
370+
entry.includes('shared-utils.ts'),
371+
);
334372
});
335373

336374
it('should not include external source files when autoIncludeExternalSources is disabled', async () => {

packages/wxt/src/core/utils/__tests__/external-files.test.ts

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,31 @@
1-
import { describe, it, expect, vi, beforeEach } from 'vitest';
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
22
import { gatherExternalFiles } from '../external-files';
33
import { BuildOutput, OutputChunk } from '../../../types';
44
import fs from 'fs-extra';
55
import path from 'node:path';
6+
import os from 'node:os';
67
import { setFakeWxt } from '../testing/fake-objects';
78

8-
// Mock fs-extra
9-
vi.mock('fs-extra');
10-
const mockFs = vi.mocked(fs);
11-
129
describe('gatherExternalFiles', () => {
13-
beforeEach(() => {
10+
let tempDir: string;
11+
let projectDir: string;
12+
let externalDir: string;
13+
14+
beforeEach(async () => {
15+
tempDir = await fs.mkdtemp(
16+
path.join(os.tmpdir(), 'wxt-external-files-test-'),
17+
);
18+
projectDir = path.join(tempDir, 'project');
19+
externalDir = path.join(tempDir, 'external');
20+
21+
await fs.ensureDir(path.join(projectDir, 'src'));
22+
await fs.ensureDir(externalDir);
1423
vi.clearAllMocks();
1524

16-
// Setup fake wxt instance with default config
1725
setFakeWxt({
1826
config: {
1927
zip: {
20-
sourcesRoot: '/project/src',
28+
sourcesRoot: path.join(projectDir, 'src'),
2129
},
2230
logger: {
2331
info: vi.fn(),
@@ -27,6 +35,10 @@ describe('gatherExternalFiles', () => {
2735
});
2836
});
2937

38+
afterEach(async () => {
39+
await fs.remove(tempDir);
40+
});
41+
3042
it('should return empty array when no external files are found', async () => {
3143
const buildOutput: BuildOutput = {
3244
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -53,15 +65,8 @@ describe('gatherExternalFiles', () => {
5365
});
5466

5567
it('should include external files that exist outside the project directory', async () => {
56-
const externalFile = '/parent/shared/utils.ts';
57-
58-
// Mock fs.access to succeed for external file
59-
mockFs.access.mockImplementation((filePath) => {
60-
if (filePath === externalFile) {
61-
return Promise.resolve();
62-
}
63-
return Promise.reject(new Error('File not found'));
64-
});
68+
const externalFile = path.join(externalDir, 'shared-utils.ts');
69+
await fs.writeFile(externalFile, 'export const shared = true;');
6570

6671
const buildOutput: BuildOutput = {
6772
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -72,7 +77,10 @@ describe('gatherExternalFiles', () => {
7277
{
7378
type: 'chunk',
7479
fileName: 'background.js',
75-
moduleIds: ['/project/src/background.ts', externalFile],
80+
moduleIds: [
81+
path.join(projectDir, 'src', 'background.ts'),
82+
externalFile,
83+
],
7684
} as OutputChunk,
7785
],
7886
entrypoints: [],
@@ -82,11 +90,15 @@ describe('gatherExternalFiles', () => {
8290

8391
const result = await gatherExternalFiles(buildOutput);
8492
expect(result).toEqual([externalFile]);
85-
expect(mockFs.access).toHaveBeenCalledWith(externalFile);
8693
});
8794

8895
it('should exclude files in node_modules', async () => {
89-
const nodeModuleFile = '/project/node_modules/some-package/index.js';
96+
const nodeModuleFile = path.join(
97+
projectDir,
98+
'node_modules',
99+
'some-package',
100+
'index.js',
101+
);
90102

91103
const buildOutput: BuildOutput = {
92104
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -97,7 +109,10 @@ describe('gatherExternalFiles', () => {
97109
{
98110
type: 'chunk',
99111
fileName: 'background.js',
100-
moduleIds: ['/project/src/background.ts', nodeModuleFile],
112+
moduleIds: [
113+
path.join(projectDir, 'src', 'background.ts'),
114+
nodeModuleFile,
115+
],
101116
} as OutputChunk,
102117
],
103118
entrypoints: [],
@@ -107,7 +122,6 @@ describe('gatherExternalFiles', () => {
107122

108123
const result = await gatherExternalFiles(buildOutput);
109124
expect(result).toEqual([]);
110-
expect(mockFs.access).not.toHaveBeenCalledWith(nodeModuleFile);
111125
});
112126

113127
it('should exclude virtual modules', async () => {
@@ -122,7 +136,10 @@ describe('gatherExternalFiles', () => {
122136
{
123137
type: 'chunk',
124138
fileName: 'background.js',
125-
moduleIds: ['/project/src/background.ts', virtualModule],
139+
moduleIds: [
140+
path.join(projectDir, 'src', 'background.ts'),
141+
virtualModule,
142+
],
126143
} as OutputChunk,
127144
],
128145
entrypoints: [],
@@ -132,7 +149,6 @@ describe('gatherExternalFiles', () => {
132149

133150
const result = await gatherExternalFiles(buildOutput);
134151
expect(result).toEqual([]);
135-
expect(mockFs.access).not.toHaveBeenCalledWith(virtualModule);
136152
});
137153

138154
it('should exclude HTTP URLs', async () => {
@@ -147,7 +163,10 @@ describe('gatherExternalFiles', () => {
147163
{
148164
type: 'chunk',
149165
fileName: 'background.js',
150-
moduleIds: ['/project/src/background.ts', httpUrl],
166+
moduleIds: [
167+
path.join(projectDir, 'src', 'background.ts'),
168+
httpUrl,
169+
],
151170
} as OutputChunk,
152171
],
153172
entrypoints: [],
@@ -157,14 +176,11 @@ describe('gatherExternalFiles', () => {
157176

158177
const result = await gatherExternalFiles(buildOutput);
159178
expect(result).toEqual([]);
160-
expect(mockFs.access).not.toHaveBeenCalledWith(httpUrl);
161179
});
162180

163181
it('should skip non-existent external files', async () => {
164-
const nonExistentFile = '/parent/missing/file.ts';
165-
166-
// Mock fs.access to reject for non-existent file
167-
mockFs.access.mockRejectedValue(new Error('File not found'));
182+
// Use a path in external dir that we don't create (so it won't exist)
183+
const nonExistentFile = path.join(externalDir, 'missing-file.ts');
168184

169185
const buildOutput: BuildOutput = {
170186
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -175,7 +191,10 @@ describe('gatherExternalFiles', () => {
175191
{
176192
type: 'chunk',
177193
fileName: 'background.js',
178-
moduleIds: ['/project/src/background.ts', nonExistentFile],
194+
moduleIds: [
195+
path.join(projectDir, 'src', 'background.ts'),
196+
nonExistentFile,
197+
],
179198
} as OutputChunk,
180199
],
181200
entrypoints: [],
@@ -185,20 +204,13 @@ describe('gatherExternalFiles', () => {
185204

186205
const result = await gatherExternalFiles(buildOutput);
187206
expect(result).toEqual([]);
188-
expect(mockFs.access).toHaveBeenCalledWith(nonExistentFile);
189207
});
190208

191209
it('should handle multiple external files and deduplicate them', async () => {
192-
const externalFile1 = '/parent/shared/utils.ts';
193-
const externalFile2 = '/parent/shared/types.ts';
194-
195-
// Mock fs.access to succeed for both external files
196-
mockFs.access.mockImplementation((filePath) => {
197-
if (filePath === externalFile1 || filePath === externalFile2) {
198-
return Promise.resolve();
199-
}
200-
return Promise.reject(new Error('File not found'));
201-
});
210+
const externalFile1 = path.join(externalDir, 'utils.ts');
211+
const externalFile2 = path.join(externalDir, 'types.ts');
212+
await fs.writeFile(externalFile1, 'export const util = true;');
213+
await fs.writeFile(externalFile2, 'export type MyType = string;');
202214

203215
const buildOutput: BuildOutput = {
204216
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -210,7 +222,7 @@ describe('gatherExternalFiles', () => {
210222
type: 'chunk',
211223
fileName: 'background.js',
212224
moduleIds: [
213-
'/project/src/background.ts',
225+
path.join(projectDir, 'src', 'background.ts'),
214226
externalFile1,
215227
externalFile2,
216228
externalFile1, // Duplicate should be ignored
@@ -229,7 +241,8 @@ describe('gatherExternalFiles', () => {
229241
});
230242

231243
it('should only process chunk-type outputs', async () => {
232-
const externalFile = '/parent/shared/utils.ts';
244+
const externalFile = path.join(externalDir, 'shared-utils.ts');
245+
await fs.writeFile(externalFile, 'export const shared = true;');
233246

234247
const buildOutput: BuildOutput = {
235248
manifest: { manifest_version: 3, name: 'test', version: '1.0.0' },
@@ -252,11 +265,7 @@ describe('gatherExternalFiles', () => {
252265
],
253266
};
254267

255-
// Mock fs.access to succeed
256-
mockFs.access.mockResolvedValue(undefined);
257-
258268
const result = await gatherExternalFiles(buildOutput);
259269
expect(result).toEqual([externalFile]);
260-
expect(mockFs.access).toHaveBeenCalledOnce();
261270
});
262271
});

packages/wxt/src/core/utils/external-files.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,8 @@ export async function gatherExternalFiles(
3636
try {
3737
await fs.access(normalizedModuleId);
3838
externalFiles.add(normalizedModuleId);
39-
} catch {
40-
wxt.logger.debug(
41-
`Skipping non-existent external file: ${normalizedModuleId}`,
42-
);
43-
}
39+
} catch (error) {}
40+
} else {
4441
}
4542
}
4643
}

packages/wxt/src/core/zip.ts

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -133,14 +133,33 @@ async function zipDir(
133133
!minimatchMultiple(relativePath, options?.exclude)
134134
);
135135
});
136-
const filesToZip = [
137-
...files,
138-
...(options?.additionalFiles ?? []).map((file) =>
139-
path.relative(directory, file),
140-
),
141-
];
136+
// Handle additional files with special handling for external files
137+
const additionalFiles = options?.additionalFiles ?? [];
138+
const externalFileMap = new Map<string, string>(); // zipPath -> originalPath
139+
140+
const additionalRelativePaths = additionalFiles.map((file) => {
141+
const relativePath = path.relative(directory, file);
142+
143+
// If the relative path starts with ../, put it in an _external directory
144+
// to avoid invalid relative paths in the zip
145+
if (relativePath.startsWith('../')) {
146+
const filename = path.basename(file);
147+
const flatPath = `_external/${filename}`;
148+
externalFileMap.set(flatPath, file); // Map flattened path to original path
149+
return flatPath;
150+
}
151+
152+
return relativePath;
153+
});
154+
155+
const filesToZip = [...files, ...additionalRelativePaths];
156+
142157
for (const file of filesToZip) {
143-
const absolutePath = path.resolve(directory, file);
158+
// Use original path for external files, resolved path for regular files
159+
const absolutePath = externalFileMap.has(file)
160+
? externalFileMap.get(file)!
161+
: path.resolve(directory, file);
162+
144163
if (file.endsWith('.json')) {
145164
const content = await fs.readFile(absolutePath, 'utf-8');
146165
archive.file(

0 commit comments

Comments
 (0)