-
Notifications
You must be signed in to change notification settings - Fork 230
Expand file tree
/
Copy pathbrowser.bundle.test.ts
More file actions
205 lines (168 loc) · 7.92 KB
/
Copy pathbrowser.bundle.test.ts
File metadata and controls
205 lines (168 loc) · 7.92 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
/**
* Browser bundle safety tests
*
* These tests verify that the browser bundle:
* 1. Does not contain Node.js-only imports
* 2. Does not include browser-excluded commands like yq/xan/sqlite3
* 3. Shows helpful error messages for browser-excluded commands
*/
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import { describe, expect, it } from "vitest";
import { Bash } from "./Bash.js";
import { BROWSER_EXCLUDED_COMMANDS } from "./commands/browser-excluded.js";
import { getCommandNames, getPythonCommandNames } from "./commands/registry.js";
const browserBundlePath = resolve(__dirname, "../dist/bundle/browser.js");
describe("browser bundle safety", () => {
describe("bundle contents", () => {
it("should not contain sql.js imports", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
expect(bundleContent).not.toContain("sql.js");
});
it("should not contain sqlite3 command registration", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
// The sqlite3 command should not be in the bundle at all
// since it's excluded via __BROWSER__ flag
expect(bundleContent).not.toContain('name:"sqlite3"');
expect(bundleContent).not.toContain("sqlite3Command");
});
it("should not contain yq command registration", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
expect(bundleContent).not.toContain('name:"yq"');
expect(bundleContent).not.toContain("yqCommand");
});
it("should not contain xan command registration", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
expect(bundleContent).not.toContain('name:"xan"');
expect(bundleContent).not.toContain("xanCommand");
});
it("should not contain tar command registration", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
expect(bundleContent).not.toContain('name:"tar"');
expect(bundleContent).not.toContain("tarCommand");
});
it("should not contain direct node: protocol imports in bundle code", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
// The browser bundle should externalize all node: imports
// Check for common patterns that indicate node: modules are bundled
// Note: We check for function calls, not just string presence
// since the external declaration might still reference them
expect(bundleContent).not.toMatch(/require\s*\(\s*["']node:/);
expect(bundleContent).not.toMatch(/from\s*["']node:fs["']/);
expect(bundleContent).not.toMatch(/from\s*["']node:path["']/);
expect(bundleContent).not.toMatch(/from\s*["']node:child_process["']/);
});
it("should not contain native module artifacts", () => {
const bundleContent = readFileSync(browserBundlePath, "utf-8");
// Native modules (.node files) cannot work in browsers
// This catches any native dependency that gets accidentally bundled
expect(bundleContent).not.toMatch(/\.node["']/); // .node file references
expect(bundleContent).not.toMatch(/prebuild-install/); // native module installer
expect(bundleContent).not.toMatch(/node-gyp/); // native build tool
expect(bundleContent).not.toMatch(/napi_/); // N-API bindings
expect(bundleContent).not.toMatch(/\.binding\(/); // native binding loader
});
});
describe("browser-excluded commands list", () => {
it("should include tar in browser-excluded commands", () => {
expect(BROWSER_EXCLUDED_COMMANDS).toContain("tar");
});
it("should include yq in browser-excluded commands", () => {
expect(BROWSER_EXCLUDED_COMMANDS).toContain("yq");
});
it("should include xan in browser-excluded commands", () => {
expect(BROWSER_EXCLUDED_COMMANDS).toContain("xan");
});
it("should include sqlite3 in browser-excluded commands", () => {
expect(BROWSER_EXCLUDED_COMMANDS).toContain("sqlite3");
});
it("should have browser-excluded commands available in Node.js registry", () => {
// In Node.js environment (where tests run), all commands are available
// This verifies that browser-excluded commands exist in the full registry
// Note: python commands are opt-in, so they're in a separate list
const commandNames = [...getCommandNames(), ...getPythonCommandNames()];
for (const excludedCmd of BROWSER_EXCLUDED_COMMANDS) {
// These commands should be available in Node.js
expect(commandNames).toContain(excludedCmd);
}
});
});
describe("sqlite3 in Node.js", () => {
it("sqlite3 should be available by default in Node.js", async () => {
const bash = new Bash();
const result = await bash.exec("sqlite3 :memory: 'SELECT 1'");
expect(result.stdout).toBe("1\n");
expect(result.exitCode).toBe(0);
});
});
describe("tar in Node.js", () => {
it("tar should be available by default in Node.js", async () => {
const bash = new Bash();
const result = await bash.exec("tar --help");
expect(result.stdout).toContain("Usage:");
expect(result.exitCode).toBe(0);
});
});
describe("helpful error messages for excluded commands", () => {
it("should show helpful error when tar is used but not available", async () => {
const availableCommands = getCommandNames().filter(
(cmd) => cmd !== "tar",
) as import("./commands/registry.js").CommandName[];
const bash = new Bash({
commands: availableCommands,
});
const result = await bash.exec("tar -tf archive.tar");
expect(result.stderr).toContain("tar");
expect(result.stderr).toContain("not available in browser");
expect(result.stderr).toContain("Exclude");
expect(result.exitCode).toBe(127);
});
it("should show helpful error when yq is used but not available", async () => {
const availableCommands = getCommandNames().filter(
(cmd) => cmd !== "yq",
) as import("./commands/registry.js").CommandName[];
const bash = new Bash({
commands: availableCommands,
});
const result = await bash.exec("yq '.' test.yaml");
expect(result.stderr).toContain("yq");
expect(result.stderr).toContain("not available in browser");
expect(result.stderr).toContain("Exclude");
expect(result.exitCode).toBe(127);
});
it("should show helpful error when xan is used but not available", async () => {
const availableCommands = getCommandNames().filter(
(cmd) => cmd !== "xan",
) as import("./commands/registry.js").CommandName[];
const bash = new Bash({
commands: availableCommands,
});
const result = await bash.exec("xan count data.csv");
expect(result.stderr).toContain("xan");
expect(result.stderr).toContain("not available in browser");
expect(result.stderr).toContain("Exclude");
expect(result.exitCode).toBe(127);
});
it("should show helpful error when sqlite3 is used but not available", async () => {
const availableCommands = getCommandNames().filter(
(cmd) => cmd !== "sqlite3",
) as import("./commands/registry.js").CommandName[];
const bash = new Bash({
commands: availableCommands,
});
const result = await bash.exec("sqlite3 :memory: 'SELECT 1'");
expect(result.stderr).toContain("sqlite3");
expect(result.stderr).toContain("not available in browser");
expect(result.stderr).toContain("Exclude");
expect(result.exitCode).toBe(127);
});
it("should show standard command not found for non-excluded commands", async () => {
const bash = new Bash();
const result = await bash.exec("nonexistentcmd arg1 arg2");
// Regular unknown command should just say "command not found"
expect(result.stderr).toContain("command not found");
expect(result.stderr).not.toContain("browser");
expect(result.exitCode).toBe(127);
});
});
});