Skip to content

Commit c5b42ab

Browse files
committed
Add comprehensive unit tests for utils and generators (208 total tests)
1 parent 7d60642 commit c5b42ab

File tree

6 files changed

+999
-0
lines changed

6 files changed

+999
-0
lines changed
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import * as assert from 'assert';
2+
import { generateDocblockLines } from '../../generators/docblockGenerator';
3+
import { Tag } from '../../types';
4+
5+
suite('Docblock Generator Test Suite', () => {
6+
suite('generateDocblockLines', () => {
7+
test('Should generate basic docblock with description only', () => {
8+
const description = 'My function description';
9+
const params: Tag[] = [];
10+
11+
const lines = generateDocblockLines(description, params);
12+
13+
assert.ok(lines.length > 0);
14+
assert.strictEqual(lines[0], '/**');
15+
assert.strictEqual(lines[1], ` * ${description}`);
16+
assert.strictEqual(lines[lines.length - 1], ' */');
17+
});
18+
19+
test('Should include param tags', () => {
20+
const description = 'Function with parameters';
21+
const params: Tag[] = [
22+
{ name: 'param', types: ['string'], variable: '$content', content: 'The content' },
23+
];
24+
25+
const lines = generateDocblockLines(description, params);
26+
27+
const paramLine = lines.find((line) => line.includes('@param'));
28+
assert.ok(paramLine, 'Should have a @param line');
29+
assert.ok(paramLine?.includes('string'));
30+
assert.ok(paramLine?.includes('$content'));
31+
assert.ok(paramLine?.includes('The content'));
32+
});
33+
34+
test('Should include multiple param tags', () => {
35+
const description = 'Function with multiple parameters';
36+
const params: Tag[] = [
37+
{ name: 'param', types: ['string'], variable: '$content', content: 'The content' },
38+
{ name: 'param', types: ['int'], variable: '$id', content: 'The ID' },
39+
{ name: 'param', types: ['bool'], variable: '$force', content: 'Force update' },
40+
];
41+
42+
const lines = generateDocblockLines(description, params);
43+
44+
const paramLines = lines.filter((line) => line.includes('@param'));
45+
assert.strictEqual(paramLines.length, 3, 'Should have 3 @param lines');
46+
});
47+
48+
test('Should handle union types in params', () => {
49+
const description = 'Function with union type';
50+
const params: Tag[] = [
51+
{ name: 'param', types: ['string', 'int', 'null'], variable: '$value', content: 'The value' },
52+
];
53+
54+
const lines = generateDocblockLines(description, params);
55+
56+
const paramLine = lines.find((line) => line.includes('@param'));
57+
assert.ok(paramLine);
58+
assert.ok(paramLine?.includes('string|int|null'));
59+
});
60+
61+
test('Should include return tag when provided', () => {
62+
const description = 'Function with return value';
63+
const params: Tag[] = [];
64+
const returnParam: Tag = {
65+
name: 'return',
66+
types: ['bool'],
67+
content: 'True on success',
68+
};
69+
70+
const lines = generateDocblockLines(description, params, returnParam);
71+
72+
const returnLine = lines.find((line) => line.includes('@return'));
73+
assert.ok(returnLine, 'Should have a @return line');
74+
assert.ok(returnLine?.includes('bool'));
75+
assert.ok(returnLine?.includes('True on success'));
76+
});
77+
78+
test('Should align param types and names', () => {
79+
const description = 'Function with aligned params';
80+
const params: Tag[] = [
81+
{ name: 'param', types: ['string'], variable: '$a', content: 'Short' },
82+
{ name: 'param', types: ['int'], variable: '$longer_name', content: 'Longer' },
83+
];
84+
85+
const lines = generateDocblockLines(description, params);
86+
87+
const paramLines = lines.filter((line) => line.includes('@param'));
88+
assert.strictEqual(paramLines.length, 2);
89+
90+
// Check that padding is applied (types and names should be padded)
91+
assert.ok(paramLines[0].includes('string'));
92+
assert.ok(paramLines[1].includes('int'));
93+
});
94+
95+
test('Should handle params without types', () => {
96+
const description = 'Function with untyped param';
97+
const params: Tag[] = [
98+
{ name: 'param', variable: '$value', content: 'Some value' },
99+
];
100+
101+
const lines = generateDocblockLines(description, params);
102+
103+
const paramLine = lines.find((line) => line.includes('@param'));
104+
assert.ok(paramLine);
105+
assert.ok(paramLine?.includes('$value'));
106+
assert.ok(paramLine?.includes('Some value'));
107+
});
108+
109+
test('Should handle params without variable names', () => {
110+
const description = 'Function with unnamed param';
111+
const params: Tag[] = [
112+
{ name: 'param', types: ['string'], content: 'Some value' },
113+
];
114+
115+
const lines = generateDocblockLines(description, params);
116+
117+
const paramLine = lines.find((line) => line.includes('@param'));
118+
assert.ok(paramLine);
119+
assert.ok(paramLine?.includes('string'));
120+
});
121+
122+
test('Should generate complete docblock structure', () => {
123+
const description = 'Complete function';
124+
const params: Tag[] = [
125+
{ name: 'param', types: ['string'], variable: '$content', content: 'The content' },
126+
];
127+
const returnParam: Tag = {
128+
name: 'return',
129+
types: ['string'],
130+
content: 'Modified content',
131+
};
132+
133+
const lines = generateDocblockLines(description, params, returnParam);
134+
135+
assert.strictEqual(lines[0], '/**');
136+
assert.ok(lines[1].includes('Complete function'));
137+
assert.ok(lines.some((line) => line.includes('@param')));
138+
assert.ok(lines.some((line) => line.includes('@return')));
139+
assert.strictEqual(lines[lines.length - 1], ' */');
140+
});
141+
142+
test('Should handle empty description', () => {
143+
const description = '';
144+
const params: Tag[] = [];
145+
146+
const lines = generateDocblockLines(description, params);
147+
148+
assert.strictEqual(lines[0], '/**');
149+
assert.strictEqual(lines[1], ' * ');
150+
assert.strictEqual(lines[lines.length - 1], ' */');
151+
});
152+
153+
test('Should include blank line after description', () => {
154+
const description = 'My function';
155+
const params: Tag[] = [
156+
{ name: 'param', types: ['string'], variable: '$value', content: 'Value' },
157+
];
158+
159+
const lines = generateDocblockLines(description, params);
160+
161+
assert.strictEqual(lines[2], ' *', 'Third line should be blank comment line');
162+
});
163+
});
164+
});

src/test/suite/hookHelpers.test.ts

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import * as assert from 'assert';
2+
import * as vscode from 'vscode';
3+
import { getHook, getHookSlug, getHookDescription, getHookCompletion } from '../../utils/hookHelpers';
4+
5+
suite('Hook Helpers Test Suite', () => {
6+
suite('getHook', () => {
7+
test('Should find common action hooks', () => {
8+
const knownActions = ['init', 'wp_head', 'admin_init', 'wp_footer'];
9+
10+
knownActions.forEach((hookName) => {
11+
const hook = getHook(hookName);
12+
assert.ok(hook, `Should find action: ${hookName}`);
13+
assert.strictEqual(hook.name, hookName);
14+
});
15+
});
16+
17+
test('Should find common filter hooks', () => {
18+
const knownFilters = ['the_content', 'the_title', 'body_class'];
19+
20+
knownFilters.forEach((hookName) => {
21+
const hook = getHook(hookName);
22+
assert.ok(hook, `Should find filter: ${hookName}`);
23+
assert.strictEqual(hook.name, hookName);
24+
});
25+
});
26+
27+
test('Should return undefined for non-existent hooks', () => {
28+
const nonExistent = getHook('non_existent_hook_12345');
29+
assert.strictEqual(nonExistent, undefined);
30+
});
31+
32+
test('Should find hooks by alias if they exist', () => {
33+
// Try to find any hook with aliases and test it
34+
const hook = getHook('init');
35+
if (hook && hook.aliases && hook.aliases.length > 0) {
36+
const aliasHook = getHook(hook.aliases[0]);
37+
assert.ok(aliasHook, 'Should find hook by alias');
38+
}
39+
});
40+
41+
test('Should have required properties', () => {
42+
const hook = getHook('init');
43+
assert.ok(hook);
44+
assert.ok(hook.name);
45+
assert.ok(hook.doc);
46+
assert.ok(hook.doc.description);
47+
});
48+
49+
test('Should check filters before actions', () => {
50+
// If a name exists in both, should return the filter
51+
const hook = getHook('the_content');
52+
assert.ok(hook);
53+
// the_content is a filter, so it should be found first
54+
assert.strictEqual(hook.name, 'the_content');
55+
});
56+
});
57+
58+
suite('getHookSlug', () => {
59+
test('Should convert hook name to slug', () => {
60+
const tests = [
61+
{ hook: { name: 'init', doc: {} } as any, expected: 'init' },
62+
{ hook: { name: 'wp_head', doc: {} } as any, expected: 'wp_head' },
63+
{ hook: { name: 'the_content', doc: {} } as any, expected: 'the_content' },
64+
];
65+
66+
tests.forEach(({ hook, expected }) => {
67+
const slug = getHookSlug(hook);
68+
assert.strictEqual(slug, expected);
69+
});
70+
});
71+
72+
test('Should remove invalid characters', () => {
73+
const tests = [
74+
{ hook: { name: 'hook@123', doc: {} } as any, expected: 'hook' },
75+
{ hook: { name: 'hook#name', doc: {} } as any, expected: 'hookname' },
76+
{ hook: { name: 'hook name', doc: {} } as any, expected: 'hookname' },
77+
];
78+
79+
tests.forEach(({ hook, expected }) => {
80+
const slug = getHookSlug(hook);
81+
assert.strictEqual(slug, expected);
82+
});
83+
});
84+
85+
test('Should preserve underscores and hyphens', () => {
86+
const hook = { name: 'my-hook_name', doc: {} } as any;
87+
const slug = getHookSlug(hook);
88+
assert.strictEqual(slug, 'my-hook_name');
89+
});
90+
91+
test('Should convert to lowercase', () => {
92+
const hook = { name: 'MyHook', doc: {} } as any;
93+
const slug = getHookSlug(hook);
94+
assert.strictEqual(slug, 'myhook');
95+
});
96+
});
97+
98+
suite('getHookDescription', () => {
99+
test('Should return MarkdownString', () => {
100+
const hook = getHook('init');
101+
assert.ok(hook);
102+
103+
const description = getHookDescription(hook);
104+
assert.ok(description instanceof vscode.MarkdownString);
105+
});
106+
107+
test('Should include long description', () => {
108+
const hook = getHook('init');
109+
assert.ok(hook);
110+
111+
const description = getHookDescription(hook);
112+
const value = description.value;
113+
114+
assert.ok(value.length > 0);
115+
assert.ok(value.includes(hook.doc.long_description || hook.doc.description));
116+
});
117+
118+
test('Should include developer.wordpress.org link', () => {
119+
const hook = getHook('init');
120+
assert.ok(hook);
121+
122+
const description = getHookDescription(hook);
123+
assert.ok(description.value.includes('developer.wordpress.org'));
124+
assert.ok(description.value.includes('/reference/hooks/'));
125+
});
126+
127+
test('Should include param tags if present', () => {
128+
const hook = getHook('the_content');
129+
assert.ok(hook);
130+
131+
const description = getHookDescription(hook);
132+
const params = hook.doc.tags?.filter((tag) => tag.name === 'param');
133+
134+
if (params && params.length > 0) {
135+
assert.ok(description.value.includes('@param'));
136+
}
137+
});
138+
139+
test('Should format param tags with types and variables', () => {
140+
const hook = getHook('the_content');
141+
assert.ok(hook);
142+
143+
const description = getHookDescription(hook);
144+
const params = hook.doc.tags?.filter((tag) => tag.name === 'param');
145+
146+
if (params && params.length > 0 && params[0].variable) {
147+
assert.ok(description.value.includes(params[0].variable));
148+
}
149+
});
150+
});
151+
152+
suite('getHookCompletion', () => {
153+
test('Should return CompletionItem', () => {
154+
const hook = getHook('init');
155+
assert.ok(hook);
156+
157+
const completion = getHookCompletion(hook);
158+
assert.ok(completion instanceof vscode.CompletionItem);
159+
});
160+
161+
test('Should set label to hook name', () => {
162+
const hook = getHook('init');
163+
assert.ok(hook);
164+
165+
const completion = getHookCompletion(hook);
166+
assert.strictEqual(completion.label, 'init');
167+
});
168+
169+
test('Should set detail to description', () => {
170+
const hook = getHook('init');
171+
assert.ok(hook);
172+
173+
const completion = getHookCompletion(hook);
174+
assert.strictEqual(completion.detail, hook.doc.description);
175+
});
176+
177+
test('Should set documentation to formatted description', () => {
178+
const hook = getHook('init');
179+
assert.ok(hook);
180+
181+
const completion = getHookCompletion(hook);
182+
assert.ok(completion.documentation instanceof vscode.MarkdownString);
183+
});
184+
185+
test('Should set filterText to aliases if present', () => {
186+
// Find a hook with aliases
187+
const hook = getHook('init');
188+
assert.ok(hook);
189+
190+
const completion = getHookCompletion(hook);
191+
192+
if (hook.aliases && hook.aliases.length > 0) {
193+
assert.ok(completion.filterText);
194+
assert.ok(completion.filterText?.includes(hook.aliases[0]));
195+
}
196+
});
197+
198+
test('Should set kind to Value', () => {
199+
const hook = getHook('init');
200+
assert.ok(hook);
201+
202+
const completion = getHookCompletion(hook);
203+
assert.strictEqual(completion.kind, vscode.CompletionItemKind.Value);
204+
});
205+
});
206+
});

0 commit comments

Comments
 (0)