@bestend/confluence-cli 1.16.1 → 2.0.0
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/.claude/skills/confluence/SKILL.md +722 -0
- package/README.md +251 -21
- package/bin/confluence.js +848 -128
- package/bin/index.js +6 -1
- package/lib/config.js +242 -40
- package/lib/confluence-client.js +299 -61
- package/package.json +10 -4
- package/.eslintrc.js +0 -23
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -34
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -26
- package/.github/ISSUE_TEMPLATE/feedback.md +0 -37
- package/.github/pull_request_template.md +0 -31
- package/.github/workflows/ci.yml +0 -28
- package/.github/workflows/publish.yml +0 -38
- package/.releaserc +0 -17
- package/AGENTS.md +0 -105
- package/CHANGELOG.md +0 -232
- package/CONTRIBUTING.md +0 -246
- package/docs/PROMOTION.md +0 -63
- package/eslint.config.js +0 -33
- package/examples/copy-tree-example.sh +0 -117
- package/examples/create-child-page-example.sh +0 -67
- package/examples/demo-page-management.sh +0 -68
- package/examples/demo.sh +0 -43
- package/examples/sample-page.md +0 -30
- package/jest.config.js +0 -13
- package/llms.txt +0 -46
- package/tests/confluence-client.test.js +0 -458
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
const ConfluenceClient = require('../lib/confluence-client');
|
|
2
|
-
const MockAdapter = require('axios-mock-adapter');
|
|
3
|
-
|
|
4
|
-
describe('ConfluenceClient', () => {
|
|
5
|
-
let client;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
client = new ConfluenceClient({
|
|
9
|
-
domain: 'test.atlassian.net',
|
|
10
|
-
token: 'test-token'
|
|
11
|
-
});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
describe('api path handling', () => {
|
|
15
|
-
test('defaults to /rest/api when path is not provided', () => {
|
|
16
|
-
const defaultClient = new ConfluenceClient({
|
|
17
|
-
domain: 'example.com',
|
|
18
|
-
token: 'no-path-token'
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
expect(defaultClient.baseURL).toBe('https://example.com/rest/api');
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
test('normalizes custom api paths', () => {
|
|
25
|
-
const customClient = new ConfluenceClient({
|
|
26
|
-
domain: 'cloud.example',
|
|
27
|
-
token: 'custom-path',
|
|
28
|
-
apiPath: 'wiki/rest/api/'
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
expect(customClient.baseURL).toBe('https://cloud.example/wiki/rest/api');
|
|
32
|
-
});
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
describe('authentication setup', () => {
|
|
36
|
-
test('uses bearer token headers by default', () => {
|
|
37
|
-
const bearerClient = new ConfluenceClient({
|
|
38
|
-
domain: 'test.atlassian.net',
|
|
39
|
-
token: 'bearer-token'
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
expect(bearerClient.client.defaults.headers.Authorization).toBe('Bearer bearer-token');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('builds basic auth headers when email is provided', () => {
|
|
46
|
-
const basicClient = new ConfluenceClient({
|
|
47
|
-
domain: 'test.atlassian.net',
|
|
48
|
-
token: 'basic-token',
|
|
49
|
-
authType: 'basic',
|
|
50
|
-
email: 'user@example.com'
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const encoded = Buffer.from('user@example.com:basic-token').toString('base64');
|
|
54
|
-
expect(basicClient.client.defaults.headers.Authorization).toBe(`Basic ${encoded}`);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
test('throws when basic auth is missing an email', () => {
|
|
58
|
-
expect(() => new ConfluenceClient({
|
|
59
|
-
domain: 'test.atlassian.net',
|
|
60
|
-
token: 'missing-email',
|
|
61
|
-
authType: 'basic'
|
|
62
|
-
})).toThrow('Basic authentication requires an email address.');
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
describe('extractPageId', () => {
|
|
67
|
-
test('should return numeric page ID as is', async () => {
|
|
68
|
-
expect(await client.extractPageId('123456789')).toBe('123456789');
|
|
69
|
-
expect(await client.extractPageId(123456789)).toBe(123456789);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('should extract page ID from URL with pageId parameter', async () => {
|
|
73
|
-
const url = 'https://test.atlassian.net/wiki/spaces/TEST/pages/123456789/Page+Title';
|
|
74
|
-
expect(await client.extractPageId(url + '?pageId=987654321')).toBe('987654321');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('should resolve display URLs', async () => {
|
|
78
|
-
// Mock the API response for display URL resolution
|
|
79
|
-
const mock = new MockAdapter(client.client);
|
|
80
|
-
|
|
81
|
-
mock.onGet('/content').reply(200, {
|
|
82
|
-
results: [{
|
|
83
|
-
id: '12345',
|
|
84
|
-
title: 'Page Title',
|
|
85
|
-
_links: { webui: '/display/TEST/Page+Title' }
|
|
86
|
-
}]
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const displayUrl = 'https://test.atlassian.net/display/TEST/Page+Title';
|
|
90
|
-
expect(await client.extractPageId(displayUrl)).toBe('12345');
|
|
91
|
-
|
|
92
|
-
mock.restore();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
test('should resolve nested display URLs', async () => {
|
|
96
|
-
// Mock the API response for display URL resolution
|
|
97
|
-
const mock = new MockAdapter(client.client);
|
|
98
|
-
|
|
99
|
-
mock.onGet('/content').reply(200, {
|
|
100
|
-
results: [{
|
|
101
|
-
id: '67890',
|
|
102
|
-
title: 'Child Page',
|
|
103
|
-
_links: { webui: '/display/TEST/Parent/Child+Page' }
|
|
104
|
-
}]
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const displayUrl = 'https://test.atlassian.net/display/TEST/Parent/Child+Page';
|
|
108
|
-
expect(await client.extractPageId(displayUrl)).toBe('67890');
|
|
109
|
-
|
|
110
|
-
mock.restore();
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('should throw error when display URL cannot be resolved', async () => {
|
|
114
|
-
const mock = new MockAdapter(client.client);
|
|
115
|
-
|
|
116
|
-
// Mock empty result
|
|
117
|
-
mock.onGet('/content').reply(200, {
|
|
118
|
-
results: []
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const displayUrl = 'https://test.atlassian.net/display/TEST/NonExistentPage';
|
|
122
|
-
await expect(client.extractPageId(displayUrl)).rejects.toThrow(/Could not resolve page ID/);
|
|
123
|
-
|
|
124
|
-
mock.restore();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('markdownToStorage', () => {
|
|
129
|
-
test('should convert basic markdown to native Confluence storage format', () => {
|
|
130
|
-
const markdown = '# Hello World\n\nThis is a **test** page with *italic* text.';
|
|
131
|
-
const result = client.markdownToStorage(markdown);
|
|
132
|
-
|
|
133
|
-
expect(result).toContain('<h1>Hello World</h1>');
|
|
134
|
-
expect(result).toContain('<p>This is a <strong>test</strong> page with <em>italic</em> text.</p>');
|
|
135
|
-
expect(result).not.toContain('<ac:structured-macro ac:name="html">');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test('should convert code blocks to Confluence code macro', () => {
|
|
139
|
-
const markdown = '```javascript\nconsole.log("Hello World");\n```';
|
|
140
|
-
const result = client.markdownToStorage(markdown);
|
|
141
|
-
|
|
142
|
-
expect(result).toContain('<ac:structured-macro ac:name="code">');
|
|
143
|
-
expect(result).toContain('<ac:parameter ac:name="language">javascript</ac:parameter>');
|
|
144
|
-
expect(result).toContain('console.log("Hello World");');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
test('should convert lists to native Confluence format', () => {
|
|
148
|
-
const markdown = '- Item 1\n- Item 2\n\n1. First\n2. Second';
|
|
149
|
-
const result = client.markdownToStorage(markdown);
|
|
150
|
-
|
|
151
|
-
expect(result).toContain('<ul>');
|
|
152
|
-
expect(result).toContain('<li><p>Item 1</p></li>');
|
|
153
|
-
expect(result).toContain('<ol>');
|
|
154
|
-
expect(result).toContain('<li><p>First</p></li>');
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test('should convert Confluence admonitions', () => {
|
|
158
|
-
const markdown = '[!info]\nThis is an info message';
|
|
159
|
-
const result = client.markdownToStorage(markdown);
|
|
160
|
-
|
|
161
|
-
expect(result).toContain('<ac:structured-macro ac:name="info">');
|
|
162
|
-
expect(result).toContain('This is an info message');
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test('should convert tables to native Confluence format', () => {
|
|
166
|
-
const markdown = '| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1 | Cell 2 |';
|
|
167
|
-
const result = client.markdownToStorage(markdown);
|
|
168
|
-
|
|
169
|
-
expect(result).toContain('<table>');
|
|
170
|
-
expect(result).toContain('<th><p>Header 1</p></th>');
|
|
171
|
-
expect(result).toContain('<td><p>Cell 1</p></td>');
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
test('should keep links as standard HTML anchors', () => {
|
|
175
|
-
const markdown = '[Example Link](https://example.com)';
|
|
176
|
-
const result = client.markdownToStorage(markdown);
|
|
177
|
-
|
|
178
|
-
expect(result).toContain('<a href="https://example.com">');
|
|
179
|
-
expect(result).toContain('Example Link');
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('markdownToNativeStorage', () => {
|
|
184
|
-
test('should act as an alias to htmlToConfluenceStorage via markdown render', () => {
|
|
185
|
-
const markdown = '# Native Storage Test';
|
|
186
|
-
const result = client.markdownToNativeStorage(markdown);
|
|
187
|
-
|
|
188
|
-
expect(result).toContain('<h1>Native Storage Test</h1>');
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
test('should handle code blocks correctly', () => {
|
|
192
|
-
const markdown = '```javascript\nconst a = 1;\n```';
|
|
193
|
-
const result = client.markdownToNativeStorage(markdown);
|
|
194
|
-
|
|
195
|
-
expect(result).toContain('<ac:structured-macro ac:name="code">');
|
|
196
|
-
expect(result).toContain('const a = 1;');
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
describe('storageToMarkdown', () => {
|
|
201
|
-
test('should convert Confluence storage format to markdown', () => {
|
|
202
|
-
const storage = '<h1>Hello World</h1><p>This is a <strong>test</strong> page.</p>';
|
|
203
|
-
const result = client.storageToMarkdown(storage);
|
|
204
|
-
|
|
205
|
-
expect(result).toContain('# Hello World');
|
|
206
|
-
expect(result).toContain('**test**');
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
test('should convert Confluence code macro to markdown', () => {
|
|
210
|
-
const storage = '<ac:structured-macro ac:name="code"><ac:parameter ac:name="language">javascript</ac:parameter><ac:plain-text-body><![CDATA[console.log("Hello");]]></ac:plain-text-body></ac:structured-macro>';
|
|
211
|
-
const result = client.storageToMarkdown(storage);
|
|
212
|
-
|
|
213
|
-
expect(result).toContain('```javascript');
|
|
214
|
-
expect(result).toContain('console.log("Hello");');
|
|
215
|
-
expect(result).toContain('```');
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
test('should convert Confluence macros to admonitions', () => {
|
|
219
|
-
const storage = '<ac:structured-macro ac:name="info"><ac:rich-text-body><p>This is info</p></ac:rich-text-body></ac:structured-macro>';
|
|
220
|
-
const result = client.storageToMarkdown(storage);
|
|
221
|
-
|
|
222
|
-
expect(result).toContain('[!info]');
|
|
223
|
-
expect(result).toContain('This is info');
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
test('should convert Confluence links to markdown', () => {
|
|
227
|
-
const storage = '<ac:link><ri:url ri:value="https://example.com" /><ac:plain-text-link-body><![CDATA[Example]]></ac:plain-text-link-body></ac:link>';
|
|
228
|
-
const result = client.storageToMarkdown(storage);
|
|
229
|
-
|
|
230
|
-
expect(result).toContain('[Example](https://example.com)');
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
describe('htmlToMarkdown', () => {
|
|
235
|
-
test('should convert basic HTML to markdown', () => {
|
|
236
|
-
const html = '<h2>Title</h2><p>Some <strong>bold</strong> and <em>italic</em> text.</p>';
|
|
237
|
-
const result = client.htmlToMarkdown(html);
|
|
238
|
-
|
|
239
|
-
expect(result).toContain('## Title');
|
|
240
|
-
expect(result).toContain('**bold**');
|
|
241
|
-
expect(result).toContain('*italic*');
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
test('should convert HTML lists to markdown', () => {
|
|
245
|
-
const html = '<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>';
|
|
246
|
-
const result = client.htmlToMarkdown(html);
|
|
247
|
-
|
|
248
|
-
expect(result).toContain('- Item 1');
|
|
249
|
-
expect(result).toContain('- Item 2');
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
test('should convert HTML tables to markdown', () => {
|
|
253
|
-
const html = '<table><tr><th><p>Header</p></th></tr><tr><td><p>Cell</p></td></tr></table>';
|
|
254
|
-
const result = client.htmlToMarkdown(html);
|
|
255
|
-
|
|
256
|
-
expect(result).toContain('| Header |');
|
|
257
|
-
expect(result).toContain('| --- |');
|
|
258
|
-
expect(result).toContain('| Cell |');
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
describe('page creation and updates', () => {
|
|
263
|
-
test('should have required methods for page management', () => {
|
|
264
|
-
expect(typeof client.createPage).toBe('function');
|
|
265
|
-
expect(typeof client.updatePage).toBe('function');
|
|
266
|
-
expect(typeof client.getPageForEdit).toBe('function');
|
|
267
|
-
expect(typeof client.createChildPage).toBe('function');
|
|
268
|
-
expect(typeof client.findPageByTitle).toBe('function');
|
|
269
|
-
expect(typeof client.deletePage).toBe('function');
|
|
270
|
-
});
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
describe('deletePage', () => {
|
|
274
|
-
test('should delete a page by ID', async () => {
|
|
275
|
-
const mock = new MockAdapter(client.client);
|
|
276
|
-
mock.onDelete('/content/123456789').reply(204);
|
|
277
|
-
|
|
278
|
-
await expect(client.deletePage('123456789')).resolves.toEqual({ id: '123456789' });
|
|
279
|
-
|
|
280
|
-
mock.restore();
|
|
281
|
-
});
|
|
282
|
-
|
|
283
|
-
test('should delete a page by URL', async () => {
|
|
284
|
-
const mock = new MockAdapter(client.client);
|
|
285
|
-
mock.onDelete('/content/987654321').reply(204);
|
|
286
|
-
|
|
287
|
-
await expect(
|
|
288
|
-
client.deletePage('https://test.atlassian.net/wiki/viewpage.action?pageId=987654321')
|
|
289
|
-
).resolves.toEqual({ id: '987654321' });
|
|
290
|
-
|
|
291
|
-
mock.restore();
|
|
292
|
-
});
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
describe('page tree operations', () => {
|
|
296
|
-
test('should have required methods for tree operations', () => {
|
|
297
|
-
expect(typeof client.getChildPages).toBe('function');
|
|
298
|
-
expect(typeof client.getAllDescendantPages).toBe('function');
|
|
299
|
-
expect(typeof client.copyPageTree).toBe('function');
|
|
300
|
-
expect(typeof client.buildPageTree).toBe('function');
|
|
301
|
-
expect(typeof client.shouldExcludePage).toBe('function');
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
test('should correctly exclude pages based on patterns', () => {
|
|
305
|
-
const patterns = ['temp*', 'test*', '*draft*'];
|
|
306
|
-
|
|
307
|
-
expect(client.shouldExcludePage('temporary document', patterns)).toBe(true);
|
|
308
|
-
expect(client.shouldExcludePage('test page', patterns)).toBe(true);
|
|
309
|
-
expect(client.shouldExcludePage('my draft page', patterns)).toBe(true);
|
|
310
|
-
expect(client.shouldExcludePage('normal document', patterns)).toBe(false);
|
|
311
|
-
expect(client.shouldExcludePage('production page', patterns)).toBe(false);
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
test('should handle empty exclude patterns', () => {
|
|
315
|
-
expect(client.shouldExcludePage('any page', [])).toBe(false);
|
|
316
|
-
expect(client.shouldExcludePage('any page', null)).toBe(false);
|
|
317
|
-
expect(client.shouldExcludePage('any page', undefined)).toBe(false);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
test('globToRegExp should escape regex metacharacters and match case-insensitively', () => {
|
|
321
|
-
const patterns = [
|
|
322
|
-
'file.name*', // dot should be literal
|
|
323
|
-
'[draft]?', // brackets should be literal
|
|
324
|
-
'Plan (Q1)?', // parentheses literal, ? wildcard
|
|
325
|
-
'DATA*SET', // case-insensitive
|
|
326
|
-
];
|
|
327
|
-
const rx = patterns.map(p => client.globToRegExp(p));
|
|
328
|
-
expect('file.name.v1').toMatch(rx[0]);
|
|
329
|
-
expect('filexname').not.toMatch(rx[0]);
|
|
330
|
-
expect('[draft]1').toMatch(rx[1]);
|
|
331
|
-
expect('[draft]AB').not.toMatch(rx[1]);
|
|
332
|
-
expect('Plan (Q1)A').toMatch(rx[2]);
|
|
333
|
-
expect('Plan Q1A').not.toMatch(rx[2]);
|
|
334
|
-
expect('data big set').toMatch(rx[3]);
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
test('buildPageTree should link children by parentId and collect orphans at root', () => {
|
|
338
|
-
const rootId = 'root';
|
|
339
|
-
const pages = [
|
|
340
|
-
{ id: 'a', title: 'A', parentId: rootId },
|
|
341
|
-
{ id: 'b', title: 'B', parentId: 'a' },
|
|
342
|
-
{ id: 'c', title: 'C', parentId: 'missing' }, // orphan
|
|
343
|
-
];
|
|
344
|
-
const tree = client.buildPageTree(pages, rootId);
|
|
345
|
-
// tree should contain A and C at top-level (B is child of A)
|
|
346
|
-
const topTitles = tree.map(n => n.title).sort();
|
|
347
|
-
expect(topTitles).toEqual(['A', 'C']);
|
|
348
|
-
const a = tree.find(n => n.title === 'A');
|
|
349
|
-
expect(a.children.map(n => n.title)).toEqual(['B']);
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
test('exclude parser should tolerate spaces and empty items', () => {
|
|
353
|
-
const raw = ' temp* , , *draft* ,,test? ';
|
|
354
|
-
const patterns = raw.split(',').map(p => p.trim()).filter(Boolean);
|
|
355
|
-
expect(patterns).toEqual(['temp*', '*draft*', 'test?']);
|
|
356
|
-
expect(client.shouldExcludePage('temp file', patterns)).toBe(true);
|
|
357
|
-
expect(client.shouldExcludePage('my draft page', patterns)).toBe(true);
|
|
358
|
-
expect(client.shouldExcludePage('test1', patterns)).toBe(true);
|
|
359
|
-
expect(client.shouldExcludePage('production', patterns)).toBe(false);
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
describe('comments', () => {
|
|
364
|
-
test('should list comments with location filter', async () => {
|
|
365
|
-
const mock = new MockAdapter(client.client);
|
|
366
|
-
mock.onGet('/content/123/child/comment').reply(config => {
|
|
367
|
-
expect(config.params.location).toBe('inline');
|
|
368
|
-
expect(config.params.expand).toContain('body.storage');
|
|
369
|
-
expect(config.params.expand).toContain('ancestors');
|
|
370
|
-
return [200, {
|
|
371
|
-
results: [
|
|
372
|
-
{
|
|
373
|
-
id: 'c1',
|
|
374
|
-
status: 'current',
|
|
375
|
-
body: { storage: { value: '<p>Hello</p>' } },
|
|
376
|
-
history: { createdBy: { displayName: 'Ada' }, createdDate: '2025-01-01' },
|
|
377
|
-
version: { number: 1 },
|
|
378
|
-
ancestors: [{ id: 'c0', type: 'comment' }],
|
|
379
|
-
extensions: {
|
|
380
|
-
location: 'inline',
|
|
381
|
-
inlineProperties: { selection: 'Hello', originalSelection: 'Hello' },
|
|
382
|
-
resolution: { status: 'open' }
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
],
|
|
386
|
-
_links: { next: '/rest/api/content/123/child/comment?start=2' }
|
|
387
|
-
}];
|
|
388
|
-
});
|
|
389
|
-
|
|
390
|
-
const page = await client.listComments('123', { location: 'inline' });
|
|
391
|
-
expect(page.results).toHaveLength(1);
|
|
392
|
-
expect(page.results[0].location).toBe('inline');
|
|
393
|
-
expect(page.results[0].resolution).toBe('open');
|
|
394
|
-
expect(page.results[0].parentId).toBe('c0');
|
|
395
|
-
expect(page.nextStart).toBe(2);
|
|
396
|
-
|
|
397
|
-
mock.restore();
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
test('should create inline comment with inline properties', async () => {
|
|
401
|
-
const mock = new MockAdapter(client.client);
|
|
402
|
-
mock.onPost('/content').reply(config => {
|
|
403
|
-
const payload = JSON.parse(config.data);
|
|
404
|
-
expect(payload.type).toBe('comment');
|
|
405
|
-
expect(payload.container.id).toBe('123');
|
|
406
|
-
expect(payload.body.storage.value).toBe('<p>Hi</p>');
|
|
407
|
-
expect(payload.ancestors[0].id).toBe('c0');
|
|
408
|
-
expect(payload.extensions.location).toBe('inline');
|
|
409
|
-
expect(payload.extensions.inlineProperties.originalSelection).toBe('Hi');
|
|
410
|
-
expect(payload.extensions.inlineProperties.markerRef).toBe('comment-1');
|
|
411
|
-
return [200, { id: 'c1', type: 'comment' }];
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
await client.createComment('123', '<p>Hi</p>', 'storage', {
|
|
415
|
-
parentId: 'c0',
|
|
416
|
-
location: 'inline',
|
|
417
|
-
inlineProperties: {
|
|
418
|
-
selection: 'Hi',
|
|
419
|
-
originalSelection: 'Hi',
|
|
420
|
-
markerRef: 'comment-1'
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
mock.restore();
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
test('should delete a comment by ID', async () => {
|
|
428
|
-
const mock = new MockAdapter(client.client);
|
|
429
|
-
mock.onDelete('/content/456').reply(204);
|
|
430
|
-
|
|
431
|
-
await expect(client.deleteComment('456')).resolves.toEqual({ id: '456' });
|
|
432
|
-
|
|
433
|
-
mock.restore();
|
|
434
|
-
});
|
|
435
|
-
});
|
|
436
|
-
|
|
437
|
-
describe('attachments', () => {
|
|
438
|
-
test('should have required methods for attachment handling', () => {
|
|
439
|
-
expect(typeof client.listAttachments).toBe('function');
|
|
440
|
-
expect(typeof client.getAllAttachments).toBe('function');
|
|
441
|
-
expect(typeof client.downloadAttachment).toBe('function');
|
|
442
|
-
});
|
|
443
|
-
|
|
444
|
-
test('matchesPattern should respect glob patterns', () => {
|
|
445
|
-
expect(client.matchesPattern('report.png', '*.png')).toBe(true);
|
|
446
|
-
expect(client.matchesPattern('report.png', '*.jpg')).toBe(false);
|
|
447
|
-
expect(client.matchesPattern('report.png', ['*.jpg', 'report.*'])).toBe(true);
|
|
448
|
-
expect(client.matchesPattern('report.png', null)).toBe(true);
|
|
449
|
-
expect(client.matchesPattern('report.png', [])).toBe(true);
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
test('parseNextStart should read start query param when present', () => {
|
|
453
|
-
expect(client.parseNextStart('/rest/api/content/1/child/attachment?start=25')).toBe(25);
|
|
454
|
-
expect(client.parseNextStart('/rest/api/content/1/child/attachment?limit=50')).toBeNull();
|
|
455
|
-
expect(client.parseNextStart(null)).toBeNull();
|
|
456
|
-
});
|
|
457
|
-
});
|
|
458
|
-
});
|