chore: show toast message when update test run/case (#25)

This commit is contained in:
Takeshi Kimata
2024-09-07 18:19:47 +09:00
committed by GitHub
parent cc84f16235
commit 1ab7bf9c1b
10 changed files with 41 additions and 25 deletions

View File

@@ -189,6 +189,7 @@
"back_to_cases": "Back to test cases", "back_to_cases": "Back to test cases",
"updating": "Updating...", "updating": "Updating...",
"update": "Update", "update": "Update",
"updated_test_case": "Updated test case",
"basic": "Basic", "basic": "Basic",
"title": "Title", "title": "Title",
"please_enter_title": "Please enter title", "please_enter_title": "Please enter title",
@@ -241,6 +242,7 @@
"back_to_runs": "Back to test runs", "back_to_runs": "Back to test runs",
"updating": "Updating...", "updating": "Updating...",
"update": "Update", "update": "Update",
"updated_test_run": "Updated test run",
"progress": "Progress", "progress": "Progress",
"refresh": "Refresh", "refresh": "Refresh",
"id": "ID", "id": "ID",

View File

@@ -190,6 +190,7 @@
"back_to_cases": "テストケース一覧に戻る", "back_to_cases": "テストケース一覧に戻る",
"updating": "更新中...", "updating": "更新中...",
"update": "更新", "update": "更新",
"updated_test_case": "テストケースを更新しました",
"basic": "基本データ", "basic": "基本データ",
"title": "タイトル", "title": "タイトル",
"please_enter_title": "タイトルを入力してください", "please_enter_title": "タイトルを入力してください",
@@ -242,6 +243,7 @@
"back_to_runs": "テストラン一覧に戻る", "back_to_runs": "テストラン一覧に戻る",
"updating": "更新中...", "updating": "更新中...",
"update": "更新", "update": "更新",
"updated_test_run": "テストランを更新しました",
"progress": "進捗", "progress": "進捗",
"refresh": "再読み込み", "refresh": "再読み込み",
"id": "ID", "id": "ID",

View File

@@ -10,6 +10,7 @@ import { fetchCase, updateCase } from '@/utils/caseControl';
import { updateSteps } from './stepControl'; import { updateSteps } from './stepControl';
import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl'; import { fetchCreateAttachments, fetchDownloadAttachment, fetchDeleteAttachment } from './attachmentControl';
import { TokenContext } from '@/utils/TokenProvider'; import { TokenContext } from '@/utils/TokenProvider';
import { ToastContext } from '@/utils/ToastProvider';
import { useFormGuard } from '@/utils/formGuard'; import { useFormGuard } from '@/utils/formGuard';
import { CaseType, AttachmentType, CaseMessages, StepType } from '@/types/case'; import { CaseType, AttachmentType, CaseMessages, StepType } from '@/types/case';
import { PriorityMessages } from '@/types/priority'; import { PriorityMessages } from '@/types/priority';
@@ -52,7 +53,8 @@ export default function CaseEditor({
priorityMessages, priorityMessages,
locale, locale,
}: Props) { }: Props) {
const context = useContext(TokenContext); const toakenContext = useContext(TokenContext);
const toastContext = useContext(ToastContext);
const [testCase, setTestCase] = useState<CaseType>(defaultTestCase); const [testCase, setTestCase] = useState<CaseType>(defaultTestCase);
const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false); const [isTitleInvalid, setIsTitleInvalid] = useState<boolean>(false);
const [isUpdating, setIsUpdating] = useState<boolean>(false); const [isUpdating, setIsUpdating] = useState<boolean>(false);
@@ -192,11 +194,11 @@ export default function CaseEditor({
useEffect(() => { useEffect(() => {
async function fetchDataEffect() { async function fetchDataEffect() {
if (!context.isSignedIn()) { if (!toakenContext.isSignedIn()) {
return; return;
} }
try { try {
const data = await fetchCase(context.token.access_token, Number(caseId)); const data = await fetchCase(toakenContext.token.access_token, Number(caseId));
data.Steps.forEach((step: StepType) => { data.Steps.forEach((step: StepType) => {
step.editState = 'notChanged'; step.editState = 'notChanged';
}); });
@@ -207,7 +209,7 @@ export default function CaseEditor({
} }
fetchDataEffect(); fetchDataEffect();
}, [context]); }, [toakenContext]);
return ( return (
<> <>
@@ -230,15 +232,17 @@ export default function CaseEditor({
<Button <Button
startContent={<Save size={16} />} startContent={<Save size={16} />}
size="sm" size="sm"
isDisabled={!context.isProjectDeveloper(Number(projectId))} isDisabled={!toakenContext.isProjectDeveloper(Number(projectId))}
color="primary" color="primary"
isLoading={isUpdating} isLoading={isUpdating}
onPress={async () => { onPress={async () => {
setIsUpdating(true); setIsUpdating(true);
await updateCase(context.token.access_token, testCase); await updateCase(toakenContext.token.access_token, testCase);
if (testCase.Steps) { if (testCase.Steps) {
await updateSteps(context.token.access_token, Number(caseId), testCase.Steps); await updateSteps(toakenContext.token.access_token, Number(caseId), testCase.Steps);
} }
toastContext.showToast(messages.updatedTestCase, 'dark');
setIsUpdating(false); setIsUpdating(false);
setIsDirty(false); setIsDirty(false);
}} }}
@@ -383,7 +387,7 @@ export default function CaseEditor({
<Button <Button
startContent={<Plus size={16} />} startContent={<Plus size={16} />}
size="sm" size="sm"
isDisabled={!context.isProjectDeveloper(Number(projectId))} isDisabled={!toakenContext.isProjectDeveloper(Number(projectId))}
color="primary" color="primary"
className="ms-3" className="ms-3"
onPress={() => onPlusClick(1)} onPress={() => onPlusClick(1)}
@@ -393,7 +397,7 @@ export default function CaseEditor({
</div> </div>
{testCase.Steps && ( {testCase.Steps && (
<CaseStepsEditor <CaseStepsEditor
isDisabled={!context.isProjectDeveloper(Number(projectId))} isDisabled={!toakenContext.isProjectDeveloper(Number(projectId))}
steps={testCase.Steps} steps={testCase.Steps}
onStepUpdate={(stepId, changeStep) => { onStepUpdate={(stepId, changeStep) => {
if (testCase.Steps) { if (testCase.Steps) {
@@ -421,7 +425,7 @@ export default function CaseEditor({
<h6 className="font-bold">{messages.attachments}</h6> <h6 className="font-bold">{messages.attachments}</h6>
{testCase.Attachments && ( {testCase.Attachments && (
<CaseAttachmentsEditor <CaseAttachmentsEditor
isDisabled={!context.isProjectDeveloper(Number(projectId))} isDisabled={!toakenContext.isProjectDeveloper(Number(projectId))}
attachments={testCase.Attachments} attachments={testCase.Attachments}
onAttachmentDownload={(attachmentId: number, downloadFileName: string) => onAttachmentDownload={(attachmentId: number, downloadFileName: string) =>
fetchDownloadAttachment(attachmentId, downloadFileName) fetchDownloadAttachment(attachmentId, downloadFileName)

View File

@@ -18,6 +18,7 @@ export default function Page({
backToCases: t('back_to_cases'), backToCases: t('back_to_cases'),
updating: t('updating'), updating: t('updating'),
update: t('update'), update: t('update'),
updatedTestCase: t('updated_test_case'),
basic: t('basic'), basic: t('basic'),
title: t('title'), title: t('title'),
pleaseEnterTitle: t('please_enter_title'), pleaseEnterTitle: t('please_enter_title'),

View File

@@ -35,6 +35,7 @@ import {
} from '../runsControl'; } from '../runsControl';
import { fetchFolders } from '../../folders/foldersControl'; import { fetchFolders } from '../../folders/foldersControl';
import { TokenContext } from '@/utils/TokenProvider'; import { TokenContext } from '@/utils/TokenProvider';
import { ToastContext } from '@/utils/ToastProvider';
import { useTheme } from 'next-themes'; import { useTheme } from 'next-themes';
import { useFormGuard } from '@/utils/formGuard'; import { useFormGuard } from '@/utils/formGuard';
import { PriorityMessages } from '@/types/priority'; import { PriorityMessages } from '@/types/priority';
@@ -73,7 +74,8 @@ export default function RunEditor({
testTypeMessages, testTypeMessages,
locale, locale,
}: Props) { }: Props) {
const context = useContext(TokenContext); const tokenContext = useContext(TokenContext);
const toastContext = useContext(ToastContext);
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
const [testRun, setTestRun] = useState<RunType>(defaultTestRun); const [testRun, setTestRun] = useState<RunType>(defaultTestRun);
const [folders, setFolders] = useState<FolderType[]>([]); const [folders, setFolders] = useState<FolderType[]>([]);
@@ -89,13 +91,13 @@ export default function RunEditor({
useFormGuard(isDirty, messages.areYouSureLeave); useFormGuard(isDirty, messages.areYouSureLeave);
const fetchRunAndStatusCount = async () => { const fetchRunAndStatusCount = async () => {
const { run, statusCounts } = await fetchRun(context.token.access_token, Number(runId)); const { run, statusCounts } = await fetchRun(tokenContext.token.access_token, Number(runId));
setTestRun(run); setTestRun(run);
setRunStatusCounts(statusCounts); setRunStatusCounts(statusCounts);
}; };
const initTestCases = async () => { const initTestCases = async () => {
const casesData = await fetchProjectCases(context.token.access_token, Number(projectId)); const casesData = await fetchProjectCases(tokenContext.token.access_token, Number(projectId));
casesData.forEach((testCase: CaseType) => { casesData.forEach((testCase: CaseType) => {
if (testCase.RunCases && testCase.RunCases.length > 0) { if (testCase.RunCases && testCase.RunCases.length > 0) {
testCase.RunCases[0].editState = 'notChanged'; testCase.RunCases[0].editState = 'notChanged';
@@ -106,13 +108,13 @@ export default function RunEditor({
useEffect(() => { useEffect(() => {
async function fetchDataEffect() { async function fetchDataEffect() {
if (!context.isSignedIn()) { if (!tokenContext.isSignedIn()) {
return; return;
} }
try { try {
await fetchRunAndStatusCount(); await fetchRunAndStatusCount();
const foldersData = await fetchFolders(context.token.access_token, Number(projectId)); const foldersData = await fetchFolders(tokenContext.token.access_token, Number(projectId));
setFolders(foldersData); setFolders(foldersData);
setSelectedFolder(foldersData[0]); setSelectedFolder(foldersData[0]);
initTestCases(); initTestCases();
@@ -122,7 +124,7 @@ export default function RunEditor({
} }
fetchDataEffect(); fetchDataEffect();
}, [context]); }, [tokenContext]);
useEffect(() => { useEffect(() => {
function onFilter() { function onFilter() {
@@ -168,9 +170,11 @@ export default function RunEditor({
const onSave = async () => { const onSave = async () => {
setIsUpdating(true); setIsUpdating(true);
await updateRun(context.token.access_token, testRun); await updateRun(tokenContext.token.access_token, testRun);
await updateRunCases(context.token.access_token, Number(runId), testCases); await updateRunCases(tokenContext.token.access_token, Number(runId), testCases);
await initTestCases(); await initTestCases();
toastContext.showToast(messages.updatedTestRun, 'dark');
setIsUpdating(false); setIsUpdating(false);
setIsDirty(false); setIsDirty(false);
}; };
@@ -199,7 +203,7 @@ export default function RunEditor({
<Button <Button
startContent={<Save size={16} />} startContent={<Save size={16} />}
size="sm" size="sm"
isDisabled={!context.isProjectReporter(Number(projectId))} isDisabled={!tokenContext.isProjectReporter(Number(projectId))}
color="primary" color="primary"
isLoading={isUpdating} isLoading={isUpdating}
onPress={onSave} onPress={onSave}
@@ -294,7 +298,7 @@ export default function RunEditor({
<DropdownTrigger> <DropdownTrigger>
<Button <Button
size="sm" size="sm"
isDisabled={!context.isProjectReporter(Number(projectId))} isDisabled={!tokenContext.isProjectReporter(Number(projectId))}
color="primary" color="primary"
endContent={<ChevronDown size={16} />} endContent={<ChevronDown size={16} />}
> >
@@ -341,7 +345,7 @@ export default function RunEditor({
<div className="w-9/12"> <div className="w-9/12">
<TestCaseSelector <TestCaseSelector
cases={filteredTestCases} cases={filteredTestCases}
isDisabled={!context.isProjectReporter(Number(projectId))} isDisabled={!tokenContext.isProjectReporter(Number(projectId))}
selectedKeys={selectedKeys} selectedKeys={selectedKeys}
onSelectionChange={setSelectedKeys} onSelectionChange={setSelectedKeys}
onChangeStatus={handleChangeStatus} onChangeStatus={handleChangeStatus}

View File

@@ -11,6 +11,7 @@ export default function Page({ params }: { params: { projectId: string; runId: s
backToRuns: t('back_to_runs'), backToRuns: t('back_to_runs'),
updating: t('updating'), updating: t('updating'),
update: t('update'), update: t('update'),
updatedTestRun: t('updated_test_run'),
progress: t('progress'), progress: t('progress'),
refresh: t('refresh'), refresh: t('refresh'),
id: t('id'), id: t('id'),

View File

@@ -82,6 +82,7 @@ type CaseMessages = {
backToCases: string; backToCases: string;
updating: string; updating: string;
update: string; update: string;
updatedTestCase: string;
basic: string; basic: string;
title: string; title: string;
pleaseEnterTitle: string; pleaseEnterTitle: string;

View File

@@ -56,6 +56,7 @@ type RunMessages = {
backToRuns: string; backToRuns: string;
updating: string; updating: string;
update: string; update: string;
updatedTestRun: string;
progress: string; progress: string;
refresh: string; refresh: string;
id: string; id: string;

View File

@@ -8,5 +8,5 @@ export type ToastMessages = {
}; };
export type ToastContextType = { export type ToastContextType = {
showToast: (text: string, mode: string) => void; showToast: (text: string, mode: 'error' | 'dark') => void;
}; };

View File

@@ -6,7 +6,7 @@ import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css'; import 'react-toastify/dist/ReactToastify.css';
const defaultContext = { const defaultContext = {
showToast: (text: string, mode: string) => {}, showToast: (text: string, mode: 'error' | 'dark') => {},
}; };
const ToastContext = createContext<ToastContextType>(defaultContext); const ToastContext = createContext<ToastContextType>(defaultContext);
@@ -15,7 +15,7 @@ const ToastProvider = ({ children }: ToastProps) => {
if (mode === 'error') { if (mode === 'error') {
toast.error(text); toast.error(text);
} else { } else {
toast(text, { theme: 'light' }); toast(text, { theme: 'dark' });
} }
}; };
@@ -25,7 +25,7 @@ const ToastProvider = ({ children }: ToastProps) => {
return ( return (
<ToastContext.Provider value={toastContext}> <ToastContext.Provider value={toastContext}>
<ToastContainer position="bottom-right" hideProgressBar={true} theme="colored" /> <ToastContainer position="bottom-right" hideProgressBar={true} />
{children} {children}
</ToastContext.Provider> </ToastContext.Provider>
); );