/**
* Unit tests for lib/syntax.js
*/
import test from 'brittle'
import { detectLanguage, getPrismLanguageUrl, getPrismAssets, isBinaryContent, getFileIcon } from '../../lib/syntax.js'
import { sampleFiles } from '../helpers/fixtures.js'
// ========================================
// detectLanguage tests
// ========================================
test('detectLanguage - returns plaintext for empty input', async (t) => {
t.is(detectLanguage(''), 'plaintext')
t.is(detectLanguage(null), 'plaintext')
t.is(detectLanguage(undefined), 'plaintext')
})
test('detectLanguage - detects JavaScript files', async (t) => {
t.is(detectLanguage('app.js'), 'javascript')
t.is(detectLanguage('index.mjs'), 'javascript')
t.is(detectLanguage('utils.cjs'), 'javascript')
t.is(detectLanguage('/path/to/file.js'), 'javascript')
})
test('detectLanguage - detects TypeScript files', async (t) => {
t.is(detectLanguage('app.ts'), 'typescript')
t.is(detectLanguage('component.tsx'), 'tsx')
t.is(detectLanguage('types.d.ts'), 'typescript')
})
test('detectLanguage - detects JSX/TSX files', async (t) => {
t.is(detectLanguage('Component.jsx'), 'jsx')
t.is(detectLanguage('Page.tsx'), 'tsx')
})
test('detectLanguage - detects Python files', async (t) => {
t.is(detectLanguage('main.py'), 'python')
t.is(detectLanguage('script.pyw'), 'python')
})
test('detectLanguage - detects Ruby files', async (t) => {
t.is(detectLanguage('app.rb'), 'ruby')
t.is(detectLanguage('template.erb'), 'erb')
})
test('detectLanguage - detects Go files', async (t) => {
t.is(detectLanguage('main.go'), 'go')
t.is(detectLanguage('go.mod'), 'go-module')
})
test('detectLanguage - detects Rust files', async (t) => {
t.is(detectLanguage('main.rs'), 'rust')
t.is(detectLanguage('lib.rs'), 'rust')
})
test('detectLanguage - detects C/C++ files', async (t) => {
t.is(detectLanguage('main.c'), 'c')
t.is(detectLanguage('header.h'), 'c')
t.is(detectLanguage('app.cpp'), 'cpp')
t.is(detectLanguage('utils.hpp'), 'cpp')
})
test('detectLanguage - detects web files', async (t) => {
t.is(detectLanguage('index.html'), 'html')
t.is(detectLanguage('page.htm'), 'html')
t.is(detectLanguage('styles.css'), 'css')
t.is(detectLanguage('theme.scss'), 'scss')
t.is(detectLanguage('variables.less'), 'less')
})
test('detectLanguage - detects data format files', async (t) => {
t.is(detectLanguage('package.json'), 'json')
t.is(detectLanguage('config.yaml'), 'yaml')
t.is(detectLanguage('settings.yml'), 'yaml')
t.is(detectLanguage('config.toml'), 'toml')
})
test('detectLanguage - detects shell scripts', async (t) => {
t.is(detectLanguage('script.sh'), 'bash')
t.is(detectLanguage('deploy.bash'), 'bash')
t.is(detectLanguage('setup.zsh'), 'bash')
})
test('detectLanguage - detects markdown', async (t) => {
t.is(detectLanguage('README.md'), 'markdown')
t.is(detectLanguage('docs.markdown'), 'markdown')
})
test('detectLanguage - detects special filenames', async (t) => {
t.is(detectLanguage('Dockerfile'), 'docker')
t.is(detectLanguage('Makefile'), 'makefile')
t.is(detectLanguage('Gemfile'), 'ruby')
t.is(detectLanguage('Rakefile'), 'ruby')
})
test('detectLanguage - detects dotfiles', async (t) => {
t.is(detectLanguage('.gitignore'), 'gitignore')
t.is(detectLanguage('.editorconfig'), 'ini')
})
test('detectLanguage - case insensitive', async (t) => {
t.is(detectLanguage('FILE.JS'), 'javascript')
t.is(detectLanguage('README.MD'), 'markdown')
t.is(detectLanguage('DOCKERFILE'), 'docker')
})
test('detectLanguage - handles full paths', async (t) => {
t.is(detectLanguage('/home/user/project/src/index.js'), 'javascript')
t.is(detectLanguage('src/components/App.tsx'), 'tsx')
t.is(detectLanguage('./lib/utils.py'), 'python')
})
test('detectLanguage - returns plaintext for unknown extensions', async (t) => {
t.is(detectLanguage('file.xyz'), 'plaintext')
t.is(detectLanguage('data.unknown'), 'plaintext')
})
// ========================================
// getPrismLanguageUrl tests
// ========================================
test('getPrismLanguageUrl - returns null for plaintext', async (t) => {
t.is(getPrismLanguageUrl('plaintext'), null)
})
test('getPrismLanguageUrl - returns CDN URL for languages', async (t) => {
const url = getPrismLanguageUrl('javascript')
t.ok(url.includes('prism-javascript'))
t.ok(url.includes('cdnjs.cloudflare.com'))
})
// ========================================
// getPrismAssets tests
// ========================================
test('getPrismAssets - returns CSS and JS arrays', async (t) => {
const assets = getPrismAssets('javascript')
t.ok(Array.isArray(assets.css))
t.ok(Array.isArray(assets.js))
t.ok(assets.css.length > 0)
t.ok(assets.js.length > 0)
})
test('getPrismAssets - includes theme CSS', async (t) => {
const assets = getPrismAssets('javascript')
t.ok(assets.css.some(url => url.includes('prism-tomorrow')))
})
test('getPrismAssets - includes core JS', async (t) => {
const assets = getPrismAssets('javascript')
t.ok(assets.js.some(url => url.includes('prism.min.js')))
})
test('getPrismAssets - includes language-specific JS', async (t) => {
const assets = getPrismAssets('python')
t.ok(assets.js.some(url => url.includes('prism-python')))
})
test('getPrismAssets - excludes language JS for plaintext', async (t) => {
const assets = getPrismAssets('plaintext')
t.not(assets.js.some(url => url.includes('prism-plaintext')))
})
// ========================================
// isBinaryContent tests
// ========================================
test('isBinaryContent - returns false for empty input', async (t) => {
t.is(isBinaryContent(''), false)
t.is(isBinaryContent(null), false)
})
test('isBinaryContent - returns false for text content', async (t) => {
t.is(isBinaryContent('Hello, World!'), false)
t.is(isBinaryContent('const x = 1;\nconst y = 2;'), false)
t.is(isBinaryContent('Line 1\nLine 2\tTab\rCarriage'), false)
})
test('isBinaryContent - returns true for null bytes', async (t) => {
t.is(isBinaryContent('hello\x00world'), true)
t.is(isBinaryContent('\x00\x00\x00'), true)
})
test('isBinaryContent - returns true for high non-printable ratio', async (t) => {
// Create content with many non-printable characters
let binary = ''
for (let i = 0; i < 100; i++) {
binary += String.fromCharCode(1) // Non-printable
}
t.is(isBinaryContent(binary), true)
})
test('isBinaryContent - handles mixed content', async (t) => {
// Content with some non-printable but below threshold
const content = 'Normal text content\nwith newlines\tand tabs'
t.is(isBinaryContent(content), false)
})
// ========================================
// getFileIcon tests
// ========================================
test('getFileIcon - returns folder icon for directories', async (t) => {
t.is(getFileIcon('javascript', 'directory'), '📁')
t.is(getFileIcon('', 'directory'), '📁')
})
test('getFileIcon - returns package icon for submodules', async (t) => {
t.is(getFileIcon('javascript', 'submodule'), '📦')
})
test('getFileIcon - returns language-specific icons', async (t) => {
t.is(getFileIcon('javascript'), '📜')
t.is(getFileIcon('typescript'), '📘')
t.is(getFileIcon('python'), '🐍')
t.is(getFileIcon('ruby'), '💎')
t.is(getFileIcon('go'), '🐹')
t.is(getFileIcon('rust'), '🦀')
t.is(getFileIcon('java'), '☕')
t.is(getFileIcon('docker'), '🐳')
t.is(getFileIcon('markdown'), '📝')
})
test('getFileIcon - returns default icon for unknown languages', async (t) => {
t.is(getFileIcon('plaintext'), '📄')
t.is(getFileIcon('unknown'), '📄')
t.is(getFileIcon(''), '📄')
})