windows-path-fix.test.js

 1import { describe, test, expect } from 'bun:test';
 2import path from 'path';
 3import { fileURLToPath } from 'node:url';
 4
 5// ---------------------------------------------------------------------------
 6// Regression: Windows drive-letter doubling (#95)
 7//
 8// On Windows, `new URL(import.meta.url).pathname` returns `/C:/foo/bar`
 9// (leading slash). Passing that to `path.resolve()` or `path.join()` on
10// Windows produces a doubled drive letter like `C:\C:\...`. The canonical
11// fix is to use `fileURLToPath()` from `node:url`, which strips the leading
12// slash on Windows.
13//
14// These tests verify the contract that `fileURLToPath` behaves correctly on
15// both POSIX and Windows-style file URLs, and that the source no longer uses
16// the raw `.pathname` accessor for local path construction.
17// ---------------------------------------------------------------------------
18
19describe('Windows path doubling fix (#95)', () => {
20  test('fileURLToPath strips leading slash from Windows file URLs', () => {
21    // Simulates the exact scenario: file:///C:/Users/foo/detect-antipatterns.mjs
22    const winUrl = new URL('file:///C:/Users/foo/cli/engine/detect-antipatterns.mjs');
23
24    // Raw .pathname returns '/C:/Users/foo/cli/engine/detect-antipatterns.mjs'
25    expect(winUrl.pathname).toBe('/C:/Users/foo/cli/engine/detect-antipatterns.mjs');
26
27    // fileURLToPath returns 'C:\\Users\\...' on Windows or '/C:/Users/...' on POSIX,
28    // but crucially never returns '/C:/...' on Windows (which causes the double-drive bug)
29    const resolved = fileURLToPath(winUrl);
30
31    // The resolved path should NOT start with /C: on either platform when joined
32    // On POSIX, fileURLToPath('file:///C:/...') returns '/C:/...' which is fine
33    // because POSIX doesn't have drive letters.
34    // The key assertion: path.resolve won't produce a doubled drive letter
35    const dirPart = path.dirname(resolved);
36    const joined = path.resolve(dirPart, 'detect-antipatterns-browser.js');
37    // Should never contain doubled drive pattern like C:\C:\ or /C:/C:/
38    expect(joined).not.toMatch(/[A-Z]:[/\\][A-Z]:/i);
39  });
40
41  test('fileURLToPath handles POSIX file URLs correctly', () => {
42    const posixUrl = new URL('file:///home/user/cli/engine/detect-antipatterns.mjs');
43    const resolved = fileURLToPath(posixUrl);
44    expect(resolved).toBe('/home/user/cli/engine/detect-antipatterns.mjs');
45  });
46
47  test('import.meta.url produces a valid file URL', () => {
48    // Ensure import.meta.url is a file:// URL that fileURLToPath can handle
49    expect(import.meta.url).toMatch(/^file:\/\//);
50    const thisFile = fileURLToPath(import.meta.url);
51    expect(thisFile).toContain('windows-path-fix.test');
52  });
53
54  test('URL detector source no longer uses raw .pathname for path construction', () => {
55    const fs = require('fs');
56    const src = fs.readFileSync(
57      path.join(__dirname, '..', 'cli', 'engine', 'engines', 'browser', 'detect-url.mjs'),
58      'utf-8'
59    );
60
61    // The bug pattern: using new URL(import.meta.url).pathname in path.resolve/join
62    // After the fix, all occurrences should use fileURLToPath instead
63    const pathnameBugPattern = /path\.(resolve|join|dirname)\(\s*new URL\(import\.meta\.url\)\.pathname/g;
64    const matches = src.match(pathnameBugPattern);
65    expect(matches).toBeNull();
66
67    // Verify fileURLToPath is imported
68    expect(src).toContain('fileURLToPath');
69
70    // Verify fileURLToPath is used with import.meta.url
71    expect(src).toMatch(/fileURLToPath\(import\.meta\.url\)/);
72  });
73});