diff --git a/client/src/components/songbook/PracticeLogger.jsx b/client/src/components/songbook/PracticeLogger.jsx
index 3688bced8e..38b143b07c 100644
--- a/client/src/components/songbook/PracticeLogger.jsx
+++ b/client/src/components/songbook/PracticeLogger.jsx
@@ -59,17 +59,17 @@ export default function PracticeLogger({ song, onLogged, className = '' }) {
-
+
{SONG_PRACTICE_RATINGS.map((rating) => (
))}
diff --git a/client/src/components/songbook/PracticeLogger.test.jsx b/client/src/components/songbook/PracticeLogger.test.jsx
index 02fe38b0fe..8d2c42525c 100644
--- a/client/src/components/songbook/PracticeLogger.test.jsx
+++ b/client/src/components/songbook/PracticeLogger.test.jsx
@@ -47,13 +47,21 @@ describe('PracticeLogger', () => {
expect(screen.getByText(/1 session ·/)).toBeTruthy();
});
+ it('renders each rating hint visibly for touch users, not hover-only', () => {
+ render(
);
+ expect(screen.getByText('Fell apart — regress a stage and practice again today')).toBeTruthy();
+ expect(screen.getByText('Got through it — hold the stage, review sooner')).toBeTruthy();
+ expect(screen.getByText('Played it with hesitation — advance a stage')).toBeTruthy();
+ expect(screen.getByText('Played it clean — advance a stage, review later')).toBeTruthy();
+ });
+
it('posts the grade and hands the updated record back — no client-side scheduling', async () => {
const updated = { ...song(), stage: 'learned', practice: { nextReview: future(), sessions: 1 } };
practiceSong.mockResolvedValue(updated);
const onLogged = vi.fn();
render(
);
- fireEvent.click(screen.getByRole('button', { name: 'Solid' }));
+ fireEvent.click(screen.getByRole('button', { name: /Solid/ }));
await waitFor(() => expect(onLogged).toHaveBeenCalledWith(updated));
// The grade is the ONLY thing sent; the server owns stage + schedule.
@@ -64,7 +72,7 @@ describe('PracticeLogger', () => {
it('sends the low grade for a failed run', async () => {
practiceSong.mockResolvedValue({ ...song(), stage: 'learning', practice: { nextReview: new Date().toISOString() } });
render(
);
- fireEvent.click(screen.getByRole('button', { name: 'Struggled' }));
+ fireEvent.click(screen.getByRole('button', { name: /Struggled/ }));
await waitFor(() => expect(practiceSong).toHaveBeenCalledWith('song-1', 0, { silent: true }));
});
@@ -73,7 +81,7 @@ describe('PracticeLogger', () => {
const onLogged = vi.fn();
render(
);
- fireEvent.click(screen.getByRole('button', { name: 'Clean' }));
+ fireEvent.click(screen.getByRole('button', { name: /Clean/ }));
await waitFor(() => expect(toast.error).toHaveBeenCalled());
expect(onLogged).not.toHaveBeenCalled();