@sveltia/ui 0.36.1 → 0.37.1

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.
@@ -1,229 +0,0 @@
1
- /* eslint-disable jsdoc/require-jsdoc */
2
-
3
- import { describe, expect, it, vi } from 'vitest';
4
- import { createEditorStore } from './store.svelte.js';
5
-
6
- describe('createEditorStore', () => {
7
- it('should have correct initial values', () => {
8
- const store = createEditorStore();
9
-
10
- expect(store.initialized).toBe(false);
11
- expect(store.editor).toBeUndefined();
12
- expect(store.inputValue).toBe('');
13
- expect(store.useRichText).toBe(true);
14
- expect(store.hasConverterError).toBe(false);
15
- expect(store.showConverterError).toBe(false);
16
- expect(store.selection).toEqual({
17
- blockNodeKey: null,
18
- blockType: 'paragraph',
19
- inlineTypes: [],
20
- });
21
- });
22
-
23
- it('should return a non-empty editorId string', () => {
24
- const store = createEditorStore();
25
-
26
- expect(typeof store.editorId).toBe('string');
27
- expect(store.editorId.length).toBeGreaterThan(0);
28
- });
29
-
30
- it('should generate unique editorIds for each store instance', () => {
31
- const a = createEditorStore();
32
- const b = createEditorStore();
33
-
34
- expect(a.editorId).not.toBe(b.editorId);
35
- });
36
-
37
- it('should set initialized', () => {
38
- const store = createEditorStore();
39
-
40
- store.initialized = true;
41
- expect(store.initialized).toBe(true);
42
- });
43
-
44
- it('should set useRichText to true when first mode is rich-text', () => {
45
- const store = createEditorStore();
46
-
47
- store.config = {
48
- modes: ['rich-text'],
49
- enabledButtons: [],
50
- components: [],
51
- isCodeEditor: false,
52
- };
53
- expect(store.useRichText).toBe(true);
54
- });
55
-
56
- it('should set useRichText to false when first mode is plain-text', () => {
57
- const store = createEditorStore();
58
-
59
- store.config = {
60
- modes: ['plain-text'],
61
- enabledButtons: [],
62
- components: [],
63
- isCodeEditor: false,
64
- };
65
- expect(store.useRichText).toBe(false);
66
- });
67
-
68
- it('should set useRichText to true when isCodeEditor is true regardless of modes', () => {
69
- const store = createEditorStore();
70
-
71
- store.config = { modes: [], enabledButtons: [], components: [], isCodeEditor: true };
72
- expect(store.useRichText).toBe(true);
73
- });
74
-
75
- it('should set useRichText to false when modes is empty and isCodeEditor is false', () => {
76
- const store = createEditorStore();
77
-
78
- store.config = { modes: [], enabledButtons: [], components: [], isCodeEditor: false };
79
- expect(store.useRichText).toBe(false);
80
- });
81
-
82
- it('should set hasConverterError and cascade useRichText and showConverterError', () => {
83
- const store = createEditorStore();
84
-
85
- store.hasConverterError = true;
86
- expect(store.hasConverterError).toBe(true);
87
- expect(store.useRichText).toBe(false);
88
- expect(store.showConverterError).toBe(true);
89
- });
90
-
91
- it('should allow clearing hasConverterError without re-enabling rich text', () => {
92
- const store = createEditorStore();
93
-
94
- store.hasConverterError = true;
95
- store.hasConverterError = false;
96
- // Setting to false does NOT automatically re-enable rich text
97
- expect(store.hasConverterError).toBe(false);
98
- expect(store.useRichText).toBe(false);
99
- });
100
-
101
- it('should allow setting showConverterError directly', () => {
102
- const store = createEditorStore();
103
-
104
- store.showConverterError = true;
105
- expect(store.showConverterError).toBe(true);
106
- store.showConverterError = false;
107
-
108
- expect(store.showConverterError).toBe(false);
109
- });
110
-
111
- it('should allow setting and reading selection', () => {
112
- const store = createEditorStore();
113
-
114
- const sel = {
115
- blockNodeKey: 'abc',
116
- blockType: /** @type {const} */ ('heading-2'),
117
- inlineTypes: /** @type {import('../../typedefs').TextEditorInlineType[]} */ ([
118
- 'bold',
119
- 'italic',
120
- ]),
121
- };
122
-
123
- store.selection = sel;
124
- expect(store.selection).toEqual(sel);
125
- });
126
-
127
- it('should allow setting inputValue when useRichText is false', () => {
128
- const store = createEditorStore();
129
-
130
- store.useRichText = false;
131
- store.inputValue = 'hello world';
132
- expect(store.inputValue).toBe('hello world');
133
- });
134
-
135
- it('should not change inputValue on duplicate assignment', () => {
136
- const store = createEditorStore();
137
-
138
- store.useRichText = false;
139
- store.inputValue = 'same';
140
- store.inputValue = 'same'; // no-op since unchanged
141
- expect(store.inputValue).toBe('same');
142
- });
143
-
144
- it('should expose convertMarkdown as a function', () => {
145
- const store = createEditorStore();
146
-
147
- expect(typeof store.convertMarkdown).toBe('function');
148
- });
149
-
150
- it('should allow getting and setting the editor (covers line 63)', () => {
151
- const store = createEditorStore();
152
- const fakeEditor = /** @type {any} */ ({ __testId: 'test-editor' });
153
-
154
- store.editor = fakeEditor; // line 63: editor = newValue
155
- // Svelte 5 $state wraps objects in a Proxy, so use toEqual for value comparison
156
- // @ts-ignore
157
- expect(store.editor?.__testId).toBe('test-editor');
158
- });
159
-
160
- it('should allow reading the config via getter (covers line 72)', () => {
161
- const store = createEditorStore();
162
- const cfg = store.config; // line 72: return config
163
-
164
- expect(cfg.modes).toHaveLength(0);
165
- expect(cfg.isCodeEditor).toBe(false);
166
- });
167
-
168
- it('should call convertMarkdown (returns early) when inputValue changes with useRichText=true', async () => {
169
- const store = createEditorStore();
170
-
171
- // useRichText is true by default; no editor set → convertMarkdown returns early (line 40)
172
- store.inputValue = 'hello'; // triggers line 89: convertMarkdown()
173
- await new Promise((r) => {
174
- setTimeout(r, 0);
175
- });
176
- expect(store.inputValue).toBe('hello');
177
- expect(store.hasConverterError).toBe(false);
178
- });
179
-
180
- it('should set hasConverterError when convertMarkdownToLexical throws', async () => {
181
- const store = createEditorStore();
182
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
183
- // Mock editor that has no .update() method → convertMarkdownToLexical will throw inside the
184
- // new Promise executor, rejecting it and triggering the catch block (lines 48-52)
185
- const mockEditor = /** @type {any} */ ({ getEditorState: () => ({ isEmpty: () => false }) });
186
-
187
- store.editor = mockEditor;
188
- store.initialized = true;
189
- store.inputValue = 'some text'; // triggers convertMarkdown (line 89), which awaits the rejection
190
- await new Promise((r) => {
191
- setTimeout(r, 0);
192
- });
193
- expect(store.hasConverterError).toBe(true);
194
- consoleSpy.mockRestore();
195
- });
196
-
197
- it('should pass empty string fallback to convertMarkdownToLexical when inputValue is empty (branch 2)', async () => {
198
- const store = createEditorStore();
199
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
200
- const mockEditor = /** @type {any} */ ({ getEditorState: () => ({ isEmpty: () => false }) });
201
-
202
- store.editor = mockEditor;
203
- store.initialized = true;
204
- store.inputValue = 'hello'; // non-empty: inputValue || '' uses inputValue (count[0])
205
- store.inputValue = ''; // empty: inputValue || '' uses '' fallback (count[1])
206
- await new Promise((r) => {
207
- setTimeout(r, 0);
208
- });
209
- expect(store.inputValue).toBe('');
210
- consoleSpy.mockRestore();
211
- });
212
-
213
- it('should trigger convertMarkdown when isEmpty() is true even though value is unchanged (branch 6)', async () => {
214
- const store = createEditorStore();
215
- const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
216
- // isEmpty() returns true → triggers convertMarkdown even when hasChange = false
217
- const mockEditor = /** @type {any} */ ({ getEditorState: () => ({ isEmpty: () => true }) });
218
-
219
- store.editor = mockEditor;
220
- store.initialized = true;
221
- store.inputValue = 'hello'; // hasChange=true → count[1] of binary-expr at line 88
222
- store.inputValue = 'hello'; // hasChange=false, isEmpty()=true → count[2] is hit
223
- await new Promise((r) => {
224
- setTimeout(r, 0);
225
- });
226
- expect(store.inputValue).toBe('hello');
227
- consoleSpy.mockRestore();
228
- });
229
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,106 +0,0 @@
1
- // cSpell:ignore-words horizontalrule
2
-
3
- import { describe, expect, it, vi } from 'vitest';
4
- import { HR } from './hr.js';
5
-
6
- // Mock @lexical/extension so we can call HR.replace without a real Lexical editor context
7
- vi.mock('@lexical/extension', () => {
8
- const mockHRNode = {
9
- __type: 'horizontalrule',
10
- selectNext: vi.fn(),
11
- };
12
-
13
- return {
14
- $createHorizontalRuleNode: vi.fn(() => mockHRNode),
15
- // eslint-disable-next-line jsdoc/require-jsdoc
16
- $isHorizontalRuleNode: (/** @type {{ __type: string; }} */ node) =>
17
- node?.__type === 'horizontalrule',
18
- /**
19
- *
20
- */
21
- HorizontalRuleNode: class {},
22
- };
23
- });
24
-
25
- describe('HR transformer', () => {
26
- it('should have the correct type', () => {
27
- expect(HR.type).toBe('element');
28
- });
29
-
30
- it('should include HorizontalRuleNode in dependencies', () => {
31
- expect(HR.dependencies).toHaveLength(1);
32
- });
33
-
34
- it('regExp should match --- divider', () => {
35
- expect(HR.regExp.test('---')).toBe(true);
36
- });
37
-
38
- it('regExp should match *** divider', () => {
39
- expect(HR.regExp.test('***')).toBe(true);
40
- });
41
-
42
- it('regExp should match ___ divider', () => {
43
- expect(HR.regExp.test('___')).toBe(true);
44
- });
45
-
46
- it('regExp should match dividers with trailing space', () => {
47
- expect(HR.regExp.test('--- ')).toBe(true);
48
- expect(HR.regExp.test('*** ')).toBe(true);
49
- });
50
-
51
- it('regExp should not match partial dividers', () => {
52
- expect(HR.regExp.test('--')).toBe(false);
53
- expect(HR.regExp.test('**')).toBe(false);
54
- expect(HR.regExp.test('__')).toBe(false);
55
- });
56
-
57
- it('export should return null for a non-HR node', () => {
58
- const fakeNode = { __type: 'paragraph' };
59
-
60
- expect(HR.export(/** @type {any} */ (fakeNode), () => '')).toBeNull();
61
- });
62
-
63
- it('export should return "***" for an HR node', () => {
64
- const hrNode = { __type: 'horizontalrule' };
65
-
66
- expect(HR.export(/** @type {any} */ (hrNode), () => '')).toBe('***');
67
- });
68
-
69
- it('replace should call parentNode.replace when isImport is true', () => {
70
- const parentNode = {
71
- replace: vi.fn(),
72
- insertBefore: vi.fn(),
73
- getNextSibling: vi.fn().mockReturnValue(null),
74
- };
75
-
76
- HR.replace(/** @type {any} */ (parentNode), [], /** @type {any} */ (['---']), true);
77
- expect(parentNode.replace).toHaveBeenCalledOnce();
78
- expect(parentNode.insertBefore).not.toHaveBeenCalled();
79
- });
80
-
81
- it('replace should call parentNode.replace when there is a next sibling', () => {
82
- const siblingNode = { __type: 'paragraph' };
83
-
84
- const parentNode = {
85
- replace: vi.fn(),
86
- insertBefore: vi.fn(),
87
- getNextSibling: vi.fn().mockReturnValue(siblingNode),
88
- };
89
-
90
- HR.replace(/** @type {any} */ (parentNode), [], /** @type {any} */ (['---']), false);
91
- expect(parentNode.replace).toHaveBeenCalledOnce();
92
- expect(parentNode.insertBefore).not.toHaveBeenCalled();
93
- });
94
-
95
- it('replace should call parentNode.insertBefore when not import and no next sibling', () => {
96
- const parentNode = {
97
- replace: vi.fn(),
98
- insertBefore: vi.fn(),
99
- getNextSibling: vi.fn().mockReturnValue(null),
100
- };
101
-
102
- HR.replace(/** @type {any} */ (parentNode), [], /** @type {any} */ (['---']), false);
103
- expect(parentNode.insertBefore).toHaveBeenCalledOnce();
104
- expect(parentNode.replace).not.toHaveBeenCalled();
105
- });
106
- });
@@ -1,28 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { TABLE } from './table.js';
3
-
4
- describe('TABLE transformer', () => {
5
- it('should have the correct type', () => {
6
- expect(TABLE.type).toBe('element');
7
- });
8
-
9
- it('should have TableNode, TableRowNode, and TableCellNode in dependencies', () => {
10
- expect(TABLE.dependencies).toHaveLength(3);
11
- });
12
-
13
- it('regExp should match a table row line', () => {
14
- expect(TABLE.regExp.test('| foo | bar |')).toBe(true);
15
- expect(TABLE.regExp.test('| single |')).toBe(true);
16
- });
17
-
18
- it('regExp should not match lines without pipe borders', () => {
19
- expect(TABLE.regExp.test('foo bar')).toBe(false);
20
- expect(TABLE.regExp.test('- item')).toBe(false);
21
- });
22
-
23
- it('export should return null for a non-table node', () => {
24
- const fakeNode = { __type: 'paragraph' };
25
-
26
- expect(TABLE.export(/** @type {any} */ (fakeNode), () => '')).toBeNull();
27
- });
28
- });
@@ -1 +0,0 @@
1
- export {};