vitestでNuxtのnavigateToをモックする

# はじめに
Nuxtの`navigateTo`を以下のような感じでテストしたかったのですが、
```typescript:utils/sample.ts
export default async () => {
  await navigateTo({
    path: "/",
    query: { redirect: undefined, tenantId: undefined },
  })
}
```
```typescript:utils/sample.spec.ts
describe('sample', () => {
  it('navigate to top', async () => {
    const mock = vi.fn() // いい感じにnavigateToをモック
    const expectArgs = {
      path: "/",
      query: { redirect: undefined, tenantId: undefined },
    }

    await sample()
    expect(mock).toHaveBeenCalledWith(expectArgs)
  })
})
```
そこそこ詰まったので解決方法を記載します。

# 環境
Nuxt: 3.12.1
vitest: 1.6.0

# TL;DR
```typescript:utils/sample.spec.ts
import * as Nuxt from "#app/composables/router"
import sample from './sample'

describe('sample', () => {
  it('navigate to top', async () => {
    const mock = vi.spyOn(Nuxt, 'navigateTo')
    const expectArgs = {
      path: "/",
      query: { redirect: undefined, tenantId: undefined },
    }

    await sample()
    expect(mock).toHaveBeenCalledWith(expectArgs)
  })
})
```


# 内容
まず以下記事を参考に、

https://qiita.com/GleapPost/items/c50fbfc4e63118d3b19e

https://qiita.com/seiten59/items/6a95ff0477fb2ce47a6a

以下のようなコードで検証しました。

```typescript:utils/sample.spec.ts
import * as Nuxt from "nuxt/app"
import sample from './sample'

describe('sample', () => {
  it('navigate to top', async () => {
    const mock = vi.spyOn(Nuxt, 'navigateTo')
    // vi.stubGlobal('navigateTo', mock)
    const expectArgs = {
      path: "/",
      query: { redirect: undefined, tenantId: undefined },
    }
    await sample()
    expect(mock).toHaveBeenCalledWith(expectArgs)
  })
})
```

しかし、上記コードでテストを実行すると以下エラーとなりました。
(`vi.stubGlobal`を実行しても変わらず)
```plaintext:エラー
FAIL  utils/sample.spec.ts > sample > navigate to top
AssertionError: expected "navigateTo" to be called with arguments: [ { path: '/', query: { …(2) } } ]

Received: 



Number of calls: 0

 ❯ utils/sample.spec.ts:15:18
     13| 
     14|     await sample()
     15|     expect(mock).toHaveBeenCalledWith(expectArgs)
       |                  ^
     16|   })
     17| })
```

また、`vi.stubGlobal`を実行した状態で`sample.ts`側で`global.navigateTo`を実行するとテスト成功しました。
```typescript:utils/sample.ts
export default async () => {
  await global.navigateTo({
    path: '/',
    query: { redirect: undefined, tenantId: undefined },
  })
}
```
```plaintext:テスト結果
 ✓ utils/sample.spec.ts (1)
   ✓ sample (1)
     ✓ navigate to top

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  15:39:45
   Duration  221ms
```

そのため、`navigateTo`は少なくともglobalと違うところで定義されているものを読み込んでいると思われます。

というわけで、`.nuxt/imports.d.ts`を見て`navigateTo`がどこからimportされているか確認したところ、以下でした。
```typescript:.nuxt/imports.d.ts
export { abortNavigation, addRouteMiddleware, defineNuxtRouteMiddleware, setPageLayout, navigateTo, useRoute, useRouter, onBeforeRouteLeave, onBeforeRouteUpdate } from '#app/composables/router';
```

そのため、モックのためにimportしているモジュールの記載を変更しました。

```diff_typescript:utils/sample.spec.ts
- import * as Nuxt from "nuxt/app"
+ import * as Nuxt from "#app/composables/router"
import sample from './sample'

describe('sample', () => {
  it('navigate to top', async () => {
    const mock = vi.spyOn(Nuxt, 'navigateTo')
    const expectArgs = {
      path: "/",
      query: { redirect: undefined, tenantId: undefined },
    }

    await sample()
    expect(mock).toHaveBeenCalledWith(expectArgs)
  })
})
```

これでテスト成功しました。
```plaintext:テスト結果
 ✓ utils/sample.spec.ts (1)
   ✓ sample (1)
     ✓ navigate to top

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  16:13:30
   Duration  163ms
```