fix search feature
Build & Push / Pipeline Tests (push) Failing after 1m14s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Successful in 53s
Test / API Unit Tests (push) Failing after 50s
Test / Homepage Unit Tests (push) Successful in 45s
Test / Carplace Unit Tests (push) Successful in 44s
Test / Admin Unit Tests (push) Successful in 42s
Test / Dashboard Unit Tests (push) Successful in 43s
Test / API Integration Tests (push) Successful in 1m4s

This commit is contained in:
root
2026-07-26 01:18:05 -04:00
parent f6fcd7ce54
commit 2b422ca197
23 changed files with 880 additions and 51 deletions
+35 -1
View File
@@ -6,6 +6,7 @@ function installBrowser(token?: string) {
value: {
location: {
hostname: 'localhost',
origin: 'http://localhost',
},
localStorage: {
getItem: vi.fn(() => token ?? null),
@@ -28,7 +29,7 @@ afterEach(() => {
})
describe('dashboard apiFetch', () => {
it('adds JSON headers and sends cookies for browser requests', async () => {
it('sends cookies without forcing JSON headers for bodyless browser requests', async () => {
installBrowser()
const fetchMock = vi.fn(async () => ({
ok: true,
@@ -39,6 +40,23 @@ describe('dashboard apiFetch', () => {
const { apiFetch } = await import('./api')
await expect(apiFetch('/team')).resolves.toEqual({ ok: true })
expect(fetchMock).toHaveBeenCalledWith(expect.stringMatching(/\/api\/v1\/team$/), expect.objectContaining({
credentials: 'include',
headers: expect.not.objectContaining({ 'Content-Type': expect.any(String) }),
}))
})
it('adds JSON headers for JSON payload requests', async () => {
installBrowser()
const fetchMock = vi.fn(async () => ({
ok: true,
json: async () => ({ data: { ok: true } }),
}))
Object.defineProperty(globalThis, 'fetch', { configurable: true, value: fetchMock })
const { apiFetch } = await import('./api')
await expect(apiFetch('/team', { method: 'POST', body: JSON.stringify({ name: 'Ops' }) })).resolves.toEqual({ ok: true })
expect(fetchMock).toHaveBeenCalledWith(expect.stringMatching(/\/api\/v1\/team$/), expect.objectContaining({
credentials: 'include',
headers: expect.objectContaining({
@@ -133,6 +151,22 @@ describe('dashboard apiFetch', () => {
expect(calledUrl).not.toContain('/api/v1/api/v1')
})
it('resolves the API origin for realtime connections from relative and absolute API bases', async () => {
installBrowser()
setBrowserHostname('rentaldrivego.ma')
process.env.NEXT_PUBLIC_API_URL = '/dashboard/api/v1'
let api = await import('./api')
expect(api.resolveApiOrigin()).toBe('http://localhost')
vi.resetModules()
installBrowser()
process.env.NEXT_PUBLIC_API_URL = 'http://localhost:4000/api/v1'
api = await import('./api')
expect(api.resolveApiOrigin()).toBe('http://localhost:4000')
})
it('does not force JSON content type for FormData payloads', async () => {
installBrowser()
const fetchMock = vi.fn(async () => ({ ok: true, json: async () => ({ data: { uploaded: true } }) }))
+11 -1
View File
@@ -34,6 +34,16 @@ export function resolveApiBase(): string {
return normalizeApiBase(shouldUseDashboardProxy(configuredApiBase) ? DASHBOARD_PROXY_API_BASE : (configuredApiBase || DASHBOARD_PROXY_API_BASE))
}
export function resolveApiOrigin(): string | null {
if (typeof window === 'undefined') return null
try {
return new URL(resolveApiBase(), window.location.origin).origin
} catch {
return window.location.origin
}
}
export const API_BASE = resolveApiBase()
export const EMPLOYEE_PROFILE_KEY = 'employee_profile'
@@ -45,7 +55,7 @@ export async function apiFetch<T>(path: string, options?: RequestInit): Promise<
...(options?.headers as Record<string, string> ?? {}),
}
if (!isFormData) {
if (!isFormData && options?.body !== undefined) {
headers['Content-Type'] = 'application/json'
}
@@ -35,6 +35,12 @@ export const dashboardRoutePolicies: Record<string, DashboardRoutePolicy> = {
'/subscription': OWNER_ONLY_BILLING_RECOVERY_POLICY,
'/subscription/success': OWNER_ONLY_BILLING_RECOVERY_POLICY,
'/subscription/cancel': OWNER_ONLY_BILLING_RECOVERY_POLICY,
'/search': {
authenticationRequired: true,
allowedRoles: null,
subscriptionRequired: true,
menuRegistrationRequired: false,
},
}
export function resolveDashboardRoutePolicy(pathname: string): DashboardRoutePolicy {