Make XP progress formatting hydration-safe

This commit is contained in:
2026-08-29 21:28:26 +02:00
parent 99d94c9333
commit 187292a374
2 changed files with 67 additions and 1 deletions
@@ -0,0 +1,65 @@
import React from 'react'
import { hydrateRoot } from 'react-dom/client'
import { act, render, screen } from '@testing-library/react'
import { renderToString } from 'react-dom/server'
import { afterEach, describe, expect, it } from 'vitest'
import XPProgressBar from './XPProgressBar'
import { formatProfileNumber } from '../../lib/profileFormatters'
const fixture = {
xp: 3024,
currentLevelXp: 2000,
nextLevelXp: 5000,
progressPercent: 34,
}
describe('XPProgressBar locale-stable formatting', () => {
afterEach(() => {
document.body.innerHTML = ''
})
it.each([
[0, '0'],
[1, '1'],
[999, '999'],
[1000, '1,000'],
[3024, '3,024'],
[1000000, '1,000,000'],
[9529106, '9,529,106'],
])('formats %s as %s', (value, expected) => {
expect(formatProfileNumber(value)).toBe(expected)
})
it('renders the non-zero production-shaped XP value', () => {
render(<XPProgressBar {...fixture} />)
expect(screen.getByText('3,024 XP')).toBeTruthy()
expect(screen.getByText('2,000 / 5,000 XP')).toBeTruthy()
})
it('keeps XP zero formatting stable', () => {
render(<XPProgressBar xp={0} currentLevelXp={0} nextLevelXp={100} progressPercent={0} />)
expect(screen.getByText('0 XP')).toBeTruthy()
expect(screen.getByText('0 / 100 XP')).toBeTruthy()
})
it('hydrates the same SSR markup without recoverable errors', async () => {
const container = document.createElement('div')
container.innerHTML = renderToString(<XPProgressBar {...fixture} />)
document.body.appendChild(container)
const recoverableErrors = []
const root = hydrateRoot(container, <XPProgressBar {...fixture} />, {
onRecoverableError: (error) => recoverableErrors.push(error),
})
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 0))
})
expect(recoverableErrors).toHaveLength(0)
expect(container.textContent).toContain('3,024 XP')
expect(container.textContent).toContain('2,000 / 5,000 XP')
await act(async () => root.unmount())
})
})