99 * be spread across those arms, so a new type either satisfies them or fails
1010 * here.
1111 */
12+ import { Table as TableIcon } from '@sim/emcn/icons'
1213import { describe , expect , it } from 'vitest'
1314import type { ColumnType } from '@/lib/table/column-types'
1415import {
@@ -42,6 +43,17 @@ describe('registry shape', () => {
4243 expect ( isColumnType ( 'currency' ) ) . toBe ( true )
4344 } )
4445
46+ it ( 'registers reference columns as configured string-backed columns' , ( ) => {
47+ const definition = COLUMN_TYPE_REGISTRY . reference
48+
49+ expect ( definition . label ) . toBe ( 'Reference' )
50+ expect ( definition . icon ) . toBe ( TableIcon )
51+ expect ( definition . requiresConfigurationOnCreate ) . toBe ( true )
52+ expect ( definition . hasConfiguration ) . toBe ( true )
53+ expect ( definition . ownedMetadata ) . toEqual ( [ 'referenceTableId' ] )
54+ expect ( definition . jsonbCast ) . toBeNull ( )
55+ } )
56+
4557 it ( 'only casts to numeric/timestamptz for types whose storage is actually that' , ( ) => {
4658 // A wrong cast makes every filter and sort on the column fail in SQL.
4759 for ( const definition of ALL_COLUMN_TYPES ) {
@@ -190,19 +202,23 @@ describe('metadata ownership', () => {
190202 const options = [ { id : 'opt_a' , name : 'A' } ]
191203
192204 it . each `
193- label | definition | valid | needle
194- ${ 'options on select' } | ${ column ( { type : 'select' , options } ) } | ${ true } | ${ '' }
195- ${ 'options on string' } | ${ column ( { type : 'string' , options } ) } | ${ false } | ${ 'cannot define options' }
196- ${ 'options on currency' } | ${ column ( { type : 'currency' , options } ) } | ${ false } | ${ 'cannot define options' }
197- ${ 'multiple on number' } | ${ column ( { type : 'number' , multiple : true } ) } | ${ false } | ${ 'cannot be multiple' }
198- ${ 'code on currency' } | ${ column ( { type : 'currency' , currencyCode : 'USD' } ) } | ${ true } | ${ '' }
199- ${ 'code on number' } | ${ column ( { type : 'number' , currencyCode : 'USD' } ) } | ${ false } | ${ 'cannot define a currency' }
200- ${ 'code on select' } | ${ column ( { type : 'select' , currencyCode : 'USD' , options } ) } | ${ false } | ${ 'cannot define a currency' }
201- ${ 'unsupported code' } | ${ column ( { type : 'currency' , currencyCode : 'ZZZ' } ) } | ${ false } | ${ 'invalid currency code' }
202- ${ 'unique on select' } | ${ column ( { type : 'select' , unique : true , options } ) } | ${ false } | ${ 'cannot be unique' }
203- ${ 'unique on currency' } | ${ column ( { type : 'currency' , unique : true } ) } | ${ true } | ${ '' }
204- ${ 'select with no option' } | ${ column ( { type : 'select' } ) } | ${ false } | ${ 'at least one option' }
205- ${ 'unknown type' } | ${ column ( { type : 'percent' as ColumnDefinition [ 'type' ] } ) } | ${ false } | ${ 'invalid type' }
205+ label | definition | valid | needle
206+ ${ 'options on select' } | ${ column ( { type : 'select' , options } ) } | ${ true } | ${ '' }
207+ ${ 'options on string' } | ${ column ( { type : 'string' , options } ) } | ${ false } | ${ 'cannot define options' }
208+ ${ 'options on currency' } | ${ column ( { type : 'currency' , options } ) } | ${ false } | ${ 'cannot define options' }
209+ ${ 'multiple on number' } | ${ column ( { type : 'number' , multiple : true } ) } | ${ false } | ${ 'cannot be multiple' }
210+ ${ 'code on currency' } | ${ column ( { type : 'currency' , currencyCode : 'USD' } ) } | ${ true } | ${ '' }
211+ ${ 'code on number' } | ${ column ( { type : 'number' , currencyCode : 'USD' } ) } | ${ false } | ${ 'cannot define a currency' }
212+ ${ 'code on select' } | ${ column ( { type : 'select' , currencyCode : 'USD' , options } ) } | ${ false } | ${ 'cannot define a currency' }
213+ ${ 'unsupported code' } | ${ column ( { type : 'currency' , currencyCode : 'ZZZ' } ) } | ${ false } | ${ 'invalid currency code' }
214+ ${ 'target on reference' } | ${ column ( { type : 'reference' , referenceTableId : 'tbl_anything' } ) } | ${ true } | ${ '' }
215+ ${ 'missing target' } | ${ column ( { type : 'reference' } ) } | ${ false } | ${ 'reference table' }
216+ ${ 'empty target' } | ${ column ( { type : 'reference' , referenceTableId : '' } ) } | ${ false } | ${ 'reference table' }
217+ ${ 'target on string' } | ${ column ( { type : 'string' , referenceTableId : 'tbl_other' } ) } | ${ false } | ${ 'reference another table' }
218+ ${ 'unique on select' } | ${ column ( { type : 'select' , unique : true , options } ) } | ${ false } | ${ 'cannot be unique' }
219+ ${ 'unique on currency' } | ${ column ( { type : 'currency' , unique : true } ) } | ${ true } | ${ '' }
220+ ${ 'select with no option' } | ${ column ( { type : 'select' } ) } | ${ false } | ${ 'at least one option' }
221+ ${ 'unknown type' } | ${ column ( { type : 'percent' as ColumnDefinition [ 'type' ] } ) } | ${ false } | ${ 'invalid type' }
206222 ` (
207223 'rejects $label' ,
208224 ( {
@@ -219,6 +235,23 @@ describe('metadata ownership', () => {
219235 if ( ! valid ) expect ( result . errors . join ( ' ' ) . toLowerCase ( ) ) . toContain ( needle . toLowerCase ( ) )
220236 }
221237 )
238+
239+ it ( 'accepts arbitrary row-id strings without resolving them' , ( ) => {
240+ const column = {
241+ name : 'account' ,
242+ type : 'reference' ,
243+ referenceTableId : 'tbl_accounts' ,
244+ } as ColumnDefinition
245+ const definition = COLUMN_TYPE_REGISTRY . reference
246+
247+ expect ( definition . coerce ( 'not-a-real-row-id' , column ) ) . toEqual ( {
248+ ok : true ,
249+ value : 'not-a-real-row-id' ,
250+ } )
251+ expect ( definition . coerce ( 97 , column ) ) . toEqual ( { ok : true , value : '97' } )
252+ expect ( definition . coerce ( true , column ) ) . toEqual ( { ok : true , value : 'true' } )
253+ expect ( definition . validateCell ( 'not-a-real-row-id' , column ) ) . toBeNull ( )
254+ } )
222255} )
223256
224257/**
0 commit comments