mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
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:
@@ -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>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user