import { describe, expect, test } from 'bun:test'; import * as spec from './plugin-spec.js'; describe('parseNpmSpec', () => { test('unscoped: no version', () => { expect(spec.parseNpmSpec('foo')).toEqual({ name: 'foo', version: null }); }); test('unscoped: exact version', () => { expect(spec.parseNpmSpec('foo@1.2.3')).toEqual({ name: 'foo', version: '1.2.3' }); }); test('unscoped: range version', () => { expect(spec.parseNpmSpec('foo@^1.2.0')).toEqual({ name: 'foo', version: '^1.2.0' }); }); test('unscoped: dist-tag', () => { expect(spec.parseNpmSpec('foo@latest')).toEqual({ name: 'foo', version: 'latest' }); }); test('scoped: no version', () => { expect(spec.parseNpmSpec('@scope/foo')).toEqual({ name: '@scope/foo', version: null }); }); test('scoped: exact version', () => { expect(spec.parseNpmSpec('@scope/foo@1.2.3')).toEqual({ name: '@scope/foo', version: '1.2.3' }); }); test('scoped: dist-tag', () => { expect(spec.parseNpmSpec('@scope/foo@beta')).toEqual({ name: '@scope/foo', version: 'beta' }); }); test('malformed: empty string', () => { expect(spec.parseNpmSpec('')).toEqual({ malformed: true, raw: '' }); }); test('malformed: bare @', () => { expect(spec.parseNpmSpec('@')).toEqual({ malformed: true, raw: '@' }); }); test('malformed: @@', () => { expect(spec.parseNpmSpec('@@')).toEqual({ malformed: true, raw: '@@' }); }); test('malformed: empty version after @', () => { expect(spec.parseNpmSpec('foo@')).toEqual({ malformed: true, raw: 'foo@' }); }); test('malformed: scoped empty name after slash', () => { expect(spec.parseNpmSpec('@scope/')).toEqual({ malformed: true, raw: '@scope/' }); }); test('malformed: scoped empty version', () => { expect(spec.parseNpmSpec('@scope/foo@')).toEqual({ malformed: true, raw: '@scope/foo@' }); }); test('malformed: null input', () => { expect(spec.parseNpmSpec(null)).toEqual({ malformed: true, raw: 'null' }); }); test('malformed: undefined input', () => { expect(spec.parseNpmSpec(undefined)).toEqual({ malformed: true, raw: 'undefined' }); }); test('malformed: number input', () => { expect(spec.parseNpmSpec(42)).toEqual({ malformed: true, raw: '42' }); }); test('malformed: array input', () => { expect(spec.parseNpmSpec(['foo'])).toEqual({ malformed: true, raw: 'foo' }); }); test('malformed: object input', () => { expect(spec.parseNpmSpec({})).toEqual({ malformed: true, raw: '[object Object]' }); }); }); describe('isExactSemver', () => { test('plain semver', () => { expect(spec.isExactSemver('1.2.3')).toBe(true); }); test('semver with pre-release', () => { expect(spec.isExactSemver('1.2.3-beta.1')).toBe(true); }); test('semver with build metadata', () => { expect(spec.isExactSemver('1.2.3+build.5')).toBe(true); }); test('range: caret', () => { expect(spec.isExactSemver('^1.2.0')).toBe(false); }); test('dist-tag', () => { expect(spec.isExactSemver('latest')).toBe(false); }); test('empty string', () => { expect(spec.isExactSemver('')).toBe(false); }); test('partial: major.minor only', () => { expect(spec.isExactSemver('1.2')).toBe(false); }); test('partial: major only', () => { expect(spec.isExactSemver('1')).toBe(false); }); }); describe('parsePathSpec', () => { test('identifies Windows absolute paths as path specs', () => { expect(spec.isPathSpec('C:\\Users\\me\\plugin.js')).toBe(true); expect(spec.isPathSpec('\\\\server\\share\\plugin.js')).toBe(true); expect(spec.isPathSpec('@scope/plugin')).toBe(false); }); test('tilde home shorthand with subpath', () => { expect(spec.parsePathSpec('~/x.js', { homedir: '/home/u', cwd: '/p' })).toEqual({ absolutePath: '/home/u/x.js', }); }); test('bare tilde = homedir', () => { expect(spec.parsePathSpec('~', { homedir: '/home/u', cwd: '/p' })).toEqual({ absolutePath: '/home/u', }); }); test('relative ./', () => { expect(spec.parsePathSpec('./x.js', { homedir: '/home/u', cwd: '/p' })).toEqual({ absolutePath: '/p/x.js', }); }); test('relative ../', () => { expect(spec.parsePathSpec('../x.js', { homedir: '/home/u', cwd: '/p/a' })).toEqual({ absolutePath: '/p/x.js', }); }); test('absolute path passthrough', () => { expect(spec.parsePathSpec('/abs/x.js', { homedir: '/home/u', cwd: '/p' })).toEqual({ absolutePath: '/abs/x.js', }); }); test('Windows absolute path passthrough', () => { expect(spec.parsePathSpec('C:\\Users\\me\\plugin.js', { homedir: '/home/u', cwd: '/p' })).toEqual({ absolutePath: 'C:\\Users\\me\\plugin.js', }); }); });