feat: /judge review + scoring + leaderboard — TDD
Final screen — replaces the last WorkshopStub; App.tsx now has zero stubs: - SubmissionList: live review queue with scored markers + selection - AddReview: read-only render of a submitted 5-layer ADD - ScoreForm: 0-10 rubric per layer, auto-summed total, notes - Leaderboard: ranked teams by average judge total - Judge page (behind name+code AccessGate): queue + review/score + leaderboard, refreshes on scoring and on any collective event 21 new tests; suite 140/140 green, typecheck + lint clean, build OK. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6c07788fab
commit
bd3250c9c7
@@ -0,0 +1,82 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
import { render, screen, waitFor, within } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { MemoryRouter } from 'react-router-dom'
|
||||
import type { SubmissionDTO } from '@/types'
|
||||
|
||||
const api = vi.hoisted(() => ({
|
||||
getSubmissions: vi.fn(),
|
||||
getSubmission: vi.fn(),
|
||||
getLeaderboard: vi.fn(),
|
||||
postScore: vi.fn(),
|
||||
openCollective: vi.fn(() => () => {}),
|
||||
}))
|
||||
vi.mock('@/lib/api', () => api)
|
||||
|
||||
import { Judge } from './Judge'
|
||||
|
||||
const dto: SubmissionDTO = {
|
||||
teamId: 'a',
|
||||
teamName: 'Alpha',
|
||||
code: 'KIT-01-AAA',
|
||||
add: { L1: { goal: 'g' }, L2: 'two', L3: 'three', L4: 'four', L5: 'five' },
|
||||
submittedAt: 'x',
|
||||
}
|
||||
|
||||
function renderJudge() {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<Judge />
|
||||
</MemoryRouter>,
|
||||
)
|
||||
}
|
||||
|
||||
describe('Judge', () => {
|
||||
beforeEach(() => {
|
||||
sessionStorage.clear()
|
||||
sessionStorage.setItem('apess_judge_code', 'judge-code')
|
||||
sessionStorage.setItem('apess_judge_code_name', 'Dr. D')
|
||||
api.getSubmissions.mockResolvedValue([{ teamId: 'a', teamName: 'Alpha', submittedAt: 'x', scored: false }])
|
||||
api.getLeaderboard.mockResolvedValue([{ teamId: 'a', teamName: 'Alpha', avgTotal: 0, scoreCount: 0 }])
|
||||
api.getSubmission.mockResolvedValue(dto)
|
||||
api.postScore.mockResolvedValue({ id: 1 })
|
||||
})
|
||||
|
||||
it('lists the submission queue and leaderboard on load', async () => {
|
||||
renderJudge()
|
||||
await waitFor(() => expect(screen.getByTestId('submission-list')).toHaveTextContent('Alpha'))
|
||||
expect(screen.getByTestId('leaderboard')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('loads a submission for review when selected', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderJudge()
|
||||
await waitFor(() => expect(screen.getByTestId('submission-list')).toBeInTheDocument())
|
||||
await user.click(within(screen.getByTestId('submission-list')).getByText('Alpha'))
|
||||
await waitFor(() => expect(screen.getByTestId('add-review')).toHaveTextContent('KIT-01-AAA'))
|
||||
})
|
||||
|
||||
it('posts a score and refreshes', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderJudge()
|
||||
await waitFor(() => expect(screen.getByTestId('submission-list')).toBeInTheDocument())
|
||||
await user.click(within(screen.getByTestId('submission-list')).getByText('Alpha'))
|
||||
await waitFor(() => expect(screen.getByTestId('score-form')).toBeInTheDocument())
|
||||
|
||||
const perception = screen.getByLabelText(/perception/i)
|
||||
await user.clear(perception)
|
||||
await user.type(perception, '7')
|
||||
await user.click(screen.getByRole('button', { name: /submit score/i }))
|
||||
|
||||
await waitFor(() => expect(api.postScore).toHaveBeenCalled())
|
||||
const arg = api.postScore.mock.calls[0]
|
||||
expect(arg[0]).toBe('judge-code')
|
||||
expect(arg[1]).toMatchObject({ teamId: 'a', judge: 'Dr. D', total: 7 })
|
||||
})
|
||||
|
||||
it('gates behind name + code when none stored', () => {
|
||||
sessionStorage.clear()
|
||||
renderJudge()
|
||||
expect(screen.getByRole('button', { name: /unlock/i })).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user