fix: fix at tests + /api proxy

This commit is contained in:
2026-08-17 13:04:29 +07:00
parent 76f26430ba
commit 4e788312ab
5 changed files with 33 additions and 19 deletions

View File

@@ -98,7 +98,7 @@ describe('api client', () => {
mockFetch(async (input) => { mockFetch(async (input) => {
calls++; calls++;
const url = urlOf(input); const url = urlOf(input);
if (url.startsWith('/auth/refresh')) { if (url.startsWith('/api/auth/refresh')) {
return jsonResponse({ access_token: 'new', refresh_token: 'new-r', expires_at: 9999999999 }); return jsonResponse({ access_token: 'new', refresh_token: 'new-r', expires_at: 9999999999 });
} }
if (calls === 1) return jsonResponse({ detail: 'expired' }, 401); if (calls === 1) return jsonResponse({ detail: 'expired' }, 401);
@@ -113,7 +113,7 @@ describe('api client', () => {
setStoredTokens({ access_token: ACCESS, refresh_token: REFRESH, expires_at: 9999999999 }); setStoredTokens({ access_token: ACCESS, refresh_token: REFRESH, expires_at: 9999999999 });
mockFetch(async (input) => { mockFetch(async (input) => {
const url = urlOf(input); const url = urlOf(input);
if (url.startsWith('/auth/refresh')) return jsonResponse({ detail: 'invalid' }, 401); if (url.startsWith('/api/auth/refresh')) return jsonResponse({ detail: 'invalid' }, 401);
return jsonResponse({ detail: 'expired' }, 401); return jsonResponse({ detail: 'expired' }, 401);
}); });
await expect(api.getMe()).rejects.toMatchObject({ status: 401 }); await expect(api.getMe()).rejects.toMatchObject({ status: 401 });

View File

@@ -87,7 +87,7 @@ async function refreshTokens(): Promise<boolean> {
refreshing = (async () => { refreshing = (async () => {
try { try {
const res = await fetch( const res = await fetch(
`/auth/refresh?refresh_token=${encodeURIComponent(refresh)}&iss=credentials`, `/api/auth/refresh?refresh_token=${encodeURIComponent(refresh)}&iss=credentials`,
{ method: 'POST' }, { method: 'POST' },
); );
if (!res.ok) return false; if (!res.ok) return false;
@@ -155,29 +155,29 @@ async function request<T>(path: string, opts: RequestOptions = {}): Promise<T> {
/* ── Public API ───────────────────────────────────────────── */ /* ── Public API ───────────────────────────────────────────── */
export const api = { export const api = {
signup(payload: SignupPayload): Promise<LoginResponse> { signup(payload: SignupPayload): Promise<LoginResponse> {
return request<LoginResponse>('/auth/signup', { method: 'POST', body: payload, auth: false }); return request<LoginResponse>('/api/auth/signup', { method: 'POST', body: payload, auth: false });
}, },
login(payload: LoginPayload): Promise<LoginResponse> { login(payload: LoginPayload): Promise<LoginResponse> {
return request<LoginResponse>('/auth/login', { method: 'POST', body: payload, auth: false }); return request<LoginResponse>('/api/auth/login', { method: 'POST', body: payload, auth: false });
}, },
getPlans(): Promise<PricingPlans> { getPlans(): Promise<PricingPlans> {
return request<PricingPlans>('/plans/', { auth: false }); return request<PricingPlans>('/api/plans/', { auth: false });
}, },
checkout(order: OrderDetails): Promise<CheckoutResponse> { checkout(order: OrderDetails): Promise<CheckoutResponse> {
return request<CheckoutResponse>('/orders/checkout', { method: 'POST', body: order }); return request<CheckoutResponse>('/api/orders/checkout', { method: 'POST', body: order });
}, },
getMe(): Promise<UserInfo> { getMe(): Promise<UserInfo> {
return request<UserInfo>('/users/me'); return request<UserInfo>('/api/users/me');
}, },
getSubscription(): Promise<SubscriptionData> { getSubscription(): Promise<SubscriptionData> {
return request<SubscriptionData>('/users/subscription'); return request<SubscriptionData>('/api/users/subscription');
}, },
getDevices(): Promise<Device[]> { getDevices(): Promise<Device[]> {
return request<Device[]>('/users/subscription/hwid'); return request<Device[]>('/api/users/subscription/hwid');
}, },
deleteDevice(hwid: string): Promise<OperationResult> { deleteDevice(hwid: string): Promise<OperationResult> {
return request<OperationResult>( return request<OperationResult>(
`/users/subscription/hwid?hwid=${encodeURIComponent(hwid)}`, `/api/users/subscription/hwid?hwid=${encodeURIComponent(hwid)}`,
{ method: 'DELETE' }, { method: 'DELETE' },
); );
}, },

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest'; import { describe, it, expect, vi, afterEach, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react'; import { render, screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { MemoryRouter, Routes, Route } from 'react-router-dom'; import { MemoryRouter, Routes, Route } from 'react-router-dom';
import { AccountPage } from './AccountPage'; import { AccountPage } from './AccountPage';
@@ -163,9 +163,9 @@ describe('AccountPage', () => {
await user.click(await screen.findByRole('button', { name: /показать клиенты для подключения/i })); await user.click(await screen.findByRole('button', { name: /показать клиенты для подключения/i }));
expect(await screen.findByRole('dialog', { name: /выберите клиент/i })).toBeInTheDocument(); expect(await screen.findByRole('dialog', { name: /выберите клиент/i })).toBeInTheDocument();
expect(screen.getAllByRole('link', { name: 'v2raytun' })[0]).toHaveAttribute( expect(within(screen.getByRole('region', { name: 'Android' })).getByRole('link', { name: 'v2raytun' })).toHaveAttribute(
'href', 'href',
'https://apps.apple.com/en/app/v2raytun/id6476628951', 'https://play.google.com/store/apps/details?id=com.v2raytun.android&hl=ru',
); );
expect(screen.getAllByText(/разрешить отправку hwid/i)).toHaveLength(2); expect(screen.getAllByText(/разрешить отправку hwid/i)).toHaveLength(2);
}); });

View File

@@ -1,7 +1,20 @@
import '@testing-library/jest-dom/vitest'; import '@testing-library/jest-dom/vitest';
import { afterEach } from 'vitest'; import { afterEach, vi } from 'vitest';
import { cleanup } from '@testing-library/react'; import { cleanup } from '@testing-library/react';
const storage = new Map<string, string>();
vi.stubGlobal('localStorage', {
get length() {
return storage.size;
},
clear: () => storage.clear(),
getItem: (key: string) => storage.get(key) ?? null,
key: (index: number) => [...storage.keys()][index] ?? null,
removeItem: (key: string) => storage.delete(key),
setItem: (key: string, value: string) => storage.set(key, String(value)),
} satisfies Storage);
afterEach(() => { afterEach(() => {
cleanup(); cleanup();
localStorage.clear(); localStorage.clear();

View File

@@ -7,10 +7,11 @@ export default defineConfig({
server: { server: {
port: 5173, port: 5173,
proxy: { proxy: {
'/auth': { target: 'http://127.0.0.1:8000', changeOrigin: true }, '/api': {
'/plans': { target: 'http://127.0.0.1:8000', changeOrigin: true }, target: 'http://127.0.0.1:8000',
'/orders': { target: 'http://127.0.0.1:8000', changeOrigin: true }, changeOrigin: true,
'/users': { target: 'http://127.0.0.1:8000', changeOrigin: true }, rewrite: (path) => path.replace(/^\/api/, ''),
},
}, },
}, },
test: { test: {