92 lines
4.2 KiB
TypeScript
92 lines
4.2 KiB
TypeScript
// @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(<OverviewTab task={baseTask} />);
|
|
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(<OverviewTab task={baseTask} />);
|
|
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(<OverviewTab task={saved} />);
|
|
// 保存済み表示 → 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(<OverviewTab task={baseTask} />);
|
|
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(<OverviewTab task={saved} />);
|
|
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();
|
|
});
|
|
});
|