Skip to content

Commit fa7a34c

Browse files
committed
[null] Add comprehensive tests for listeners, delete semantics, and transactions
1 parent 3a98bb4 commit fa7a34c

File tree

1 file changed

+150
-0
lines changed

1 file changed

+150
-0
lines changed

test/unit/core/store/store-nulls.test.ts

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,3 +187,153 @@ describe('Null with schemas', () => {
187187
expect(store.getCell('t1', 'r1', 'name')).toBe('Alice');
188188
});
189189
});
190+
191+
describe('Null listeners and events', () => {
192+
let store: Store;
193+
194+
beforeEach(() => {
195+
store = createStore();
196+
});
197+
198+
test('Cell listener fires when setting to null', () => {
199+
expect.assertions(2);
200+
store.addCellListener('t1', 'r1', 'c1', (_store, _table, _row, _cell, newCell, oldCell) => {
201+
expect(newCell).toBe(null);
202+
expect(oldCell).toBeUndefined();
203+
});
204+
205+
store.setCell('t1', 'r1', 'c1', null);
206+
});
207+
208+
test('Cell listener fires when changing from null', () => {
209+
store.setCell('t1', 'r1', 'c1', null);
210+
expect.assertions(2);
211+
store.addCellListener('t1', 'r1', 'c1', (_store, _table, _row, _cell, newCell, oldCell) => {
212+
expect(newCell).toBe('hello');
213+
expect(oldCell).toBe(null);
214+
});
215+
216+
store.setCell('t1', 'r1', 'c1', 'hello');
217+
});
218+
219+
test('Value listener fires when setting to null', () => {
220+
expect.assertions(2);
221+
store.addValueListener('v1', (_store, _valueId, newValue, oldValue) => {
222+
expect(newValue).toBe(null);
223+
expect(oldValue).toBeUndefined();
224+
});
225+
226+
store.setValue('v1', null);
227+
});
228+
229+
test('HasCell listener distinguishes null from deleted', () => {
230+
expect.assertions(2);
231+
let callCount = 0;
232+
store.addHasCellListener('t1', 'r1', 'c1', (_store, _table, _row, _cell, hasCell) => {
233+
if (callCount === 0) {
234+
expect(hasCell).toBe(true);
235+
} else if (callCount === 1) {
236+
expect(hasCell).toBe(false);
237+
}
238+
callCount++;
239+
});
240+
241+
store.setCell('t1', 'r1', 'c1', null);
242+
store.delCell('t1', 'r1', 'c1');
243+
});
244+
});
245+
246+
describe('Null vs Delete semantics', () => {
247+
let store: Store;
248+
249+
beforeEach(() => {
250+
store = createStore();
251+
});
252+
253+
test('setCell(null) creates a cell, delCell removes it', () => {
254+
store.setCell('t1', 'r1', 'c1', null);
255+
expect(store.hasCell('t1', 'r1', 'c1')).toBe(true);
256+
expect(store.getCell('t1', 'r1', 'c1')).toBe(null);
257+
expect(store.getCellIds('t1', 'r1')).toEqual(['c1']);
258+
259+
store.delCell('t1', 'r1', 'c1');
260+
expect(store.hasCell('t1', 'r1', 'c1')).toBe(false);
261+
expect(store.getCell('t1', 'r1', 'c1')).toBeUndefined();
262+
expect(store.getCellIds('t1', 'r1')).toEqual([]);
263+
});
264+
265+
test('setValue(null) creates a value, delValue removes it', () => {
266+
store.setValue('v1', null);
267+
expect(store.hasValue('v1')).toBe(true);
268+
expect(store.getValue('v1')).toBe(null);
269+
expect(store.getValueIds()).toEqual(['v1']);
270+
271+
store.delValue('v1');
272+
expect(store.hasValue('v1')).toBe(false);
273+
expect(store.getValue('v1')).toBeUndefined();
274+
expect(store.getValueIds()).toEqual([]);
275+
});
276+
277+
test('Null cells appear in iteration', () => {
278+
store.setTables({
279+
t1: {
280+
r1: {c1: 'hello', c2: null, c3: 42},
281+
},
282+
});
283+
284+
const cells: [string, any][] = [];
285+
store.forEachCell('t1', 'r1', (cellId, cell) => {
286+
cells.push([cellId, cell]);
287+
});
288+
289+
expect(cells).toEqual([
290+
['c1', 'hello'],
291+
['c2', null],
292+
['c3', 42],
293+
]);
294+
});
295+
296+
test('Null values appear in iteration', () => {
297+
store.setValues({v1: 'test', v2: null, v3: 123});
298+
299+
const values: [string, any][] = [];
300+
store.forEachValue((valueId, value) => {
301+
values.push([valueId, value]);
302+
});
303+
304+
expect(values).toEqual([
305+
['v1', 'test'],
306+
['v2', null],
307+
['v3', 123],
308+
]);
309+
});
310+
});
311+
312+
describe('Transactions with null', () => {
313+
let store: Store;
314+
315+
beforeEach(() => {
316+
store = createStore();
317+
});
318+
319+
test('Transaction can handle null values', () => {
320+
store.setCell('t1', 'r1', 'c1', 'initial');
321+
322+
store.startTransaction();
323+
store.setCell('t1', 'r1', 'c1', null);
324+
expect(store.getCell('t1', 'r1', 'c1')).toBe(null);
325+
store.finishTransaction();
326+
327+
expect(store.getCell('t1', 'r1', 'c1')).toBe(null);
328+
});
329+
330+
test('Transaction can commit null values', () => {
331+
store.transaction(() => {
332+
store.setCell('t1', 'r1', 'c1', null);
333+
store.setValue('v1', null);
334+
});
335+
336+
expect(store.getCell('t1', 'r1', 'c1')).toBe(null);
337+
expect(store.getValue('v1')).toBe(null);
338+
});
339+
});

0 commit comments

Comments
 (0)