// @vitest-environment jsdom
import '../../../test/dom-setup';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders } from '../../../test/render-helpers';
import { OverviewTab } from './OverviewTab';
import { __resetDraftPruneForTest } from '../../../hooks/useDraft';
import type { LocalTask } from '../../../api';
const DRAFT_KEY = 'maestro:draft:v1:feedback-comment:7';
const baseTask = {
id: 7,
title: 'タスク',
body: '本文',
pieceName: 'chat',
priority: 'medium',
latestJob: { status: 'succeeded' },
feedbackRating: null,
feedbackTags: [],
feedbackComment: null,
} as unknown as LocalTask;
beforeEach(() => {
localStorage.clear();
__resetDraftPruneForTest();
vi.stubGlobal('fetch', vi.fn(async () => new Response('{}', { status: 404 })));
});
describe('FeedbackPanel comment draft', () => {
it('👍を押してコメントを書くと下書きが保存され、再マウントで復元される', async () => {
const user = userEvent.setup();
const first = renderWithProviders();
await user.click(screen.getByRole('button', { name: /👍/ }));
await user.type(screen.getByRole('textbox'), '精度が良かった');
first.unmount();
expect(JSON.parse(localStorage.getItem(DRAFT_KEY)!).text).toBe('精度が良かった');
renderWithProviders();
await user.click(screen.getByRole('button', { name: /👍/ }));
expect(screen.getByRole('textbox')).toHaveValue('精度が良かった');
});
it('保存済みコメントがあるタスクではそちらを優先する', async () => {
localStorage.setItem(DRAFT_KEY, JSON.stringify({ text: '古い下書き', updatedAt: Date.now() }));
const saved = {
...baseTask,
feedbackRating: 'good',
feedbackComment: '確定済みコメント',
} as unknown as LocalTask;
const user = userEvent.setup();
renderWithProviders();
// 保存済み表示 → change で編集に入る
await user.click(screen.getByRole('button', { name: /change|変更/i }));
expect(screen.getByRole('textbox')).toHaveValue('確定済みコメント');
});
it('フィードバック送信が成功すると下書きが削除される', async () => {
// 送信前にデバウンス書き込みのタイミングに依存しないよう、下書きは
// localStorage へ直接シードする(既存テストと同じ堅牢なパターン)。
localStorage.setItem(DRAFT_KEY, JSON.stringify({ text: '送信予定のコメント', updatedAt: Date.now() }));
vi.stubGlobal('fetch', vi.fn(async (url: string, init?: RequestInit) => {
if (typeof url === 'string' && url.includes('/feedback') && init?.method === 'PUT') {
return new Response(JSON.stringify({ task: { ...baseTask, feedbackRating: 'good' } }), { status: 200 });
}
return new Response('{}', { status: 404 });
}));
const user = userEvent.setup();
renderWithProviders();
await user.click(screen.getByRole('button', { name: /👍/ }));
await user.click(screen.getByRole('button', { name: /submit|送信/i }));
await waitFor(() => expect(localStorage.getItem(DRAFT_KEY)).toBeNull());
});
it('キャンセルすると下書きが削除される', async () => {
const saved = {
...baseTask,
feedbackRating: 'good',
feedbackComment: '確定済みコメント',
} as unknown as LocalTask;
const user = userEvent.setup();
renderWithProviders();
await user.click(screen.getByRole('button', { name: /change|変更/i }));
await user.type(screen.getByRole('textbox'), '書きかけの変更');
// saveDraft はデバウンス書き込みのため、反映を待ってからキャンセルする
await waitFor(() => expect(JSON.parse(localStorage.getItem(DRAFT_KEY)!).text).toContain('書きかけの変更'));
await user.click(screen.getByRole('button', { name: /cancel|キャンセル/i }));
expect(localStorage.getItem(DRAFT_KEY)).toBeNull();
});
});