mcpbrowser 0.2.28 โ†’ 0.2.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,341 @@
1
+ import assert from 'assert';
2
+ import { getBaseDomain, isLikelyAuthUrl, detectRedirectType } from '../src/mcp-browser.js';
3
+
4
+ console.log('๐Ÿงช Testing redirect detection functions\n');
5
+
6
+ let testsPassed = 0;
7
+ let testsFailed = 0;
8
+
9
+ function test(description, fn) {
10
+ try {
11
+ fn();
12
+ console.log(`โœ… ${description}`);
13
+ testsPassed++;
14
+ } catch (err) {
15
+ console.log(`โŒ ${description}`);
16
+ console.log(` Error: ${err.message}`);
17
+ testsFailed++;
18
+ }
19
+ }
20
+
21
+ // ============================================================================
22
+ // getBaseDomain Tests
23
+ // ============================================================================
24
+
25
+ console.log('\n๐Ÿ“‹ Testing getBaseDomain()');
26
+
27
+ test('Should extract base domain from subdomain', () => {
28
+ const result = getBaseDomain('mail.google.com');
29
+ assert.strictEqual(result, 'google.com', 'Should return google.com');
30
+ });
31
+
32
+ test('Should handle simple domain', () => {
33
+ const result = getBaseDomain('example.com');
34
+ assert.strictEqual(result, 'example.com', 'Should return same domain');
35
+ });
36
+
37
+ test('Should handle deep subdomain', () => {
38
+ const result = getBaseDomain('app.dashboard.example.com');
39
+ assert.strictEqual(result, 'example.com', 'Should return base domain');
40
+ });
41
+
42
+ test('Should handle single part hostname', () => {
43
+ const result = getBaseDomain('localhost');
44
+ assert.strictEqual(result, 'localhost', 'Should return same hostname');
45
+ });
46
+
47
+ test('Should handle IP address', () => {
48
+ const result = getBaseDomain('192.168.1.1');
49
+ assert.strictEqual(result, '1.1', 'Should return last two parts');
50
+ });
51
+
52
+ // ============================================================================
53
+ // isLikelyAuthUrl Tests
54
+ // ============================================================================
55
+
56
+ console.log('\n๐Ÿ“‹ Testing isLikelyAuthUrl()');
57
+
58
+ // Path-based auth patterns
59
+ test('Should detect /login path', () => {
60
+ const result = isLikelyAuthUrl('https://example.com/login');
61
+ assert.strictEqual(result, true, 'Should detect /login');
62
+ });
63
+
64
+ test('Should detect /signin path', () => {
65
+ const result = isLikelyAuthUrl('https://example.com/signin');
66
+ assert.strictEqual(result, true, 'Should detect /signin');
67
+ });
68
+
69
+ test('Should detect /auth path', () => {
70
+ const result = isLikelyAuthUrl('https://example.com/auth');
71
+ assert.strictEqual(result, true, 'Should detect /auth');
72
+ });
73
+
74
+ test('Should detect /sso path', () => {
75
+ const result = isLikelyAuthUrl('https://example.com/sso');
76
+ assert.strictEqual(result, true, 'Should detect /sso');
77
+ });
78
+
79
+ test('Should detect /oauth path', () => {
80
+ const result = isLikelyAuthUrl('https://example.com/oauth');
81
+ assert.strictEqual(result, true, 'Should detect /oauth');
82
+ });
83
+
84
+ test('Should detect /authenticate path', () => {
85
+ const result = isLikelyAuthUrl('https://example.com/authenticate');
86
+ assert.strictEqual(result, true, 'Should detect /authenticate');
87
+ });
88
+
89
+ test('Should detect /saml path', () => {
90
+ const result = isLikelyAuthUrl('https://example.com/saml');
91
+ assert.strictEqual(result, true, 'Should detect /saml');
92
+ });
93
+
94
+ // Subdomain-based auth patterns
95
+ test('Should detect login. subdomain', () => {
96
+ const result = isLikelyAuthUrl('https://login.example.com');
97
+ assert.strictEqual(result, true, 'Should detect login. subdomain');
98
+ });
99
+
100
+ test('Should detect auth. subdomain', () => {
101
+ const result = isLikelyAuthUrl('https://auth.example.com');
102
+ assert.strictEqual(result, true, 'Should detect auth. subdomain');
103
+ });
104
+
105
+ test('Should detect sso. subdomain', () => {
106
+ const result = isLikelyAuthUrl('https://sso.example.com');
107
+ assert.strictEqual(result, true, 'Should detect sso. subdomain');
108
+ });
109
+
110
+ test('Should detect accounts. subdomain', () => {
111
+ const result = isLikelyAuthUrl('https://accounts.google.com/signin');
112
+ assert.strictEqual(result, true, 'Should detect accounts. subdomain');
113
+ });
114
+
115
+ test('Should detect id. subdomain', () => {
116
+ const result = isLikelyAuthUrl('https://id.example.com');
117
+ assert.strictEqual(result, true, 'Should detect id. subdomain');
118
+ });
119
+
120
+ test('Should detect identity. subdomain', () => {
121
+ const result = isLikelyAuthUrl('https://identity.example.com');
122
+ assert.strictEqual(result, true, 'Should detect identity. subdomain');
123
+ });
124
+
125
+ test('Should detect signin. subdomain', () => {
126
+ const result = isLikelyAuthUrl('https://signin.example.com');
127
+ assert.strictEqual(result, true, 'Should detect signin. subdomain');
128
+ });
129
+
130
+ test('Should detect authentication. subdomain', () => {
131
+ const result = isLikelyAuthUrl('https://authentication.example.com');
132
+ assert.strictEqual(result, true, 'Should detect authentication. subdomain');
133
+ });
134
+
135
+ test('Should detect idp. subdomain', () => {
136
+ const result = isLikelyAuthUrl('https://idp.example.com');
137
+ assert.strictEqual(result, true, 'Should detect idp. subdomain');
138
+ });
139
+
140
+ // False positives - should NOT be detected
141
+ test('Should NOT detect non-auth path with login word', () => {
142
+ const result = isLikelyAuthUrl('https://example.com/login-help');
143
+ assert.strictEqual(result, false, 'Should not detect /login-help');
144
+ });
145
+
146
+ test('Should NOT detect blog post about login', () => {
147
+ const result = isLikelyAuthUrl('https://example.com/blog/how-to-login');
148
+ assert.strictEqual(result, false, 'Should not detect blog post');
149
+ });
150
+
151
+ test('Should NOT detect accounts in middle of hostname', () => {
152
+ const result = isLikelyAuthUrl('https://myaccounts.example.com');
153
+ assert.strictEqual(result, false, 'Should not detect accounts in middle');
154
+ });
155
+
156
+ test('Should NOT detect normal page', () => {
157
+ const result = isLikelyAuthUrl('https://example.com/dashboard');
158
+ assert.strictEqual(result, false, 'Should not detect dashboard');
159
+ });
160
+
161
+ test('Should NOT detect home page', () => {
162
+ const result = isLikelyAuthUrl('https://example.com');
163
+ assert.strictEqual(result, false, 'Should not detect home page');
164
+ });
165
+
166
+ test('Should NOT detect gmail.com', () => {
167
+ const result = isLikelyAuthUrl('https://gmail.com');
168
+ assert.strictEqual(result, false, 'Should not detect gmail.com');
169
+ });
170
+
171
+ test('Should NOT detect mail.google.com', () => {
172
+ const result = isLikelyAuthUrl('https://mail.google.com');
173
+ assert.strictEqual(result, false, 'Should not detect mail.google.com');
174
+ });
175
+
176
+ // Edge cases
177
+ test('Should handle invalid URL gracefully', () => {
178
+ const result = isLikelyAuthUrl('not-a-url');
179
+ assert.strictEqual(result, false, 'Should return false for invalid URL');
180
+ });
181
+
182
+ test('Should be case insensitive', () => {
183
+ const result = isLikelyAuthUrl('https://example.com/LOGIN');
184
+ assert.strictEqual(result, true, 'Should detect uppercase LOGIN');
185
+ });
186
+
187
+ test('Should detect /sign-in with hyphen', () => {
188
+ const result = isLikelyAuthUrl('https://example.com/sign-in');
189
+ assert.strictEqual(result, true, 'Should detect /sign-in');
190
+ });
191
+
192
+ // ============================================================================
193
+ // detectRedirectType Tests
194
+ // ============================================================================
195
+
196
+ console.log('\n๐Ÿ“‹ Testing detectRedirectType()');
197
+
198
+ test('No redirect - same domain, same path', () => {
199
+ const result = detectRedirectType(
200
+ 'https://example.com/page',
201
+ 'example.com',
202
+ 'https://example.com/page',
203
+ 'example.com'
204
+ );
205
+ assert.strictEqual(result.type, 'none', 'Should detect no redirect');
206
+ });
207
+
208
+ test('Permanent redirect - gmail.com โ†’ mail.google.com', () => {
209
+ const result = detectRedirectType(
210
+ 'https://gmail.com',
211
+ 'gmail.com',
212
+ 'https://mail.google.com',
213
+ 'mail.google.com'
214
+ );
215
+ assert.strictEqual(result.type, 'permanent', 'Should detect permanent redirect');
216
+ assert.strictEqual(result.currentHostname, 'mail.google.com', 'Should include current hostname');
217
+ });
218
+
219
+ test('Permanent redirect - different domain, no auth pattern', () => {
220
+ const result = detectRedirectType(
221
+ 'https://oldsite.com',
222
+ 'oldsite.com',
223
+ 'https://newsite.com',
224
+ 'newsite.com'
225
+ );
226
+ assert.strictEqual(result.type, 'permanent', 'Should detect permanent redirect');
227
+ });
228
+
229
+ test('Auth flow - cross-domain to SSO provider', () => {
230
+ const result = detectRedirectType(
231
+ 'https://app.example.com/dashboard',
232
+ 'app.example.com',
233
+ 'https://login.microsoftonline.com/oauth',
234
+ 'login.microsoftonline.com'
235
+ );
236
+ assert.strictEqual(result.type, 'auth', 'Should detect auth flow');
237
+ assert.strictEqual(result.flowType, 'cross-domain redirect', 'Should be cross-domain');
238
+ });
239
+
240
+ test('Auth flow - same domain path change', () => {
241
+ const result = detectRedirectType(
242
+ 'https://example.com/dashboard',
243
+ 'example.com',
244
+ 'https://example.com/login',
245
+ 'example.com'
246
+ );
247
+ assert.strictEqual(result.type, 'auth', 'Should detect auth flow');
248
+ assert.strictEqual(result.flowType, 'same-domain path change', 'Should be same-domain');
249
+ });
250
+
251
+ test('Auth flow - to Google accounts', () => {
252
+ const result = detectRedirectType(
253
+ 'https://myapp.com',
254
+ 'myapp.com',
255
+ 'https://accounts.google.com/signin',
256
+ 'accounts.google.com'
257
+ );
258
+ assert.strictEqual(result.type, 'auth', 'Should detect auth flow');
259
+ assert.strictEqual(result.flowType, 'cross-domain redirect', 'Should be cross-domain');
260
+ });
261
+
262
+ test('Requested auth page - user requested login page directly', () => {
263
+ const result = detectRedirectType(
264
+ 'https://example.com/login',
265
+ 'example.com',
266
+ 'https://example.com/login',
267
+ 'example.com'
268
+ );
269
+ assert.strictEqual(result.type, 'requested_auth', 'Should detect requested auth page');
270
+ assert.strictEqual(result.currentHostname, 'example.com', 'Should include hostname');
271
+ });
272
+
273
+ test('Requested auth page - user requested accounts.google.com', () => {
274
+ const result = detectRedirectType(
275
+ 'https://accounts.google.com',
276
+ 'accounts.google.com',
277
+ 'https://accounts.google.com/signin',
278
+ 'accounts.google.com'
279
+ );
280
+ assert.strictEqual(result.type, 'requested_auth', 'Should detect requested auth page');
281
+ });
282
+
283
+ test('Auth flow - subdomain auth (login.example.com)', () => {
284
+ const result = detectRedirectType(
285
+ 'https://example.com/app',
286
+ 'example.com',
287
+ 'https://login.example.com',
288
+ 'login.example.com'
289
+ );
290
+ assert.strictEqual(result.type, 'auth', 'Should detect auth flow');
291
+ assert.strictEqual(result.flowType, 'cross-domain redirect', 'Should be cross-domain');
292
+ assert.strictEqual(result.originalBase, 'example.com', 'Should preserve original base');
293
+ });
294
+
295
+ test('Auth flow - Okta SSO', () => {
296
+ const result = detectRedirectType(
297
+ 'https://mycompany.com',
298
+ 'mycompany.com',
299
+ 'https://mycompany.okta.com/login',
300
+ 'mycompany.okta.com'
301
+ );
302
+ assert.strictEqual(result.type, 'auth', 'Should detect auth flow');
303
+ });
304
+
305
+ test('Permanent redirect - subdomain change without auth', () => {
306
+ const result = detectRedirectType(
307
+ 'https://www.example.com',
308
+ 'www.example.com',
309
+ 'https://app.example.com',
310
+ 'app.example.com'
311
+ );
312
+ assert.strictEqual(result.type, 'permanent', 'Should detect permanent redirect');
313
+ });
314
+
315
+ test('Should include all required fields for auth flow', () => {
316
+ const result = detectRedirectType(
317
+ 'https://site.com',
318
+ 'site.com',
319
+ 'https://auth.site.com/login',
320
+ 'auth.site.com'
321
+ );
322
+ assert.strictEqual(result.type, 'auth');
323
+ assert.ok(result.flowType, 'Should have flowType');
324
+ assert.ok(result.originalBase, 'Should have originalBase');
325
+ assert.ok(result.currentUrl, 'Should have currentUrl');
326
+ assert.ok(result.hostname, 'Should have hostname');
327
+ assert.ok(result.currentHostname, 'Should have currentHostname');
328
+ });
329
+
330
+ // ============================================================================
331
+ // Summary
332
+ // ============================================================================
333
+
334
+ console.log('\n' + '='.repeat(50));
335
+ console.log(`Tests passed: ${testsPassed}`);
336
+ console.log(`Tests failed: ${testsFailed}`);
337
+ console.log('='.repeat(50));
338
+
339
+ if (testsFailed > 0) {
340
+ process.exit(1);
341
+ }
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'child_process';
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname, join } from 'path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+
9
+ const tests = [
10
+ 'redirect-detection.test.js',
11
+ 'auth-flow.test.js',
12
+ 'prepare-html.test.js'
13
+ ];
14
+
15
+ console.log('๐Ÿงช Running all MCPBrowser tests\n');
16
+ console.log('='.repeat(60));
17
+
18
+ let totalPassed = 0;
19
+ let totalFailed = 0;
20
+ let completedTests = 0;
21
+
22
+ function runTest(testFile) {
23
+ return new Promise((resolve) => {
24
+ console.log(`\nโ–ถ๏ธ Running ${testFile}...`);
25
+ console.log('-'.repeat(60));
26
+
27
+ const child = spawn('node', [join(__dirname, testFile)], {
28
+ stdio: 'inherit',
29
+ shell: true
30
+ });
31
+
32
+ child.on('close', (code) => {
33
+ completedTests++;
34
+ if (code === 0) {
35
+ console.log(`โœ… ${testFile} passed`);
36
+ } else {
37
+ console.log(`โŒ ${testFile} failed (exit code: ${code})`);
38
+ totalFailed++;
39
+ }
40
+ resolve(code);
41
+ });
42
+
43
+ child.on('error', (err) => {
44
+ console.error(`โŒ Failed to run ${testFile}:`, err);
45
+ totalFailed++;
46
+ completedTests++;
47
+ resolve(1);
48
+ });
49
+ });
50
+ }
51
+
52
+ async function runAllTests() {
53
+ for (const test of tests) {
54
+ const exitCode = await runTest(test);
55
+ if (exitCode !== 0) {
56
+ totalFailed++;
57
+ } else {
58
+ totalPassed++;
59
+ }
60
+ }
61
+
62
+ console.log('\n' + '='.repeat(60));
63
+ console.log('\n๐Ÿ“Š Test Summary:');
64
+ console.log(` Total test suites: ${tests.length}`);
65
+ console.log(` Passed: ${totalPassed}`);
66
+ console.log(` Failed: ${totalFailed}`);
67
+ console.log('\n' + '='.repeat(60));
68
+
69
+ if (totalFailed > 0) {
70
+ process.exit(1);
71
+ }
72
+ }
73
+
74
+ runAllTests().catch((err) => {
75
+ console.error('Error running tests:', err);
76
+ process.exit(1);
77
+ });