@amirdaraee/namewise 0.5.3 → 0.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/index.js +0 -0
- package/package.json +2 -2
- package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -82
- package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -61
- package/.github/workflows/auto-release.yml +0 -81
- package/.github/workflows/build.yml +0 -55
- package/.github/workflows/publish.yml +0 -134
- package/.github/workflows/test.yml +0 -45
- package/eng.traineddata +0 -0
- package/src/cli/commands.ts +0 -64
- package/src/cli/rename.ts +0 -171
- package/src/index.ts +0 -54
- package/src/parsers/excel-parser.ts +0 -66
- package/src/parsers/factory.ts +0 -38
- package/src/parsers/pdf-parser.ts +0 -99
- package/src/parsers/text-parser.ts +0 -43
- package/src/parsers/word-parser.ts +0 -50
- package/src/services/ai-factory.ts +0 -39
- package/src/services/claude-service.ts +0 -119
- package/src/services/file-renamer.ts +0 -141
- package/src/services/lmstudio-service.ts +0 -161
- package/src/services/ollama-service.ts +0 -191
- package/src/services/openai-service.ts +0 -117
- package/src/types/index.ts +0 -76
- package/src/types/pdf-extraction.d.ts +0 -7
- package/src/utils/ai-prompts.ts +0 -76
- package/src/utils/file-templates.ts +0 -275
- package/src/utils/naming-conventions.ts +0 -67
- package/src/utils/pdf-to-image.ts +0 -137
- package/tests/data/console-test-1.txt +0 -1
- package/tests/data/console-test-2.txt +0 -1
- package/tests/data/console-test-long-filename-for-display-testing.txt +0 -1
- package/tests/data/empty-file.txt +0 -0
- package/tests/data/failure.txt +0 -1
- package/tests/data/file1.txt +0 -1
- package/tests/data/file2.txt +0 -1
- package/tests/data/much-longer-filename-to-test-clearing.txt +0 -1
- package/tests/data/sample-markdown.md +0 -9
- package/tests/data/sample-pdf.pdf +0 -0
- package/tests/data/sample-text.txt +0 -25
- package/tests/data/short.txt +0 -1
- package/tests/data/single-file.txt +0 -1
- package/tests/data/success.txt +0 -1
- package/tests/data/this-is-a-very-long-filename-that-should-be-truncated-for-better-display-purposes.txt +0 -1
- package/tests/data/very-long-filename-that-should-be-cleared-properly.txt +0 -1
- package/tests/data/x.txt +0 -1
- package/tests/integration/ai-prompting.test.ts +0 -386
- package/tests/integration/end-to-end.test.ts +0 -209
- package/tests/integration/person-name-extraction.test.ts +0 -440
- package/tests/integration/workflow.test.ts +0 -336
- package/tests/mocks/mock-ai-service.ts +0 -58
- package/tests/unit/cli/commands.test.ts +0 -169
- package/tests/unit/parsers/factory.test.ts +0 -100
- package/tests/unit/parsers/pdf-parser.test.ts +0 -63
- package/tests/unit/parsers/text-parser.test.ts +0 -85
- package/tests/unit/services/ai-factory.test.ts +0 -85
- package/tests/unit/services/claude-service.test.ts +0 -188
- package/tests/unit/services/file-renamer.test.ts +0 -514
- package/tests/unit/services/lmstudio-service.test.ts +0 -326
- package/tests/unit/services/ollama-service.test.ts +0 -264
- package/tests/unit/services/openai-service.test.ts +0 -196
- package/tests/unit/utils/ai-prompts.test.ts +0 -213
- package/tests/unit/utils/file-templates.test.ts +0 -199
- package/tests/unit/utils/naming-conventions.test.ts +0 -88
- package/tests/unit/utils/pdf-to-image.test.ts +0 -127
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -30
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
-
import { OpenAIService } from '../../../src/services/openai-service.js';
|
|
3
|
-
|
|
4
|
-
// Mock the OpenAI SDK
|
|
5
|
-
vi.mock('openai', () => {
|
|
6
|
-
const MockOpenAI = vi.fn().mockImplementation(() => ({
|
|
7
|
-
chat: {
|
|
8
|
-
completions: {
|
|
9
|
-
create: vi.fn()
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}));
|
|
13
|
-
return {
|
|
14
|
-
default: MockOpenAI
|
|
15
|
-
};
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe('OpenAIService', () => {
|
|
19
|
-
let service: OpenAIService;
|
|
20
|
-
let mockClient: any;
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
service = new OpenAIService('test-api-key');
|
|
24
|
-
mockClient = (service as any).client;
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe('Basic Properties', () => {
|
|
28
|
-
it('should have correct name', () => {
|
|
29
|
-
expect(service.name).toBe('OpenAI');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('should initialize with API key', () => {
|
|
33
|
-
expect(service).toBeDefined();
|
|
34
|
-
});
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
describe('generateFileName() with different naming conventions', () => {
|
|
38
|
-
const sampleContent = 'This is a user manual for software installation and configuration.';
|
|
39
|
-
const originalName = 'manual.docx';
|
|
40
|
-
|
|
41
|
-
beforeEach(() => {
|
|
42
|
-
mockClient.chat.completions.create.mockResolvedValue({
|
|
43
|
-
choices: [
|
|
44
|
-
{
|
|
45
|
-
message: {
|
|
46
|
-
content: 'user manual software installation configuration'
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
]
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should generate filename with kebab-case convention (default)', async () => {
|
|
54
|
-
const result = await service.generateFileName(sampleContent, originalName);
|
|
55
|
-
|
|
56
|
-
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
|
57
|
-
expect.objectContaining({
|
|
58
|
-
model: 'gpt-3.5-turbo',
|
|
59
|
-
messages: [expect.objectContaining({
|
|
60
|
-
role: 'user',
|
|
61
|
-
content: expect.stringContaining('Use lowercase with hyphens between words')
|
|
62
|
-
})],
|
|
63
|
-
temperature: 0.3
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
expect(result).toBe('user-manual-software-installation-configuration');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should generate filename with snake_case convention', async () => {
|
|
71
|
-
const result = await service.generateFileName(sampleContent, originalName, 'snake_case');
|
|
72
|
-
|
|
73
|
-
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
|
74
|
-
expect.objectContaining({
|
|
75
|
-
messages: [expect.objectContaining({
|
|
76
|
-
content: expect.stringContaining('Use lowercase with underscores between words')
|
|
77
|
-
})]
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(result).toBe('user_manual_software_installation_configuration');
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should generate filename with camelCase convention', async () => {
|
|
85
|
-
const result = await service.generateFileName(sampleContent, originalName, 'camelCase');
|
|
86
|
-
|
|
87
|
-
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
|
88
|
-
expect.objectContaining({
|
|
89
|
-
messages: [expect.objectContaining({
|
|
90
|
-
content: expect.stringContaining('Use camelCase format starting with lowercase')
|
|
91
|
-
})]
|
|
92
|
-
})
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
expect(result).toBe('userManualSoftwareInstallationConfiguration');
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
it('should generate filename with UPPERCASE convention', async () => {
|
|
99
|
-
const result = await service.generateFileName(sampleContent, originalName, 'UPPERCASE');
|
|
100
|
-
|
|
101
|
-
expect(result).toBe('USERMANUALSOFTWAREINSTALLATIONCONFIGURATION');
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('should include original filename and content in prompt', async () => {
|
|
105
|
-
await service.generateFileName(sampleContent, originalName, 'kebab-case');
|
|
106
|
-
|
|
107
|
-
const call = mockClient.chat.completions.create.mock.calls[0][0];
|
|
108
|
-
expect(call.messages[0].content).toContain(sampleContent.substring(0, 2000));
|
|
109
|
-
expect(call.messages[0].content).toContain('Document content (first 2000 characters)');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('should use correct OpenAI parameters', async () => {
|
|
113
|
-
await service.generateFileName(sampleContent, originalName);
|
|
114
|
-
|
|
115
|
-
expect(mockClient.chat.completions.create).toHaveBeenCalledWith(
|
|
116
|
-
expect.objectContaining({
|
|
117
|
-
model: 'gpt-3.5-turbo',
|
|
118
|
-
max_tokens: 100,
|
|
119
|
-
temperature: 0.3
|
|
120
|
-
})
|
|
121
|
-
);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
describe('Error Handling', () => {
|
|
126
|
-
it('should handle API errors gracefully', async () => {
|
|
127
|
-
mockClient.chat.completions.create.mockRejectedValue(new Error('OpenAI API Error'));
|
|
128
|
-
|
|
129
|
-
await expect(service.generateFileName('content', 'file.txt')).rejects.toThrow(
|
|
130
|
-
'Failed to generate filename with OpenAI: OpenAI API Error'
|
|
131
|
-
);
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should handle empty choices response', async () => {
|
|
135
|
-
mockClient.chat.completions.create.mockResolvedValue({
|
|
136
|
-
choices: []
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const result = await service.generateFileName('content', 'file.txt');
|
|
140
|
-
expect(result).toBe('untitled-document');
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
it('should handle missing message content', async () => {
|
|
144
|
-
mockClient.chat.completions.create.mockResolvedValue({
|
|
145
|
-
choices: [
|
|
146
|
-
{
|
|
147
|
-
message: {
|
|
148
|
-
content: null
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const result = await service.generateFileName('content', 'file.txt');
|
|
155
|
-
expect(result).toBe('untitled-document');
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
it('should handle undefined choices', async () => {
|
|
159
|
-
mockClient.chat.completions.create.mockResolvedValue({
|
|
160
|
-
choices: [undefined]
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const result = await service.generateFileName('content', 'file.txt');
|
|
164
|
-
expect(result).toBe('untitled-document');
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
describe('Filename Sanitization', () => {
|
|
169
|
-
beforeEach(() => {
|
|
170
|
-
mockClient.chat.completions.create.mockResolvedValue({
|
|
171
|
-
choices: [
|
|
172
|
-
{
|
|
173
|
-
message: {
|
|
174
|
-
content: 'Test@Document#With$Special%Characters.xlsx'
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
]
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should sanitize special characters and apply naming convention', async () => {
|
|
182
|
-
const result = await service.generateFileName('content', 'original.txt', 'kebab-case');
|
|
183
|
-
expect(result).toBe('testdocumentwithspecialcharacters');
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
it('should handle different conventions for sanitized input', async () => {
|
|
187
|
-
const kebabResult = await service.generateFileName('content', 'file.txt', 'kebab-case');
|
|
188
|
-
const snakeResult = await service.generateFileName('content', 'file.txt', 'snake_case');
|
|
189
|
-
const camelResult = await service.generateFileName('content', 'file.txt', 'camelCase');
|
|
190
|
-
|
|
191
|
-
expect(kebabResult).toBe('testdocumentwithspecialcharacters');
|
|
192
|
-
expect(snakeResult).toBe('testdocumentwithspecialcharacters');
|
|
193
|
-
expect(camelResult).toBe('testdocumentwithspecialcharacters');
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
});
|
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../../../src/utils/ai-prompts.js';
|
|
3
|
-
import { FileInfo } from '../../../src/types/index.js';
|
|
4
|
-
|
|
5
|
-
describe('AI Prompts', () => {
|
|
6
|
-
describe('buildFileNamePrompt', () => {
|
|
7
|
-
it('should build basic prompt with minimal context', () => {
|
|
8
|
-
const prompt = buildFileNamePrompt({
|
|
9
|
-
content: 'This is a sample document about contract negotiations.',
|
|
10
|
-
originalName: 'document.pdf',
|
|
11
|
-
namingConvention: 'kebab-case',
|
|
12
|
-
category: 'general'
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
expect(prompt).toContain('Based on the following document information');
|
|
16
|
-
expect(prompt).toContain('Descriptive and meaningful');
|
|
17
|
-
expect(prompt).toContain('Between 3-10 words');
|
|
18
|
-
expect(prompt).toContain('hyphens between words');
|
|
19
|
-
expect(prompt).toContain('This is a sample document about contract negotiations');
|
|
20
|
-
expect(prompt).toContain('Respond with only the filename');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('should include file metadata when provided', () => {
|
|
24
|
-
const mockFileInfo: FileInfo = {
|
|
25
|
-
name: 'contract.pdf',
|
|
26
|
-
path: '/documents/contract.pdf',
|
|
27
|
-
extension: '.pdf',
|
|
28
|
-
size: 1024 * 50, // 50KB
|
|
29
|
-
createdAt: new Date('2024-01-15'),
|
|
30
|
-
modifiedAt: new Date('2024-02-01'),
|
|
31
|
-
parentFolder: 'contracts',
|
|
32
|
-
folderPath: ['home', 'documents', 'contracts']
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const prompt = buildFileNamePrompt({
|
|
36
|
-
content: 'Employment contract for John Doe.',
|
|
37
|
-
originalName: 'contract.pdf',
|
|
38
|
-
namingConvention: 'kebab-case',
|
|
39
|
-
category: 'document',
|
|
40
|
-
fileInfo: mockFileInfo
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
expect(prompt).toContain('File Information:');
|
|
44
|
-
expect(prompt).toContain('- Original filename: contract.pdf');
|
|
45
|
-
expect(prompt).toContain('- File size: 50KB');
|
|
46
|
-
expect(prompt).toContain('- Parent folder: contracts');
|
|
47
|
-
expect(prompt).toContain('- Folder path: home > documents > contracts');
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('should include document metadata when available', () => {
|
|
51
|
-
const mockFileInfo: FileInfo = {
|
|
52
|
-
name: 'report.pdf',
|
|
53
|
-
path: '/reports/report.pdf',
|
|
54
|
-
extension: '.pdf',
|
|
55
|
-
size: 1024 * 100,
|
|
56
|
-
createdAt: new Date('2024-01-15'),
|
|
57
|
-
modifiedAt: new Date('2024-02-01'),
|
|
58
|
-
parentFolder: 'reports',
|
|
59
|
-
folderPath: ['home', 'reports'],
|
|
60
|
-
documentMetadata: {
|
|
61
|
-
title: 'Annual Financial Report',
|
|
62
|
-
author: 'Jane Smith',
|
|
63
|
-
creator: 'Microsoft Word',
|
|
64
|
-
subject: 'Company Finances',
|
|
65
|
-
keywords: ['finance', 'annual', 'report'],
|
|
66
|
-
creationDate: new Date('2024-01-10'),
|
|
67
|
-
modificationDate: new Date('2024-01-20'),
|
|
68
|
-
pages: 25,
|
|
69
|
-
wordCount: 5000
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
const prompt = buildFileNamePrompt({
|
|
74
|
-
content: 'This report covers the annual financial performance...',
|
|
75
|
-
originalName: 'report.pdf',
|
|
76
|
-
namingConvention: 'snake_case',
|
|
77
|
-
category: 'document',
|
|
78
|
-
fileInfo: mockFileInfo
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
expect(prompt).toContain('Document Properties:');
|
|
82
|
-
expect(prompt).toContain('- Title: Annual Financial Report');
|
|
83
|
-
expect(prompt).toContain('- Author: Jane Smith');
|
|
84
|
-
expect(prompt).toContain('- Creator: Microsoft Word');
|
|
85
|
-
expect(prompt).toContain('- Subject: Company Finances');
|
|
86
|
-
expect(prompt).toContain('- Keywords: finance, annual, report');
|
|
87
|
-
expect(prompt).toContain('- Pages: 25');
|
|
88
|
-
expect(prompt).toContain('- Word count: 5000');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('should include person name detection instructions', () => {
|
|
92
|
-
const prompt = buildFileNamePrompt({
|
|
93
|
-
content: 'Visa application for Setareh Ahmadi to visit Canada.',
|
|
94
|
-
originalName: 'visa-app.pdf',
|
|
95
|
-
namingConvention: 'kebab-case',
|
|
96
|
-
category: 'document'
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(prompt).toContain('If the document is specifically for/about a person (based on content), include their name at the beginning');
|
|
100
|
-
expect(prompt).toContain('Important: If this document is specifically for or about a particular person mentioned in the content, start the filename with their name');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('should include folder name filtering instructions', () => {
|
|
104
|
-
const prompt = buildFileNamePrompt({
|
|
105
|
-
content: 'Meeting notes from Q4 planning session.',
|
|
106
|
-
originalName: 'notes.txt',
|
|
107
|
-
namingConvention: 'camelCase',
|
|
108
|
-
category: 'general'
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
expect(prompt).toContain('Ignore irrelevant folder names that don\'t describe the document content');
|
|
112
|
-
expect(prompt).toContain('Focus on the document\'s actual content and purpose, not just metadata');
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('should handle different naming conventions', () => {
|
|
116
|
-
const kebabPrompt = buildFileNamePrompt({
|
|
117
|
-
content: 'Test content',
|
|
118
|
-
originalName: 'test.txt',
|
|
119
|
-
namingConvention: 'kebab-case',
|
|
120
|
-
category: 'general'
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const snakePrompt = buildFileNamePrompt({
|
|
124
|
-
content: 'Test content',
|
|
125
|
-
originalName: 'test.txt',
|
|
126
|
-
namingConvention: 'snake_case',
|
|
127
|
-
category: 'general'
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
expect(kebabPrompt).toContain('hyphens between words');
|
|
131
|
-
expect(snakePrompt).toContain('underscores between words');
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
it('should handle different file categories', () => {
|
|
135
|
-
const documentPrompt = buildFileNamePrompt({
|
|
136
|
-
content: 'Contract content',
|
|
137
|
-
originalName: 'contract.pdf',
|
|
138
|
-
namingConvention: 'kebab-case',
|
|
139
|
-
category: 'document'
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
const moviePrompt = buildFileNamePrompt({
|
|
143
|
-
content: 'Movie file content',
|
|
144
|
-
originalName: 'movie.mp4',
|
|
145
|
-
namingConvention: 'kebab-case',
|
|
146
|
-
category: 'movie'
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
expect(documentPrompt).toContain('document');
|
|
150
|
-
expect(moviePrompt).toContain('movie');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
it('should truncate content to 2000 characters', () => {
|
|
154
|
-
const longContent = 'a'.repeat(3000);
|
|
155
|
-
|
|
156
|
-
const prompt = buildFileNamePrompt({
|
|
157
|
-
content: longContent,
|
|
158
|
-
originalName: 'long.txt',
|
|
159
|
-
namingConvention: 'kebab-case',
|
|
160
|
-
category: 'general'
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
const contentSection = prompt.substring(prompt.indexOf('Document content'));
|
|
164
|
-
expect(contentSection).toContain('a'.repeat(2000));
|
|
165
|
-
expect(contentSection).not.toContain('a'.repeat(2001));
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
it('should handle missing optional metadata gracefully', () => {
|
|
169
|
-
const mockFileInfo: FileInfo = {
|
|
170
|
-
name: 'simple.txt',
|
|
171
|
-
path: '/simple.txt',
|
|
172
|
-
extension: '.txt',
|
|
173
|
-
size: 1024,
|
|
174
|
-
createdAt: new Date('2024-01-15'),
|
|
175
|
-
modifiedAt: new Date('2024-02-01'),
|
|
176
|
-
parentFolder: 'root',
|
|
177
|
-
folderPath: ['root'],
|
|
178
|
-
documentMetadata: {
|
|
179
|
-
// Only some fields provided
|
|
180
|
-
title: 'Simple Document'
|
|
181
|
-
// author, creator, etc. are undefined
|
|
182
|
-
}
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const prompt = buildFileNamePrompt({
|
|
186
|
-
content: 'Simple document content.',
|
|
187
|
-
originalName: 'simple.txt',
|
|
188
|
-
namingConvention: 'kebab-case',
|
|
189
|
-
category: 'general',
|
|
190
|
-
fileInfo: mockFileInfo
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
expect(prompt).toContain('- Title: Simple Document');
|
|
194
|
-
expect(prompt).not.toContain('- Author:');
|
|
195
|
-
expect(prompt).not.toContain('- Creator:');
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe('AI_SYSTEM_PROMPT', () => {
|
|
200
|
-
it('should provide clear system instructions', () => {
|
|
201
|
-
expect(AI_SYSTEM_PROMPT).toContain('helpful assistant');
|
|
202
|
-
expect(AI_SYSTEM_PROMPT).toContain('generates descriptive filenames');
|
|
203
|
-
expect(AI_SYSTEM_PROMPT).toContain('just the filename');
|
|
204
|
-
expect(AI_SYSTEM_PROMPT).toContain('no explanation');
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
it('should be a non-empty string', () => {
|
|
208
|
-
expect(AI_SYSTEM_PROMPT).toBeTruthy();
|
|
209
|
-
expect(typeof AI_SYSTEM_PROMPT).toBe('string');
|
|
210
|
-
expect(AI_SYSTEM_PROMPT.length).toBeGreaterThan(20);
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
});
|
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import {
|
|
3
|
-
categorizeFile,
|
|
4
|
-
applyTemplate,
|
|
5
|
-
getTemplateInstructions,
|
|
6
|
-
FILE_TEMPLATES,
|
|
7
|
-
FileCategory
|
|
8
|
-
} from '../../../src/utils/file-templates.js';
|
|
9
|
-
|
|
10
|
-
describe('File Templates', () => {
|
|
11
|
-
describe('categorizeFile()', () => {
|
|
12
|
-
it('should categorize document files', () => {
|
|
13
|
-
expect(categorizeFile('/path/contract.pdf')).toBe('document');
|
|
14
|
-
expect(categorizeFile('/path/license.docx')).toBe('document');
|
|
15
|
-
expect(categorizeFile('/path/certificate.txt')).toBe('document');
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
it('should categorize document files by content', () => {
|
|
19
|
-
expect(categorizeFile('/path/file.pdf', 'This is a work contract agreement')).toBe('document');
|
|
20
|
-
expect(categorizeFile('/path/file.pdf', 'License application form')).toBe('document');
|
|
21
|
-
expect(categorizeFile('/path/file.pdf', 'Invoice for services rendered')).toBe('document');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
it('should categorize movie files', () => {
|
|
25
|
-
expect(categorizeFile('/path/movie.mp4')).toBe('movie');
|
|
26
|
-
expect(categorizeFile('/path/film.mkv')).toBe('movie');
|
|
27
|
-
expect(categorizeFile('/path/video.avi')).toBe('movie');
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should categorize series files', () => {
|
|
31
|
-
expect(categorizeFile('/path/show.s01e01.mkv')).toBe('series');
|
|
32
|
-
expect(categorizeFile('/path/series.season.1.mp4')).toBe('series');
|
|
33
|
-
expect(categorizeFile('/path/tv.episode.avi')).toBe('series');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should categorize music files', () => {
|
|
37
|
-
expect(categorizeFile('/path/song.mp3')).toBe('music');
|
|
38
|
-
expect(categorizeFile('/path/audio.flac')).toBe('music');
|
|
39
|
-
expect(categorizeFile('/path/track.wav')).toBe('music');
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it('should categorize photo files', () => {
|
|
43
|
-
expect(categorizeFile('/path/image.jpg')).toBe('photo');
|
|
44
|
-
expect(categorizeFile('/path/picture.png')).toBe('photo');
|
|
45
|
-
expect(categorizeFile('/path/photo.heic')).toBe('photo');
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should categorize book files', () => {
|
|
49
|
-
expect(categorizeFile('/path/novel.epub')).toBe('book');
|
|
50
|
-
expect(categorizeFile('/path/book.mobi')).toBe('book');
|
|
51
|
-
expect(categorizeFile('/path/ebook.azw')).toBe('book');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('should default to general for unknown types', () => {
|
|
55
|
-
expect(categorizeFile('/path/unknown.xyz')).toBe('general');
|
|
56
|
-
expect(categorizeFile('/path/file')).toBe('general');
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
it('should prioritize series over movie for video files with series keywords', () => {
|
|
60
|
-
expect(categorizeFile('/path/breaking-bad.s01e01.mp4')).toBe('series');
|
|
61
|
-
expect(categorizeFile('/path/movie.mkv', 'This is season 1 episode 1')).toBe('series');
|
|
62
|
-
});
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
describe('applyTemplate()', () => {
|
|
66
|
-
it('should apply document template with personal name and date', () => {
|
|
67
|
-
const result = applyTemplate(
|
|
68
|
-
'driving-license',
|
|
69
|
-
'document',
|
|
70
|
-
{ category: 'document', personalName: 'amirhossein', dateFormat: 'YYYYMMDD' },
|
|
71
|
-
'kebab-case'
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
// Should match pattern: {content}-{personalName}-{date}
|
|
75
|
-
expect(result).toMatch(/^driving-license-amirhossein-\d{8}$/);
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('should apply movie template with year', () => {
|
|
79
|
-
const result = applyTemplate(
|
|
80
|
-
'the-dark-knight',
|
|
81
|
-
'movie',
|
|
82
|
-
{ category: 'movie', dateFormat: 'YYYY' },
|
|
83
|
-
'kebab-case'
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
// Movies don't use personal names in their template pattern
|
|
87
|
-
expect(result).toBe('the-dark-knight');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should apply different date formats', () => {
|
|
91
|
-
const baseOptions = { category: 'document' as FileCategory, personalName: 'john' };
|
|
92
|
-
|
|
93
|
-
const yyyymmdd = applyTemplate('contract', 'document', { ...baseOptions, dateFormat: 'YYYYMMDD' }, 'kebab-case');
|
|
94
|
-
const yyyymmdd2 = applyTemplate('contract', 'document', { ...baseOptions, dateFormat: 'YYYY-MM-DD' }, 'kebab-case');
|
|
95
|
-
const yyyy = applyTemplate('contract', 'document', { ...baseOptions, dateFormat: 'YYYY' }, 'kebab-case');
|
|
96
|
-
|
|
97
|
-
expect(yyyymmdd).toMatch(/^contract-john-\d{8}$/);
|
|
98
|
-
expect(yyyymmdd2).toMatch(/^contract-john-\d{4}-\d{2}-\d{2}$/);
|
|
99
|
-
expect(yyyy).toMatch(/^contract-john-\d{4}$/);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('should handle no date format', () => {
|
|
103
|
-
const result = applyTemplate(
|
|
104
|
-
'document',
|
|
105
|
-
'document',
|
|
106
|
-
{ category: 'document', personalName: 'jane', dateFormat: 'none' },
|
|
107
|
-
'kebab-case'
|
|
108
|
-
);
|
|
109
|
-
|
|
110
|
-
expect(result).toBe('document-jane');
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
it('should handle missing personal name', () => {
|
|
114
|
-
const result = applyTemplate(
|
|
115
|
-
'report',
|
|
116
|
-
'document',
|
|
117
|
-
{ category: 'document', dateFormat: 'YYYY' },
|
|
118
|
-
'kebab-case'
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
expect(result).toMatch(/^report-\d{4}$/);
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
it('should apply naming conventions correctly', () => {
|
|
125
|
-
const baseOptions = { category: 'document' as FileCategory, personalName: 'test-user', dateFormat: 'none' as const };
|
|
126
|
-
|
|
127
|
-
const kebab = applyTemplate('My Document', 'document', baseOptions, 'kebab-case');
|
|
128
|
-
const snake = applyTemplate('My Document', 'document', baseOptions, 'snake_case');
|
|
129
|
-
const camel = applyTemplate('My Document', 'document', baseOptions, 'camelCase');
|
|
130
|
-
|
|
131
|
-
expect(kebab).toBe('my-document-test-user');
|
|
132
|
-
expect(snake).toBe('my_document_test_user');
|
|
133
|
-
expect(camel).toBe('myDocumentTestUser');
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
it('should clean up multiple separators', () => {
|
|
137
|
-
const result = applyTemplate(
|
|
138
|
-
'test--document',
|
|
139
|
-
'document',
|
|
140
|
-
{ category: 'document', personalName: 'user', dateFormat: 'none' },
|
|
141
|
-
'kebab-case'
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
expect(result).toBe('test-document-user');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('should handle general category', () => {
|
|
148
|
-
const result = applyTemplate(
|
|
149
|
-
'meeting-notes',
|
|
150
|
-
'general',
|
|
151
|
-
{ category: 'general', personalName: 'admin', dateFormat: 'YYYY' },
|
|
152
|
-
'kebab-case'
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(result).toBe('meeting-notes');
|
|
156
|
-
});
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
describe('getTemplateInstructions()', () => {
|
|
160
|
-
it('should return instructions for each category', () => {
|
|
161
|
-
const categories: FileCategory[] = ['document', 'movie', 'music', 'series', 'photo', 'book', 'general'];
|
|
162
|
-
|
|
163
|
-
categories.forEach(category => {
|
|
164
|
-
const instructions = getTemplateInstructions(category);
|
|
165
|
-
expect(instructions).toContain(category);
|
|
166
|
-
expect(instructions.length).toBeGreaterThan(10);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('should include examples in instructions', () => {
|
|
171
|
-
const documentInstructions = getTemplateInstructions('document');
|
|
172
|
-
expect(documentInstructions).toContain('driving-license-amirhossein');
|
|
173
|
-
|
|
174
|
-
const movieInstructions = getTemplateInstructions('movie');
|
|
175
|
-
expect(movieInstructions).toContain('the-dark-knight-2008');
|
|
176
|
-
});
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
describe('FILE_TEMPLATES', () => {
|
|
180
|
-
it('should have all required template categories', () => {
|
|
181
|
-
const expectedCategories: FileCategory[] = ['document', 'movie', 'music', 'series', 'photo', 'book', 'general'];
|
|
182
|
-
|
|
183
|
-
expectedCategories.forEach(category => {
|
|
184
|
-
expect(FILE_TEMPLATES[category]).toBeDefined();
|
|
185
|
-
expect(FILE_TEMPLATES[category].category).toBe(category);
|
|
186
|
-
expect(FILE_TEMPLATES[category].pattern).toBeDefined();
|
|
187
|
-
expect(FILE_TEMPLATES[category].description).toBeDefined();
|
|
188
|
-
expect(FILE_TEMPLATES[category].examples.length).toBeGreaterThan(0);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
it('should have valid patterns', () => {
|
|
193
|
-
Object.values(FILE_TEMPLATES).forEach(template => {
|
|
194
|
-
expect(template.pattern).toContain('{content}');
|
|
195
|
-
expect(template.examples.length).toBeGreaterThan(2);
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
});
|