datagrok-tools 6.5.4 → 6.5.6
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 +17 -1
- package/bin/commands/add.js +116 -1
- package/bin/commands/api.js +381 -2
- package/bin/commands/help.js +19 -2
- package/bin/commands/publish.js +22 -0
- package/bin/commands/report.js +68 -19
- package/bin/utils/ent-helpers.js +13 -0
- package/bin/utils/utils.js +6 -0
- package/domain-schema.schema.json +253 -0
- package/entity-template/domain-app.js +11 -0
- package/entity-template/domain-app.ts +11 -0
- package/package.json +2 -1
- package/bin/__tests__/build.test.js +0 -116
- package/bin/__tests__/build.test.ts +0 -101
- package/bin/__tests__/jira-wiki-converter.test.js +0 -101
- package/bin/__tests__/jira-wiki-converter.test.ts +0 -150
- package/bin/__tests__/node-dapi.connections.test.js +0 -120
- package/bin/__tests__/node-dapi.connections.test.ts +0 -84
- package/bin/__tests__/node-dapi.groups.test.js +0 -467
- package/bin/__tests__/node-dapi.groups.test.ts +0 -298
- package/bin/__tests__/node-dapi.integration.test.ts +0 -447
- package/bin/__tests__/node-dapi.shares.test.ts +0 -70
- package/bin/__tests__/node-dapi.users.test.ts +0 -58
- package/bin/__tests__/server-output.test.js +0 -171
- package/bin/__tests__/server-output.test.ts +0 -133
- package/bin/__tests__/server.test.js +0 -277
- package/bin/__tests__/server.test.ts +0 -197
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _vitest = require("vitest");
|
|
4
|
-
var _build = require("../commands/build");
|
|
5
|
-
(0, _vitest.describe)('getNestedValue', () => {
|
|
6
|
-
(0, _vitest.it)('returns value for a simple key', () => {
|
|
7
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
8
|
-
name: 'Chem'
|
|
9
|
-
}, 'name')).toBe('Chem');
|
|
10
|
-
});
|
|
11
|
-
(0, _vitest.it)('returns value for a nested path', () => {
|
|
12
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
13
|
-
a: {
|
|
14
|
-
b: {
|
|
15
|
-
c: 42
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
}, 'a.b.c')).toBe(42);
|
|
19
|
-
});
|
|
20
|
-
(0, _vitest.it)('returns undefined for a missing key', () => {
|
|
21
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
22
|
-
name: 'Chem'
|
|
23
|
-
}, 'version')).toBeUndefined();
|
|
24
|
-
});
|
|
25
|
-
(0, _vitest.it)('returns undefined when a mid-path segment is null', () => {
|
|
26
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
27
|
-
a: null
|
|
28
|
-
}, 'a.b')).toBeUndefined();
|
|
29
|
-
});
|
|
30
|
-
(0, _vitest.it)('returns undefined when a mid-path segment is missing', () => {
|
|
31
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
32
|
-
a: {}
|
|
33
|
-
}, 'a.b.c')).toBeUndefined();
|
|
34
|
-
});
|
|
35
|
-
(0, _vitest.it)('returns undefined for an empty path (splits to empty string key)', () => {
|
|
36
|
-
(0, _vitest.expect)((0, _build.getNestedValue)({
|
|
37
|
-
x: 1
|
|
38
|
-
}, '')).toBeUndefined();
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
const pkg = overrides => ({
|
|
42
|
-
dir: '/tmp/pkg',
|
|
43
|
-
name: overrides.name ?? 'test-pkg',
|
|
44
|
-
friendlyName: overrides.friendlyName ?? overrides.name ?? 'Test Pkg',
|
|
45
|
-
version: overrides.version ?? '1.0.0',
|
|
46
|
-
packageJson: overrides
|
|
47
|
-
});
|
|
48
|
-
(0, _vitest.describe)('applyFilter', () => {
|
|
49
|
-
const packages = [pkg({
|
|
50
|
-
name: 'Chem',
|
|
51
|
-
version: '1.5.0',
|
|
52
|
-
category: 'Cheminformatics'
|
|
53
|
-
}), pkg({
|
|
54
|
-
name: 'Bio',
|
|
55
|
-
version: '2.0.0',
|
|
56
|
-
category: 'Bioinformatics'
|
|
57
|
-
}), pkg({
|
|
58
|
-
name: 'PowerGrid',
|
|
59
|
-
version: '1.5.0',
|
|
60
|
-
category: 'Viewers'
|
|
61
|
-
})];
|
|
62
|
-
(0, _vitest.it)('returns all packages when filter matches all', () => {
|
|
63
|
-
(0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:.')).toHaveLength(3);
|
|
64
|
-
});
|
|
65
|
-
(0, _vitest.it)('filters by exact name match', () => {
|
|
66
|
-
const result = (0, _build.applyFilter)(packages, 'name:^Chem$');
|
|
67
|
-
(0, _vitest.expect)(result).toHaveLength(1);
|
|
68
|
-
(0, _vitest.expect)(result[0].name).toBe('Chem');
|
|
69
|
-
});
|
|
70
|
-
(0, _vitest.it)('filters by partial name (regex substring)', () => {
|
|
71
|
-
const result = (0, _build.applyFilter)(packages, 'name:Bio');
|
|
72
|
-
(0, _vitest.expect)(result).toHaveLength(1);
|
|
73
|
-
(0, _vitest.expect)(result[0].name).toBe('Bio');
|
|
74
|
-
});
|
|
75
|
-
(0, _vitest.it)('returns empty array when nothing matches', () => {
|
|
76
|
-
(0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:NOMATCH')).toHaveLength(0);
|
|
77
|
-
});
|
|
78
|
-
(0, _vitest.it)('filters by version', () => {
|
|
79
|
-
const result = (0, _build.applyFilter)(packages, 'version:^1\\.5');
|
|
80
|
-
(0, _vitest.expect)(result).toHaveLength(2);
|
|
81
|
-
(0, _vitest.expect)(result.map(p => p.name)).toEqual(_vitest.expect.arrayContaining(['Chem', 'PowerGrid']));
|
|
82
|
-
});
|
|
83
|
-
(0, _vitest.it)('applies && conjunction (both conditions must match)', () => {
|
|
84
|
-
const result = (0, _build.applyFilter)(packages, 'name:Chem && version:1\\.5');
|
|
85
|
-
(0, _vitest.expect)(result).toHaveLength(1);
|
|
86
|
-
(0, _vitest.expect)(result[0].name).toBe('Chem');
|
|
87
|
-
});
|
|
88
|
-
(0, _vitest.it)('returns empty when one part of && conjunction fails', () => {
|
|
89
|
-
(0, _vitest.expect)((0, _build.applyFilter)(packages, 'name:Chem && version:^2')).toHaveLength(0);
|
|
90
|
-
});
|
|
91
|
-
(0, _vitest.it)('filters by nested field', () => {
|
|
92
|
-
const withNested = [pkg({
|
|
93
|
-
name: 'A',
|
|
94
|
-
datagrok: {
|
|
95
|
-
apiVersion: '1.0'
|
|
96
|
-
}
|
|
97
|
-
}), pkg({
|
|
98
|
-
name: 'B',
|
|
99
|
-
datagrok: {
|
|
100
|
-
apiVersion: '2.0'
|
|
101
|
-
}
|
|
102
|
-
})];
|
|
103
|
-
const result = (0, _build.applyFilter)(withNested, 'datagrok.apiVersion:^1');
|
|
104
|
-
(0, _vitest.expect)(result).toHaveLength(1);
|
|
105
|
-
(0, _vitest.expect)(result[0].name).toBe('A');
|
|
106
|
-
});
|
|
107
|
-
(0, _vitest.it)('returns empty when field does not exist', () => {
|
|
108
|
-
(0, _vitest.expect)((0, _build.applyFilter)(packages, 'nonexistent:anything')).toHaveLength(0);
|
|
109
|
-
});
|
|
110
|
-
(0, _vitest.it)('treats filter with no colon as field name with match-all pattern', () => {
|
|
111
|
-
// No colon → field = whole string, pattern = /./ (matches any value)
|
|
112
|
-
// The function returns packages where the field exists and is non-empty
|
|
113
|
-
const result = (0, _build.applyFilter)(packages, 'name');
|
|
114
|
-
(0, _vitest.expect)(result).toHaveLength(3);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {getNestedValue, applyFilter} from '../commands/build';
|
|
3
|
-
|
|
4
|
-
describe('getNestedValue', () => {
|
|
5
|
-
it('returns value for a simple key', () => {
|
|
6
|
-
expect(getNestedValue({name: 'Chem'}, 'name')).toBe('Chem');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('returns value for a nested path', () => {
|
|
10
|
-
expect(getNestedValue({a: {b: {c: 42}}}, 'a.b.c')).toBe(42);
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
it('returns undefined for a missing key', () => {
|
|
14
|
-
expect(getNestedValue({name: 'Chem'}, 'version')).toBeUndefined();
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('returns undefined when a mid-path segment is null', () => {
|
|
18
|
-
expect(getNestedValue({a: null}, 'a.b')).toBeUndefined();
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('returns undefined when a mid-path segment is missing', () => {
|
|
22
|
-
expect(getNestedValue({a: {}}, 'a.b.c')).toBeUndefined();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('returns undefined for an empty path (splits to empty string key)', () => {
|
|
26
|
-
expect(getNestedValue({x: 1}, '')).toBeUndefined();
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
const pkg = (overrides: Record<string, any>) => ({
|
|
31
|
-
dir: '/tmp/pkg',
|
|
32
|
-
name: overrides.name ?? 'test-pkg',
|
|
33
|
-
friendlyName: overrides.friendlyName ?? overrides.name ?? 'Test Pkg',
|
|
34
|
-
version: overrides.version ?? '1.0.0',
|
|
35
|
-
packageJson: overrides,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe('applyFilter', () => {
|
|
39
|
-
const packages = [
|
|
40
|
-
pkg({name: 'Chem', version: '1.5.0', category: 'Cheminformatics'}),
|
|
41
|
-
pkg({name: 'Bio', version: '2.0.0', category: 'Bioinformatics'}),
|
|
42
|
-
pkg({name: 'PowerGrid', version: '1.5.0', category: 'Viewers'}),
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
it('returns all packages when filter matches all', () => {
|
|
46
|
-
expect(applyFilter(packages, 'name:.')).toHaveLength(3);
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('filters by exact name match', () => {
|
|
50
|
-
const result = applyFilter(packages, 'name:^Chem$');
|
|
51
|
-
expect(result).toHaveLength(1);
|
|
52
|
-
expect(result[0].name).toBe('Chem');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('filters by partial name (regex substring)', () => {
|
|
56
|
-
const result = applyFilter(packages, 'name:Bio');
|
|
57
|
-
expect(result).toHaveLength(1);
|
|
58
|
-
expect(result[0].name).toBe('Bio');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('returns empty array when nothing matches', () => {
|
|
62
|
-
expect(applyFilter(packages, 'name:NOMATCH')).toHaveLength(0);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('filters by version', () => {
|
|
66
|
-
const result = applyFilter(packages, 'version:^1\\.5');
|
|
67
|
-
expect(result).toHaveLength(2);
|
|
68
|
-
expect(result.map((p) => p.name)).toEqual(expect.arrayContaining(['Chem', 'PowerGrid']));
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
it('applies && conjunction (both conditions must match)', () => {
|
|
72
|
-
const result = applyFilter(packages, 'name:Chem && version:1\\.5');
|
|
73
|
-
expect(result).toHaveLength(1);
|
|
74
|
-
expect(result[0].name).toBe('Chem');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('returns empty when one part of && conjunction fails', () => {
|
|
78
|
-
expect(applyFilter(packages, 'name:Chem && version:^2')).toHaveLength(0);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('filters by nested field', () => {
|
|
82
|
-
const withNested = [
|
|
83
|
-
pkg({name: 'A', datagrok: {apiVersion: '1.0'}}),
|
|
84
|
-
pkg({name: 'B', datagrok: {apiVersion: '2.0'}}),
|
|
85
|
-
];
|
|
86
|
-
const result = applyFilter(withNested, 'datagrok.apiVersion:^1');
|
|
87
|
-
expect(result).toHaveLength(1);
|
|
88
|
-
expect(result[0].name).toBe('A');
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
it('returns empty when field does not exist', () => {
|
|
92
|
-
expect(applyFilter(packages, 'nonexistent:anything')).toHaveLength(0);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('treats filter with no colon as field name with match-all pattern', () => {
|
|
96
|
-
// No colon → field = whole string, pattern = /./ (matches any value)
|
|
97
|
-
// The function returns packages where the field exists and is non-empty
|
|
98
|
-
const result = applyFilter(packages, 'name');
|
|
99
|
-
expect(result).toHaveLength(3);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _vitest = require("vitest");
|
|
4
|
-
var _report = require("../commands/report");
|
|
5
|
-
(0, _vitest.describe)('markdownToJiraWiki — basic rules', () => {
|
|
6
|
-
(0, _vitest.it)('converts H1', () => {
|
|
7
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('# Title')).toBe('h1. Title');
|
|
8
|
-
});
|
|
9
|
-
(0, _vitest.it)('converts H2 / H3 / H6', () => {
|
|
10
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('## Sub')).toBe('h2. Sub');
|
|
11
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('### Sub-sub')).toBe('h3. Sub-sub');
|
|
12
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('###### Deep')).toBe('h6. Deep');
|
|
13
|
-
});
|
|
14
|
-
(0, _vitest.it)('converts bold', () => {
|
|
15
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('hello **world** foo')).toBe('hello *world* foo');
|
|
16
|
-
});
|
|
17
|
-
(0, _vitest.it)('converts italic with single asterisks', () => {
|
|
18
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('hello *world* foo')).toBe('hello _world_ foo');
|
|
19
|
-
});
|
|
20
|
-
(0, _vitest.it)('converts strikethrough', () => {
|
|
21
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('~~gone~~')).toBe('-gone-');
|
|
22
|
-
});
|
|
23
|
-
(0, _vitest.it)('converts links', () => {
|
|
24
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('see [docs](https://x.example/y)')).toBe('see [docs|https://x.example/y]');
|
|
25
|
-
});
|
|
26
|
-
(0, _vitest.it)('converts blockquote', () => {
|
|
27
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('> quoted line')).toBe('bq. quoted line');
|
|
28
|
-
});
|
|
29
|
-
(0, _vitest.it)('converts unordered list', () => {
|
|
30
|
-
const md = '- one\n- two\n- three';
|
|
31
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('* one\n* two\n* three');
|
|
32
|
-
});
|
|
33
|
-
(0, _vitest.it)('converts one level of nested unordered list', () => {
|
|
34
|
-
const md = '- top\n - sub\n- back';
|
|
35
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('* top\n** sub\n* back');
|
|
36
|
-
});
|
|
37
|
-
(0, _vitest.it)('converts ordered list', () => {
|
|
38
|
-
const md = '1. one\n2. two\n3. three';
|
|
39
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('# one\n# two\n# three');
|
|
40
|
-
});
|
|
41
|
-
(0, _vitest.it)('converts inline code', () => {
|
|
42
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('use `foo()` here')).toBe('use {{foo()}} here');
|
|
43
|
-
});
|
|
44
|
-
(0, _vitest.it)('converts plain code fence to noformat', () => {
|
|
45
|
-
const md = '```\nraw code\nmore\n```';
|
|
46
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('{noformat}\nraw code\nmore\n{noformat}');
|
|
47
|
-
});
|
|
48
|
-
(0, _vitest.it)('converts code fence with language tag to {code:lang}', () => {
|
|
49
|
-
const md = '```js\nconst x = 1;\n```';
|
|
50
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('{code:js}\nconst x = 1;\n{code}');
|
|
51
|
-
});
|
|
52
|
-
(0, _vitest.it)('converts HTML entities / & / < / >', () => {
|
|
53
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('a b')).toBe('a b');
|
|
54
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('a&b')).toBe('a&b');
|
|
55
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('a<b>c')).toBe('a<b>c');
|
|
56
|
-
});
|
|
57
|
-
});
|
|
58
|
-
(0, _vitest.describe)('markdownToJiraWiki — content-protection inside code fences', () => {
|
|
59
|
-
(0, _vitest.it)('does not transform `{{plates}}` inside a fenced block (run-#4 false-positive case)', () => {
|
|
60
|
-
const md = ['Before', '```', 'context: {{plates}} should stay literal', '# not a heading', '- not a list', '```', 'After'].join('\n');
|
|
61
|
-
const out = (0, _report.markdownToJiraWiki)(md);
|
|
62
|
-
(0, _vitest.expect)(out).toContain('{noformat}\ncontext: {{plates}} should stay literal');
|
|
63
|
-
(0, _vitest.expect)(out).toContain('# not a heading');
|
|
64
|
-
(0, _vitest.expect)(out).toContain('- not a list');
|
|
65
|
-
(0, _vitest.expect)(out).not.toContain('h1.');
|
|
66
|
-
});
|
|
67
|
-
(0, _vitest.it)('does not transform headings/links/lists inside an inline code span', () => {
|
|
68
|
-
const md = 'use `# not a heading` and `[ref](u)` here';
|
|
69
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(md)).toBe('use {{# not a heading}} and {{[ref](u)}} here');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
(0, _vitest.describe)('markdownToJiraWiki — edge cases', () => {
|
|
73
|
-
(0, _vitest.it)('handles bold containing italic: **bold *inside* bold**', () => {
|
|
74
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('**bold *inside* bold**')).toBe('*bold _inside_ bold*');
|
|
75
|
-
});
|
|
76
|
-
(0, _vitest.it)('preserves bold and italic on the same line', () => {
|
|
77
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('**a** and *b*')).toBe('*a* and _b_');
|
|
78
|
-
});
|
|
79
|
-
(0, _vitest.it)('handles a heading whose text contains backticks', () => {
|
|
80
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('## use `foo()` for x')).toBe('h2. use {{foo()}} for x');
|
|
81
|
-
});
|
|
82
|
-
(0, _vitest.it)('passes plain text through unchanged', () => {
|
|
83
|
-
const plain = 'Just a normal sentence with no markdown.';
|
|
84
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)(plain)).toBe(plain);
|
|
85
|
-
});
|
|
86
|
-
(0, _vitest.it)('returns empty string unchanged', () => {
|
|
87
|
-
(0, _vitest.expect)((0, _report.markdownToJiraWiki)('')).toBe('');
|
|
88
|
-
});
|
|
89
|
-
(0, _vitest.it)('handles a multi-block document end-to-end', () => {
|
|
90
|
-
const md = ['# Handoff', '', 'Some **bold** intro and a [link](https://x.example).', '', '## Findings', '', '- first', '- second', '', '> note: this matters', '', '```js', 'const x = 1;', '```', '', 'Done here.'].join('\n');
|
|
91
|
-
const out = (0, _report.markdownToJiraWiki)(md);
|
|
92
|
-
(0, _vitest.expect)(out).toContain('h1. Handoff');
|
|
93
|
-
(0, _vitest.expect)(out).toContain('h2. Findings');
|
|
94
|
-
(0, _vitest.expect)(out).toContain('Some *bold* intro and a [link|https://x.example].');
|
|
95
|
-
(0, _vitest.expect)(out).toContain('* first\n* second');
|
|
96
|
-
(0, _vitest.expect)(out).toContain('bq. note: this matters');
|
|
97
|
-
(0, _vitest.expect)(out).toContain('{code:js}\nconst x = 1;\n{code}');
|
|
98
|
-
(0, _vitest.expect)(out).toContain('Done here.');
|
|
99
|
-
(0, _vitest.expect)(out).not.toContain(' ');
|
|
100
|
-
});
|
|
101
|
-
});
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {markdownToJiraWiki} from '../commands/report';
|
|
3
|
-
|
|
4
|
-
describe('markdownToJiraWiki — basic rules', () => {
|
|
5
|
-
it('converts H1', () => {
|
|
6
|
-
expect(markdownToJiraWiki('# Title')).toBe('h1. Title');
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it('converts H2 / H3 / H6', () => {
|
|
10
|
-
expect(markdownToJiraWiki('## Sub')).toBe('h2. Sub');
|
|
11
|
-
expect(markdownToJiraWiki('### Sub-sub')).toBe('h3. Sub-sub');
|
|
12
|
-
expect(markdownToJiraWiki('###### Deep')).toBe('h6. Deep');
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
it('converts bold', () => {
|
|
16
|
-
expect(markdownToJiraWiki('hello **world** foo')).toBe('hello *world* foo');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('converts italic with single asterisks', () => {
|
|
20
|
-
expect(markdownToJiraWiki('hello *world* foo')).toBe('hello _world_ foo');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it('converts strikethrough', () => {
|
|
24
|
-
expect(markdownToJiraWiki('~~gone~~')).toBe('-gone-');
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
it('converts links', () => {
|
|
28
|
-
expect(markdownToJiraWiki('see [docs](https://x.example/y)'))
|
|
29
|
-
.toBe('see [docs|https://x.example/y]');
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('converts blockquote', () => {
|
|
33
|
-
expect(markdownToJiraWiki('> quoted line')).toBe('bq. quoted line');
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('converts unordered list', () => {
|
|
37
|
-
const md = '- one\n- two\n- three';
|
|
38
|
-
expect(markdownToJiraWiki(md)).toBe('* one\n* two\n* three');
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it('converts one level of nested unordered list', () => {
|
|
42
|
-
const md = '- top\n - sub\n- back';
|
|
43
|
-
expect(markdownToJiraWiki(md)).toBe('* top\n** sub\n* back');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('converts ordered list', () => {
|
|
47
|
-
const md = '1. one\n2. two\n3. three';
|
|
48
|
-
expect(markdownToJiraWiki(md)).toBe('# one\n# two\n# three');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('converts inline code', () => {
|
|
52
|
-
expect(markdownToJiraWiki('use `foo()` here')).toBe('use {{foo()}} here');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('converts plain code fence to noformat', () => {
|
|
56
|
-
const md = '```\nraw code\nmore\n```';
|
|
57
|
-
expect(markdownToJiraWiki(md)).toBe('{noformat}\nraw code\nmore\n{noformat}');
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it('converts code fence with language tag to {code:lang}', () => {
|
|
61
|
-
const md = '```js\nconst x = 1;\n```';
|
|
62
|
-
expect(markdownToJiraWiki(md)).toBe('{code:js}\nconst x = 1;\n{code}');
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
it('converts HTML entities / & / < / >', () => {
|
|
66
|
-
expect(markdownToJiraWiki('a b')).toBe('a b');
|
|
67
|
-
expect(markdownToJiraWiki('a&b')).toBe('a&b');
|
|
68
|
-
expect(markdownToJiraWiki('a<b>c')).toBe('a<b>c');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe('markdownToJiraWiki — content-protection inside code fences', () => {
|
|
73
|
-
it('does not transform `{{plates}}` inside a fenced block (run-#4 false-positive case)', () => {
|
|
74
|
-
const md = [
|
|
75
|
-
'Before',
|
|
76
|
-
'```',
|
|
77
|
-
'context: {{plates}} should stay literal',
|
|
78
|
-
'# not a heading',
|
|
79
|
-
'- not a list',
|
|
80
|
-
'```',
|
|
81
|
-
'After',
|
|
82
|
-
].join('\n');
|
|
83
|
-
const out = markdownToJiraWiki(md);
|
|
84
|
-
expect(out).toContain('{noformat}\ncontext: {{plates}} should stay literal');
|
|
85
|
-
expect(out).toContain('# not a heading');
|
|
86
|
-
expect(out).toContain('- not a list');
|
|
87
|
-
expect(out).not.toContain('h1.');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('does not transform headings/links/lists inside an inline code span', () => {
|
|
91
|
-
const md = 'use `# not a heading` and `[ref](u)` here';
|
|
92
|
-
expect(markdownToJiraWiki(md))
|
|
93
|
-
.toBe('use {{# not a heading}} and {{[ref](u)}} here');
|
|
94
|
-
});
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
describe('markdownToJiraWiki — edge cases', () => {
|
|
98
|
-
it('handles bold containing italic: **bold *inside* bold**', () => {
|
|
99
|
-
expect(markdownToJiraWiki('**bold *inside* bold**'))
|
|
100
|
-
.toBe('*bold _inside_ bold*');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('preserves bold and italic on the same line', () => {
|
|
104
|
-
expect(markdownToJiraWiki('**a** and *b*')).toBe('*a* and _b_');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('handles a heading whose text contains backticks', () => {
|
|
108
|
-
expect(markdownToJiraWiki('## use `foo()` for x'))
|
|
109
|
-
.toBe('h2. use {{foo()}} for x');
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('passes plain text through unchanged', () => {
|
|
113
|
-
const plain = 'Just a normal sentence with no markdown.';
|
|
114
|
-
expect(markdownToJiraWiki(plain)).toBe(plain);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
it('returns empty string unchanged', () => {
|
|
118
|
-
expect(markdownToJiraWiki('')).toBe('');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('handles a multi-block document end-to-end', () => {
|
|
122
|
-
const md = [
|
|
123
|
-
'# Handoff',
|
|
124
|
-
'',
|
|
125
|
-
'Some **bold** intro and a [link](https://x.example).',
|
|
126
|
-
'',
|
|
127
|
-
'## Findings',
|
|
128
|
-
'',
|
|
129
|
-
'- first',
|
|
130
|
-
'- second',
|
|
131
|
-
'',
|
|
132
|
-
'> note: this matters',
|
|
133
|
-
'',
|
|
134
|
-
'```js',
|
|
135
|
-
'const x = 1;',
|
|
136
|
-
'```',
|
|
137
|
-
'',
|
|
138
|
-
'Done here.',
|
|
139
|
-
].join('\n');
|
|
140
|
-
const out = markdownToJiraWiki(md);
|
|
141
|
-
expect(out).toContain('h1. Handoff');
|
|
142
|
-
expect(out).toContain('h2. Findings');
|
|
143
|
-
expect(out).toContain('Some *bold* intro and a [link|https://x.example].');
|
|
144
|
-
expect(out).toContain('* first\n* second');
|
|
145
|
-
expect(out).toContain('bq. note: this matters');
|
|
146
|
-
expect(out).toContain('{code:js}\nconst x = 1;\n{code}');
|
|
147
|
-
expect(out).toContain('Done here.');
|
|
148
|
-
expect(out).not.toContain(' ');
|
|
149
|
-
});
|
|
150
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _vitest = require("vitest");
|
|
4
|
-
var _nodeDapi = require("../utils/node-dapi");
|
|
5
|
-
function makeMock(responder) {
|
|
6
|
-
const calls = [];
|
|
7
|
-
const client = {
|
|
8
|
-
async request(method, path, body) {
|
|
9
|
-
calls.push({
|
|
10
|
-
method,
|
|
11
|
-
path,
|
|
12
|
-
body
|
|
13
|
-
});
|
|
14
|
-
return responder(method, path, body);
|
|
15
|
-
},
|
|
16
|
-
get(path) {
|
|
17
|
-
return this.request('GET', path);
|
|
18
|
-
},
|
|
19
|
-
post(path, body) {
|
|
20
|
-
return this.request('POST', path, body);
|
|
21
|
-
},
|
|
22
|
-
del(path) {
|
|
23
|
-
return this.request('DELETE', path);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
return {
|
|
27
|
-
client,
|
|
28
|
-
calls
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const CONN = {
|
|
32
|
-
name: 'TestPg',
|
|
33
|
-
dataSource: 'PostgreSQL',
|
|
34
|
-
server: 'localhost',
|
|
35
|
-
port: 5432,
|
|
36
|
-
db: 'datagrok'
|
|
37
|
-
};
|
|
38
|
-
(0, _vitest.describe)('NodeConnectionsDataSource.save', () => {
|
|
39
|
-
(0, _vitest.it)('POSTs to /public/v1/connections without saveCredentials by default', async () => {
|
|
40
|
-
const {
|
|
41
|
-
client,
|
|
42
|
-
calls
|
|
43
|
-
} = makeMock((method, path, body) => {
|
|
44
|
-
if (method === 'POST' && path === '/public/v1/connections') return {
|
|
45
|
-
...body,
|
|
46
|
-
id: 'new-id'
|
|
47
|
-
};
|
|
48
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
49
|
-
});
|
|
50
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
51
|
-
const saved = await ds.save(CONN);
|
|
52
|
-
(0, _vitest.expect)(saved.id).toBe('new-id');
|
|
53
|
-
(0, _vitest.expect)(calls[0].path).toBe('/public/v1/connections');
|
|
54
|
-
(0, _vitest.expect)(calls[0].body).toEqual(CONN);
|
|
55
|
-
});
|
|
56
|
-
(0, _vitest.it)('appends saveCredentials=true when requested', async () => {
|
|
57
|
-
const {
|
|
58
|
-
client,
|
|
59
|
-
calls
|
|
60
|
-
} = makeMock((method, path, body) => {
|
|
61
|
-
if (method === 'POST' && path === '/public/v1/connections?saveCredentials=true') return {
|
|
62
|
-
...body,
|
|
63
|
-
id: 'new-id'
|
|
64
|
-
};
|
|
65
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
66
|
-
});
|
|
67
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
68
|
-
await ds.save(CONN, true);
|
|
69
|
-
(0, _vitest.expect)(calls[0].path).toBe('/public/v1/connections?saveCredentials=true');
|
|
70
|
-
});
|
|
71
|
-
(0, _vitest.it)('omits saveCredentials query param when false', async () => {
|
|
72
|
-
const {
|
|
73
|
-
client,
|
|
74
|
-
calls
|
|
75
|
-
} = makeMock((method, path) => {
|
|
76
|
-
if (method === 'POST' && path === '/public/v1/connections') return {
|
|
77
|
-
id: 'new-id'
|
|
78
|
-
};
|
|
79
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
80
|
-
});
|
|
81
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
82
|
-
await ds.save(CONN, false);
|
|
83
|
-
(0, _vitest.expect)(calls[0].path).toBe('/public/v1/connections');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
(0, _vitest.describe)('NodeConnectionsDataSource.test', () => {
|
|
87
|
-
(0, _vitest.it)('POSTs the connection body to /public/v1/connections/test and resolves on "ok"', async () => {
|
|
88
|
-
const {
|
|
89
|
-
client,
|
|
90
|
-
calls
|
|
91
|
-
} = makeMock((method, path) => {
|
|
92
|
-
if (method === 'POST' && path === '/public/v1/connections/test') return 'ok';
|
|
93
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
94
|
-
});
|
|
95
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
96
|
-
await ds.test(CONN);
|
|
97
|
-
(0, _vitest.expect)(calls[0].body).toEqual(CONN);
|
|
98
|
-
});
|
|
99
|
-
(0, _vitest.it)('strips surrounding quotes on a JSON-encoded "ok"', async () => {
|
|
100
|
-
const {
|
|
101
|
-
client
|
|
102
|
-
} = makeMock(() => '"ok"');
|
|
103
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
104
|
-
await (0, _vitest.expect)(ds.test(CONN)).resolves.toBeUndefined();
|
|
105
|
-
});
|
|
106
|
-
(0, _vitest.it)('throws the server error text when the response is not ok', async () => {
|
|
107
|
-
const {
|
|
108
|
-
client
|
|
109
|
-
} = makeMock(() => 'connection refused: tcp://localhost:5432');
|
|
110
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
111
|
-
await (0, _vitest.expect)(ds.test(CONN)).rejects.toThrow(/connection refused/);
|
|
112
|
-
});
|
|
113
|
-
(0, _vitest.it)('throws a generic message on empty response', async () => {
|
|
114
|
-
const {
|
|
115
|
-
client
|
|
116
|
-
} = makeMock(() => '');
|
|
117
|
-
const ds = new _nodeDapi.NodeConnectionsDataSource(client);
|
|
118
|
-
await (0, _vitest.expect)(ds.test(CONN)).rejects.toThrow(/failed/i);
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import {describe, it, expect} from 'vitest';
|
|
2
|
-
import {NodeConnectionsDataSource} from '../utils/node-dapi';
|
|
3
|
-
|
|
4
|
-
interface Call {method: string; path: string; body?: any}
|
|
5
|
-
|
|
6
|
-
function makeMock(responder: (method: string, path: string, body?: any) => any) {
|
|
7
|
-
const calls: Call[] = [];
|
|
8
|
-
const client: any = {
|
|
9
|
-
async request(method: string, path: string, body?: any) {
|
|
10
|
-
calls.push({method, path, body});
|
|
11
|
-
return responder(method, path, body);
|
|
12
|
-
},
|
|
13
|
-
get(path: string) { return this.request('GET', path); },
|
|
14
|
-
post(path: string, body?: any) { return this.request('POST', path, body); },
|
|
15
|
-
del(path: string) { return this.request('DELETE', path); },
|
|
16
|
-
};
|
|
17
|
-
return {client, calls};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const CONN = {name: 'TestPg', dataSource: 'PostgreSQL', server: 'localhost', port: 5432, db: 'datagrok'};
|
|
21
|
-
|
|
22
|
-
describe('NodeConnectionsDataSource.save', () => {
|
|
23
|
-
it('POSTs to /public/v1/connections without saveCredentials by default', async () => {
|
|
24
|
-
const {client, calls} = makeMock((method, path, body) => {
|
|
25
|
-
if (method === 'POST' && path === '/public/v1/connections') return {...body, id: 'new-id'};
|
|
26
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
27
|
-
});
|
|
28
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
29
|
-
const saved = await ds.save(CONN);
|
|
30
|
-
expect(saved.id).toBe('new-id');
|
|
31
|
-
expect(calls[0].path).toBe('/public/v1/connections');
|
|
32
|
-
expect(calls[0].body).toEqual(CONN);
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it('appends saveCredentials=true when requested', async () => {
|
|
36
|
-
const {client, calls} = makeMock((method, path, body) => {
|
|
37
|
-
if (method === 'POST' && path === '/public/v1/connections?saveCredentials=true') return {...body, id: 'new-id'};
|
|
38
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
39
|
-
});
|
|
40
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
41
|
-
await ds.save(CONN, true);
|
|
42
|
-
expect(calls[0].path).toBe('/public/v1/connections?saveCredentials=true');
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
it('omits saveCredentials query param when false', async () => {
|
|
46
|
-
const {client, calls} = makeMock((method, path) => {
|
|
47
|
-
if (method === 'POST' && path === '/public/v1/connections') return {id: 'new-id'};
|
|
48
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
49
|
-
});
|
|
50
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
51
|
-
await ds.save(CONN, false);
|
|
52
|
-
expect(calls[0].path).toBe('/public/v1/connections');
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
describe('NodeConnectionsDataSource.test', () => {
|
|
57
|
-
it('POSTs the connection body to /public/v1/connections/test and resolves on "ok"', async () => {
|
|
58
|
-
const {client, calls} = makeMock((method, path) => {
|
|
59
|
-
if (method === 'POST' && path === '/public/v1/connections/test') return 'ok';
|
|
60
|
-
throw new Error(`unexpected ${method} ${path}`);
|
|
61
|
-
});
|
|
62
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
63
|
-
await ds.test(CONN);
|
|
64
|
-
expect(calls[0].body).toEqual(CONN);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('strips surrounding quotes on a JSON-encoded "ok"', async () => {
|
|
68
|
-
const {client} = makeMock(() => '"ok"');
|
|
69
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
70
|
-
await expect(ds.test(CONN)).resolves.toBeUndefined();
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('throws the server error text when the response is not ok', async () => {
|
|
74
|
-
const {client} = makeMock(() => 'connection refused: tcp://localhost:5432');
|
|
75
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
76
|
-
await expect(ds.test(CONN)).rejects.toThrow(/connection refused/);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
it('throws a generic message on empty response', async () => {
|
|
80
|
-
const {client} = makeMock(() => '');
|
|
81
|
-
const ds = new NodeConnectionsDataSource(client);
|
|
82
|
-
await expect(ds.test(CONN)).rejects.toThrow(/failed/i);
|
|
83
|
-
});
|
|
84
|
-
});
|