fix(tests): try to get tests working again
This commit is contained in:
parent
6089d2c445
commit
20111b1e36
18 changed files with 97 additions and 92 deletions
|
|
@ -17,7 +17,7 @@ import type { TTSAudiobookChapter } from '@/types/tts';
|
|||
import type { AudiobookGenerationSettings } from '@/types/client';
|
||||
import { resolveDocumentId } from '@/lib/dexie';
|
||||
import { RateLimitBanner } from '@/components/rate-limit-banner';
|
||||
import { useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
|
||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
||||
|
||||
|
|
@ -26,7 +26,7 @@ export default function EPUBPage() {
|
|||
const router = useRouter();
|
||||
const { setCurrentDocument, currDocName, clearCurrDoc, createFullAudioBook: createEPUBAudioBook, regenerateChapter: regenerateEPUBChapter } = useEPUB();
|
||||
const { stop } = useTTS();
|
||||
const { isAtLimit } = useAutoRateLimit();
|
||||
const { isAtLimit } = useAuthRateLimit();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ import TTSPlayer from '@/components/player/TTSPlayer';
|
|||
import { resolveDocumentId } from '@/lib/dexie';
|
||||
import { DocumentHeaderMenu } from '@/components/DocumentHeaderMenu';
|
||||
import { RateLimitBanner } from '@/components/rate-limit-banner';
|
||||
import { useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
|
||||
export default function HTMLPage() {
|
||||
const { id } = useParams();
|
||||
const router = useRouter();
|
||||
const { setCurrentDocument, currDocName, clearCurrDoc } = useHTML();
|
||||
const { stop } = useTTS();
|
||||
const { isAtLimit } = useAutoRateLimit();
|
||||
const { isAtLimit } = useAuthRateLimit();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isSettingsOpen, setIsSettingsOpen] = useState(false);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import TTSPlayer from '@/components/player/TTSPlayer';
|
|||
import { RateLimitPauseButton } from '@/components/player/RateLimitPauseButton';
|
||||
import { resolveDocumentId } from '@/lib/dexie';
|
||||
import { RateLimitBanner } from '@/components/rate-limit-banner';
|
||||
import { useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
|
||||
const isDev = process.env.NEXT_PUBLIC_NODE_ENV !== 'production' || process.env.NODE_ENV == null;
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ export default function PDFViewerPage() {
|
|||
const router = useRouter();
|
||||
const { setCurrentDocument, currDocName, clearCurrDoc, currDocPage, currDocPages, createFullAudioBook: createPDFAudioBook, regenerateChapter: regeneratePDFChapter } = usePDF();
|
||||
const { stop } = useTTS();
|
||||
const { isAtLimit } = useAutoRateLimit();
|
||||
const { isAtLimit } = useAuthRateLimit();
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [zoomLevel, setZoomLevel] = useState<number>(100);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { TTSProvider } from '@/contexts/TTSContext';
|
|||
import { ThemeProvider } from '@/contexts/ThemeContext';
|
||||
import { ConfigProvider } from '@/contexts/ConfigContext';
|
||||
import { HTMLProvider } from '@/contexts/HTMLContext';
|
||||
import { AutoRateLimitProvider } from '@/contexts/AutoRateLimitContext';
|
||||
import { AuthRateLimitProvider } from '@/contexts/AuthRateLimitContext';
|
||||
import { PrivacyPopup } from '@/components/privacy-popup';
|
||||
|
||||
interface ProvidersProps {
|
||||
|
|
@ -18,7 +18,7 @@ interface ProvidersProps {
|
|||
|
||||
export function Providers({ children, authEnabled, authBaseUrl }: ProvidersProps) {
|
||||
return (
|
||||
<AutoRateLimitProvider authEnabled={authEnabled} authBaseUrl={authBaseUrl}>
|
||||
<AuthRateLimitProvider authEnabled={authEnabled} authBaseUrl={authBaseUrl}>
|
||||
<ThemeProvider>
|
||||
<ConfigProvider>
|
||||
<DocumentProvider>
|
||||
|
|
@ -35,6 +35,6 @@ export function Providers({ children, authEnabled, authBaseUrl }: ProvidersProps
|
|||
</DocumentProvider>
|
||||
</ConfigProvider>
|
||||
</ThemeProvider>
|
||||
</AutoRateLimitProvider>
|
||||
</AuthRateLimitProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Button, Input } from '@headlessui/react';
|
|||
import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { getAuthClient } from '@/lib/auth-client';
|
||||
import { useAuthConfig, useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig, useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { showPrivacyPopup } from '@/components/privacy-popup';
|
||||
import { wasSignedOut, clearSignedOut } from '@/lib/session-utils';
|
||||
import { GithubIcon } from '@/components/icons/Icons';
|
||||
|
|
@ -32,7 +32,7 @@ function SignInContent() {
|
|||
const [justSignedOut, setJustSignedOut] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { authEnabled, baseUrl } = useAuthConfig();
|
||||
const { refresh: refreshRateLimit } = useAutoRateLimit();
|
||||
const { refresh: refreshRateLimit } = useAuthRateLimit();
|
||||
|
||||
const isAnyLoading = loadingEmail || loadingGithub || loadingGuest;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { Button, Input } from '@headlessui/react';
|
|||
import { useRouter } from 'next/navigation';
|
||||
import Link from 'next/link';
|
||||
import { getAuthClient } from '@/lib/auth-client';
|
||||
import { useAuthConfig, useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig, useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { showPrivacyPopup } from '@/components/privacy-popup';
|
||||
import { LoadingSpinner } from '@/components/Spinner';
|
||||
import toast from 'react-hot-toast';
|
||||
|
|
@ -19,7 +19,7 @@ export default function SignUpPage() {
|
|||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { authEnabled, baseUrl } = useAuthConfig();
|
||||
const { refresh: refreshRateLimit } = useAutoRateLimit();
|
||||
const { refresh: refreshRateLimit } = useAuthRateLimit();
|
||||
|
||||
// Check if auth is enabled, redirect home if not
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { GithubIcon } from '@/components/icons/Icons'
|
||||
import { showPrivacyPopup } from '@/components/privacy-popup'
|
||||
import { useAuthConfig } from '@/contexts/AutoRateLimitContext'
|
||||
import { useAuthConfig } from '@/contexts/AuthRateLimitContext'
|
||||
|
||||
export function Footer() {
|
||||
const { authEnabled } = useAuthConfig();
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ import { BaseDocument } from '@/types/documents';
|
|||
import { getAuthClient } from '@/lib/auth-client';
|
||||
import { useAuthSession } from '@/hooks/useAuth';
|
||||
import { clearSignedOut } from '@/lib/session-utils';
|
||||
import { useAuthConfig, useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig, useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { showPrivacyPopup } from '@/components/privacy-popup';
|
||||
|
||||
|
|
@ -86,7 +86,7 @@ export function SettingsModal({ className = '' }: { className?: string }) {
|
|||
const [showDeleteAccountConfirm, setShowDeleteAccountConfirm] = useState(false);
|
||||
const { progress, setProgress, estimatedTimeRemaining } = useTimeEstimation();
|
||||
const { authEnabled, baseUrl: authBaseUrl } = useAuthConfig();
|
||||
const { refresh: refreshRateLimit } = useAutoRateLimit();
|
||||
const { refresh: refreshRateLimit } = useAuthRateLimit();
|
||||
const { data: session } = useAuthSession();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
'use client';
|
||||
|
||||
import { useEffect, useState, ReactNode } from 'react';
|
||||
import { useAuthConfig, useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig, useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useAuthSession } from '@/hooks/useAuth';
|
||||
import { getAuthClient } from '@/lib/auth-client';
|
||||
import { LoadingSpinner } from '@/components/Spinner';
|
||||
|
||||
export function AuthLoader({ children }: { children: ReactNode }) {
|
||||
const { authEnabled, baseUrl } = useAuthConfig();
|
||||
const { refresh: refreshRateLimit } = useAutoRateLimit();
|
||||
const { refresh: refreshRateLimit } = useAuthRateLimit();
|
||||
const { data: session, isPending } = useAuthSession();
|
||||
const [isAutoLoggingIn, setIsAutoLoggingIn] = useState(false);
|
||||
const [isCheckingSignOut, setIsCheckingSignOut] = useState(true);
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { Button } from '@headlessui/react';
|
||||
import Link from 'next/link';
|
||||
import { useAuthConfig, useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig, useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import { useAuthSession } from '@/hooks/useAuth';
|
||||
import { getAuthClient } from '@/lib/auth-client';
|
||||
import { clearSignedOut } from '@/lib/session-utils';
|
||||
|
|
@ -10,7 +10,7 @@ import { useRouter } from 'next/navigation';
|
|||
|
||||
export function UserMenu({ className = '' }: { className?: string }) {
|
||||
const { authEnabled, baseUrl } = useAuthConfig();
|
||||
const { refresh: refreshRateLimit } = useAutoRateLimit();
|
||||
const { refresh: refreshRateLimit } = useAuthRateLimit();
|
||||
const { data: session, isPending } = useAuthSession();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import { useAutoRateLimit, formatCharCount } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthRateLimit, formatCharCount } from '@/contexts/AuthRateLimitContext';
|
||||
import Link from 'next/link';
|
||||
|
||||
interface RateLimitBannerProps {
|
||||
|
|
@ -8,7 +8,7 @@ interface RateLimitBannerProps {
|
|||
}
|
||||
|
||||
export function RateLimitBanner({ className = '' }: RateLimitBannerProps) {
|
||||
const { status, isAtLimit, timeUntilReset, authEnabled } = useAutoRateLimit();
|
||||
const { status, isAtLimit, timeUntilReset, authEnabled } = useAuthRateLimit();
|
||||
|
||||
// Don't show banner if auth is not enabled or if not at limit
|
||||
if (!authEnabled || !status?.authEnabled || !isAtLimit) {
|
||||
|
|
@ -49,7 +49,7 @@ export function RateLimitBanner({ className = '' }: RateLimitBannerProps) {
|
|||
* Compact version for inline display
|
||||
*/
|
||||
export function RateLimitIndicator({ className = '' }: RateLimitBannerProps) {
|
||||
const { status, isAtLimit, authEnabled } = useAutoRateLimit();
|
||||
const { status, isAtLimit, authEnabled } = useAuthRateLimit();
|
||||
|
||||
// Don't show if auth is not enabled
|
||||
if (!authEnabled || !status?.authEnabled) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export interface RateLimitStatus {
|
|||
authEnabled: boolean;
|
||||
}
|
||||
|
||||
interface AutoRateLimitContextType {
|
||||
interface AuthRateLimitContextType {
|
||||
// Auth Config
|
||||
authEnabled: boolean;
|
||||
authBaseUrl: string | null;
|
||||
|
|
@ -30,24 +30,24 @@ interface AutoRateLimitContextType {
|
|||
triggerRateLimit: () => void;
|
||||
}
|
||||
|
||||
const AutoRateLimitContext = createContext<AutoRateLimitContextType | null>(null);
|
||||
const AuthRateLimitContext = createContext<AuthRateLimitContextType | null>(null);
|
||||
|
||||
export function useAutoRateLimit(): AutoRateLimitContextType {
|
||||
const context = useContext(AutoRateLimitContext);
|
||||
export function useAuthRateLimit(): AuthRateLimitContextType {
|
||||
const context = useContext(AuthRateLimitContext);
|
||||
if (!context) {
|
||||
throw new Error('useAutoRateLimit must be used within a AutoRateLimitProvider');
|
||||
throw new Error('useAuthRateLimit must be used within an AuthRateLimitProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Re-export specific hooks for backward compatibility or convenience if needed
|
||||
export function useAuthConfig() {
|
||||
const { authEnabled, authBaseUrl } = useAutoRateLimit();
|
||||
const { authEnabled, authBaseUrl } = useAuthRateLimit();
|
||||
return { authEnabled, baseUrl: authBaseUrl };
|
||||
}
|
||||
|
||||
export function useRateLimit() {
|
||||
return useAutoRateLimit();
|
||||
return useAuthRateLimit();
|
||||
}
|
||||
|
||||
function calculateTimeUntilReset(resetTime: Date): string {
|
||||
|
|
@ -82,13 +82,13 @@ export function formatCharCount(count: number): string {
|
|||
return count.toString();
|
||||
}
|
||||
|
||||
interface AutoRateLimitProviderProps {
|
||||
interface AuthRateLimitProviderProps {
|
||||
children: ReactNode;
|
||||
authEnabled: boolean;
|
||||
authBaseUrl: string | null;
|
||||
}
|
||||
|
||||
export function AutoRateLimitProvider({ children, authEnabled, authBaseUrl }: AutoRateLimitProviderProps) {
|
||||
export function AuthRateLimitProvider({ children, authEnabled, authBaseUrl }: AuthRateLimitProviderProps) {
|
||||
const [status, setStatus] = useState<RateLimitStatus | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
|
@ -209,7 +209,7 @@ export function AutoRateLimitProvider({ children, authEnabled, authBaseUrl }: Au
|
|||
};
|
||||
}, []);
|
||||
|
||||
const contextValue: AutoRateLimitContextType = {
|
||||
const contextValue: AuthRateLimitContextType = {
|
||||
authEnabled,
|
||||
authBaseUrl,
|
||||
status,
|
||||
|
|
@ -225,8 +225,8 @@ export function AutoRateLimitProvider({ children, authEnabled, authBaseUrl }: Au
|
|||
};
|
||||
|
||||
return (
|
||||
<AutoRateLimitContext.Provider value={contextValue}>
|
||||
<AuthRateLimitContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</AutoRateLimitContext.Provider>
|
||||
</AuthRateLimitContext.Provider>
|
||||
);
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ import { useBackgroundState } from '@/hooks/audio/useBackgroundState';
|
|||
import { withRetry, generateTTS, alignAudio } from '@/lib/client';
|
||||
import { preprocessSentenceForAudio, splitTextToTtsBlocks, splitTextToTtsBlocksEPUB } from '@/lib/nlp';
|
||||
import { isKokoroModel } from '@/utils/voice';
|
||||
import { useAutoRateLimit } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthRateLimit } from '@/contexts/AuthRateLimitContext';
|
||||
import type {
|
||||
TTSLocation,
|
||||
TTSSmartMergeResult,
|
||||
|
|
@ -299,7 +299,7 @@ export function TTSProvider({ children }: { children: ReactNode }): ReactElement
|
|||
const audioContext = useAudioContext();
|
||||
const audioCache = useAudioCache(25);
|
||||
const { availableVoices, fetchVoices } = useVoiceManagement(openApiKey, openApiBaseUrl, configTTSProvider, configTTSModel);
|
||||
const { onTTSStart, onTTSComplete, refresh: refreshRateLimit, triggerRateLimit, isAtLimit } = useAutoRateLimit();
|
||||
const { onTTSStart, onTTSComplete, refresh: refreshRateLimit, triggerRateLimit, isAtLimit } = useAuthRateLimit();
|
||||
|
||||
// Add ref for location change handler
|
||||
const locationChangeHandlerRef = useRef<((location: TTSLocation) => void) | null>(null);
|
||||
|
|
|
|||
|
|
@ -1,45 +1,9 @@
|
|||
'use client';
|
||||
|
||||
import { useMemo } from 'react';
|
||||
import { useAuthConfig } from '@/contexts/AutoRateLimitContext';
|
||||
import { useAuthConfig } from '@/contexts/AuthRateLimitContext';
|
||||
import { getAuthClient } from '@/lib/auth-client';
|
||||
|
||||
/**
|
||||
* Hook that provides auth client methods configured with the correct baseUrl from server.
|
||||
* Use this hook in components that need to call auth methods (signIn, signUp, etc.)
|
||||
*/
|
||||
export function useAuth() {
|
||||
const { baseUrl, authEnabled } = useAuthConfig();
|
||||
|
||||
const client = useMemo(() => {
|
||||
if (!authEnabled || !baseUrl) return null;
|
||||
return getAuthClient(baseUrl);
|
||||
}, [baseUrl, authEnabled]);
|
||||
|
||||
if (!client) {
|
||||
// Safe no-op implementation when auth is disabled
|
||||
return {
|
||||
authEnabled: false,
|
||||
baseUrl: null,
|
||||
signIn: async () => ({ data: null, error: null }),
|
||||
signUp: async () => ({ data: null, error: null }),
|
||||
signOut: async () => ({ data: null, error: null }),
|
||||
useSession: () => ({ data: null, isPending: false, error: null }),
|
||||
getSession: async () => ({ data: null, error: null }),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
authEnabled,
|
||||
baseUrl,
|
||||
signIn: client.signIn,
|
||||
signUp: client.signUp,
|
||||
signOut: client.signOut,
|
||||
useSession: client.useSession,
|
||||
getSession: client.getSession,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for session that uses the correct baseUrl from context
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -14,14 +14,14 @@ const clientCache = new Map<string, ReturnType<typeof createAuthClientWithUrl>>(
|
|||
|
||||
/**
|
||||
* Factory function to get auth client with specific baseUrl.
|
||||
* In most cases, you should use the useAuth() hook instead of calling this directly.
|
||||
* In components, prefer reading `baseUrl` from `useAuthConfig()` and then calling `getAuthClient(baseUrl)`.
|
||||
* @param baseUrl - The auth server base URL. If null, will throw an error.
|
||||
*/
|
||||
export function getAuthClient(baseUrl: string | null) {
|
||||
if (!baseUrl) {
|
||||
throw new Error(
|
||||
'Cannot create auth client without baseUrl. ' +
|
||||
'Use the useAuth() hook in components to get the properly configured client.'
|
||||
'Use useAuthConfig() in components to get the properly configured baseUrl.'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -30,4 +30,4 @@ export function getAuthClient(baseUrl: string | null) {
|
|||
}
|
||||
|
||||
return clientCache.get(baseUrl)!;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('API health checks', () => {
|
||||
test('GET /api/tts/voices returns 200 and a non-empty voices array', async ({ request }) => {
|
||||
const res = await request.get('/api/tts/voices');
|
||||
expect(res.ok()).toBeTruthy();
|
||||
const json = await res.json();
|
||||
expect(Array.isArray(json.voices)).toBeTruthy();
|
||||
expect(json.voices.length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
|
@ -6,6 +6,50 @@ const DIR = './tests/files/';
|
|||
const TTS_MOCK_PATH = path.join(__dirname, 'files', 'sample.mp3');
|
||||
let ttsMockBuffer: Buffer | null = null;
|
||||
|
||||
type RateLimitStatusResponse = {
|
||||
authEnabled: boolean;
|
||||
userType?: 'anonymous' | 'authenticated' | 'unauthenticated';
|
||||
};
|
||||
|
||||
async function getRateLimitStatus(page: Page): Promise<RateLimitStatusResponse | null> {
|
||||
try {
|
||||
const res = await page.request.get('/api/rate-limit/status');
|
||||
if (!res.ok()) return null;
|
||||
return (await res.json()) as RateLimitStatusResponse;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureAnonymousSession(page: Page): Promise<void> {
|
||||
const initial = await getRateLimitStatus(page);
|
||||
if (!initial) return;
|
||||
if (!initial.authEnabled) return;
|
||||
if (initial.userType && initial.userType !== 'unauthenticated') return;
|
||||
|
||||
// Create a session cookie for this test context.
|
||||
// This avoids races where the app makes authenticated API calls before AuthLoader finishes.
|
||||
try {
|
||||
await page.request.post('/api/auth/sign-in/anonymous', { data: {} });
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Wait until the server sees us as anonymous/authenticated (i.e. cookie persisted).
|
||||
const deadline = Date.now() + 15_000;
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
while (true) {
|
||||
const next = await getRateLimitStatus(page);
|
||||
if (next && (!next.authEnabled || (next.userType && next.userType !== 'unauthenticated'))) {
|
||||
return;
|
||||
}
|
||||
if (Date.now() > deadline) {
|
||||
throw new Error('Timed out waiting for anonymous auth session in tests');
|
||||
}
|
||||
await page.waitForTimeout(200);
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureTtsRouteMock(page: Page) {
|
||||
if (!ttsMockBuffer) {
|
||||
ttsMockBuffer = fs.readFileSync(TTS_MOCK_PATH);
|
||||
|
|
@ -119,10 +163,21 @@ export async function setupTest(page: Page) {
|
|||
// Mock the TTS API so tests don't hit the real TTS service.
|
||||
await ensureTtsRouteMock(page);
|
||||
|
||||
// If auth is enabled, establish an anonymous session BEFORE navigation.
|
||||
// This keeps each test self-contained (no shared storageState) while ensuring
|
||||
// server routes that require auth don't intermittently 401 during app startup.
|
||||
await ensureAnonymousSession(page);
|
||||
|
||||
// Navigate to the home page before each test
|
||||
await page.goto('/');
|
||||
await page.waitForLoadState('networkidle');
|
||||
|
||||
// AuthLoader may show a full-screen overlay while session is loading.
|
||||
// Wait for it to be gone before interacting with underlying UI.
|
||||
await page
|
||||
.waitForSelector('.fixed.inset-0.bg-base.z-50', { state: 'detached', timeout: 15_000 })
|
||||
.catch(() => { });
|
||||
|
||||
// Privacy modal should come first in onboarding.
|
||||
// Be tolerant if it's already accepted (e.g., reused context).
|
||||
const privacyBtn = page.getByRole('button', { name: 'I Understand' });
|
||||
|
|
@ -219,7 +274,7 @@ export async function deleteDocumentByName(page: Page, fileName: string) {
|
|||
// Open Settings modal and navigate to Documents tab
|
||||
export async function openSettingsDocumentsTab(page: Page) {
|
||||
await page.getByRole('button', { name: 'Settings' }).click();
|
||||
await page.getByRole('tab', { name: '📄 Documents' }).click();
|
||||
await page.getByRole('tab', { name: '📄 Docs' }).click();
|
||||
}
|
||||
|
||||
// Delete all local documents through Settings and close dialogs
|
||||
|
|
|
|||
|
|
@ -61,9 +61,6 @@ test.describe('Document Upload Tests', () => {
|
|||
});
|
||||
|
||||
test('uploads and converts a DOCX document', async ({ page }) => {
|
||||
// This test only runs in development mode
|
||||
test.skip(process.env.NODE_ENV === 'production', 'DOCX upload is only available in development mode');
|
||||
|
||||
await uploadFile(page, 'sample.docx');
|
||||
// Should see the converting message
|
||||
await expect(page.getByText('Converting DOCX to PDF...')).toBeVisible();
|
||||
|
|
|
|||
Loading…
Reference in a new issue