149 lines
4.9 KiB
TypeScript
149 lines
4.9 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
import * as os from 'node:os';
|
|
import { createDocumentTools } from '../src/document-tools.js';
|
|
|
|
describe('createDocumentTools', () => {
|
|
let tmpDir: string;
|
|
|
|
beforeEach(() => {
|
|
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'waggle-docx-'));
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
});
|
|
|
|
it('creates a generate_docx tool', () => {
|
|
const tools = createDocumentTools(tmpDir);
|
|
expect(tools).toHaveLength(1);
|
|
expect(tools[0].name).toBe('generate_docx');
|
|
expect(tools[0].description).toContain('Word document');
|
|
});
|
|
|
|
it('generates a basic .docx file', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'test.docx',
|
|
content: '# Hello World\n\nThis is a test document.\n\n- Item 1\n- Item 2',
|
|
});
|
|
|
|
expect(result).toContain('Successfully generated test.docx');
|
|
expect(result).toContain('1 headings');
|
|
expect(result).toContain('1 paragraphs');
|
|
expect(result).toContain('2 list items');
|
|
|
|
const filePath = path.join(tmpDir, 'test.docx');
|
|
expect(fs.existsSync(filePath)).toBe(true);
|
|
const stat = fs.statSync(filePath);
|
|
expect(stat.size).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('generates a docx with title page', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'report.docx',
|
|
content: '# Introduction\n\nContent here.\n\n## Section 2\n\nMore content.',
|
|
title: 'Market Analysis Report',
|
|
author: 'Waggle AI',
|
|
subject: 'Liquid Cooling Market',
|
|
});
|
|
|
|
expect(result).toContain('Successfully generated report.docx');
|
|
expect(fs.existsSync(path.join(tmpDir, 'report.docx'))).toBe(true);
|
|
});
|
|
|
|
it('handles tables in markdown', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'tables.docx',
|
|
content: '# Data\n\n| Name | Value |\n|------|-------|\n| A | 10 |\n| B | 20 |',
|
|
});
|
|
|
|
expect(result).toContain('1 tables');
|
|
});
|
|
|
|
it('handles numbered lists', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'lists.docx',
|
|
content: '1. First item\n2. Second item\n3. Third item',
|
|
});
|
|
|
|
expect(result).toContain('3 list items');
|
|
});
|
|
|
|
it('creates subdirectories as needed', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'reports/2024/q1/analysis.docx',
|
|
content: '# Q1 Analysis\n\nContent.',
|
|
});
|
|
|
|
expect(result).toContain('Successfully');
|
|
expect(fs.existsSync(path.join(tmpDir, 'reports', '2024', 'q1', 'analysis.docx'))).toBe(true);
|
|
});
|
|
|
|
it('rejects non-.docx extension', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'test.txt',
|
|
content: 'Hello',
|
|
});
|
|
|
|
expect(result).toContain('Error: Output path must end with .docx');
|
|
});
|
|
|
|
it('rejects path traversal', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: '../../../etc/evil.docx',
|
|
content: 'Hello',
|
|
});
|
|
|
|
expect(result).toContain('Error');
|
|
});
|
|
|
|
it('handles inline formatting', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'formatted.docx',
|
|
content: 'This has **bold**, *italic*, and `code` formatting.',
|
|
});
|
|
|
|
expect(result).toContain('Successfully generated formatted.docx');
|
|
});
|
|
|
|
it('handles page breaks', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const result = await tool.execute({
|
|
path: 'multipage.docx',
|
|
content: '# Page 1\n\nContent.\n\n---pagebreak---\n\n# Page 2\n\nMore content.',
|
|
});
|
|
|
|
expect(result).toContain('Successfully');
|
|
expect(result).toContain('2 headings');
|
|
});
|
|
|
|
it('includes a Summary in the chat result with meaningful content', async () => {
|
|
const [tool] = createDocumentTools(tmpDir);
|
|
const content =
|
|
'# Market Analysis Report\n\n' +
|
|
'The global liquid cooling market is projected to reach $8.5 billion by 2028, ' +
|
|
'driven by increasing demand for high-performance computing and AI workloads.\n\n' +
|
|
'## Key Findings\n\n' +
|
|
'- Data center cooling accounts for 40% of energy costs\n' +
|
|
'- Immersion cooling adoption grew 65% year-over-year\n\n' +
|
|
'## Recommendations\n\n' +
|
|
'Organizations should evaluate hybrid cooling strategies that combine air and liquid approaches.';
|
|
|
|
const result = await tool.execute({ path: 'summary-test.docx', content });
|
|
|
|
expect(result).toContain('Summary:');
|
|
expect(result.length).toBeGreaterThan(100);
|
|
// Summary should contain stripped plain text from content, not markdown symbols
|
|
expect(result).toContain('liquid cooling market');
|
|
});
|
|
});
|