@@ -99,4 +99,99 @@ describe('useInternalNavFun', () => {
9999 expect ( routerNav ) . toHaveBeenCalledTimes ( 2 ) ;
100100 } ) ;
101101 } ) ;
102+
103+ it ( 'resolves a single navigation promise without usePathname' , async ( ) => {
104+ const routerNav = vi . fn ( ) ;
105+ render ( < Harness routerNav = { routerNav } /> ) ;
106+
107+ let promise ! : Promise < void > ;
108+ act ( ( ) => {
109+ promise = navigate ( '/dashboard' ) ;
110+ } ) ;
111+
112+ await waitFor ( ( ) => {
113+ expect ( routerNav ) . toHaveBeenCalledWith ( '/dashboard' ) ;
114+ } ) ;
115+
116+ // Promise resolves via isPending cycling alone — no usePathname needed
117+ await expect ( promise ) . resolves . toBeUndefined ( ) ;
118+ } ) ;
119+
120+ it ( 'flushes pre-existing window buffer on mount' , async ( ) => {
121+ const routerNav = vi . fn ( ) ;
122+
123+ // Simulate promises left in the window buffer from a previous component instance
124+ // (e.g., ClerkProvider unmounted during navigation — the core scenario from #2899)
125+ let resolved1 = false ;
126+ let resolved2 = false ;
127+ window . __clerk_internal_navigations = {
128+ push : {
129+ promisesBuffer : [
130+ ( ) => {
131+ resolved1 = true ;
132+ } ,
133+ ( ) => {
134+ resolved2 = true ;
135+ } ,
136+ ] ,
137+ } ,
138+ } as unknown as typeof window . __clerk_internal_navigations ;
139+
140+ render ( < Harness routerNav = { routerNav } /> ) ;
141+
142+ // The mount effect should flush the pre-existing buffer
143+ await waitFor ( ( ) => {
144+ expect ( resolved1 ) . toBe ( true ) ;
145+ expect ( resolved2 ) . toBe ( true ) ;
146+ } ) ;
147+ } ) ;
148+
149+ it ( 'flushes pending promises on unmount' , async ( ) => {
150+ const routerNav = vi . fn ( ) ;
151+ const { unmount } = render ( < Harness routerNav = { routerNav } /> ) ;
152+
153+ // Manually add resolvers to the buffer to simulate pending navigations
154+ let resolved = false ;
155+ const nav = window . __clerk_internal_navigations . push ;
156+ nav . promisesBuffer = [
157+ ( ) => {
158+ resolved = true ;
159+ } ,
160+ ] ;
161+
162+ unmount ( ) ;
163+
164+ // The useEffect cleanup should have flushed the promise buffer
165+ expect ( resolved ) . toBe ( true ) ;
166+ } ) ;
167+
168+ it ( 'uses history pushState for internal navigations' , async ( ) => {
169+ const routerNav = vi . fn ( ) ;
170+ const mockWindowNav = vi . fn ( ) ;
171+
172+ let internalNavigate : NavigationFunction | undefined ;
173+ const InternalHarness = ( ) => {
174+ internalNavigate = useInternalNavFun ( {
175+ windowNav : mockWindowNav as typeof window . history . pushState ,
176+ routerNav : routerNav as any ,
177+ name : 'push' ,
178+ } ) ;
179+ return null ;
180+ } ;
181+
182+ render ( < InternalHarness /> ) ;
183+
184+ act ( ( ) => {
185+ internalNavigate ! ( '/shallow-path' , {
186+ __internal_metadata : { navigationType : 'internal' } ,
187+ windowNavigate : vi . fn ( ) ,
188+ } ) ;
189+ } ) ;
190+
191+ await waitFor ( ( ) => {
192+ expect ( mockWindowNav ) . toHaveBeenCalledWith ( null , '' , '/shallow-path' ) ;
193+ } ) ;
194+
195+ expect ( routerNav ) . not . toHaveBeenCalled ( ) ;
196+ } ) ;
102197} ) ;
0 commit comments