diff --git a/client/src/components/Settings/PhotoProvidersSection.test.tsx b/client/src/components/Settings/PhotoProvidersSection.test.tsx
index e26c72da5..cff5e5f70 100644
--- a/client/src/components/Settings/PhotoProvidersSection.test.tsx
+++ b/client/src/components/Settings/PhotoProvidersSection.test.tsx
@@ -1,4 +1,4 @@
-// FE-COMP-PHOTOPROVIDERS-001 to FE-COMP-PHOTOPROVIDERS-026
+// FE-COMP-PHOTOPROVIDERS-001 to FE-COMP-PHOTOPROVIDERS-027
import { render, screen, waitFor } from '../../../tests/helpers/render';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
@@ -263,6 +263,23 @@ describe('PhotoProvidersSection', () => {
expect(await screen.findByText(/Auth failed/i)).toBeInTheDocument();
});
+ it('FE-COMP-PHOTOPROVIDERS-027: a failed test reads "provider: reason" with no stray space', async () => {
+ const user = userEvent.setup();
+ server.use(
+ http.post('/api/addons/immich/test', () => HttpResponse.json({ connected: false, error: 'Auth failed' })),
+ );
+ seedMemoriesEnabled();
+ render(
+ <>
+
+
+ >,
+ );
+ await screen.findByText('Immich');
+ await user.click(screen.getByRole('button', { name: /test connection/i }));
+ expect(await screen.findByText('Could not connect to Immich: Auth failed')).toBeInTheDocument();
+ });
+
it('FE-COMP-PHOTOPROVIDERS-016: Test button is disabled while test is in progress', async () => {
const user = userEvent.setup();
let resolveTest!: () => void;
diff --git a/client/src/components/Settings/PhotoProvidersSection.tsx b/client/src/components/Settings/PhotoProvidersSection.tsx
index 4c58bd7e8..ae2db0df1 100644
--- a/client/src/components/Settings/PhotoProvidersSection.tsx
+++ b/client/src/components/Settings/PhotoProvidersSection.tsx
@@ -27,7 +27,7 @@ export default function PhotoProvidersSection(): React.ReactElement {
isProviderSaveDisabled,
handleSaveProvider,
handleTestProvider,
- } = usePhotoProviderConnections({ statusRouteOwnsBadge: false, testErrorGap: ' ' })
+ } = usePhotoProviderConnections({ statusRouteOwnsBadge: false })
const renderPhotoProviderSection = (provider: PhotoProviderAddon): React.ReactElement => {
const fields = getProviderFields(provider)
diff --git a/client/src/components/Settings/usePhotoProviderConnections.test.ts b/client/src/components/Settings/usePhotoProviderConnections.test.ts
index 1c5fe6e93..cbf202302 100644
--- a/client/src/components/Settings/usePhotoProviderConnections.test.ts
+++ b/client/src/components/Settings/usePhotoProviderConnections.test.ts
@@ -81,8 +81,8 @@ function stubGets(byPath: Record Promise)>) {
});
}
-function render(statusRouteOwnsBadge: boolean, testErrorGap = '') {
- return renderHook(() => usePhotoProviderConnections({ statusRouteOwnsBadge, testErrorGap }));
+function render(statusRouteOwnsBadge: boolean) {
+ return renderHook(() => usePhotoProviderConnections({ statusRouteOwnsBadge }));
}
beforeEach(() => {
@@ -216,16 +216,19 @@ describe('usePhotoProviderConnections', () => {
expect(result.current.providerTesting.immich).toBe(false);
});
- it('FE-COMP-PHOTOCONN-010: a failed probe joins the server reason with the gap each shell asks for', async () => {
+ it('FE-COMP-PHOTOCONN-010: a failed probe joins the server reason as ": reason" on both shells', async () => {
seedProviders([PROVIDER]);
stubGets({ '/addons/immich/settings': { data: {} }, '/addons/immich/status': { data: {} } });
vi.spyOn(apiClient, 'post').mockResolvedValue({ data: { connected: false, error: 'bad key' } });
- const phone = render(true, '');
+ const phone = render(true);
await act(() => phone.result.current.handleTestProvider(PROVIDER));
expect(toast.error).toHaveBeenLastCalledWith('memories.connectionError(Immich): bad key');
- const desktop = render(false, ' ');
+ const desktop = render(false);
await act(() => desktop.result.current.handleTestProvider(PROVIDER));
- expect(toast.error).toHaveBeenLastCalledWith('memories.connectionError(Immich) : bad key');
+ expect(toast.error).toHaveBeenLastCalledWith('memories.connectionError(Immich): bad key');
+ vi.mocked(apiClient.post).mockResolvedValue({ data: { connected: false } });
+ await act(() => desktop.result.current.handleTestProvider(PROVIDER));
+ expect(toast.error).toHaveBeenLastCalledWith('memories.connectionError(Immich)');
vi.mocked(apiClient.post).mockRejectedValue(new Error('x'));
await act(() => desktop.result.current.handleTestProvider(PROVIDER));
expect(toast.error).toHaveBeenLastCalledWith('memories.connectionError(Immich)');
diff --git a/client/src/components/Settings/usePhotoProviderConnections.ts b/client/src/components/Settings/usePhotoProviderConnections.ts
index 98ff592c9..2d379c785 100644
--- a/client/src/components/Settings/usePhotoProviderConnections.ts
+++ b/client/src/components/Settings/usePhotoProviderConnections.ts
@@ -56,8 +56,6 @@ export interface PhotoProviderConnectionOptions {
* race. The phone does this; the desktop takes whichever answer lands last.
*/
statusRouteOwnsBadge: boolean;
- /** What goes between the failed test sentence and the server's ": reason" ('' on the phone, ' ' on the desktop). */
- testErrorGap: string;
}
/**
@@ -66,7 +64,7 @@ export interface PhotoProviderConnectionOptions {
* (secrets never prefilled), the connection badge, and save and test. The desktop
* section and its phone twin render their own markup over this.
*/
-export function usePhotoProviderConnections({ statusRouteOwnsBadge, testErrorGap }: PhotoProviderConnectionOptions) {
+export function usePhotoProviderConnections({ statusRouteOwnsBadge }: PhotoProviderConnectionOptions) {
const { t } = useTranslation();
const toast = useToast();
const { isEnabled: addonEnabled, addons } = useAddonStore();
@@ -220,7 +218,7 @@ export function usePhotoProviderConnections({ statusRouteOwnsBadge, testErrorGap
toast.success(t('memories.connectionSuccess', { provider_name: provider.name }));
} else {
const detail = res.data?.error ? `: ${String(res.data.error)}` : '';
- toast.error(`${t('memories.connectionError', { provider_name: provider.name })}${testErrorGap}${detail}`);
+ toast.error(`${t('memories.connectionError', { provider_name: provider.name })}${detail}`);
}
} catch {
toast.error(t('memories.connectionError', { provider_name: provider.name }));
diff --git a/client/src/mobile/screens/settings/MPhotoProvidersSection.tsx b/client/src/mobile/screens/settings/MPhotoProvidersSection.tsx
index 5d7be597a..29f9e2820 100644
--- a/client/src/mobile/screens/settings/MPhotoProvidersSection.tsx
+++ b/client/src/mobile/screens/settings/MPhotoProvidersSection.tsx
@@ -30,7 +30,7 @@ export default function MPhotoProvidersSection(): React.ReactElement {
isProviderSaveDisabled,
handleSaveProvider,
handleTestProvider,
- } = usePhotoProviderConnections({ statusRouteOwnsBadge: true, testErrorGap: '' })
+ } = usePhotoProviderConnections({ statusRouteOwnsBadge: true })
const renderPhotoProviderSection = (provider: PhotoProviderAddon): React.ReactElement => {
const fields = getProviderFields(provider)