Files
TREK/client/tests/helpers/mobileAdmin.ts
Maurice 8906b66bc8 feat(settings): set the Google Maps key through PLACES_API_KEY
The server already put PLACES_API_KEY ahead of the key saved in the admin
panel, but nothing said so: the variable was in no template and the admin
field kept showing a stored value that no search used.

GET /me/settings now answers env_keys, the variable behind each key the
environment sets (Google, Unsplash, Amap), and null for that key's value.
The admin field turns read-only and names the variable, Test still probes
the key a search resolves to, and a save leaves those names out so the
stored fallback survives. The variable is documented in .env.example,
docker-compose.yml, the Helm chart (as a secret) and the wiki.

Addresses discussion #1881.
2026-09-24 16:50:02 +02:00

198 lines
5.9 KiB
TypeScript

import { vi } from 'vitest';
import type { useAdmin } from '../../src/pages/admin/useAdmin';
import type { AdminUser } from '../../src/pages/admin/adminModel';
/**
* Fixture for the mobile admin sections. MAdminUsersSection, MAdminSettingsSection
* and MAdminSheets are pure presentation over the useAdmin() bag, so tests render
* them with this builder instead of mounting MAdmin and its data loading.
*
* Defaults are the "nothing configured yet" state; every setter and handler is a
* spy so tests can assert the exact call, including state updater functions.
*/
export type AdminHook = ReturnType<typeof useAdmin>;
export function buildAdminUser(overrides: Partial<AdminUser> = {}): AdminUser {
return {
id: 1,
username: 'admin',
email: 'admin@example.com',
role: 'admin',
created_at: '2025-01-01T00:00:00.000Z',
avatar_url: null,
...overrides,
};
}
export function buildAdminToast(): Record<string, ReturnType<typeof vi.fn>> {
return { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn() };
}
export function buildAdminHook(overrides: Record<string, unknown> = {}): AdminHook {
const base: Record<string, unknown> = {
// store-derived
demoMode: false,
serverTimezone: 'UTC',
hour12: false,
mcpEnabled: false,
devMode: false,
managed: false,
currentUser: buildAdminUser(),
updateApiKeys: vi.fn(async () => undefined),
setAppRequireMfa: vi.fn(),
setTripRemindersEnabled: vi.fn(),
setPlacesPhotosEnabled: vi.fn(),
setPlacesAutocompleteEnabled: vi.fn(),
setPlacesDetailsEnabled: vi.fn(),
setPlacesEnrichEnabled: vi.fn(),
logout: vi.fn(async () => undefined),
navigate: vi.fn(),
toast: buildAdminToast(),
// users
activeTab: 'users',
setActiveTab: vi.fn(),
users: [] as AdminUser[],
setUsers: vi.fn(),
stats: null,
isLoading: false,
editingUser: null,
setEditingUser: vi.fn(),
editForm: { username: '', email: '', role: 'user', password: '' },
setEditForm: vi.fn(),
showCreateUser: false,
setShowCreateUser: vi.fn(),
createForm: { username: '', email: '', password: '', role: 'user' },
setCreateForm: vi.fn(),
// feature flags
bagTrackingEnabled: false,
setBagTrackingEnabled: vi.fn(),
placesPhotosEnabled: false,
setPlacesPhotosEnabledState: vi.fn(),
placesAutocompleteEnabled: false,
setPlacesAutocompleteEnabledState: vi.fn(),
placesDetailsEnabled: false,
setPlacesDetailsEnabledState: vi.fn(),
placesEnrichEnabled: true,
placesGoogleOnly: false,
handleTogglePlacesGoogleOnly: vi.fn(),
setPlacesEnrichEnabledState: vi.fn(),
// Fail-open on the server, so the harness starts where an unset row lands.
trekPlacesEnabled: true,
setTrekPlacesEnabledState: vi.fn(),
placeShadowEnabled: false,
setPlaceShadowEnabledState: vi.fn(),
collabFeatures: { chat: true, notes: true, polls: true, whatsnext: true },
setCollabFeatures: vi.fn(),
// auth / OIDC
oidcConfig: {
issuer: '',
client_id: '',
client_secret: '',
client_secret_set: false,
display_name: '',
discovery_url: '',
},
setOidcConfig: vi.fn(),
savingOidc: false,
setSavingOidc: vi.fn(),
passwordLogin: true,
setPasswordLogin: vi.fn(),
passwordRegistration: true,
setPasswordRegistration: vi.fn(),
oidcLogin: false,
setOidcLogin: vi.fn(),
oidcRegistration: false,
setOidcRegistration: vi.fn(),
envOverrideOidcOnly: false,
setEnvOverrideOidcOnly: vi.fn(),
oidcConfigured: false,
setOidcConfigured: vi.fn(),
requireMfa: false,
setRequireMfa: vi.fn(),
passkeyLogin: false,
setPasskeyLogin: vi.fn(),
passkeyConfigured: false,
webauthnRpId: '',
setWebauthnRpId: vi.fn(),
webauthnOrigins: '',
setWebauthnOrigins: vi.fn(),
savingWebauthn: false,
handleSaveWebauthn: vi.fn(),
// invites
invites: [] as unknown[],
setInvites: vi.fn(),
inviteTrips: [] as { id: number; title: string }[],
showCreateInvite: false,
setShowCreateInvite: vi.fn(),
inviteForm: { max_uses: 1, expires_in_days: 7, trip_id: '' },
setInviteForm: vi.fn(),
// misc settings
allowedFileTypes: 'jpg,png,pdf',
setAllowedFileTypes: vi.fn(),
savingFileTypes: false,
setSavingFileTypes: vi.fn(),
smtpValues: {},
setSmtpValues: vi.fn(),
smtpLoaded: true,
mapsKey: '',
setMapsKey: vi.fn(),
weatherKey: '',
setWeatherKey: vi.fn(),
unsplashKey: '',
setUnsplashKey: vi.fn(),
amapKey: '',
setAmapKey: vi.fn(),
hasMapsKey: false,
hasAmapKey: false,
// No key set by the environment unless a test says so (#1881).
keyInputProps: () => ({ disabled: false, placeholder: 'Enter key...' }),
placesProvider: 'auto',
savingPlacesProvider: false,
showKeys: {},
setShowKeys: vi.fn(),
savingKeys: false,
validating: {},
validation: {},
// update / danger zone
updateInfo: null,
setUpdateInfo: vi.fn(),
showUpdateModal: false,
setShowUpdateModal: vi.fn(),
showRotateJwtModal: false,
setShowRotateJwtModal: vi.fn(),
rotatingJwt: false,
setRotatingJwt: vi.fn(),
// handlers
loadData: vi.fn(),
loadAppConfig: vi.fn(),
loadApiKeys: vi.fn(),
handleToggleAuthSetting: vi.fn(),
handleToggleRequireMfa: vi.fn(),
toggleKey: vi.fn(),
handleSaveApiKeys: vi.fn(),
handleValidateKeys: vi.fn(),
handleValidateKey: vi.fn(),
handleSavePlacesProvider: vi.fn(),
handleCreateUser: vi.fn(),
handleCreateInvite: vi.fn(),
handleDeleteInvite: vi.fn(),
copyInviteLink: vi.fn(),
handleEditUser: vi.fn(),
handleSaveUser: vi.fn(),
handleDeleteUser: vi.fn(),
};
// Derived like the hook does, so a test that only sets mapsKey keeps its Test button.
const merged = { ...base, ...overrides };
if (!('mapsKeyTestable' in overrides)) merged.mapsKeyTestable = !!merged.mapsKey;
return merged as unknown as AdminHook;
}