tree-hugger-js
Version:
A friendly tree-sitter wrapper for JavaScript and TypeScript
187 lines (158 loc) • 3.81 kB
text/typescript
import { parse, TreeHugger, TreeNode } from '../../src';
import { readFileSync } from 'fs';
import { join } from 'path';
export function loadFixture(filename: string): string {
const fixturePath = join(__dirname, '..', 'fixtures', filename);
return readFileSync(fixturePath, 'utf-8');
}
export function createTree(code: string, language?: string): TreeHugger {
return parse(code, language ? { language } : undefined);
}
export function expectNode(node: TreeNode | null, type?: string): asserts node is TreeNode {
expect(node).not.toBeNull();
if (type) {
expect(node!.type).toBe(type);
}
}
export function getNodeText(nodes: TreeNode[]): string[] {
return nodes.map(n => n.text);
}
export function getNodeTypes(nodes: TreeNode[]): string[] {
return nodes.map(n => n.type);
}
// Helper to create test code snippets
export const testCode = {
simple: {
js: 'const x = 42;',
ts: 'const x: number = 42;',
jsx: '<div>Hello</div>',
tsx: '<div className="test">Hello</div>',
},
functions: {
js: `
function greet(name) {
console.log("Hello " + name);
}
const add = (a, b) => a + b;
async function fetchData() {
return await fetch('/api');
}
`,
ts: `
function greet(name: string): void {
console.log(\`Hello \${name}\`);
}
const add = (a: number, b: number): number => a + b;
async function fetchData<T>(): Promise<T> {
const response = await fetch('/api');
return response.json();
}
`,
},
classes: {
js: `
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(this.name + ' makes a sound');
}
}
class Dog extends Animal {
bark() {
console.log('Woof!');
}
}
`,
ts: `
class Animal {
constructor(public name: string) {}
speak(): void {
console.log(\`\${this.name} makes a sound\`);
}
}
class Dog extends Animal {
bark(): void {
console.log('Woof!');
}
}
`,
},
imports: {
withUnused: `
import React from 'react';
import { useState, useEffect } from 'react';
import axios from 'axios';
import _ from 'lodash';
const Component = () => {
const [data, setData] = useState(null);
useEffect(() => {
// Only using axios, not lodash
axios.get('/api').then(setData);
}, []);
return React.createElement('div', null, data);
};
`,
allUsed: `
import React from 'react';
import { useState } from 'react';
export function Component() {
const [count, setCount] = useState(0);
return React.createElement('div', null, count);
}
`,
},
jsx: {
component: `
import React from 'react';
export const Button = ({ onClick, disabled, children, className = 'btn' }) => {
return (
<button
className={className}
onClick={onClick}
disabled={disabled}
data-testid="button"
>
{children}
</button>
);
};
export const Card = () => (
<div className="card">
<h2>Title</h2>
<Button onClick={() => alert('Clicked')}>
Click me
</Button>
</div>
);
`,
},
withErrors: {
syntax: 'const x = ;',
incomplete: 'function test() {',
invalid: 'class { constructor() {} }',
},
};
// Test assertion helpers
export function expectTransform(
code: string,
transform: (tree: TreeHugger) => string,
expected: string
): void {
const tree = createTree(code);
const result = transform(tree);
expect(result).toBe(expected);
}
export function expectPattern(code: string, pattern: string, expectedCount: number): void {
const tree = createTree(code);
const nodes = tree.findAll(pattern);
expect(nodes).toHaveLength(expectedCount);
}
// Performance testing helper
export function measureTime<T>(fn: () => T): { result: T; time: number } {
const start = performance.now();
const result = fn();
const time = performance.now() - start;
return { result, time };
}