Files
supabase/apps/studio/components/grid/utils/queueOperationUtils.test.ts
Ali Waseem ea1b95d29b feature: batch and save operations for cell content updates (#42120)
* added initial queue operations and feature flag

* updated types

* added dirty state tracking on columns

* updated queue operations

* updated operation types and queue

* updated spacing

* removed on cancel

* updated to support saving

* updated to include eye details

* updated spacing for orders

* updated to support shortcuts

* added feature preview

* updated to unify queue methods

* added key generation

* used unique keys rather than random uuid

* updated based on code review

* operation key

* updated handle cancel

* updated remove operation button

* updated views for toast

* updated logic to support optimistic updates

* updated types

* code cleanup: remove LLM slop

* updated PR bug

* updated preview for logout

* updated based on code review

* removed use effect as it was causing problems

* fixed toast mounting away from sql editor

* removed toast for dedicated action bar

* cleaned up logic

* updated queue operations

* renamed method

* updated name for types

* updated comment

* fixed code rabbit solution

* added check for changed column

* added tests
2026-01-28 06:54:30 -07:00

218 lines
6.8 KiB
TypeScript

import { describe, test, expect } from 'vitest'
import {
generateTableChangeKey,
generateTableChangeKeyFromOperation,
rowMatchesIdentifiers,
applyCellEdit,
} from './queueOperationUtils'
import { QueuedOperationType } from '@/state/table-editor-operation-queue.types'
describe('generateTableChangeKey', () => {
test('should generate key with row identifiers', () => {
const key = generateTableChangeKey({
type: QueuedOperationType.EDIT_CELL_CONTENT,
tableId: 1,
columnName: 'name',
rowIdentifiers: { id: 1 },
})
expect(key).toBe('edit_cell_content:1:name:id:1')
})
test('should generate key with empty row identifiers', () => {
const key = generateTableChangeKey({
type: QueuedOperationType.EDIT_CELL_CONTENT,
tableId: 1,
columnName: 'name',
rowIdentifiers: {},
})
expect(key).toBe('edit_cell_content:1:name:')
})
test('should generate key with multiple row identifiers sorted alphabetically', () => {
const key = generateTableChangeKey({
type: QueuedOperationType.EDIT_CELL_CONTENT,
tableId: 1,
columnName: 'name',
rowIdentifiers: { z_id: 3, a_id: 1 },
})
expect(key).toBe('edit_cell_content:1:name:a_id:1|z_id:3')
})
})
describe('generateTableChangeKeyFromOperation', () => {
test('should generate key from EDIT_CELL_CONTENT operation', () => {
const operation = {
type: QueuedOperationType.EDIT_CELL_CONTENT,
tableId: 1,
payload: {
rowIdentifiers: { id: 1 },
columnName: 'name',
oldValue: 'old',
newValue: 'new',
table: {} as any,
},
}
const key = generateTableChangeKeyFromOperation(operation)
expect(key).toBe('edit_cell_content:1:name: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(() => generateTableChangeKeyFromOperation(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('applyCellEdit', () => {
test('should apply cell edit to matching row', () => {
const rows = [
{ idx: 0, id: 1, name: 'old' },
{ idx: 1, id: 2, name: 'test' },
]
const result = applyCellEdit(rows, 'name', { id: 1 }, 'new')
expect(result).toEqual([
{ idx: 0, id: 1, name: 'new' },
{ idx: 1, id: 2, name: 'test' },
])
})
test('should not affect non-matching rows', () => {
const rows = [
{ idx: 0, id: 1, name: 'old' },
{ idx: 1, id: 2, name: 'test' },
]
const result = applyCellEdit(rows, 'name', { id: 3 }, 'new')
expect(result).toEqual([
{ idx: 0, id: 1, name: 'old' },
{ idx: 1, id: 2, name: 'test' },
])
})
test('should create new row instances for matching row', () => {
const rows = [{ idx: 0, id: 1, name: 'old' }]
const result = applyCellEdit(rows, 'name', { id: 1 }, 'new')
expect(result[0]).not.toBe(rows[0])
expect(result[0]).toEqual({ idx: 0, id: 1, name: 'new' })
})
test('should not modify original array', () => {
const rows = [{ idx: 0, id: 1, name: 'old' }]
const originalRows = [...rows]
applyCellEdit(rows, 'name', { id: 1 }, 'new')
expect(rows).toEqual(originalRows)
})
test('should handle multiple matching rows with composite keys', () => {
const rows = [
{ idx: 0, id: 1, org_id: 10, name: 'old1' },
{ idx: 1, id: 1, org_id: 20, name: 'old2' },
{ idx: 2, id: 2, org_id: 10, name: 'old3' },
]
const result = applyCellEdit(rows, 'name', { id: 1, org_id: 10 }, 'new')
expect(result).toEqual([
{ idx: 0, id: 1, org_id: 10, name: 'new' },
{ idx: 1, id: 1, org_id: 20, name: 'old2' },
{ idx: 2, id: 2, org_id: 10, name: 'old3' },
])
})
test('should handle setting value to null', () => {
const rows = [{ idx: 0, id: 1, name: 'test' }]
const result = applyCellEdit(rows, 'name', { id: 1 }, null)
expect(result).toEqual([{ idx: 0, id: 1, name: null }])
})
test('should handle setting value to undefined', () => {
const rows = [{ idx: 0, id: 1, name: 'test' }]
const result = applyCellEdit(rows, 'name', { id: 1 }, undefined)
expect(result).toEqual([{ idx: 0, id: 1, name: undefined }])
})
test('should handle numeric values', () => {
const rows = [{ idx: 0, id: 1, count: 0 }]
const result = applyCellEdit(rows, 'count', { id: 1 }, 42)
expect(result).toEqual([{ idx: 0, id: 1, count: 42 }])
})
test('should handle object values', () => {
const rows = [{ idx: 0, id: 1, data: null }]
const newValue = { nested: { value: 123 } }
const result = applyCellEdit(rows, 'data', { id: 1 }, newValue)
expect(result).toEqual([{ idx: 0, id: 1, data: newValue }])
})
test('should handle empty rows array', () => {
const rows: any[] = []
const result = applyCellEdit(rows, 'name', { id: 1 }, 'new')
expect(result).toEqual([])
})
test('should update all matching rows with same identifier', () => {
const rows = [
{ idx: 0, id: 1, name: 'row1' },
{ idx: 1, id: 1, name: 'row2' },
{ idx: 2, id: 2, name: 'row3' },
]
const result = applyCellEdit(rows, 'name', { id: 1 }, 'updated')
expect(result).toEqual([
{ idx: 0, id: 1, name: 'updated' },
{ idx: 1, id: 1, name: 'updated' },
{ idx: 2, id: 2, name: 'row3' },
])
})
})