import fse from 'fs-extra';
import os from 'node:os';
import path from 'node:path';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { convertJavadoc } from '../src/sdk/converters/javadoc.js';
const TRAVERSAL_INDEX = 'typeSearchIndex = [{"p":"com/../../../etc","l":"Passwd"}];';
import type { SdkReference } from '../src/sdk/types.js';
const page = (title: string, body: string) => `
${title} (fake API)
${body}
`;
const greeterHtml = page(
'Greeter',
`
public class Greeter
Greets people politely. Supports many languages.
-
Method Summary
String
SUMMARY_ROW_MARKER greet a person.
Methods inherited from class java.lang.Object
equals, hashCode, toString
-
Method Details
-
greet
public String greet(String name)
Builds a greeting for the given
Person name.
- Parameters:
name - who to greet.
`
);
const personHtml = page(
'Person',
`
public interface Person
A person that can be greeted.
`
);
let dir: string;
let ref: SdkReference;
beforeAll(async () => {
dir = await fse.mkdtemp(path.join(os.tmpdir(), 'javadoc-test-'));
await fse.writeFile(path.join(dir, 'element-list'), 'com.example\n');
const pkgDir = path.join(dir, 'com', 'example');
await fse.mkdirp(pkgDir);
await fse.writeFile(path.join(pkgDir, 'Greeter.html'), greeterHtml);
await fse.writeFile(path.join(pkgDir, 'Person.html'), personHtml);
await fse.writeFile(
path.join(pkgDir, 'package-summary.html'),
page('com.example', 'Package com.example
')
);
ref = await convertJavadoc(dir);
});
afterAll(async () => {
await fse.remove(dir);
});
describe('convertJavadoc', () => {
it('emits one page per type with package-based slugs', () => {
expect(ref.pages.map((p) => p.slug).sort()).toEqual([
'com-example/Greeter',
'com-example/Person',
]);
});
it('groups types by package', () => {
expect(ref.groups).toEqual([
{ group: 'com.example', pages: ['com-example/Greeter', 'com-example/Person'] },
]);
});
it('extracts title, tag, and first-sentence description', () => {
const greeter = ref.pages.find((p) => p.slug === 'com-example/Greeter');
expect(greeter?.title).toBe('Greeter');
expect(greeter?.tag).toBe('CLASS');
expect(greeter?.description).toBe('Greets people politely.');
const person = ref.pages.find((p) => p.slug === 'com-example/Person');
expect(person?.tag).toBe('INTERFACE');
});
it('keeps method detail sections and rewrites type links', () => {
const content = ref.pages.find((p) => p.slug === 'com-example/Greeter')?.content ?? '';
expect(content).toContain('### greet');
expect(content).toContain('public String greet(String name)');
expect(content).toContain('`name` - who to greet.');
expect(content).toContain('[Person](/com-example/Person)');
});
it('drops summary tables and inherited-method boilerplate', () => {
const content = ref.pages.find((p) => p.slug === 'com-example/Greeter')?.content ?? '';
expect(content).not.toContain('SUMMARY_ROW_MARKER');
expect(content).not.toContain('Method Summary');
expect(content).not.toContain('Methods inherited from');
});
it('ignores search-index entries with invalid package names', async () => {
const evil = await fse.mkdtemp(path.join(os.tmpdir(), 'javadoc-evil-'));
await fse.writeFile(path.join(evil, 'type-search-index.js'), TRAVERSAL_INDEX);
await expect(convertJavadoc(evil)).rejects.toThrow(/No element-list or package-list found/);
await fse.rm(evil, { recursive: true, force: true });
});
});