| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452 |
- // @ts-nocheck
- import { describe, expect, test } from 'vitest'
- import type { SupaRow } from '../types'
- import {
- formatGridDataWithOperationValues,
- generateTableChangeKey,
- rowMatchesIdentifiers,
- } from './queueOperationUtils'
- import {
- QueuedOperationType,
- type NewAddRowOperation,
- type NewDeleteRowOperation,
- type NewEditCellContentOperation,
- type QueuedOperation,
- } from '@/state/table-editor-operation-queue.types'
- describe('generateTableChangeKey', () => {
- test('should generate key for EDIT_CELL_CONTENT with row identifiers', () => {
- const operation: NewEditCellContentOperation = {
- type: QueuedOperationType.EDIT_CELL_CONTENT,
- tableId: 1,
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- }
- const key = generateTableChangeKey(operation)
- expect(key).toBe('edit_cell_content:1:name:id:1')
- })
- test('should generate key for EDIT_CELL_CONTENT with empty row identifiers', () => {
- const operation: NewEditCellContentOperation = {
- type: QueuedOperationType.EDIT_CELL_CONTENT,
- tableId: 1,
- payload: {
- rowIdentifiers: {},
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- }
- const key = generateTableChangeKey(operation)
- expect(key).toBe('edit_cell_content:1:name:')
- })
- test('should generate key with multiple row identifiers sorted alphabetically', () => {
- const operation: NewEditCellContentOperation = {
- type: QueuedOperationType.EDIT_CELL_CONTENT,
- tableId: 1,
- payload: {
- rowIdentifiers: { z_id: 3, a_id: 1 },
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- }
- const key = generateTableChangeKey(operation)
- expect(key).toBe('edit_cell_content:1:name:a_id:1|z_id:3')
- })
- test('should generate key for ADD_ROW operation', () => {
- const operation: NewAddRowOperation = {
- type: QueuedOperationType.ADD_ROW,
- tableId: 1,
- payload: {
- tempId: 'temp-123',
- rowData: { idx: -1, __tempId: 'temp-123' },
- table: {} as any,
- },
- }
- const key = generateTableChangeKey(operation)
- expect(key).toBe('add_row:1:temp-123')
- })
- test('should generate key for DELETE_ROW operation', () => {
- const operation: NewDeleteRowOperation = {
- type: QueuedOperationType.DELETE_ROW,
- tableId: 1,
- payload: {
- rowIdentifiers: { id: 1 },
- originalRow: { idx: 0, id: 1 },
- table: {} as any,
- },
- }
- const key = generateTableChangeKey(operation)
- expect(key).toBe('delete_row:1:id:1')
- })
- test('should throw error for unknown operation type', () => {
- const operation = {
- type: 'unknown' as any,
- tableId: 1,
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- }
- expect(() => generateTableChangeKey(operation)).toThrow('Unknown operation type')
- })
- })
- describe('rowMatchesIdentifiers', () => {
- test('should return false for empty row identifiers', () => {
- const result = rowMatchesIdentifiers({ id: 1 }, {})
- expect(result).toBe(false)
- })
- test('should match row with single identifier', () => {
- const result = rowMatchesIdentifiers({ id: 1 }, { id: 1 })
- expect(result).toBe(true)
- })
- test('should match row with multiple identifiers', () => {
- const result = rowMatchesIdentifiers(
- { id: 1, email: 'test@test.com' },
- { id: 1, email: 'test@test.com' }
- )
- expect(result).toBe(true)
- })
- test('should not match row with different values', () => {
- const result = rowMatchesIdentifiers({ id: 2 }, { id: 1 })
- expect(result).toBe(false)
- })
- test('should not match row with missing identifier keys', () => {
- const result = rowMatchesIdentifiers({ id: 1 }, { id: 1, email: 'test@test.com' })
- expect(result).toBe(false)
- })
- test('should match row with extra keys', () => {
- const result = rowMatchesIdentifiers({ id: 1, name: 'John', age: 30 }, { id: 1 })
- expect(result).toBe(true)
- })
- test('should match with null values', () => {
- const result = rowMatchesIdentifiers({ id: null }, { id: null })
- expect(result).toBe(true)
- })
- test('should not match with undefined values in row', () => {
- const result = rowMatchesIdentifiers({ id: undefined, name: 'test' }, { id: 1 })
- expect(result).toBe(false)
- })
- })
- describe('formatGridDataWithOperationValues', () => {
- const makeRow = (idx: number, data: Record<string, unknown> = {}): SupaRow => ({
- idx,
- ...data,
- })
- const makeEditOp = (
- overrides: Partial<QueuedOperation & { payload: any }> = {}
- ): QueuedOperation => ({
- id: 'op-1',
- tableId: 1,
- timestamp: Date.now(),
- type: QueuedOperationType.EDIT_CELL_CONTENT,
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- ...overrides,
- })
- const makeDeleteOp = (
- rowIdentifiers: Record<string, unknown>,
- originalRow: SupaRow
- ): QueuedOperation => ({
- id: 'op-del',
- tableId: 1,
- timestamp: Date.now(),
- type: QueuedOperationType.DELETE_ROW,
- payload: { rowIdentifiers, originalRow, table: {} as any },
- })
- const makeAddOp = (tempId: string, rowData: Record<string, unknown> = {}): QueuedOperation => ({
- id: 'op-add',
- tableId: 1,
- timestamp: Date.now(),
- type: QueuedOperationType.ADD_ROW,
- payload: {
- tempId,
- rowData: { idx: Number(tempId), __tempId: tempId, ...rowData },
- table: {} as any,
- },
- })
- test('should return rows unchanged when there are no operations', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' }), makeRow(1, { id: 2, name: 'Bob' })]
- const result = formatGridDataWithOperationValues({ operations: [], rows })
- expect(result).toEqual(rows)
- })
- test('should apply EDIT_CELL_CONTENT to matching row', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' }), makeRow(1, { id: 2, name: 'Bob' })]
- const op = makeEditOp({
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result[0]).toEqual({ idx: 0, id: 1, name: 'Updated' })
- expect(result[1]).toEqual(rows[1])
- })
- test('should not modify rows when EDIT_CELL_CONTENT does not match any row', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const op = makeEditOp({
- payload: {
- rowIdentifiers: { id: 999 },
- columnName: 'name',
- oldValue: 'old',
- newValue: 'new',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result).toEqual(rows)
- })
- test('should apply multiple EDIT_CELL_CONTENT operations to different rows', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' }), makeRow(1, { id: 2, name: 'Bob' })]
- const op1 = makeEditOp({
- id: 'op-1',
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated Alice',
- table: {} as any,
- },
- })
- const op2 = makeEditOp({
- id: 'op-2',
- payload: {
- rowIdentifiers: { id: 2 },
- columnName: 'name',
- oldValue: 'Bob',
- newValue: 'Updated Bob',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op1, op2], rows })
- expect(result[0].name).toBe('Updated Alice')
- expect(result[1].name).toBe('Updated Bob')
- })
- test('multiple operations targeting the same row preserve all column changes', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice', email: 'alice@test.com' })]
- const op1 = makeEditOp({
- id: 'op-1',
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated',
- table: {} as any,
- },
- })
- const op2 = makeEditOp({
- id: 'op-2',
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'email',
- oldValue: 'alice@test.com',
- newValue: 'updated@test.com',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op1, op2], rows })
- // Both column edits should be preserved
- expect(result[0].name).toBe('Updated')
- expect(result[0].email).toBe('updated@test.com')
- })
- test('should mark matching row as deleted for DELETE_ROW operation', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' }), makeRow(1, { id: 2, name: 'Bob' })]
- const op = makeDeleteOp({ id: 1 }, rows[0])
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result[0].__isDeleted).toBe(true)
- expect(result[1].__isDeleted).toBeUndefined()
- })
- test('should not modify rows when DELETE_ROW does not match any row', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const op = makeDeleteOp({ id: 999 }, makeRow(0, { id: 999 }))
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result[0].__isDeleted).toBeUndefined()
- })
- test('should not mutate the original rows array', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const op = makeEditOp({
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(rows[0].name).toBe('Alice')
- expect(result[0].name).toBe('Updated')
- })
- test('should handle mixed operation types', () => {
- const rows = [
- makeRow(0, { id: 1, name: 'Alice' }),
- makeRow(1, { id: 2, name: 'Bob' }),
- makeRow(2, { id: 3, name: 'Charlie' }),
- ]
- const editOp = makeEditOp({
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated Alice',
- table: {} as any,
- },
- })
- const deleteOp = makeDeleteOp({ id: 2 }, rows[1])
- const result = formatGridDataWithOperationValues({
- operations: [editOp, deleteOp],
- rows,
- })
- expect(result[0].name).toBe('Updated Alice')
- expect(result[1].__isDeleted).toBe(true)
- expect(result[2]).toEqual(rows[2])
- })
- test('should prepend new row for ADD_ROW operation', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const op = makeAddOp('-100', { name: 'New Row' })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result).toHaveLength(2)
- expect(result[0]).toMatchObject({ __tempId: '-100', name: 'New Row' })
- expect(result[1]).toEqual(rows[0])
- })
- test('should update existing pending row for ADD_ROW with same tempId', () => {
- const rows: SupaRow[] = [
- { idx: -100, __tempId: '-100', name: 'Original' } as any,
- makeRow(1, { id: 1, name: 'Alice' }),
- ]
- const op = makeAddOp('-100', { name: 'Updated' })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result).toHaveLength(2)
- expect(result[0]).toMatchObject({ __tempId: '-100', name: 'Updated' })
- })
- test('should handle multiple ADD_ROW operations', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const op1 = makeAddOp('-100', { name: 'Row 1' })
- const op2 = makeAddOp('-200', { name: 'Row 2' })
- const result = formatGridDataWithOperationValues({ operations: [op1, op2], rows })
- expect(result).toHaveLength(3)
- expect(result[0]).toMatchObject({ __tempId: '-200', name: 'Row 2' })
- expect(result[1]).toMatchObject({ __tempId: '-100', name: 'Row 1' })
- })
- test('should correctly delete a row after adding a new row (ADD then DELETE)', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' }), makeRow(1, { id: 2, name: 'Bob' })]
- const addOp = makeAddOp('-100', { name: 'New Row' })
- const deleteOp = makeDeleteOp({ id: 1 }, rows[0])
- const result = formatGridDataWithOperationValues({
- operations: [addOp, deleteOp],
- rows,
- })
- expect(result).toHaveLength(3)
- // New row should be preserved at position 0
- expect(result[0]).toMatchObject({ __tempId: '-100', name: 'New Row' })
- expect(result[0].__isDeleted).toBeUndefined()
- // Deleted row should be marked
- expect(result[1].__isDeleted).toBe(true)
- expect(result[1].id).toBe(1)
- // Other row unaffected
- expect(result[2]).toEqual(rows[1])
- })
- test('should correctly edit a row after adding a new row (ADD then EDIT)', () => {
- const rows = [makeRow(0, { id: 1, name: 'Alice' })]
- const addOp = makeAddOp('-100', { name: 'New Row' })
- const editOp = makeEditOp({
- payload: {
- rowIdentifiers: { id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated Alice',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({
- operations: [addOp, editOp],
- rows,
- })
- expect(result).toHaveLength(2)
- expect(result[0]).toMatchObject({ __tempId: '-100', name: 'New Row' })
- expect(result[1].name).toBe('Updated Alice')
- })
- test('should handle EDIT_CELL_CONTENT with composite primary keys', () => {
- const rows = [makeRow(0, { tenant_id: 'a', user_id: 1, name: 'Alice' })]
- const op = makeEditOp({
- payload: {
- rowIdentifiers: { tenant_id: 'a', user_id: 1 },
- columnName: 'name',
- oldValue: 'Alice',
- newValue: 'Updated',
- table: {} as any,
- },
- })
- const result = formatGridDataWithOperationValues({ operations: [op], rows })
- expect(result[0].name).toBe('Updated')
- })
- })
|