@@ -36,6 +36,24 @@ describe('useDate', () => {
3636 expect ( date ! . day ) . toBe ( 15 )
3737 } )
3838
39+ it ( 'should create date from ZonedDateTime' , ( ) => {
40+ const input = Temporal . ZonedDateTime . from ( '2024-06-15T10:30:00[America/New_York]' )
41+ const date = adapter . date ( input )
42+
43+ expect ( date ) . not . toBeNull ( )
44+ expect ( date ! . year ) . toBe ( 2024 )
45+ expect ( date ! . month ) . toBe ( 6 )
46+ expect ( date ! . day ) . toBe ( 15 )
47+ expect ( date ! . hour ) . toBe ( 10 )
48+ expect ( date ! . minute ) . toBe ( 30 )
49+ } )
50+
51+ it ( 'should validate ZonedDateTime as valid' , ( ) => {
52+ const zoned = Temporal . ZonedDateTime . from ( '2024-06-15T10:30:00[UTC]' )
53+
54+ expect ( adapter . isValid ( zoned ) ) . toBe ( true )
55+ } )
56+
3957 it ( 'should create date from JavaScript Date' , ( ) => {
4058 const jsDate = new Date ( 2024 , 5 , 15 , 10 , 30 , 0 ) // June 15, 2024
4159 const date = adapter . date ( jsDate )
@@ -614,6 +632,28 @@ describe('useDate', () => {
614632 expect ( formatted ) . toBe ( '02:30 PM' )
615633 } )
616634
635+ it ( 'should format midnight as 12:00 AM' , ( ) => {
636+ const midnight = adapter . date ( '2024-06-15T00:00:00' ) !
637+ expect ( adapter . formatByString ( midnight , 'h:mm A' ) ) . toBe ( '12:00 AM' )
638+ expect ( adapter . formatByString ( midnight , 'hh:mm a' ) ) . toBe ( '12:00 am' )
639+ } )
640+
641+ it ( 'should format noon as 12:00 PM' , ( ) => {
642+ const noon = adapter . date ( '2024-06-15T12:00:00' ) !
643+ expect ( adapter . formatByString ( noon , 'h:mm A' ) ) . toBe ( '12:00 PM' )
644+ expect ( adapter . formatByString ( noon , 'hh:mm a' ) ) . toBe ( '12:00 pm' )
645+ } )
646+
647+ it ( 'should format 1 AM correctly' , ( ) => {
648+ const oneAm = adapter . date ( '2024-06-15T01:00:00' ) !
649+ expect ( adapter . formatByString ( oneAm , 'h:mm A' ) ) . toBe ( '1:00 AM' )
650+ } )
651+
652+ it ( 'should format 1 PM correctly' , ( ) => {
653+ const onePm = adapter . date ( '2024-06-15T13:00:00' ) !
654+ expect ( adapter . formatByString ( onePm , 'h:mm A' ) ) . toBe ( '1:00 PM' )
655+ } )
656+
617657 it ( 'should get format helper text' , ( ) => {
618658 expect ( adapter . getFormatHelperText ( 'keyboardDate' ) ) . toBe ( 'mm/dd/yyyy' )
619659 } )
@@ -853,6 +893,59 @@ describe('useDate', () => {
853893 expect ( usResult . toLowerCase ( ) ) . toContain ( 'june' )
854894 expect ( deResult . toLowerCase ( ) ) . toContain ( 'juni' )
855895 } )
896+
897+ it ( 'should clear format cache when locale changes' , ( ) => {
898+ const testAdapter = new Vuetify0DateAdapter ( 'en-US' )
899+ const testDate = Temporal . PlainDateTime . from ( '2024-06-15T10:30:00' )
900+
901+ // Format with US locale
902+ const usResult = testAdapter . format ( testDate , 'month' )
903+ expect ( usResult . toLowerCase ( ) ) . toContain ( 'june' )
904+
905+ // Change locale - this should clear the cache
906+ testAdapter . locale = 'de-DE'
907+
908+ // Format again - should use new locale, not cached US formatter
909+ const deResult = testAdapter . format ( testDate , 'month' )
910+ expect ( deResult . toLowerCase ( ) ) . toContain ( 'juni' )
911+ } )
912+
913+ it ( 'should not clear cache when setting same locale' , ( ) => {
914+ const testAdapter = new Vuetify0DateAdapter ( 'en-US' )
915+ const testDate = Temporal . PlainDateTime . from ( '2024-06-15T10:30:00' )
916+
917+ // Format to populate cache
918+ const result1 = testAdapter . format ( testDate , 'fullDate' )
919+
920+ // Set same locale - should not clear cache
921+ testAdapter . locale = 'en-US'
922+
923+ // Should still produce same result (cache intact)
924+ const result2 = testAdapter . format ( testDate , 'fullDate' )
925+ expect ( result1 ) . toBe ( result2 )
926+ } )
927+
928+ it ( 'should evict oldest entries when cache exceeds limit' , ( ) => {
929+ const testAdapter = new Vuetify0DateAdapter ( 'en-US' )
930+ const testDate = Temporal . PlainDateTime . from ( '2024-06-15T10:30:00' )
931+
932+ // Generate many unique format calls to exceed MAX_CACHE_SIZE (50)
933+ // Use formatByString with unique patterns to create unique cache keys
934+ for ( let i = 0 ; i < 60 ; i ++ ) {
935+ // Each unique format string creates a new cache entry
936+ testAdapter . formatByString ( testDate , `YYYY-MM-DD-${ i } ` )
937+ }
938+
939+ // Cache should still work correctly after evictions
940+ // Verify by calling format with a known preset
941+ const result = testAdapter . format ( testDate , 'fullDate' )
942+ expect ( result ) . toBeDefined ( )
943+ expect ( result . length ) . toBeGreaterThan ( 0 )
944+
945+ // Verify formatByString still works
946+ const customResult = testAdapter . formatByString ( testDate , 'YYYY-MM-DD' )
947+ expect ( customResult ) . toBe ( '2024-06-15' )
948+ } )
856949 } )
857950 } )
858951
@@ -1095,4 +1188,190 @@ describe('useDate', () => {
10951188 expect ( ctx . adapter ) . toBeInstanceOf ( Vuetify0DateAdapter )
10961189 } )
10971190 } )
1191+
1192+ describe ( 'component lifecycle integration' , ( ) => {
1193+ it ( 'should provide and consume date context in component tree' , async ( ) => {
1194+ const { mount } = await import ( '@vue/test-utils' )
1195+ const { defineComponent } = await import ( 'vue' )
1196+
1197+ const [ useDateContext , provideDateContext , context ] = createDateContext ( {
1198+ namespace : 'test:date' ,
1199+ locale : 'en-US' ,
1200+ } )
1201+
1202+ let consumedContext : ReturnType < typeof useDateContext > | null = null
1203+
1204+ const ChildComponent = defineComponent ( {
1205+ setup ( ) {
1206+ consumedContext = useDateContext ( )
1207+ return { date : consumedContext . adapter . date ( '2024-06-15T10:30:00' ) }
1208+ } ,
1209+ template : '<div>{{ date?.year }}</div>' ,
1210+ } )
1211+
1212+ const ParentComponent = defineComponent ( {
1213+ setup ( ) {
1214+ provideDateContext ( context )
1215+ } ,
1216+ components : { ChildComponent } ,
1217+ template : '<ChildComponent />' ,
1218+ } )
1219+
1220+ const wrapper = mount ( ParentComponent )
1221+
1222+ expect ( consumedContext ) . not . toBeNull ( )
1223+ expect ( consumedContext ! . adapter ) . toBe ( context . adapter )
1224+ expect ( consumedContext ! . locale . value ) . toBe ( 'en-US' )
1225+ expect ( wrapper . text ( ) ) . toContain ( '2024' )
1226+
1227+ wrapper . unmount ( )
1228+ } )
1229+
1230+ it ( 'should use plugin-provided context in component' , async ( ) => {
1231+ const { mount } = await import ( '@vue/test-utils' )
1232+ const { defineComponent } = await import ( 'vue' )
1233+
1234+ const plugin = createDatePlugin ( {
1235+ namespace : 'test:plugin-date' ,
1236+ locale : 'de-DE' ,
1237+ } )
1238+
1239+ let consumedLocale : string | undefined
1240+
1241+ const TestComponent = defineComponent ( {
1242+ setup ( ) {
1243+ const ctx = useDate ( 'test:plugin-date' )
1244+ consumedLocale = ctx . locale . value
1245+ return { locale : ctx . locale }
1246+ } ,
1247+ template : '<div>{{ locale }}</div>' ,
1248+ } )
1249+
1250+ const wrapper = mount ( TestComponent , {
1251+ global : {
1252+ plugins : [ plugin ] ,
1253+ } ,
1254+ } )
1255+
1256+ expect ( consumedLocale ) . toBe ( 'de-DE' )
1257+ expect ( wrapper . text ( ) ) . toContain ( 'de-DE' )
1258+
1259+ wrapper . unmount ( )
1260+ } )
1261+
1262+ it ( 'should cleanup watchEffect on component unmount' , async ( ) => {
1263+ const { mount } = await import ( '@vue/test-utils' )
1264+ const { defineComponent, ref, nextTick } = await import ( 'vue' )
1265+
1266+ const customAdapter = new Vuetify0DateAdapter ( 'en-US' )
1267+ const localeChanges : string [ ] = [ ]
1268+
1269+ // Track locale changes by overriding the setter
1270+ Object . defineProperty ( customAdapter , 'locale' , {
1271+ get ( ) {
1272+ return this . _locale
1273+ } ,
1274+ set ( value : string ) {
1275+ localeChanges . push ( value )
1276+ this . _locale = value
1277+ this . formatCache ?. clear ( )
1278+ this . numberFormatCache ?. clear ( )
1279+ } ,
1280+ } )
1281+
1282+ const [ useDateContext , provideDateContext ] = createDateContext ( {
1283+ namespace : 'test:cleanup' ,
1284+ adapter : customAdapter ,
1285+ locale : 'en-US' ,
1286+ } )
1287+
1288+ const ChildComponent = defineComponent ( {
1289+ setup ( ) {
1290+ const context = useDateContext ( )
1291+ return { locale : context . locale }
1292+ } ,
1293+ template : '<div>{{ locale }}</div>' ,
1294+ } )
1295+
1296+ const showChild = ref ( true )
1297+ const ParentComponent = defineComponent ( {
1298+ setup ( ) {
1299+ provideDateContext ( )
1300+ return { showChild }
1301+ } ,
1302+ components : { ChildComponent } ,
1303+ template : '<ChildComponent v-if="showChild" />' ,
1304+ } )
1305+
1306+ const wrapper = mount ( ParentComponent )
1307+
1308+ // Initial mount should sync locale
1309+ expect ( localeChanges . length ) . toBeGreaterThanOrEqual ( 0 )
1310+ const initialCount = localeChanges . length
1311+
1312+ // Unmount child
1313+ showChild . value = false
1314+ await nextTick ( )
1315+
1316+ // No additional locale changes should occur after unmount
1317+ // (cleanup prevents further sync)
1318+ expect ( localeChanges . length ) . toBe ( initialCount )
1319+
1320+ wrapper . unmount ( )
1321+ } )
1322+ } )
1323+
1324+ describe ( 'useLocale integration' , ( ) => {
1325+ it ( 'should sync with useLocale when available' , async ( ) => {
1326+ const { mount } = await import ( '@vue/test-utils' )
1327+ const { defineComponent } = await import ( 'vue' )
1328+ const { createLocaleContext } = await import ( '#v0/composables/useLocale' )
1329+
1330+ // Create locale context with messages for different locales
1331+ const [ useLocaleContext , provideLocaleContext ] = createLocaleContext ( {
1332+ namespace : 'test:locale' ,
1333+ default : 'en' ,
1334+ messages : {
1335+ en : { hello : 'Hello' } ,
1336+ de : { hello : 'Hallo' } ,
1337+ fr : { hello : 'Bonjour' } ,
1338+ } ,
1339+ } )
1340+
1341+ const [ useDateContext , provideDateContext ] = createDateContext ( {
1342+ namespace : 'test:date-locale' ,
1343+ localeMap : { en : 'en-US' , de : 'de-DE' , fr : 'fr-FR' } ,
1344+ } )
1345+
1346+ const ChildComponent = defineComponent ( {
1347+ setup ( ) {
1348+ // Use both locale and date contexts
1349+ const localeContext = useLocaleContext ( )
1350+ const dateContext = useDateContext ( )
1351+ return {
1352+ dateLocale : dateContext . locale ,
1353+ selectLocale : localeContext . select ,
1354+ }
1355+ } ,
1356+ template : '<div>{{ dateLocale }}</div>' ,
1357+ } )
1358+
1359+ const ParentComponent = defineComponent ( {
1360+ setup ( ) {
1361+ provideLocaleContext ( )
1362+ provideDateContext ( )
1363+ } ,
1364+ components : { ChildComponent } ,
1365+ template : '<ChildComponent />' ,
1366+ } )
1367+
1368+ const wrapper = mount ( ParentComponent )
1369+
1370+ // Initial locale should be mapped from useLocale's selection
1371+ // (default is 'en' -> 'en-US')
1372+ expect ( wrapper . text ( ) ) . toContain ( 'en-US' )
1373+
1374+ wrapper . unmount ( )
1375+ } )
1376+ } )
10981377} )
0 commit comments