fix(nextcloud-files): IN-05 Browser-Anmeldung prueft den Stand, bevor sie "abgelaufen" meldet

- antwortet die Abfrage mit 404/410, laedt die Seite zuerst den Stand neu; ging die Antwort
  "verbunden" unterwegs verloren, erscheinen direkt die Dateien statt eines Fehlers
- Seitentest fuer den Fall

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 22:51:00 +02:00
parent 8679668cac
commit f0f3718b37
2 changed files with 29 additions and 2 deletions
@@ -47,7 +47,8 @@ export function ConnectPanel({
/** Name, Farbe und Logo der Nextcloud; solange sie fehlen, steht nur der Rechnername da. */
info?: NextcloudServerInfo | null;
expired: boolean;
onConnected: () => void;
/** Verbunden (oder Stand pruefen): die Seite laedt den Stand neu. */
onConnected: () => void | Promise<void>;
}) {
const t = useTranslations('nextcloudFiles.connect');
@@ -182,7 +183,18 @@ export function ConnectPanel({
if (err instanceof NextcloudFilesRequestError) {
if (err.status === 404 || err.status === 410) {
// Abgelaufen oder unbekannt (z. B. nach einem Neustart der API): von vorn beginnen.
stop({ code: 'flowExpired', message: err.message, retryAfterSeconds: null });
// Vorher den Stand neu laden (IN-05): ging die Antwort "verbunden" unterwegs verloren,
// ist das Konto in Wahrheit verbunden und die Seite zeigt gleich die Dateien.
stopped = true;
const expired: ErrorState = {
code: 'flowExpired',
message: err.message,
retryAfterSeconds: null,
};
void Promise.resolve()
.then(() => onConnectedRef.current())
.catch(() => undefined)
.finally(() => stop(expired));
} else if (err.code === 'nextcloudLocked' || (err.status < 500 && err.status !== 429)) {
stop(toErrorRef.current(err));
}
@@ -497,6 +497,21 @@ describe('Browser-Anmeldung', () => {
expect(screen.queryByRole('link', { name: 'Anmeldung bei Nextcloud öffnen' })).toBeNull();
});
it('410/404 nach verlorener "verbunden"-Antwort: erst den Stand pruefen, dann die Dateien statt "abgelaufen" (IN-05)', async () => {
await startFlow();
const before = mockGetStatus.mock.calls.length;
mockPollFlow.mockRejectedValue(new NextcloudFilesRequestError(404, 'notFound', 'x'));
mockGetStatus.mockResolvedValue(status({ account: account({ displayName: 'Zwei Faktor' }) }));
await act(async () => {
await vi.advanceTimersByTimeAsync(2000);
});
expect(await screen.findByText('Angemeldet als Zwei Faktor')).toBeTruthy();
expect(mockGetStatus.mock.calls.length).toBeGreaterThan(before);
expect(
screen.queryByText('Die Anmeldung ist abgelaufen. Bitte starten Sie sie neu.'),
).toBeNull();
});
it('"failed" beendet die Abfrage und zeigt den Text', async () => {
await startFlow();
mockPollFlow.mockResolvedValue({