anchor タグを押して任意のURLに window.location が更新されるかテストを行った時、ナビゲーション周りのconsole.error がでてハマったので備忘録を残しておきます。
使用技術
- Next.js: v.14.2.14
- React: v.18.2.0
- Jest: v.29.7.0
- @testing-library/user-event: v.13.2.1
- testing-library/react: v.14.0.0
コード
今回のテスト対象コードとテストコードは以下です。
テスト対象コード
テスト対象コードは以下です。href を受け取って Button Componentに渡し、Buttonが押された時hard navigationで画面遷移します。
const Component = (href: string) => {
const handleClick = () => {
if (href) {
window.location.href = href
}
console.log("Next!")
}
return(
<div>
<Button type="anchor" href={href} onClick={handleClick}>Topへ</Button>
</div>)
}
テストコード
このComponentに対するテストコードは以下です。ボタンを押した時、PCとSPのときそれぞれのURLに遷移するかをテストします。
test.each([
{
device: 'Pc',
url: '/top',
},
{
device: 'Sp',
url: '/sp/top',
},
])(
'$device表示時、Topへボタンをクリックすると$urlに遷移する',
async ({ url }) => {
const { findByRole } = render(<Compenent href={url} />)
const anchor = await findByRole('link', { name: 'Topへ' })
userEvent.click(anchor)
await waitFor(() => {
expect(location.href).toBe(url)
})
}
)
発生したエラー
このテストを実行した結果以下エラーが発生しました。(テスト自体は通る)
エラー内容を見てみると、どうやらナビゲーションが実装されてない?旨が書いてありそうです。
console.error
Error: Not implemented: navigation (except hash changes)
at module.exports (/***/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at navigateFetch (/****/node_modules/jsdom/lib/jsdom/living/window/navigation.js:77:3)
at exports.navigate (/***/node_modules/jsdom/lib/jsdom/living/window/navigation.js:55:3)
at Timeout._onTimeout (/***/node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
at listOnTimeout (node:internal/timers:569:17)
at processTimers (node:internal/timers:512:7) {
type: 'not implemented'
}
at VirtualConsole.<anonymous> (node_modules/jest-environment-jsdom/build/index.js:63:23)
at module.exports (node_modules/jsdom/lib/jsdom/browser/not-implemented.js:12:26)
at navigateFetch (node_modules/jsdom/lib/jsdom/living/window/navigation.js:77:3)
at exports.navigate (node_modules/jsdom/lib/jsdom/living/window/navigation.js:55:3)
at Timeout._onTimeout (node_modules/jsdom/lib/jsdom/living/nodes/HTMLHyperlinkElementUtils-impl.js:81:7)
原因
こちらの記事によると原因は以下にありそうでした。
- JSDOMはナビゲーションをサポートしてない。
- ナビゲーションをサポートしていないので、anchor タグでページ遷移処理を行おうとすると、遷移が行えないエラーが表示されてしまう。
- ただしanchor タグを押した時、hrefを元にwindow.locationオブジェクトの更新はできるので、アサーションの比較は成功しテスト自体は通る。
対応
以下のようにテスト内で、anchor タグのページ遷移処理を無効にすることでナビゲーションをサポートしていない旨のエラーをなくすことができました。
beforeAll(() => {
const defaultLocation = window.location
Object.defineProperty(window, 'location', {
writable: true,
value: {
...defaultLocation,
},
})
})
afterAll(() => {
Object.defineProperty(window, 'location', {
writable: false,
})
})
test.each([
{
device: 'Pc',
url: '/top',
},
{
device: 'Sp',
url: '/sp/top',
},
])(
'$device表示時、Topへボタンをクリックすると$urlに遷移する',
async ({ url }) => {
const { findByRole } = render(<Compenent href={url} />)
const anchor = await findByRole('link', { name: 'Topへ' })
//New: .preventDefault()でページ遷移無効
anchor.addEventListener('click', (event) => event.preventDefault(), false)
userEvent.click(anchor)
await waitFor(() => {
expect(location.href).toBe(url)
})
}
)
おまけ
ほかにもこのエラーの対応方法はありそうなのでいくつか例をあげてみました。
1.今回のエラーだけ console.error をspyする
今回のナビゲーションのエラー限定で console.error を spy する方法
const defaultErrorLog = console.error
const notImplementedErrorType =
'Not implemented: navigation (except hash changes)'
jest.spyOn(console, 'error').mockImplementation((error) => {
if (error.message === notImplementedErrorType) {
return
}
defaultErrorLog(error)
})
2.anchorタグの href に指定されているURLの比較を行う
今回のテストは実際に画面遷移したかはスコープではありません。
なので anchorタグの href に指定されているURLを比較すれば、テストの要件を満たすことができてそうでした。
参考文献