mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-23 15:36:33 +00:00
31859651d7
Every help screen checked against the v5 redesign. Tours is documented for the first time, as a screen of its own with twelve screens under it, and notifications get help of their own. All pictures are taken again on a realistic demo trip, with short loops where a still cannot show the motion. The English is plain and every language is written naturally rather than word for word. The switcher shows two levels at most, a screen's overview lists its sub-screens as cards above its guides, each wiki page is linked once, and the help sidebar is wider. Fixes found along the way: Save to Collection keeps a tour's route, a track dropped on the places column opens the Tours import, tour Share on desktop, the unsaved tour draft lives on the account, a route click no longer adds an end point, Trip places leaves the trip's tours out, Escape closes a stage's day menu, confirms before destructive deletes, DWD warnings sharing one id kept apart, a booking's date moves with it, and smaller fixes across notifications, bookings, the day plan, stays, Atlas, Collections, Vacay and the admin screens.
200 lines
6.0 KiB
TypeScript
200 lines
6.0 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(),
|
|
inviteToDelete: null,
|
|
setInviteToDelete: 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;
|
|
}
|