quarc/tests/unit/test-inject.ts

320 lines
8.6 KiB
JavaScript

#!/usr/bin/env node
import { InjectProcessor } from '../../cli/processors/inject-processor';
interface TestResult {
name: string;
passed: boolean;
error?: string;
}
const results: TestResult[] = [];
function test(name: string, fn: () => void | Promise<void>): void {
try {
const result = fn();
if (result instanceof Promise) {
result
.then(() => results.push({ name, passed: true }))
.catch((e) => results.push({ name, passed: false, error: String(e) }));
} else {
results.push({ name, passed: true });
}
} catch (e) {
results.push({ name, passed: false, error: String(e) });
}
}
function assertEqual(actual: string, expected: string, message?: string): void {
if (actual !== expected) {
throw new Error(
`${message || 'Assertion failed'}\nExpected:\n${expected}\nActual:\n${actual}`,
);
}
}
function assertContains(actual: string, expected: string, message?: string): void {
if (!actual.includes(expected)) {
throw new Error(
`${message || 'Assertion failed'}\nExpected to contain:\n${expected}\nActual:\n${actual}`,
);
}
}
function assertNotContains(actual: string, unexpected: string, message?: string): void {
if (actual.includes(unexpected)) {
throw new Error(
`${message || 'Assertion failed'}\nExpected NOT to contain:\n${unexpected}\nActual:\n${actual}`,
);
}
}
console.log('\n📦 InjectProcessor Tests\n');
const injectProcessor = new InjectProcessor();
test('Inject: transforms inject<Type>(ClassName) to inject<Type>("ClassName")', async () => {
const input = `
export class TestComponent {
private service = inject<UserService>(UserService);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject<UserService>("UserService")');
assertNotContains(result.source, 'inject<UserService>(UserService)');
});
test('Inject: transforms inject(ClassName) to inject("ClassName")', async () => {
const input = `
export class TestComponent {
private service = inject(UserService);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("UserService")');
assertNotContains(result.source, 'inject(UserService)');
});
test('Inject: handles multiple inject calls', async () => {
const input = `
export class TestComponent {
private userService = inject(UserService);
private httpClient = inject<HttpClient>(HttpClient);
private router = inject(Router);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("UserService")');
assertContains(result.source, 'inject<HttpClient>("HttpClient")');
assertContains(result.source, 'inject("Router")');
});
test('Inject: handles inject in constructor', async () => {
const input = `
export class TestComponent {
constructor() {
this.service = inject(MyService);
}
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("MyService")');
});
test('Inject: handles inject in methods', async () => {
const input = `
export class TestComponent {
public loadService(): void {
const service = inject(DynamicService);
service.load();
}
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("DynamicService")');
});
test('Inject: preserves inject with string argument', async () => {
const input = `
export class TestComponent {
private service = inject("CustomToken");
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("CustomToken")');
if (result.modified) {
throw new Error('Expected no modification for string token');
}
});
test('Inject: handles inject with whitespace', async () => {
const input = `
export class TestComponent {
private service = inject( UserService );
private http = inject<HttpClient>( HttpClient );
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("UserService")');
assertContains(result.source, 'inject<HttpClient>("HttpClient")');
});
test('Inject: no inject calls - no modification', async () => {
const input = `
export class TestComponent {
constructor(private service: UserService) {}
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
if (result.modified) {
throw new Error('Expected no modification when no inject calls present');
}
});
test('Inject: handles HTMLElement injection', async () => {
const input = `
export class TestComponent {
private element = inject(HTMLElement);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("HTMLElement")');
});
test('Inject: handles complex generic types', async () => {
const input = `
export class TestComponent {
private service = inject<Observable<User>>(UserService);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject<Observable<User>>("UserService")');
});
test('Inject: handles inject in arrow functions', async () => {
const input = `
export class TestComponent {
private factory = () => inject(FactoryService);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("FactoryService")');
});
test('Inject: handles multiple inject calls on same line', async () => {
const input = `
export class TestComponent {
private services = [inject(ServiceA), inject(ServiceB), inject(ServiceC)];
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("ServiceA")');
assertContains(result.source, 'inject("ServiceB")');
assertContains(result.source, 'inject("ServiceC")');
});
test('Inject: preserves lowercase inject calls (not class names)', async () => {
const input = `
export class TestComponent {
private value = inject(someFunction);
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject(someFunction)');
if (result.modified) {
throw new Error('Expected no modification for non-class name');
}
});
test('Inject: handles nested inject calls', async () => {
const input = `
export class TestComponent {
private service = createWrapper(inject(MyService));
}
`;
const result = await injectProcessor.process({
filePath: '/test/test.component.ts',
fileDir: '/test',
source: input,
});
assertContains(result.source, 'inject("MyService")');
});
async function runTests() {
await new Promise(resolve => setTimeout(resolve, 100));
console.log('\n' + '='.repeat(60));
console.log('📊 INJECT TEST RESULTS');
console.log('='.repeat(60));
let passed = 0;
let failed = 0;
for (const result of results) {
if (result.passed) {
console.log(`${result.name}`);
passed++;
} else {
console.log(`${result.name}`);
console.log(` Error: ${result.error}`);
failed++;
}
}
console.log('\n' + '-'.repeat(60));
console.log(`Total: ${results.length} | Passed: ${passed} | Failed: ${failed}`);
console.log('-'.repeat(60));
if (failed > 0) {
process.exit(1);
}
}
runTests();