fix(usage): synchronize setup consent and dismissal state
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { Navigate, useNavigate } from 'react-router-dom';
|
import { Navigate, useNavigate } from 'react-router-dom';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee } from 'lucide-react';
|
import { Key, Mail, Lock, Eye, EyeOff, AlertCircle, ArrowLeft, ArrowRight, Copy, Check, ExternalLink, Bug, Lightbulb, Star, Coffee } from 'lucide-react';
|
||||||
import type { LucideIcon } from 'lucide-react';
|
import type { LucideIcon } from 'lucide-react';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
@@ -65,6 +65,7 @@ export const SetupPage: React.FC = () => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { login } = useAdminAuth();
|
const { login } = useAdminAuth();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { data: status, isLoading: statusLoading, isError: statusError } = useQuery({
|
const { data: status, isLoading: statusLoading, isError: statusError } = useQuery({
|
||||||
queryKey: ['setup-status'],
|
queryKey: ['setup-status'],
|
||||||
@@ -292,7 +293,7 @@ export const SetupPage: React.FC = () => {
|
|||||||
const enableUsageReporting = async () => {
|
const enableUsageReporting = async () => {
|
||||||
setIsEnablingUsageReporting(true);
|
setIsEnablingUsageReporting(true);
|
||||||
try {
|
try {
|
||||||
await productUsageService.enable();
|
queryClient.setQueryData(['productUsage'], await productUsageService.enable());
|
||||||
toast.success(t('setup.usageReporting.enabled'));
|
toast.success(t('setup.usageReporting.enabled'));
|
||||||
} catch {
|
} catch {
|
||||||
toast.warn(t('setup.usageReporting.enableFailed'));
|
toast.warn(t('setup.usageReporting.enableFailed'));
|
||||||
@@ -307,7 +308,7 @@ export const SetupPage: React.FC = () => {
|
|||||||
// the same question seconds later on their first dashboard visit.
|
// the same question seconds later on their first dashboard visit.
|
||||||
const skipUsageReporting = async () => {
|
const skipUsageReporting = async () => {
|
||||||
try {
|
try {
|
||||||
await productUsageService.promptSeen();
|
queryClient.setQueryData(['productUsage'], await productUsageService.promptSeen());
|
||||||
} catch { /* best-effort — worst case the dashboard asks once more */ }
|
} catch { /* best-effort — worst case the dashboard asks once more */ }
|
||||||
setStep('community');
|
setStep('community');
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,12 +39,15 @@ beforeEach(() => {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
vi.mocked(usage.status).mockResolvedValue({ status: 'disabled', collector_url: 'https://custom-collector.example.test' } as never);
|
vi.mocked(usage.status).mockResolvedValue({ status: 'disabled', collector_url: 'https://custom-collector.example.test' } as never);
|
||||||
vi.mocked(usage.enable).mockResolvedValue({ status: 'active' } as never);
|
vi.mocked(usage.enable).mockResolvedValue({ status: 'active' } as never);
|
||||||
|
vi.mocked(usage.promptSeen).mockResolvedValue({ status: 'disabled', prompt_shown: true } as never);
|
||||||
HTMLDialogElement.prototype.showModal = function () { this.setAttribute('open', ''); };
|
HTMLDialogElement.prototype.showModal = function () { this.setAttribute('open', ''); };
|
||||||
});
|
});
|
||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
let client: QueryClient;
|
||||||
async function reachInvitation() {
|
async function reachInvitation() {
|
||||||
render(<QueryClientProvider client={new QueryClient({ defaultOptions: { queries: { retry: false } } })}>
|
client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||||
|
render(<QueryClientProvider client={client}>
|
||||||
<MemoryRouter><SetupPage /></MemoryRouter>
|
<MemoryRouter><SetupPage /></MemoryRouter>
|
||||||
</QueryClientProvider>);
|
</QueryClientProvider>);
|
||||||
fireEvent.change(await screen.findByLabelText('setup.tokenLabel'), { target: { value: 'test-token' } });
|
fireEvent.change(await screen.findByLabelText('setup.tokenLabel'), { target: { value: 'test-token' } });
|
||||||
@@ -82,6 +85,7 @@ it('uses the full settings disclosure and configured collector before accepting
|
|||||||
fireEvent.click(dialog.getByRole('button', { name: 'productUsage.enable' }));
|
fireEvent.click(dialog.getByRole('button', { name: 'productUsage.enable' }));
|
||||||
await screen.findByText('setup.community.mission');
|
await screen.findByText('setup.community.mission');
|
||||||
expect(usage.enable).toHaveBeenCalledTimes(1);
|
expect(usage.enable).toHaveBeenCalledTimes(1);
|
||||||
|
expect(client.getQueryData(['productUsage'])).toMatchObject({ status: 'active' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('skipping the invitation never enables reporting', async () => {
|
it('skipping the invitation never enables reporting', async () => {
|
||||||
@@ -89,6 +93,8 @@ it('skipping the invitation never enables reporting', async () => {
|
|||||||
fireEvent.click(screen.getByRole('button', { name: 'setup.usageReporting.skip' }));
|
fireEvent.click(screen.getByRole('button', { name: 'setup.usageReporting.skip' }));
|
||||||
await screen.findByText('setup.community.mission');
|
await screen.findByText('setup.community.mission');
|
||||||
expect(usage.enable).not.toHaveBeenCalled();
|
expect(usage.enable).not.toHaveBeenCalled();
|
||||||
|
expect(usage.promptSeen).toHaveBeenCalledTimes(1);
|
||||||
|
expect(client.getQueryData(['productUsage'])).toMatchObject({ status: 'disabled', prompt_shown: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
it.each(['failed', 'invalid'])('keeps setup usable when collector configuration is %s', async (failure) => {
|
it.each(['failed', 'invalid'])('keeps setup usable when collector configuration is %s', async (failure) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user