From 5428a21b679226db3a57e65b164711a61935b1d6 Mon Sep 17 00:00:00 2001 From: Richard R Date: Fri, 29 May 2026 18:03:17 -0600 Subject: [PATCH] refactor(onboarding): modularize onboarding flow logic and improve testability Extract onboarding flow step resolution and async runner logic into a dedicated module for better separation of concerns and maintainability. Refactor context/provider to use new helpers and simplify settings modal control. Add unit tests for onboarding step resolution and async runner coalescing. Update e2e helpers and selectors for onboarding modals. Adjust environment file handling for CI and Docker contexts. - Add .dockerignore for build hygiene - Refactor onboarding context and modal logic - Add src/lib/client/onboarding-flow.ts with core onboarding helpers - Add onboarding-flow unit tests - Update Playwright config and scripts for CI env loading - Improve test selectors for claim modal - Update .gitignore to exclude all .env* except .env.example This change increases onboarding logic modularity and reliability, and improves CI/test environment handling. --- .dockerignore | 7 + .github/workflows/playwright.yml | 1 - .gitignore | 5 +- package.json | 1 + playwright.config.ts | 10 ++ scripts/openreader-entrypoint.mjs | 9 ++ src/components/SettingsModal.tsx | 27 +--- src/components/auth/ClaimDataModal.tsx | 4 +- src/contexts/OnboardingFlowContext.tsx | 199 +++++++++++-------------- src/lib/client/onboarding-flow.ts | 52 +++++++ tests/helpers.ts | 11 ++ tests/unit/onboarding-flow.spec.ts | 107 +++++++++++++ 12 files changed, 294 insertions(+), 139 deletions(-) create mode 100644 .dockerignore create mode 100644 src/lib/client/onboarding-flow.ts create mode 100644 tests/unit/onboarding-flow.spec.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6c309d7 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.env +.env.* +README.md +.next +node_modules +**/node_modules +docstore diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index dfe0e8d..723e0eb 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -10,7 +10,6 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest env: - FFMPEG_BIN: /usr/bin/ffmpeg USE_EMBEDDED_WEED_MINI: true BASE_URL: http://127.0.0.1:3003 S3_ENDPOINT: http://127.0.0.1:8333 diff --git a/.gitignore b/.gitignore index 4014f63..55b2755 100644 --- a/.gitignore +++ b/.gitignore @@ -32,9 +32,8 @@ yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) -.env -.env.prod -.dockerignore +.env* +!.env.example *.creds # vercel diff --git a/package.json b/package.json index 7e7ffb4..b31ec10 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "start:raw": "next start -p 3003", "lint": "next lint", "test": "playwright test", + "test:ci-env": "CI=true playwright test", "migrate": "node drizzle/scripts/migrate.mjs", "migrate-fs": "node scripts/migrate-fs-v2.mjs", "migrate-fs:dry-run": "node scripts/migrate-fs-v2.mjs --dry-run true", diff --git a/playwright.config.ts b/playwright.config.ts index 3c080b9..a1f9f25 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -1,5 +1,15 @@ import { defineConfig, devices } from '@playwright/test'; import 'dotenv/config'; +import fs from 'node:fs'; +import path from 'node:path'; +import dotenv from 'dotenv'; + +if (process.env.CI === 'true') { + const envCiPath = path.join(process.cwd(), '.env.ci'); + if (fs.existsSync(envCiPath)) { + dotenv.config({ path: envCiPath, override: true }); + } +} /** * See https://playwright.dev/docs/test-configuration. diff --git a/scripts/openreader-entrypoint.mjs b/scripts/openreader-entrypoint.mjs index 97dcb5d..06dd1d4 100644 --- a/scripts/openreader-entrypoint.mjs +++ b/scripts/openreader-entrypoint.mjs @@ -11,6 +11,15 @@ import * as dotenv from 'dotenv'; function loadEnvFiles() { const cwd = process.cwd(); + const isCi = isTrue(process.env.CI, false); + if (isCi) { + const envCiPath = path.join(cwd, '.env.ci'); + if (fs.existsSync(envCiPath)) { + dotenv.config({ path: envCiPath }); + } + return; + } + const envPath = path.join(cwd, '.env'); const envLocalPath = path.join(cwd, '.env.local'); diff --git a/src/components/SettingsModal.tsx b/src/components/SettingsModal.tsx index 8b3c0e4..50fee42 100644 --- a/src/components/SettingsModal.tsx +++ b/src/components/SettingsModal.tsx @@ -178,7 +178,7 @@ export function SettingsModal({ const { progress, setProgress, estimatedTimeRemaining } = useTimeEstimation(); const { authEnabled, baseUrl: authBaseUrl } = useAuthConfig(); const { data: session } = useAuthSession(); - const { requestOpenSettings, registerSettingsController } = useOnboardingFlow(); + const { changelogOpenSignal } = useOnboardingFlow(); const router = useRouter(); const isBusy = isImportingLibrary; const { @@ -211,25 +211,11 @@ export function SettingsModal({ const isSharedSelected = Boolean(selectedSharedProvider); const selectedProviderOption = ttsProviders.find((p) => p.id === localProviderRef) ?? ttsProviders[0]; - const closeSettings = useCallback(() => { - setIsOpen(false); - setIsChangelogOpen(false); - }, []); - - const openSettings = useCallback((options?: { changelog?: boolean }) => { - setIsOpen(true); - setIsChangelogOpen(Boolean(options?.changelog)); - }, []); - useEffect(() => { - registerSettingsController({ - open: openSettings, - close: closeSettings, - }); - return () => { - registerSettingsController(null); - }; - }, [closeSettings, openSettings, registerSettingsController]); + if (changelogOpenSignal <= 0) return; + setIsOpen(true); + setIsChangelogOpen(true); + }, [changelogOpenSignal]); useEffect(() => { setLocalApiKey(apiKey); @@ -505,7 +491,8 @@ export function SettingsModal({ <>