feat(web): Herkunft der Fehlermeldung — Cookie tessera_desktop_client und Client-Felder in der Nutzlast

Die Middleware liest jetzt zusaetzlich dv/dc/dos aus der Anfrage und legt
daraus das Cookie tessera_desktop_client an (bereinigt per Muster, nur
wenn alle drei Werte gueltig sind); desktop-client.ts liest es zurueck.
Der Fehler-melden-Dialog fuellt daraus vier neue Nutzlastfelder
(clientKind/clientOs/clientVersion/clientCommit), damit die API die
Herkunft der Meldung ausweisen kann. Ohne das zweite Cookie (alter
Client) bleibt es bei "Desktop-App (unbekannt)".

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016g2npLxzH5gZpg8s2S6vKh
This commit is contained in:
2026-09-18 12:36:19 +02:00
parent b03cb211b3
commit f2457113a2
8 changed files with 376 additions and 10 deletions
+58 -1
View File
@@ -1,14 +1,24 @@
import { act, renderHook, waitFor } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { DESKTOP_COOKIE_NAME, isDesktopClient, useIsDesktopClient } from './desktop-client';
import {
DESKTOP_CLIENT_COOKIE_NAME,
DESKTOP_COOKIE_NAME,
getDesktopClientInfo,
isDesktopClient,
parseDesktopClientCookie,
useIsDesktopClient,
} from './desktop-client';
/**
* desktop-client.test — Gegenstueck zur Middleware (260917-h2s): liest das
* Cookie `tessera_desktop`, das die Middleware auf jede Antwort setzt, wenn
* der Desktop-Client `?desktop=1` an seine erste Navigation anhaengt.
* Seit quick-260918-gza zusaetzlich `parseDesktopClientCookie()` /
* `getDesktopClientInfo()` fuer das zweite Cookie `tessera_desktop_client`.
*/
function clearCookie() {
document.cookie = `${DESKTOP_COOKIE_NAME}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
document.cookie = `${DESKTOP_CLIENT_COOKIE_NAME}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
}
afterEach(() => {
@@ -50,3 +60,50 @@ describe('useIsDesktopClient', () => {
expect(result.current).toBe(false);
});
});
describe('getDesktopClientInfo / parseDesktopClientCookie (quick-260918-gza)', () => {
it('kodierte Form (wie der Browser sie in document.cookie haelt)', () => {
expect(
parseDesktopClientCookie('tessera_desktop=1; tessera_desktop_client=1.2.0%7Ca6d1a64%7Cwindows'),
).toEqual({ version: '1.2.0', commit: 'a6d1a64', os: 'windows' });
});
it('rohe Form (nicht kodiert)', () => {
expect(parseDesktopClientCookie('tessera_desktop_client=1.2.0|a6d1a64|windows')).toEqual({
version: '1.2.0',
commit: 'a6d1a64',
os: 'windows',
});
});
it('leerer Commit bleibt leer', () => {
expect(parseDesktopClientCookie('tessera_desktop_client=1.2.0%7C%7Clinux')).toEqual({
version: '1.2.0',
commit: '',
os: 'linux',
});
});
it('ohne Cookie: null', () => {
expect(parseDesktopClientCookie('tessera_desktop=1')).toBeNull();
});
it('unvollstaendige oder leere Pflichtfelder: null', () => {
expect(parseDesktopClientCookie('tessera_desktop_client=abc')).toBeNull();
expect(parseDesktopClientCookie('tessera_desktop_client=1.2.0|x')).toBeNull();
expect(parseDesktopClientCookie('tessera_desktop_client=|a6d1a64|linux')).toBeNull();
expect(parseDesktopClientCookie('tessera_desktop_client=1.2.0|a6d1a64|')).toBeNull();
});
it('getDesktopClientInfo() liest document.cookie', () => {
expect(getDesktopClientInfo()).toBeNull();
document.cookie = 'tessera_desktop_client=1.2.0%7Ca6d1a64%7Cwindows; path=/';
expect(getDesktopClientInfo()).toEqual({ version: '1.2.0', commit: 'a6d1a64', os: 'windows' });
});
it('getDesktopClientInfo() ohne document: null', () => {
vi.stubGlobal('document', undefined);
expect(getDesktopClientInfo()).toBeNull();
vi.unstubAllGlobals();
});
});