mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-19 13:36:48 +00:00
e3c524dd02
SonarQube's S2699 flagged 196 test cases as assertion-less. Almost all of them end on a bare `await screen.findByText(...)`, which does throw when the text never shows up — the test is real, the analyser just cannot see it. Spelling the check out keeps the behaviour identical and makes it readable. Writing them out turned three tests red, each for a good reason: - FE-COMP-NOTES-008 read the heading out of the loading panel, which renders its own copy and swaps the whole node out once the fetch settles. It now waits for the loaded state. - FE-COMP-PHOTOPROVIDERS-014 matched /connected/i, which also matches the "Not connected" the badge starts as — the test passed whether or not the button did anything. Exact text now. - FE-FILES-002 compared an empty list against the empty list it already was. It seeds a file first, so the 500 has something to leave alone. JOURNEY-SVC-066 asserted nothing at all behind a "should not throw" comment; it now says that out loud and checks no journey entry is left orphaned.
220 lines
9.1 KiB
TypeScript
220 lines
9.1 KiB
TypeScript
// FE-MOB-SETAIR-001 onwards
|
|
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { http, HttpResponse } from 'msw';
|
|
import { render, screen, waitFor } from '../../../helpers/render';
|
|
import { server } from '../../../helpers/msw/server';
|
|
import { resetAllStores } from '../../../helpers/store';
|
|
import { ToastContainer } from '../../../../src/components/shared/Toast';
|
|
import MAirTrailConnectionSection from '../../../../src/mobile/screens/settings/MAirTrailConnectionSection';
|
|
|
|
interface SavedBody {
|
|
url: string;
|
|
apiKey?: string;
|
|
allowInsecureTls?: boolean;
|
|
writeEnabled?: boolean;
|
|
}
|
|
|
|
function stubSettings(over: Record<string, unknown> = {}) {
|
|
server.use(
|
|
http.get('/api/integrations/airtrail/settings', () =>
|
|
HttpResponse.json({ url: '', allowInsecureTls: false, writeEnabled: false, connected: false, ...over }),
|
|
),
|
|
);
|
|
}
|
|
|
|
function renderSection() {
|
|
return render(
|
|
<>
|
|
<ToastContainer />
|
|
<MAirTrailConnectionSection />
|
|
</>,
|
|
);
|
|
}
|
|
|
|
describe('MAirTrailConnectionSection', () => {
|
|
beforeEach(() => {
|
|
resetAllStores();
|
|
stubSettings();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-001: hydrates the form from the stored connection', async () => {
|
|
stubSettings({ url: 'https://air.example.com', allowInsecureTls: true, writeEnabled: true, connected: true });
|
|
renderSection();
|
|
|
|
expect(await screen.findByDisplayValue('https://air.example.com')).toBeInTheDocument();
|
|
expect(screen.getByRole('switch', { name: 'Allow self-signed certificates' })).toHaveAttribute('aria-checked', 'true');
|
|
expect(screen.getByRole('switch', { name: 'Write changes back to AirTrail' })).toHaveAttribute('aria-checked', 'true');
|
|
expect(screen.getByText('Connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-002: an unconfigured instance renders empty and disconnected', async () => {
|
|
renderSection();
|
|
|
|
await waitFor(() => expect(screen.getByRole('button', { name: /Test connection/ })).toBeDisabled());
|
|
expect(screen.getByText('Not connected')).toBeInTheDocument();
|
|
expect(screen.getByPlaceholderText('Bearer API key')).toHaveValue('');
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-003: the stored key is masked by the placeholder, never prefilled', async () => {
|
|
stubSettings({ url: 'https://air.example.com', connected: true });
|
|
renderSection();
|
|
|
|
const keyInput = await screen.findByPlaceholderText('••••••••');
|
|
expect(keyInput).toHaveValue('');
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-004: a failing settings fetch still ends the loading state', async () => {
|
|
server.use(
|
|
http.get('/api/integrations/airtrail/settings', () => HttpResponse.json({ error: 'boom' }, { status: 500 })),
|
|
);
|
|
renderSection();
|
|
|
|
// Save stays disabled because there is no URL, but the buttons are rendered
|
|
// rather than stuck behind `loading`.
|
|
await waitFor(() => expect(screen.getByRole('button', { name: /Test connection/ })).toBeDisabled());
|
|
expect(screen.getByText('Not connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-005: Save stays disabled until a URL and a key are present', async () => {
|
|
const user = userEvent.setup();
|
|
renderSection();
|
|
|
|
const saveBtn = await screen.findByRole('button', { name: /Save/ });
|
|
expect(saveBtn).toBeDisabled();
|
|
|
|
await user.type(screen.getByPlaceholderText('https://airtrail.example.com'), 'https://air.example.com');
|
|
expect(saveBtn).toBeDisabled();
|
|
|
|
await user.type(screen.getByPlaceholderText('Bearer API key'), 'secret');
|
|
expect(saveBtn).toBeEnabled();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-006: an already-connected instance can be saved without retyping the key', async () => {
|
|
const user = userEvent.setup();
|
|
let body: SavedBody | undefined;
|
|
stubSettings({ url: 'https://air.example.com', connected: true });
|
|
server.use(
|
|
http.put('/api/integrations/airtrail/settings', async ({ request }) => {
|
|
body = (await request.json()) as SavedBody;
|
|
return HttpResponse.json({ ok: true });
|
|
}),
|
|
http.get('/api/integrations/airtrail/status', () => HttpResponse.json({ connected: true })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Save/ }));
|
|
await screen.findByText('AirTrail connection saved');
|
|
expect(body).toEqual({ url: 'https://air.example.com', allowInsecureTls: false, writeEnabled: false });
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-007: a typed key and the toggles are sent along and the field is cleared', async () => {
|
|
const user = userEvent.setup();
|
|
let body: SavedBody | undefined;
|
|
server.use(
|
|
http.put('/api/integrations/airtrail/settings', async ({ request }) => {
|
|
body = (await request.json()) as SavedBody;
|
|
return HttpResponse.json({ ok: true });
|
|
}),
|
|
http.get('/api/integrations/airtrail/status', () => HttpResponse.json({ connected: true })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.type(await screen.findByPlaceholderText('https://airtrail.example.com'), ' https://air.example.com ');
|
|
await user.type(screen.getByPlaceholderText('Bearer API key'), ' tok-123 ');
|
|
await user.click(screen.getByRole('switch', { name: 'Allow self-signed certificates' }));
|
|
await user.click(screen.getByRole('switch', { name: 'Write changes back to AirTrail' }));
|
|
await user.click(screen.getByRole('button', { name: /Save/ }));
|
|
|
|
await waitFor(() =>
|
|
expect(body).toEqual({
|
|
url: 'https://air.example.com',
|
|
allowInsecureTls: true,
|
|
writeEnabled: true,
|
|
apiKey: 'tok-123',
|
|
}),
|
|
);
|
|
await waitFor(() => expect(screen.getByPlaceholderText('••••••••')).toHaveValue(''));
|
|
expect(screen.getByText('Connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-008: a warning from the server replaces the success toast', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com', connected: true });
|
|
server.use(
|
|
http.put('/api/integrations/airtrail/settings', () => HttpResponse.json({ warning: 'TLS verification is off' })),
|
|
http.get('/api/integrations/airtrail/status', () => HttpResponse.json({ connected: true })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Save/ }));
|
|
await screen.findByText('TLS verification is off');
|
|
expect(screen.queryByText('AirTrail connection saved')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-009: a failing status probe after a successful save marks it disconnected', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com', connected: true });
|
|
server.use(
|
|
http.put('/api/integrations/airtrail/settings', () => HttpResponse.json({ ok: true })),
|
|
http.get('/api/integrations/airtrail/status', () => HttpResponse.json({ error: 'down' }, { status: 500 })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Save/ }));
|
|
expect(await screen.findByText('Not connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-010: a rejected save surfaces the server error message', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com', connected: true });
|
|
server.use(
|
|
http.put('/api/integrations/airtrail/settings', () =>
|
|
HttpResponse.json({ error: 'Instance unreachable' }, { status: 400 }),
|
|
),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Save/ }));
|
|
expect(await screen.findByText('Instance unreachable')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-011: a successful test reports the flight count and flips the badge', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com' });
|
|
server.use(
|
|
http.post('/api/integrations/airtrail/test', () => HttpResponse.json({ connected: true, flightCount: 12 })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Test connection/ }));
|
|
await screen.findByText('Connected — 12 flight(s) found');
|
|
expect(screen.getByText('Connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-012: a rejected test shows the returned error and keeps the badge off', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com' });
|
|
server.use(
|
|
http.post('/api/integrations/airtrail/test', () => HttpResponse.json({ connected: false, error: 'Bad token' })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Test connection/ }));
|
|
await screen.findByText('Bad token');
|
|
expect(screen.getByText('Not connected')).toBeInTheDocument();
|
|
});
|
|
|
|
it('FE-MOB-SETAIR-013: a network failure during the test falls back to the generic message', async () => {
|
|
const user = userEvent.setup();
|
|
stubSettings({ url: 'https://air.example.com' });
|
|
server.use(
|
|
http.post('/api/integrations/airtrail/test', () => HttpResponse.json({ error: 'nope' }, { status: 500 })),
|
|
);
|
|
renderSection();
|
|
|
|
await user.click(await screen.findByRole('button', { name: /Test connection/ }));
|
|
expect(await screen.findByText('Connection failed')).toBeInTheDocument();
|
|
});
|
|
});
|