Add an opt-in feedback submission flow that posts to the central feedback-api service. Integrated into the existing LogViewerCard so the same card now covers diagnostics capture (logs) and diagnostics forwarding (feedback). - Rust command `send_feedback` forwards the payload via reqwest, so the Tauri origin never needs a CORS whitelist entry server-side - First submission shows a one-time consent dialog explaining that this is the only app feature that talks to a server besides updates and Maximus sign-in - Three opt-in checkboxes (all unchecked by default): navigation context, recent error logs (appended as a suffix to the content), identify with the Maximus account - Context keys are limited to the server whitelist (page, locale, theme, viewport, userAgent, timestamp); app_version + OS are packed into userAgent via `get_feedback_user_agent` so we don't pull in an extra Tauri plugin - Error codes are stable strings (invalid, rate_limit, server_error, network_error) mapped to i18n messages on the frontend - Wording follows the cross-app convention documented in `la-compagnie-maximus/docs/feedback-hub-ops.md` - CSP `connect-src` extended with feedback.lacompagniemaximus.com - Docs: CHANGELOG (EN + FR), guide utilisateur, docs.settings (features/steps/tips) updated in both locales Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
56 lines
1.8 KiB
TypeScript
56 lines
1.8 KiB
TypeScript
import { describe, it, expect, beforeEach, beforeAll, vi } from "vitest";
|
|
import { getRecentErrorLogs, clearLogs, initLogCapture } from "./logService";
|
|
|
|
beforeAll(() => {
|
|
// Patch console.* so addEntry runs. Idempotent.
|
|
initLogCapture();
|
|
});
|
|
|
|
describe("getRecentErrorLogs", () => {
|
|
beforeEach(() => {
|
|
// Reset the in-memory buffer. clearLogs also clears sessionStorage
|
|
// which jsdom provides in vitest.
|
|
clearLogs();
|
|
});
|
|
|
|
it("returns an empty string when the log buffer is empty", () => {
|
|
expect(getRecentErrorLogs(5)).toBe("");
|
|
});
|
|
|
|
it("returns an empty string when n <= 0", () => {
|
|
console.error("boom");
|
|
expect(getRecentErrorLogs(0)).toBe("");
|
|
expect(getRecentErrorLogs(-3)).toBe("");
|
|
});
|
|
|
|
it("filters out info-level entries", () => {
|
|
// Freeze time so the ISO prefix is predictable
|
|
vi.setSystemTime(new Date("2026-04-17T15:00:00.000Z"));
|
|
console.log("just chatter");
|
|
console.warn("low fuel");
|
|
console.error("engine out");
|
|
|
|
const out = getRecentErrorLogs(10);
|
|
expect(out).not.toContain("chatter");
|
|
expect(out).toContain("WARN: low fuel");
|
|
expect(out).toContain("ERROR: engine out");
|
|
vi.useRealTimers();
|
|
});
|
|
|
|
it("keeps only the last N non-info entries in order", () => {
|
|
for (let i = 0; i < 5; i++) console.warn(`w${i}`);
|
|
const out = getRecentErrorLogs(2);
|
|
const lines = out.split("\n");
|
|
expect(lines).toHaveLength(2);
|
|
expect(lines[0]).toContain("w3");
|
|
expect(lines[1]).toContain("w4");
|
|
});
|
|
|
|
it("formats each line as `[ISO] LEVEL: message`", () => {
|
|
vi.setSystemTime(new Date("2026-04-17T15:23:45.000Z"));
|
|
console.error("export failed");
|
|
const out = getRecentErrorLogs(1);
|
|
expect(out).toMatch(/^\[2026-04-17T15:23:45\.000Z\] ERROR: export failed$/);
|
|
vi.useRealTimers();
|
|
});
|
|
});
|