refactor(storage): draw the phone backend test result with one component

The phone storage panel rendered the same test result markup for regular and degenerate mirror backends. Both rows now render MStorageTestResult, with the same elements, classes and text as before.
This commit is contained in:
Maurice
2026-10-09 15:44:13 +02:00
parent e0fd2c296b
commit 201de0bd5b
3 changed files with 76 additions and 34 deletions
@@ -0,0 +1,44 @@
// FE-COMP-MSTORETEST-001 to FE-COMP-MSTORETEST-004
import { describe, expect, it } from 'vitest';
import { render, screen } from '../../../tests/helpers/render';
import MStorageTestResult from './MStorageTestResult';
describe('MStorageTestResult', () => {
it('FE-COMP-MSTORETEST-001: renders nothing before the first test', () => {
const { container } = render(<MStorageTestResult result={undefined} />);
expect(container).toBeEmptyDOMElement();
});
it('FE-COMP-MSTORETEST-002: shows the running note while a test is in flight', () => {
render(<MStorageTestResult result="running" />);
expect(screen.getByText('Testing…')).toHaveClass('mt-2', 'font-geist', 'text-m-muted');
});
it('FE-COMP-MSTORETEST-003: lists every target under a passed verdict', () => {
render(<MStorageTestResult result={{ ok: true, targets: [{ name: 'off-box', ok: true }] }} />);
expect(screen.getByText('Connection OK')).toHaveClass('font-bold', 'text-m-ink');
expect(screen.getByText(/off-box/).textContent).toBe('✓ off-box');
});
it('FE-COMP-MSTORETEST-004: marks a failed target and appends its error', () => {
render(
<MStorageTestResult
result={{
ok: false,
targets: [
{ name: 'primary', ok: true },
{ name: 'replica', ok: false, error: 'timeout' },
],
}}
/>
);
expect(screen.getByText('Test failed')).toBeInTheDocument();
expect(screen.getByText(/primary/).textContent).toBe('✓ primary');
expect(screen.getByText(/replica/).textContent).toBe('✗ replica — timeout');
});
});
@@ -0,0 +1,29 @@
import type { StorageTestResponse } from '@trek/shared';
import { useTranslation } from '../../i18n';
/**
* The outcome of a storage backend test on the phone admin panel: a running
* note while it is in flight, then the verdict with one line per target.
* Renders nothing before the first test.
*/
export default function MStorageTestResult({ result }: { result: StorageTestResponse | 'running' | undefined }) {
const { t } = useTranslation();
if (result === 'running') {
return <p className="mt-2 font-geist text-[0.625rem] text-m-muted">{t('storage.test.running')}</p>;
}
if (!result) return null;
return (
<div className="mt-2 space-y-0.5">
<p className="text-[0.75rem] font-bold text-m-ink">
{result.ok ? t('storage.test.ok') : t('storage.test.failed')}
</p>
{result.targets.map((target) => (
<p key={target.name} className="font-geist text-[0.625rem] text-m-muted">
{target.ok ? '✓' : '✗'} {target.name}
{target.error ? ` — ${target.error}` : ''}
</p>
))}
</div>
);
}
@@ -14,6 +14,7 @@ import { CACHE_CATEGORIES, primaryNameOf } from '../../../components/Admin/stora
import { useBackendForm } from '../../../components/Admin/storage/useBackendForm'
import { categoryNames, migrationPromptLine, useStoragePanel } from '../../../components/Admin/storage/useStoragePanel'
import MToggle from '../../components/MToggle'
import MStorageTestResult from '../../components/MStorageTestResult'
import MSetPickerSheet from '../settings/MSetPickerSheet'
import MConfirmSheet from '../settings/MConfirmSheet'
import { MSetSelectRow } from '../settings/MSettingsUi'
@@ -298,23 +299,7 @@ export default function MAdminStoragePanel(): React.ReactElement {
</MAdminButton>
)}
</div>
{result === 'running' ? (
<p className="mt-2 font-geist text-[0.625rem] text-m-muted">{t('storage.test.running')}</p>
) : (
result && (
<div className="mt-2 space-y-0.5">
<p className="text-[0.75rem] font-bold text-m-ink">
{result.ok ? t('storage.test.ok') : t('storage.test.failed')}
</p>
{result.targets.map((target) => (
<p key={target.name} className="font-geist text-[0.625rem] text-m-muted">
{target.ok ? '✓' : '✗'} {target.name}
{target.error ? ` — ${target.error}` : ''}
</p>
))}
</div>
)
)}
<MStorageTestResult result={result} />
{row.mirrorTargets.length > 0 && (
<div className="mt-2">
{backfill?.status === 'running' ? (
@@ -401,23 +386,7 @@ export default function MAdminStoragePanel(): React.ReactElement {
{t('storage.actions.remove')}
</MAdminButton>
</div>
{result === 'running' ? (
<p className="mt-2 font-geist text-[0.625rem] text-m-muted">{t('storage.test.running')}</p>
) : (
result && (
<div className="mt-2 space-y-0.5">
<p className="text-[0.75rem] font-bold text-m-ink">
{result.ok ? t('storage.test.ok') : t('storage.test.failed')}
</p>
{result.targets.map((target) => (
<p key={target.name} className="font-geist text-[0.625rem] text-m-muted">
{target.ok ? '✓' : '✗'} {target.name}
{target.error ? ` — ${target.error}` : ''}
</p>
))}
</div>
)
)}
<MStorageTestResult result={result} />
</div>
)
})}