@pie-lib/graphing 3.1.1-next.0 → 3.1.1-next.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.
Files changed (197) hide show
  1. package/lib/axis/arrow.js +0 -3
  2. package/lib/axis/arrow.js.map +1 -1
  3. package/lib/axis/axes.js +0 -16
  4. package/lib/axis/axes.js.map +1 -1
  5. package/lib/axis/index.js +0 -7
  6. package/lib/axis/index.js.map +1 -1
  7. package/lib/bg.js +0 -6
  8. package/lib/bg.js.map +1 -1
  9. package/lib/container/actions.js +0 -1
  10. package/lib/container/actions.js.map +1 -1
  11. package/lib/container/index.js +4 -8
  12. package/lib/container/index.js.map +1 -1
  13. package/lib/container/marks.js +0 -2
  14. package/lib/container/marks.js.map +1 -1
  15. package/lib/container/middleware.js +0 -1
  16. package/lib/container/middleware.js.map +1 -1
  17. package/lib/container/reducer.js +0 -1
  18. package/lib/container/reducer.js.map +1 -1
  19. package/lib/coordinates-label.js +0 -11
  20. package/lib/coordinates-label.js.map +1 -1
  21. package/lib/graph-with-controls.js +3 -21
  22. package/lib/graph-with-controls.js.map +1 -1
  23. package/lib/graph.js +8 -27
  24. package/lib/graph.js.map +1 -1
  25. package/lib/grid-setup.js +0 -11
  26. package/lib/grid-setup.js.map +1 -1
  27. package/lib/grid.js +0 -22
  28. package/lib/grid.js.map +1 -1
  29. package/lib/index.js +0 -7
  30. package/lib/index.js.map +1 -1
  31. package/lib/key-legend.js +1 -2
  32. package/lib/key-legend.js.map +1 -1
  33. package/lib/label-svg-icon.js +0 -1
  34. package/lib/label-svg-icon.js.map +1 -1
  35. package/lib/labels.js +0 -13
  36. package/lib/labels.js.map +1 -1
  37. package/lib/mark-label.js +0 -15
  38. package/lib/mark-label.js.map +1 -1
  39. package/lib/toggle-bar.js +0 -17
  40. package/lib/toggle-bar.js.map +1 -1
  41. package/lib/tool-menu.js +0 -3
  42. package/lib/tool-menu.js.map +1 -1
  43. package/lib/tools/absolute/component.js +0 -1
  44. package/lib/tools/absolute/component.js.map +1 -1
  45. package/lib/tools/absolute/index.js +0 -10
  46. package/lib/tools/absolute/index.js.map +1 -1
  47. package/lib/tools/circle/bg-circle.js +0 -15
  48. package/lib/tools/circle/bg-circle.js.map +1 -1
  49. package/lib/tools/circle/component.js +2 -15
  50. package/lib/tools/circle/component.js.map +1 -1
  51. package/lib/tools/circle/index.js +0 -10
  52. package/lib/tools/circle/index.js.map +1 -1
  53. package/lib/tools/exponential/component.js +0 -1
  54. package/lib/tools/exponential/component.js.map +1 -1
  55. package/lib/tools/exponential/index.js +0 -10
  56. package/lib/tools/exponential/index.js.map +1 -1
  57. package/lib/tools/index.js +0 -1
  58. package/lib/tools/index.js.map +1 -1
  59. package/lib/tools/line/component.js +0 -2
  60. package/lib/tools/line/component.js.map +1 -1
  61. package/lib/tools/line/index.js +0 -1
  62. package/lib/tools/line/index.js.map +1 -1
  63. package/lib/tools/parabola/component.js +0 -1
  64. package/lib/tools/parabola/component.js.map +1 -1
  65. package/lib/tools/parabola/index.js +0 -10
  66. package/lib/tools/parabola/index.js.map +1 -1
  67. package/lib/tools/point/component.js +3 -15
  68. package/lib/tools/point/component.js.map +1 -1
  69. package/lib/tools/point/index.js +0 -10
  70. package/lib/tools/point/index.js.map +1 -1
  71. package/lib/tools/polygon/component.js +5 -29
  72. package/lib/tools/polygon/component.js.map +1 -1
  73. package/lib/tools/polygon/index.js +0 -12
  74. package/lib/tools/polygon/index.js.map +1 -1
  75. package/lib/tools/polygon/line.js +0 -15
  76. package/lib/tools/polygon/line.js.map +1 -1
  77. package/lib/tools/polygon/polygon.js +0 -18
  78. package/lib/tools/polygon/polygon.js.map +1 -1
  79. package/lib/tools/ray/component.js +0 -11
  80. package/lib/tools/ray/component.js.map +1 -1
  81. package/lib/tools/ray/index.js +0 -1
  82. package/lib/tools/ray/index.js.map +1 -1
  83. package/lib/tools/segment/component.js +0 -10
  84. package/lib/tools/segment/component.js.map +1 -1
  85. package/lib/tools/segment/index.js +0 -1
  86. package/lib/tools/segment/index.js.map +1 -1
  87. package/lib/tools/shared/arrow-head.js +0 -14
  88. package/lib/tools/shared/arrow-head.js.map +1 -1
  89. package/lib/tools/shared/icons/CorrectSVG.js +0 -1
  90. package/lib/tools/shared/icons/CorrectSVG.js.map +1 -1
  91. package/lib/tools/shared/icons/IncorrectSVG.js +0 -1
  92. package/lib/tools/shared/icons/IncorrectSVG.js.map +1 -1
  93. package/lib/tools/shared/icons/MissingSVG.js +0 -1
  94. package/lib/tools/shared/icons/MissingSVG.js.map +1 -1
  95. package/lib/tools/shared/line/index.js +7 -21
  96. package/lib/tools/shared/line/index.js.map +1 -1
  97. package/lib/tools/shared/line/line-path.js +0 -15
  98. package/lib/tools/shared/line/line-path.js.map +1 -1
  99. package/lib/tools/shared/line/with-root-edge.js +0 -11
  100. package/lib/tools/shared/line/with-root-edge.js.map +1 -1
  101. package/lib/tools/shared/point/arrow-point.js +0 -3
  102. package/lib/tools/shared/point/arrow-point.js.map +1 -1
  103. package/lib/tools/shared/point/arrow.js +0 -3
  104. package/lib/tools/shared/point/arrow.js.map +1 -1
  105. package/lib/tools/shared/point/base-point.js +0 -16
  106. package/lib/tools/shared/point/base-point.js.map +1 -1
  107. package/lib/tools/shared/point/index.js +0 -7
  108. package/lib/tools/shared/point/index.js.map +1 -1
  109. package/lib/tools/shared/styles.js +0 -1
  110. package/lib/tools/shared/styles.js.map +1 -1
  111. package/lib/tools/shared/types.js +0 -1
  112. package/lib/tools/shared/types.js.map +1 -1
  113. package/lib/tools/sine/component.js +0 -10
  114. package/lib/tools/sine/component.js.map +1 -1
  115. package/lib/tools/sine/index.js +0 -10
  116. package/lib/tools/sine/index.js.map +1 -1
  117. package/lib/tools/vector/component.js +0 -10
  118. package/lib/tools/vector/component.js.map +1 -1
  119. package/lib/tools/vector/index.js +0 -1
  120. package/lib/tools/vector/index.js.map +1 -1
  121. package/lib/undo-redo.js +0 -2
  122. package/lib/undo-redo.js.map +1 -1
  123. package/lib/use-debounce.js +0 -2
  124. package/lib/use-debounce.js.map +1 -1
  125. package/lib/utils.js +9 -26
  126. package/lib/utils.js.map +1 -1
  127. package/package.json +9 -9
  128. package/src/__tests__/bg.test.jsx +250 -0
  129. package/src/__tests__/coordinates-label.test.jsx +243 -0
  130. package/src/__tests__/graph-with-controls.test.jsx +9 -16
  131. package/src/__tests__/graph.test.jsx +560 -5
  132. package/src/__tests__/grid-setup.test.jsx +645 -0
  133. package/src/__tests__/grid.test.jsx +1 -1
  134. package/src/__tests__/key-legend.test.jsx +260 -0
  135. package/src/__tests__/label-svg-icon.test.jsx +278 -0
  136. package/src/__tests__/mark-label.test.jsx +1 -1
  137. package/src/__tests__/toggle-bar.test.jsx +0 -6
  138. package/src/__tests__/tool-menu.test.jsx +0 -4
  139. package/src/__tests__/use-debounce.test.js +1 -1
  140. package/src/__tests__/utils.test.js +15 -61
  141. package/src/axis/__tests__/axes.test.jsx +1 -1
  142. package/src/axis/axes.jsx +7 -21
  143. package/src/axis/index.js +1 -0
  144. package/src/bg.jsx +1 -1
  145. package/src/container/__tests__/actions.test.js +105 -0
  146. package/src/container/__tests__/index.test.jsx +319 -0
  147. package/src/container/__tests__/marks.test.js +172 -0
  148. package/src/container/__tests__/middleware.test.js +235 -0
  149. package/src/container/__tests__/reducer.test.js +324 -0
  150. package/src/container/index.jsx +2 -3
  151. package/src/coordinates-label.jsx +1 -7
  152. package/src/graph-with-controls.jsx +8 -6
  153. package/src/graph.jsx +2 -3
  154. package/src/grid-setup.jsx +1 -1
  155. package/src/key-legend.jsx +2 -1
  156. package/src/mark-label.jsx +7 -24
  157. package/src/toggle-bar.jsx +8 -1
  158. package/src/tools/absolute/__tests__/component.test.jsx +1 -2
  159. package/src/tools/absolute/component.jsx +2 -2
  160. package/src/tools/circle/__tests__/component.test.jsx +438 -0
  161. package/src/tools/circle/__tests__/index.test.js +480 -0
  162. package/src/tools/circle/bg-circle.jsx +2 -2
  163. package/src/tools/circle/component.jsx +10 -12
  164. package/src/tools/exponential/__tests__/component.test.jsx +0 -1
  165. package/src/tools/exponential/__tests__/index.test.js +729 -0
  166. package/src/tools/exponential/component.jsx +1 -1
  167. package/src/tools/line/__tests__/component.test.jsx +1 -0
  168. package/src/tools/line/component.jsx +4 -11
  169. package/src/tools/parabola/__tests__/component.test.jsx +0 -1
  170. package/src/tools/parabola/__tests__/index.test.js +470 -0
  171. package/src/tools/parabola/component.jsx +1 -1
  172. package/src/tools/point/__tests__/component.test.jsx +310 -2
  173. package/src/tools/point/__tests__/index.test.js +241 -0
  174. package/src/tools/point/component.jsx +1 -2
  175. package/src/tools/polygon/__tests__/component.test.jsx +391 -2
  176. package/src/tools/polygon/__tests__/index.test.js +237 -8
  177. package/src/tools/polygon/__tests__/line.test.jsx +13 -0
  178. package/src/tools/polygon/__tests__/polygon.test.jsx +19 -1
  179. package/src/tools/polygon/component.jsx +4 -14
  180. package/src/tools/polygon/line.jsx +1 -1
  181. package/src/tools/polygon/polygon.jsx +1 -1
  182. package/src/tools/ray/__tests__/component.test.jsx +1 -0
  183. package/src/tools/ray/component.jsx +3 -5
  184. package/src/tools/segment/__tests__/component.test.jsx +1 -0
  185. package/src/tools/segment/component.jsx +1 -1
  186. package/src/tools/shared/arrow-head.jsx +11 -6
  187. package/src/tools/shared/line/__tests__/index.test.jsx +1 -1
  188. package/src/tools/shared/line/__tests__/line-path.test.jsx +3 -3
  189. package/src/tools/shared/line/__tests__/with-root-edge.test.jsx +2 -2
  190. package/src/tools/shared/line/index.jsx +4 -6
  191. package/src/tools/shared/line/line-path.jsx +2 -8
  192. package/src/tools/shared/point/arrow-point.jsx +2 -5
  193. package/src/tools/sine/component.jsx +2 -2
  194. package/src/tools/vector/component.jsx +1 -1
  195. package/src/undo-redo.jsx +3 -9
  196. package/src/use-debounce.js +1 -1
  197. package/src/utils.js +1 -5
@@ -0,0 +1,172 @@
1
+ import marks from '../marks';
2
+
3
+ describe('marks reducer', () => {
4
+ describe('initial state', () => {
5
+ it('returns empty array as initial state', () => {
6
+ const state = marks(undefined, { type: 'INIT' });
7
+ expect(state).toEqual([]);
8
+ });
9
+
10
+ it('handles no state parameter', () => {
11
+ const state = marks(undefined, { type: 'UNKNOWN_ACTION' });
12
+ expect(Array.isArray(state)).toBe(true);
13
+ });
14
+ });
15
+
16
+ describe('CHANGE_MARKS action', () => {
17
+ it('updates state with new marks', () => {
18
+ const initialState = [];
19
+ const newMarks = [{ id: 1, type: 'point' }];
20
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
21
+
22
+ const state = marks(initialState, action);
23
+ expect(state).toEqual(newMarks);
24
+ });
25
+
26
+ it('replaces existing marks', () => {
27
+ const initialState = [{ id: 1, type: 'point' }];
28
+ const newMarks = [{ id: 2, type: 'line' }];
29
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
30
+
31
+ const state = marks(initialState, action);
32
+ expect(state).toEqual(newMarks);
33
+ expect(state.length).toBe(1);
34
+ expect(state[0].id).toBe(2);
35
+ });
36
+
37
+ it('handles empty marks array', () => {
38
+ const initialState = [{ id: 1, type: 'point' }];
39
+ const action = { type: 'CHANGE_MARKS', marks: [] };
40
+
41
+ const state = marks(initialState, action);
42
+ expect(state).toEqual([]);
43
+ });
44
+
45
+ it('handles multiple marks', () => {
46
+ const initialState = [];
47
+ const newMarks = [
48
+ { id: 1, type: 'point', x: 1, y: 2 },
49
+ { id: 2, type: 'line', from: { x: 0, y: 0 }, to: { x: 5, y: 5 } },
50
+ { id: 3, type: 'circle', center: { x: 3, y: 3 }, radius: 2 },
51
+ ];
52
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
53
+
54
+ const state = marks(initialState, action);
55
+ expect(state).toEqual(newMarks);
56
+ expect(state.length).toBe(3);
57
+ });
58
+
59
+ it('throws error when marks is not an array', () => {
60
+ const initialState = [];
61
+ const action = { type: 'CHANGE_MARKS', marks: 'not an array' };
62
+
63
+ expect(() => marks(initialState, action)).toThrow('marks must be an array');
64
+ });
65
+
66
+ it('throws error when marks is null', () => {
67
+ const initialState = [];
68
+ const action = { type: 'CHANGE_MARKS', marks: null };
69
+
70
+ expect(() => marks(initialState, action)).toThrow('marks must be an array');
71
+ });
72
+
73
+ it('throws error when marks is undefined', () => {
74
+ const initialState = [];
75
+ const action = { type: 'CHANGE_MARKS', marks: undefined };
76
+
77
+ expect(() => marks(initialState, action)).toThrow('marks must be an array');
78
+ });
79
+
80
+ it('throws error when marks is an object', () => {
81
+ const initialState = [];
82
+ const action = { type: 'CHANGE_MARKS', marks: { id: 1 } };
83
+
84
+ expect(() => marks(initialState, action)).toThrow('marks must be an array');
85
+ });
86
+
87
+ it('throws error when marks is a number', () => {
88
+ const initialState = [];
89
+ const action = { type: 'CHANGE_MARKS', marks: 123 };
90
+
91
+ expect(() => marks(initialState, action)).toThrow('marks must be an array');
92
+ });
93
+
94
+ it('preserves mark properties', () => {
95
+ const initialState = [];
96
+ const newMarks = [
97
+ {
98
+ id: 1,
99
+ type: 'point',
100
+ x: 10,
101
+ y: 20,
102
+ label: 'A',
103
+ correctness: { value: 'correct' },
104
+ editable: true,
105
+ interactive: true,
106
+ },
107
+ ];
108
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
109
+
110
+ const state = marks(initialState, action);
111
+ expect(state[0]).toEqual(newMarks[0]);
112
+ expect(state[0].correctness).toBeDefined();
113
+ expect(state[0].editable).toBe(true);
114
+ });
115
+ });
116
+
117
+ describe('unknown actions', () => {
118
+ it('returns current state for unknown action', () => {
119
+ const initialState = [{ id: 1, type: 'point' }];
120
+ const action = { type: 'UNKNOWN_ACTION' };
121
+
122
+ const state = marks(initialState, action);
123
+ expect(state).toBe(initialState);
124
+ });
125
+
126
+ it('returns current state for ADD_MARK action', () => {
127
+ const initialState = [{ id: 1, type: 'point' }];
128
+ const action = { type: 'ADD_MARK' };
129
+
130
+ const state = marks(initialState, action);
131
+ expect(state).toBe(initialState);
132
+ });
133
+
134
+ it('does not modify state for unknown actions', () => {
135
+ const initialState = [{ id: 1, type: 'point' }];
136
+ const action = { type: 'SOME_OTHER_ACTION', data: 'test' };
137
+
138
+ const state = marks(initialState, action);
139
+ expect(state).toEqual(initialState);
140
+ });
141
+ });
142
+
143
+ describe('immutability', () => {
144
+ it('does not mutate original state', () => {
145
+ const initialState = [{ id: 1, type: 'point' }];
146
+ const originalState = [...initialState];
147
+ const newMarks = [{ id: 2, type: 'line' }];
148
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
149
+
150
+ marks(initialState, action);
151
+ expect(initialState).toEqual(originalState);
152
+ });
153
+
154
+ it('returns new array reference for CHANGE_MARKS', () => {
155
+ const initialState = [{ id: 1, type: 'point' }];
156
+ const newMarks = [{ id: 1, type: 'point' }];
157
+ const action = { type: 'CHANGE_MARKS', marks: newMarks };
158
+
159
+ const state = marks(initialState, action);
160
+ expect(state).not.toBe(initialState);
161
+ expect(state).toBe(newMarks);
162
+ });
163
+
164
+ it('returns same reference for unknown actions', () => {
165
+ const initialState = [{ id: 1, type: 'point' }];
166
+ const action = { type: 'UNKNOWN_ACTION' };
167
+
168
+ const state = marks(initialState, action);
169
+ expect(state).toBe(initialState);
170
+ });
171
+ });
172
+ });
@@ -0,0 +1,235 @@
1
+ import { getLastAction, lastActionMiddleware } from '../middleware';
2
+
3
+ describe('lastActionMiddleware', () => {
4
+ describe('middleware functionality', () => {
5
+ it('creates middleware function', () => {
6
+ const middleware = lastActionMiddleware();
7
+ expect(typeof middleware).toBe('function');
8
+ });
9
+
10
+ it('middleware returns a function', () => {
11
+ const middleware = lastActionMiddleware();
12
+ const next = jest.fn();
13
+ const result = middleware(next);
14
+ expect(typeof result).toBe('function');
15
+ });
16
+
17
+ it('calls next with action', () => {
18
+ const middleware = lastActionMiddleware();
19
+ const next = jest.fn();
20
+ const dispatch = middleware(next);
21
+ const action = { type: 'TEST_ACTION' };
22
+
23
+ dispatch(action);
24
+ expect(next).toHaveBeenCalledWith(action);
25
+ });
26
+
27
+ it('returns result from next', () => {
28
+ const middleware = lastActionMiddleware();
29
+ const expectedResult = { status: 'success' };
30
+ const next = jest.fn(() => expectedResult);
31
+ const dispatch = middleware(next);
32
+ const action = { type: 'TEST_ACTION' };
33
+
34
+ const result = dispatch(action);
35
+ expect(result).toBe(expectedResult);
36
+ });
37
+
38
+ it('stores action before calling next', () => {
39
+ const middleware = lastActionMiddleware();
40
+ const next = jest.fn(() => {
41
+ const lastAction = getLastAction();
42
+ expect(lastAction).toBeDefined();
43
+ expect(lastAction.type).toBe('TEST_ACTION');
44
+ });
45
+ const dispatch = middleware(next);
46
+ const action = { type: 'TEST_ACTION' };
47
+
48
+ dispatch(action);
49
+ expect(next).toHaveBeenCalled();
50
+ });
51
+ });
52
+
53
+ describe('getLastAction', () => {
54
+ it('returns null initially', () => {
55
+ const lastAction = getLastAction();
56
+ expect(lastAction).toBeDefined();
57
+ });
58
+
59
+ it('returns last dispatched action', () => {
60
+ const middleware = lastActionMiddleware();
61
+ const next = jest.fn();
62
+ const dispatch = middleware(next);
63
+ const action = { type: 'MY_ACTION', payload: 'test' };
64
+
65
+ dispatch(action);
66
+ const lastAction = getLastAction();
67
+
68
+ expect(lastAction).toEqual(action);
69
+ expect(lastAction.type).toBe('MY_ACTION');
70
+ expect(lastAction.payload).toBe('test');
71
+ });
72
+
73
+ it('updates when new action is dispatched', () => {
74
+ const middleware = lastActionMiddleware();
75
+ const next = jest.fn();
76
+ const dispatch = middleware(next);
77
+
78
+ const action1 = { type: 'ACTION_1' };
79
+ dispatch(action1);
80
+ expect(getLastAction()).toEqual(action1);
81
+
82
+ const action2 = { type: 'ACTION_2' };
83
+ dispatch(action2);
84
+ expect(getLastAction()).toEqual(action2);
85
+ });
86
+
87
+ it('stores action with payload', () => {
88
+ const middleware = lastActionMiddleware();
89
+ const next = jest.fn();
90
+ const dispatch = middleware(next);
91
+ const action = {
92
+ type: 'CHANGE_MARKS',
93
+ marks: [{ id: 1, type: 'point' }],
94
+ };
95
+
96
+ dispatch(action);
97
+ const lastAction = getLastAction();
98
+
99
+ expect(lastAction.marks).toEqual(action.marks);
100
+ expect(lastAction.marks.length).toBe(1);
101
+ });
102
+
103
+ it('stores action with complex payload', () => {
104
+ const middleware = lastActionMiddleware();
105
+ const next = jest.fn();
106
+ const dispatch = middleware(next);
107
+ const action = {
108
+ type: 'COMPLEX_ACTION',
109
+ data: {
110
+ nested: {
111
+ value: 123,
112
+ array: [1, 2, 3],
113
+ },
114
+ },
115
+ };
116
+
117
+ dispatch(action);
118
+ const lastAction = getLastAction();
119
+
120
+ expect(lastAction.data.nested.value).toBe(123);
121
+ expect(lastAction.data.nested.array).toEqual([1, 2, 3]);
122
+ });
123
+ });
124
+
125
+ describe('action storage', () => {
126
+ it('overwrites previous action', () => {
127
+ const middleware = lastActionMiddleware();
128
+ const next = jest.fn();
129
+ const dispatch = middleware(next);
130
+
131
+ dispatch({ type: 'FIRST' });
132
+ dispatch({ type: 'SECOND' });
133
+ dispatch({ type: 'THIRD' });
134
+
135
+ const lastAction = getLastAction();
136
+ expect(lastAction.type).toBe('THIRD');
137
+ });
138
+
139
+ it('stores reference to action object', () => {
140
+ const middleware = lastActionMiddleware();
141
+ const next = jest.fn();
142
+ const dispatch = middleware(next);
143
+ const action = { type: 'TEST', data: { value: 1 } };
144
+
145
+ dispatch(action);
146
+ const lastAction = getLastAction();
147
+
148
+ // Modify original action
149
+ action.data.value = 2;
150
+
151
+ // Last action should reflect the change (same reference)
152
+ expect(lastAction.data.value).toBe(2);
153
+ });
154
+ });
155
+
156
+ describe('integration scenarios', () => {
157
+ it('works with multiple middleware calls', () => {
158
+ const middleware = lastActionMiddleware();
159
+ const next1 = jest.fn();
160
+ const next2 = jest.fn();
161
+
162
+ const dispatch1 = middleware(next1);
163
+ const dispatch2 = middleware(next2);
164
+
165
+ dispatch1({ type: 'ACTION_1' });
166
+ expect(getLastAction().type).toBe('ACTION_1');
167
+
168
+ dispatch2({ type: 'ACTION_2' });
169
+ expect(getLastAction().type).toBe('ACTION_2');
170
+ });
171
+
172
+ it('preserves action types', () => {
173
+ const middleware = lastActionMiddleware();
174
+ const next = jest.fn();
175
+ const dispatch = middleware(next);
176
+
177
+ const actionTypes = ['ADD_MARK', 'CHANGE_MARKS', 'DELETE_MARK', 'UPDATE_MARK'];
178
+
179
+ actionTypes.forEach((type) => {
180
+ dispatch({ type });
181
+ expect(getLastAction().type).toBe(type);
182
+ });
183
+ });
184
+
185
+ it('handles actions without type', () => {
186
+ const middleware = lastActionMiddleware();
187
+ const next = jest.fn();
188
+ const dispatch = middleware(next);
189
+ const action = { payload: 'data' };
190
+
191
+ dispatch(action);
192
+ const lastAction = getLastAction();
193
+
194
+ expect(lastAction).toEqual(action);
195
+ expect(lastAction.type).toBeUndefined();
196
+ });
197
+
198
+ it('handles empty action object', () => {
199
+ const middleware = lastActionMiddleware();
200
+ const next = jest.fn();
201
+ const dispatch = middleware(next);
202
+ const action = {};
203
+
204
+ dispatch(action);
205
+ const lastAction = getLastAction();
206
+
207
+ expect(lastAction).toEqual(action);
208
+ });
209
+ });
210
+
211
+ describe('error handling', () => {
212
+ it('stores action even if next throws error', () => {
213
+ const middleware = lastActionMiddleware();
214
+ const next = jest.fn(() => {
215
+ throw new Error('Next error');
216
+ });
217
+ const dispatch = middleware(next);
218
+ const action = { type: 'ERROR_ACTION' };
219
+
220
+ expect(() => dispatch(action)).toThrow('Next error');
221
+ expect(getLastAction()).toEqual(action);
222
+ });
223
+
224
+ it('propagates errors from next', () => {
225
+ const middleware = lastActionMiddleware();
226
+ const errorMessage = 'Test error';
227
+ const next = jest.fn(() => {
228
+ throw new Error(errorMessage);
229
+ });
230
+ const dispatch = middleware(next);
231
+
232
+ expect(() => dispatch({ type: 'TEST' })).toThrow(errorMessage);
233
+ });
234
+ });
235
+ });