floatnote 1.0.0 → 1.0.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.
Files changed (68) hide show
  1. package/bin/floatnote.js +16 -0
  2. package/package.json +13 -5
  3. package/.beads/config.json +0 -6
  4. package/.beads/issues/floatnote-1.md +0 -21
  5. package/.beads/issues/floatnote-10.md +0 -28
  6. package/.beads/issues/floatnote-11.md +0 -36
  7. package/.beads/issues/floatnote-12.md +0 -25
  8. package/.beads/issues/floatnote-13.md +0 -37
  9. package/.beads/issues/floatnote-14.md +0 -22
  10. package/.beads/issues/floatnote-15.md +0 -22
  11. package/.beads/issues/floatnote-16.md +0 -20
  12. package/.beads/issues/floatnote-17.md +0 -20
  13. package/.beads/issues/floatnote-18.md +0 -21
  14. package/.beads/issues/floatnote-19.md +0 -19
  15. package/.beads/issues/floatnote-2.md +0 -32
  16. package/.beads/issues/floatnote-20.md +0 -22
  17. package/.beads/issues/floatnote-3.md +0 -50
  18. package/.beads/issues/floatnote-4.md +0 -31
  19. package/.beads/issues/floatnote-5.md +0 -28
  20. package/.beads/issues/floatnote-6.md +0 -30
  21. package/.beads/issues/floatnote-7.md +0 -38
  22. package/.beads/issues/floatnote-8.md +0 -29
  23. package/.beads/issues/floatnote-9.md +0 -32
  24. package/CLAUDE.md +0 -61
  25. package/coverage/base.css +0 -224
  26. package/coverage/bin/floatnote.js.html +0 -739
  27. package/coverage/bin/index.html +0 -116
  28. package/coverage/block-navigation.js +0 -87
  29. package/coverage/favicon.png +0 -0
  30. package/coverage/index.html +0 -131
  31. package/coverage/lcov-report/base.css +0 -224
  32. package/coverage/lcov-report/bin/floatnote.js.html +0 -739
  33. package/coverage/lcov-report/bin/index.html +0 -116
  34. package/coverage/lcov-report/block-navigation.js +0 -87
  35. package/coverage/lcov-report/favicon.png +0 -0
  36. package/coverage/lcov-report/index.html +0 -131
  37. package/coverage/lcov-report/prettify.css +0 -1
  38. package/coverage/lcov-report/prettify.js +0 -2
  39. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  40. package/coverage/lcov-report/sorter.js +0 -210
  41. package/coverage/lcov-report/src/index.html +0 -146
  42. package/coverage/lcov-report/src/main.js.html +0 -1483
  43. package/coverage/lcov-report/src/preload.js.html +0 -361
  44. package/coverage/lcov-report/src/renderer.js.html +0 -8767
  45. package/coverage/lcov.info +0 -3273
  46. package/coverage/prettify.css +0 -1
  47. package/coverage/prettify.js +0 -2
  48. package/coverage/sort-arrow-sprite.png +0 -0
  49. package/coverage/sorter.js +0 -210
  50. package/coverage/src/index.html +0 -146
  51. package/coverage/src/main.js.html +0 -1483
  52. package/coverage/src/preload.js.html +0 -361
  53. package/coverage/src/renderer.js.html +0 -8767
  54. package/jest.config.js +0 -48
  55. package/src/icon-template.png +0 -0
  56. package/src/index.html +0 -296
  57. package/src/main.js +0 -494
  58. package/src/preload.js +0 -96
  59. package/src/renderer.js +0 -3203
  60. package/src/styles.css +0 -1448
  61. package/tests/cli/floatnote.test.js +0 -167
  62. package/tests/main/main.test.js +0 -287
  63. package/tests/mocks/electron.js +0 -126
  64. package/tests/mocks/fs.js +0 -17
  65. package/tests/preload/preload.test.js +0 -218
  66. package/tests/renderer/history.test.js +0 -234
  67. package/tests/renderer/notes.test.js +0 -262
  68. package/tests/renderer/settings.test.js +0 -178
@@ -1,218 +0,0 @@
1
- // Tests for src/preload.js IPC bridge
2
-
3
- describe('Preload IPC Bridge', () => {
4
- let mockIpcRenderer;
5
- let mockClipboard;
6
- let exposedAPI;
7
-
8
- beforeEach(() => {
9
- jest.clearAllMocks();
10
-
11
- // Create mock implementations
12
- mockIpcRenderer = {
13
- on: jest.fn(),
14
- send: jest.fn(),
15
- invoke: jest.fn(() => Promise.resolve({ success: true }))
16
- };
17
-
18
- mockClipboard = {
19
- availableFormats: jest.fn(() => []),
20
- readImage: jest.fn(() => ({
21
- isEmpty: jest.fn(() => true),
22
- toDataURL: jest.fn(() => ''),
23
- getSize: jest.fn(() => ({ width: 0, height: 0 }))
24
- })),
25
- readText: jest.fn(() => '')
26
- };
27
-
28
- // Create mock API similar to what preload exposes
29
- exposedAPI = {
30
- onFocusChange: (callback) => {
31
- mockIpcRenderer.on('window-focus', (event, focused) => callback(focused));
32
- },
33
- closeWindow: () => {
34
- mockIpcRenderer.send('close-window');
35
- },
36
- hideWindow: () => {
37
- mockIpcRenderer.send('hide-window');
38
- },
39
- setPinned: (pinned) => {
40
- mockIpcRenderer.send('set-pinned', pinned);
41
- },
42
- setWindowSize: (size) => {
43
- mockIpcRenderer.send('set-window-size', size);
44
- },
45
- setBackgroundMode: (mode) => {
46
- mockIpcRenderer.send('set-background-mode', mode);
47
- },
48
- getClipboardContent: () => {
49
- const formats = mockClipboard.availableFormats();
50
- const hasImage = formats.some(f => f.includes('image'));
51
- const hasText = formats.some(f => f.includes('text'));
52
- if (hasImage) {
53
- const image = mockClipboard.readImage();
54
- if (!image.isEmpty()) {
55
- return { type: 'image', dataUrl: image.toDataURL() };
56
- }
57
- }
58
- if (hasText) {
59
- const text = mockClipboard.readText();
60
- if (text && text.trim()) {
61
- return { type: 'text', content: text };
62
- }
63
- }
64
- return null;
65
- },
66
- readClipboardImage: () => {
67
- const image = mockClipboard.readImage();
68
- if (!image.isEmpty()) {
69
- return image.toDataURL();
70
- }
71
- return null;
72
- },
73
- readClipboardText: () => {
74
- return mockClipboard.readText();
75
- },
76
- saveData: (data) => {
77
- return mockIpcRenderer.invoke('save-data', data);
78
- },
79
- loadData: () => {
80
- return mockIpcRenderer.invoke('load-data');
81
- },
82
- resizeWindowLeft: (deltaX) => {
83
- mockIpcRenderer.send('resize-window-left', deltaX);
84
- },
85
- exportToFloatnote: (noteData) => {
86
- return mockIpcRenderer.invoke('export-to-floatnote', noteData);
87
- },
88
- openFloatnoteFolder: () => {
89
- return mockIpcRenderer.invoke('open-floatnote-folder');
90
- },
91
- exportPNG: (imageDataUrl) => {
92
- return mockIpcRenderer.invoke('export-png', imageDataUrl);
93
- }
94
- };
95
- });
96
-
97
- describe('Window control functions', () => {
98
- test('closeWindow sends close-window IPC message', () => {
99
- exposedAPI.closeWindow();
100
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('close-window');
101
- });
102
-
103
- test('hideWindow sends hide-window IPC message', () => {
104
- exposedAPI.hideWindow();
105
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('hide-window');
106
- });
107
-
108
- test('setPinned sends set-pinned with value', () => {
109
- exposedAPI.setPinned(true);
110
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-pinned', true);
111
-
112
- exposedAPI.setPinned(false);
113
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-pinned', false);
114
- });
115
-
116
- test('setWindowSize sends correct size', () => {
117
- exposedAPI.setWindowSize('sm');
118
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-window-size', 'sm');
119
-
120
- exposedAPI.setWindowSize('md');
121
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-window-size', 'md');
122
-
123
- exposedAPI.setWindowSize('lg');
124
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-window-size', 'lg');
125
- });
126
-
127
- test('setBackgroundMode sends correct mode', () => {
128
- exposedAPI.setBackgroundMode('blur');
129
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-background-mode', 'blur');
130
-
131
- exposedAPI.setBackgroundMode('dark');
132
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-background-mode', 'dark');
133
-
134
- exposedAPI.setBackgroundMode('transparent');
135
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('set-background-mode', 'transparent');
136
- });
137
- });
138
-
139
- describe('Focus change handling', () => {
140
- test('onFocusChange registers callback for window-focus', () => {
141
- const callback = jest.fn();
142
- exposedAPI.onFocusChange(callback);
143
- expect(mockIpcRenderer.on).toHaveBeenCalledWith('window-focus', expect.any(Function));
144
- });
145
- });
146
-
147
- describe('Clipboard functions', () => {
148
- test('getClipboardContent returns null when empty', () => {
149
- mockClipboard.availableFormats.mockReturnValue([]);
150
- const result = exposedAPI.getClipboardContent();
151
- expect(result).toBeNull();
152
- });
153
-
154
- test('getClipboardContent returns text when available', () => {
155
- mockClipboard.availableFormats.mockReturnValue(['text/plain']);
156
- mockClipboard.readText.mockReturnValue('Hello World');
157
- const result = exposedAPI.getClipboardContent();
158
- expect(result).toEqual({ type: 'text', content: 'Hello World' });
159
- });
160
-
161
- test('readClipboardText returns clipboard text', () => {
162
- mockClipboard.readText.mockReturnValue('Test text');
163
- const result = exposedAPI.readClipboardText();
164
- expect(result).toBe('Test text');
165
- });
166
-
167
- test('readClipboardImage returns null when empty', () => {
168
- mockClipboard.readImage.mockReturnValue({
169
- isEmpty: () => true,
170
- toDataURL: () => ''
171
- });
172
- const result = exposedAPI.readClipboardImage();
173
- expect(result).toBeNull();
174
- });
175
- });
176
-
177
- describe('Data persistence functions', () => {
178
- test('saveData invokes save-data IPC', async () => {
179
- const data = { notes: [], settings: {} };
180
- await exposedAPI.saveData(data);
181
- expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('save-data', data);
182
- });
183
-
184
- test('loadData invokes load-data IPC', async () => {
185
- await exposedAPI.loadData();
186
- expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('load-data');
187
- });
188
- });
189
-
190
- describe('Window resize', () => {
191
- test('resizeWindowLeft sends resize-window-left with delta', () => {
192
- exposedAPI.resizeWindowLeft(50);
193
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('resize-window-left', 50);
194
-
195
- exposedAPI.resizeWindowLeft(-30);
196
- expect(mockIpcRenderer.send).toHaveBeenCalledWith('resize-window-left', -30);
197
- });
198
- });
199
-
200
- describe('Export functions', () => {
201
- test('exportToFloatnote invokes export-to-floatnote IPC', async () => {
202
- const noteData = { id: '123', lines: [], textItems: [] };
203
- await exposedAPI.exportToFloatnote(noteData);
204
- expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('export-to-floatnote', noteData);
205
- });
206
-
207
- test('openFloatnoteFolder invokes open-floatnote-folder IPC', async () => {
208
- await exposedAPI.openFloatnoteFolder();
209
- expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('open-floatnote-folder');
210
- });
211
-
212
- test('exportPNG invokes export-png IPC with image data', async () => {
213
- const imageDataUrl = 'data:image/png;base64,test123';
214
- await exposedAPI.exportPNG(imageDataUrl);
215
- expect(mockIpcRenderer.invoke).toHaveBeenCalledWith('export-png', imageDataUrl);
216
- });
217
- });
218
- });
@@ -1,234 +0,0 @@
1
- // Tests for renderer undo/redo history
2
-
3
- describe('History Management', () => {
4
- let history;
5
- let historyIndex;
6
- const MAX_HISTORY = 50;
7
-
8
- beforeEach(() => {
9
- history = [];
10
- historyIndex = -1;
11
- });
12
-
13
- describe('saveState', () => {
14
- test('should add state to history', () => {
15
- const state = { lines: [], textItems: [], images: [] };
16
- history.push(JSON.stringify(state));
17
- historyIndex = 0;
18
-
19
- expect(history.length).toBe(1);
20
- expect(historyIndex).toBe(0);
21
- });
22
-
23
- test('should increment history index', () => {
24
- for (let i = 0; i < 5; i++) {
25
- history.push(JSON.stringify({ lines: [i] }));
26
- historyIndex++;
27
- }
28
-
29
- expect(historyIndex).toBe(4);
30
- expect(history.length).toBe(5);
31
- });
32
-
33
- test('should trim history when exceeding max', () => {
34
- for (let i = 0; i < MAX_HISTORY + 10; i++) {
35
- history.push(JSON.stringify({ lines: [i] }));
36
- }
37
-
38
- // Trim to max
39
- if (history.length > MAX_HISTORY) {
40
- history = history.slice(-MAX_HISTORY);
41
- historyIndex = Math.min(historyIndex, history.length - 1);
42
- }
43
-
44
- expect(history.length).toBe(MAX_HISTORY);
45
- });
46
-
47
- test('should truncate future history when adding new state', () => {
48
- // Add 5 states
49
- for (let i = 0; i < 5; i++) {
50
- history.push(JSON.stringify({ lines: [i] }));
51
- historyIndex++;
52
- }
53
-
54
- // Undo twice
55
- historyIndex -= 2;
56
-
57
- // Add new state (should truncate future)
58
- history = history.slice(0, historyIndex + 1);
59
- history.push(JSON.stringify({ lines: [100] }));
60
- historyIndex++;
61
-
62
- expect(history.length).toBe(4);
63
- expect(historyIndex).toBe(3);
64
- });
65
- });
66
-
67
- describe('undo', () => {
68
- test('should decrement history index', () => {
69
- for (let i = 0; i < 5; i++) {
70
- history.push(JSON.stringify({ lines: [i] }));
71
- historyIndex++;
72
- }
73
-
74
- // Perform undo
75
- if (historyIndex > 0) {
76
- historyIndex--;
77
- }
78
-
79
- expect(historyIndex).toBe(3);
80
- });
81
-
82
- test('should not go below 0', () => {
83
- history.push(JSON.stringify({ lines: [] }));
84
- historyIndex = 0;
85
-
86
- // Try to undo when at beginning
87
- if (historyIndex > 0) {
88
- historyIndex--;
89
- }
90
-
91
- expect(historyIndex).toBe(0);
92
- });
93
-
94
- test('should restore previous state', () => {
95
- history.push(JSON.stringify({ lines: [1, 2, 3] }));
96
- historyIndex = 0;
97
- history.push(JSON.stringify({ lines: [1, 2, 3, 4] }));
98
- historyIndex = 1;
99
-
100
- // Undo
101
- historyIndex--;
102
- const state = JSON.parse(history[historyIndex]);
103
-
104
- expect(state.lines).toEqual([1, 2, 3]);
105
- });
106
- });
107
-
108
- describe('redo', () => {
109
- test('should increment history index', () => {
110
- for (let i = 0; i < 5; i++) {
111
- history.push(JSON.stringify({ lines: [i] }));
112
- }
113
- historyIndex = 2;
114
-
115
- // Perform redo
116
- if (historyIndex < history.length - 1) {
117
- historyIndex++;
118
- }
119
-
120
- expect(historyIndex).toBe(3);
121
- });
122
-
123
- test('should not go beyond history length', () => {
124
- for (let i = 0; i < 3; i++) {
125
- history.push(JSON.stringify({ lines: [i] }));
126
- }
127
- historyIndex = 2;
128
-
129
- // Try to redo when at end
130
- if (historyIndex < history.length - 1) {
131
- historyIndex++;
132
- }
133
-
134
- expect(historyIndex).toBe(2);
135
- });
136
-
137
- test('should restore next state', () => {
138
- history.push(JSON.stringify({ lines: [1] }));
139
- history.push(JSON.stringify({ lines: [1, 2] }));
140
- history.push(JSON.stringify({ lines: [1, 2, 3] }));
141
- historyIndex = 0;
142
-
143
- // Redo
144
- historyIndex++;
145
- const state = JSON.parse(history[historyIndex]);
146
-
147
- expect(state.lines).toEqual([1, 2]);
148
- });
149
- });
150
-
151
- describe('canUndo and canRedo', () => {
152
- test('canUndo should be false at beginning', () => {
153
- history.push(JSON.stringify({ lines: [] }));
154
- historyIndex = 0;
155
-
156
- const canUndo = historyIndex > 0;
157
- expect(canUndo).toBe(false);
158
- });
159
-
160
- test('canUndo should be true with history', () => {
161
- for (let i = 0; i < 3; i++) {
162
- history.push(JSON.stringify({ lines: [i] }));
163
- historyIndex++;
164
- }
165
-
166
- const canUndo = historyIndex > 0;
167
- expect(canUndo).toBe(true);
168
- });
169
-
170
- test('canRedo should be false at end', () => {
171
- for (let i = 0; i < 3; i++) {
172
- history.push(JSON.stringify({ lines: [i] }));
173
- historyIndex++;
174
- }
175
-
176
- const canRedo = historyIndex < history.length - 1;
177
- expect(canRedo).toBe(false);
178
- });
179
-
180
- test('canRedo should be true after undo', () => {
181
- for (let i = 0; i < 3; i++) {
182
- history.push(JSON.stringify({ lines: [i] }));
183
- historyIndex++;
184
- }
185
-
186
- // Undo
187
- historyIndex--;
188
-
189
- const canRedo = historyIndex < history.length - 1;
190
- expect(canRedo).toBe(true);
191
- });
192
- });
193
-
194
- describe('State restoration', () => {
195
- test('should restore lines correctly', () => {
196
- const state = {
197
- lines: [
198
- { points: [{ x: 0, y: 0 }, { x: 10, y: 10 }], color: '#fff', width: 2 }
199
- ]
200
- };
201
- history.push(JSON.stringify(state));
202
-
203
- const restored = JSON.parse(history[0]);
204
- expect(restored.lines.length).toBe(1);
205
- expect(restored.lines[0].color).toBe('#fff');
206
- });
207
-
208
- test('should restore textItems correctly', () => {
209
- const state = {
210
- textItems: [
211
- { id: '1', text: 'Hello', x: 100, y: 100 }
212
- ]
213
- };
214
- history.push(JSON.stringify(state));
215
-
216
- const restored = JSON.parse(history[0]);
217
- expect(restored.textItems.length).toBe(1);
218
- expect(restored.textItems[0].text).toBe('Hello');
219
- });
220
-
221
- test('should restore images correctly', () => {
222
- const state = {
223
- images: [
224
- { id: '1', dataUrl: 'data:image/png;base64,test', x: 50, y: 50 }
225
- ]
226
- };
227
- history.push(JSON.stringify(state));
228
-
229
- const restored = JSON.parse(history[0]);
230
- expect(restored.images.length).toBe(1);
231
- expect(restored.images[0].x).toBe(50);
232
- });
233
- });
234
- });
@@ -1,262 +0,0 @@
1
- // Tests for renderer multi-note system
2
-
3
- describe('Multi-Note System', () => {
4
- let notes;
5
- let currentNoteIndex;
6
-
7
- beforeEach(() => {
8
- notes = [];
9
- currentNoteIndex = 0;
10
- });
11
-
12
- describe('Note creation', () => {
13
- test('should create a new note with correct structure', () => {
14
- const newNote = {
15
- id: Date.now().toString(),
16
- lines: [],
17
- textItems: [],
18
- images: [],
19
- createdAt: Date.now(),
20
- lastModified: Date.now()
21
- };
22
- notes.push(newNote);
23
-
24
- expect(notes.length).toBe(1);
25
- expect(notes[0].lines).toEqual([]);
26
- expect(notes[0].textItems).toEqual([]);
27
- expect(notes[0].images).toEqual([]);
28
- });
29
-
30
- test('should generate unique IDs', () => {
31
- const id1 = Date.now().toString();
32
- // Small delay to ensure different timestamp
33
- const id2 = (Date.now() + 1).toString();
34
-
35
- expect(id1).not.toBe(id2);
36
- });
37
- });
38
-
39
- describe('Note navigation', () => {
40
- beforeEach(() => {
41
- // Create 3 notes
42
- for (let i = 0; i < 3; i++) {
43
- notes.push({
44
- id: `note-${i}`,
45
- lines: [{ id: `line-${i}` }],
46
- textItems: [],
47
- images: [],
48
- createdAt: Date.now() + i,
49
- lastModified: Date.now() + i
50
- });
51
- }
52
- currentNoteIndex = 1;
53
- });
54
-
55
- test('previousNote should decrement index', () => {
56
- if (currentNoteIndex > 0) {
57
- currentNoteIndex--;
58
- }
59
- expect(currentNoteIndex).toBe(0);
60
- });
61
-
62
- test('previousNote should not go below 0', () => {
63
- currentNoteIndex = 0;
64
- if (currentNoteIndex > 0) {
65
- currentNoteIndex--;
66
- }
67
- expect(currentNoteIndex).toBe(0);
68
- });
69
-
70
- test('nextNote should increment index', () => {
71
- if (currentNoteIndex < notes.length - 1) {
72
- currentNoteIndex++;
73
- }
74
- expect(currentNoteIndex).toBe(2);
75
- });
76
-
77
- test('nextNote should not exceed notes length', () => {
78
- currentNoteIndex = 2;
79
- if (currentNoteIndex < notes.length - 1) {
80
- currentNoteIndex++;
81
- }
82
- expect(currentNoteIndex).toBe(2);
83
- });
84
-
85
- test('nextNote should create new note at end', () => {
86
- currentNoteIndex = 2;
87
- if (currentNoteIndex === notes.length - 1) {
88
- const newNote = {
89
- id: `note-${notes.length}`,
90
- lines: [],
91
- textItems: [],
92
- images: [],
93
- createdAt: Date.now(),
94
- lastModified: Date.now()
95
- };
96
- notes.push(newNote);
97
- currentNoteIndex++;
98
- }
99
- expect(notes.length).toBe(4);
100
- expect(currentNoteIndex).toBe(3);
101
- });
102
- });
103
-
104
- describe('Note counter', () => {
105
- test('should display correct counter format', () => {
106
- notes.push({ id: '1' }, { id: '2' }, { id: '3' });
107
- currentNoteIndex = 1;
108
-
109
- const counter = `${currentNoteIndex + 1}/${notes.length}`;
110
- expect(counter).toBe('2/3');
111
- });
112
-
113
- test('should update when navigating', () => {
114
- notes.push({ id: '1' }, { id: '2' });
115
- currentNoteIndex = 0;
116
-
117
- let counter = `${currentNoteIndex + 1}/${notes.length}`;
118
- expect(counter).toBe('1/2');
119
-
120
- currentNoteIndex++;
121
- counter = `${currentNoteIndex + 1}/${notes.length}`;
122
- expect(counter).toBe('2/2');
123
- });
124
- });
125
-
126
- describe('Note state management', () => {
127
- test('should save current note state before switching', () => {
128
- const currentNote = {
129
- id: 'note-1',
130
- lines: [{ id: 'line-1' }],
131
- textItems: [{ id: 'text-1' }],
132
- images: []
133
- };
134
- notes.push(currentNote);
135
- notes.push({ id: 'note-2', lines: [], textItems: [], images: [] });
136
-
137
- // Simulate saveCurrentNoteState
138
- notes[currentNoteIndex] = { ...currentNote };
139
-
140
- expect(notes[0].lines.length).toBe(1);
141
- expect(notes[0].textItems.length).toBe(1);
142
- });
143
-
144
- test('should load note state when switching', () => {
145
- notes.push({
146
- id: 'note-1',
147
- lines: [{ id: 'line-1', points: [] }],
148
- textItems: [{ id: 'text-1', text: 'Hello' }],
149
- images: []
150
- });
151
-
152
- const loadedNote = notes[currentNoteIndex];
153
- expect(loadedNote.lines.length).toBe(1);
154
- expect(loadedNote.textItems.length).toBe(1);
155
- expect(loadedNote.textItems[0].text).toBe('Hello');
156
- });
157
-
158
- test('should update lastModified timestamp', () => {
159
- const note = {
160
- id: 'note-1',
161
- lines: [],
162
- textItems: [],
163
- images: [],
164
- createdAt: 1000,
165
- lastModified: 1000
166
- };
167
- notes.push(note);
168
-
169
- // Simulate modification
170
- note.lastModified = 2000;
171
-
172
- expect(note.lastModified).toBeGreaterThan(note.createdAt);
173
- });
174
- });
175
-
176
- describe('Note data structure', () => {
177
- test('lines should have required properties', () => {
178
- const line = {
179
- points: [{ x: 0, y: 0 }, { x: 10, y: 10 }],
180
- color: '#ffffff',
181
- width: 4,
182
- objectId: 'obj-1'
183
- };
184
-
185
- expect(line.points).toBeDefined();
186
- expect(line.color).toBeDefined();
187
- expect(line.width).toBeDefined();
188
- expect(line.objectId).toBeDefined();
189
- });
190
-
191
- test('textItems should have required properties', () => {
192
- const textItem = {
193
- id: 'text-1',
194
- text: 'Sample text',
195
- x: 100,
196
- y: 200,
197
- color: '#ffffff',
198
- width: 150
199
- };
200
-
201
- expect(textItem.id).toBeDefined();
202
- expect(textItem.x).toBeDefined();
203
- expect(textItem.y).toBeDefined();
204
- expect(textItem.color).toBeDefined();
205
- });
206
-
207
- test('images should have required properties', () => {
208
- const image = {
209
- id: 'img-1',
210
- dataUrl: 'data:image/png;base64,test',
211
- x: 50,
212
- y: 50,
213
- width: 200,
214
- height: 150
215
- };
216
-
217
- expect(image.id).toBeDefined();
218
- expect(image.dataUrl).toBeDefined();
219
- expect(image.x).toBeDefined();
220
- expect(image.y).toBeDefined();
221
- expect(image.width).toBeDefined();
222
- expect(image.height).toBeDefined();
223
- });
224
- });
225
-
226
- describe('Clean slate mode', () => {
227
- test('should start with empty note when enabled', () => {
228
- const settings = { openWithCleanSlate: true };
229
-
230
- if (settings.openWithCleanSlate) {
231
- notes = [];
232
- notes.push({
233
- id: Date.now().toString(),
234
- lines: [],
235
- textItems: [],
236
- images: [],
237
- createdAt: Date.now(),
238
- lastModified: Date.now()
239
- });
240
- currentNoteIndex = 0;
241
- }
242
-
243
- expect(notes.length).toBe(1);
244
- expect(notes[0].lines).toEqual([]);
245
- });
246
-
247
- test('should load saved notes when disabled', () => {
248
- const settings = { openWithCleanSlate: false };
249
- const savedNotes = [
250
- { id: '1', lines: [{ id: 'l1' }], textItems: [], images: [] },
251
- { id: '2', lines: [], textItems: [{ id: 't1' }], images: [] }
252
- ];
253
-
254
- if (!settings.openWithCleanSlate && savedNotes.length > 0) {
255
- notes = savedNotes;
256
- }
257
-
258
- expect(notes.length).toBe(2);
259
- expect(notes[0].lines.length).toBe(1);
260
- });
261
- });
262
- });