@@ -2,6 +2,7 @@ import { describe, it, expect, beforeEach, expectTypeOf } from 'vitest'
22import { Temporal } from '@js-temporal/polyfill'
33import { createDate , createDateContext , createDateFallback , createDatePlugin , useDate } from './index'
44import { Vuetify0DateAdapter } from './adapters/v0'
5+ import type { DateAdapter , DateContext } from './index'
56
67describe ( 'useDate' , ( ) => {
78 describe ( 'Vuetify0DateAdapter' , ( ) => {
@@ -91,6 +92,21 @@ describe('useDate', () => {
9192 expect ( date ) . toBeNull ( )
9293 } )
9394
95+ it ( 'should create Feb 29 in leap year' , ( ) => {
96+ const date = adapter . date ( '2024-02-29T10:30:00' )
97+
98+ expect ( date ) . not . toBeNull ( )
99+ expect ( date ! . year ) . toBe ( 2024 )
100+ expect ( date ! . month ) . toBe ( 2 )
101+ expect ( date ! . day ) . toBe ( 29 )
102+ } )
103+
104+ it ( 'should return null for Feb 29 in non-leap year' , ( ) => {
105+ const date = adapter . date ( '2023-02-29T10:30:00' )
106+
107+ expect ( date ) . toBeNull ( )
108+ } )
109+
94110 it ( 'should convert to JavaScript Date' , ( ) => {
95111 const temporal = Temporal . PlainDateTime . from ( '2024-06-15T10:30:00' )
96112 const jsDate = adapter . toJsDate ( temporal )
@@ -156,6 +172,16 @@ describe('useDate', () => {
156172
157173 expect ( month . toLowerCase ( ) ) . toContain ( 'juin' )
158174 } )
175+
176+ it ( 'should handle invalid locale gracefully' , ( ) => {
177+ // Intl.DateTimeFormat falls back to default locale for invalid codes
178+ const invalidAdapter = new Vuetify0DateAdapter ( 'invalid-XX' )
179+ const date = invalidAdapter . date ( '2024-06-15T10:30:00' ) !
180+
181+ // Should not throw, Intl falls back gracefully
182+ expect ( ( ) => invalidAdapter . format ( date , 'fullDate' ) ) . not . toThrow ( )
183+ expect ( invalidAdapter . format ( date , 'year' ) ) . toBe ( '2024' )
184+ } )
159185 } )
160186
161187 describe ( 'navigation - start/end boundaries' , ( ) => {
@@ -1373,6 +1399,59 @@ describe('useDate', () => {
13731399
13741400 wrapper . unmount ( )
13751401 } )
1402+
1403+ it ( 'should sync adapter locale dynamically when context created in component' , async ( ) => {
1404+ const { mount } = await import ( '@vue/test-utils' )
1405+ const { defineComponent, nextTick } = await import ( 'vue' )
1406+ const { createLocalePlugin, useLocale } = await import ( '#v0/composables/useLocale' )
1407+
1408+ // Locale plugin provides context at app level
1409+ const localePlugin = createLocalePlugin ( {
1410+ default : 'en' ,
1411+ messages : {
1412+ en : { hello : 'Hello' } ,
1413+ de : { hello : 'Hallo' } ,
1414+ } ,
1415+ } )
1416+
1417+ let selectLocaleFn : ( ( id : string ) => void ) | null = null
1418+
1419+ const TestComponent = defineComponent ( {
1420+ setup ( ) {
1421+ const localeContext = useLocale ( )
1422+ // Create date context INSIDE component for dynamic sync
1423+ const dateContext = createDate ( {
1424+ localeMap : { en : 'en-US' , de : 'de-DE' } ,
1425+ } )
1426+
1427+ selectLocaleFn = localeContext . select
1428+
1429+ return {
1430+ dateLocale : dateContext . locale ,
1431+ adapterLocale : ( ) => dateContext . adapter . locale ,
1432+ }
1433+ } ,
1434+ template : '<div>{{ dateLocale }} - {{ adapterLocale() }}</div>' ,
1435+ } )
1436+
1437+ const wrapper = mount ( TestComponent , {
1438+ global : {
1439+ plugins : [ localePlugin ] ,
1440+ } ,
1441+ } )
1442+
1443+ // Initial locale should be mapped from useLocale's selection
1444+ expect ( wrapper . text ( ) ) . toContain ( 'en-US' )
1445+
1446+ // Change locale dynamically
1447+ selectLocaleFn ! ( 'de' )
1448+ await nextTick ( )
1449+
1450+ // Adapter locale should sync via watchEffect (context created in component)
1451+ expect ( wrapper . text ( ) ) . toContain ( 'de-DE' )
1452+
1453+ wrapper . unmount ( )
1454+ } )
13761455 } )
13771456
13781457 describe ( 'type safety (compile-time)' , ( ) => {
@@ -1386,6 +1465,13 @@ describe('useDate', () => {
13861465 const customCtx = createDate ( { adapter : customAdapter } )
13871466 expectTypeOf ( customCtx . adapter . date ( ) ) . toEqualTypeOf < Temporal . PlainDateTime | null > ( )
13881467
1468+ // Mock adapter with different type: verifies generic inference (type-only)
1469+ const mockDateAdapter = { } as DateAdapter < Date >
1470+ const mockCtx = createDate ( { adapter : mockDateAdapter } )
1471+ // Type assertions only - mock adapter has no runtime implementation
1472+ expectTypeOf ( mockCtx . adapter ) . toEqualTypeOf < DateAdapter < Date > > ( )
1473+ expectTypeOf ( mockCtx ) . toEqualTypeOf < DateContext < Date > > ( )
1474+
13891475 // Context trinity returns correct type
13901476 const [ , , ctx ] = createDateContext ( )
13911477 expectTypeOf ( ctx . adapter . date ( ) ) . toEqualTypeOf < Temporal . PlainDateTime | null > ( )
0 commit comments