From 4efe848f3a7f45a76aad3191c8267022ed62f8b5 Mon Sep 17 00:00:00 2001 From: Tracewebstudio Dev Date: Fri, 17 Jul 2026 00:47:32 +0200 Subject: [PATCH] fix: local dev proxy, TypeScript errors, and test quality - middleware.ts: default GATEWAY_URL to localhost:9100 (was K8s hostname); proxy all /api/* to gateway instead of falling through to SolidStart renderer - signup/index.tsx: fix kebab-case style property (align-items) - dashboard/CreditsPage.tsx: fix IIFE closing in Show children - AskAsh/index.ts: use ~ alias to fix bundler module resolution - vite.config.ts: add string type to rewrite callback - global.d.ts: add Window.__captchaCode and __testMode declarations - help-center/article/[slug].tsx: add missing ContentBlock import - test/setup.ts: add vitest/globals reference, fix IntersectionObserver mock - tests/e2e: fix implicit any, string|null/undefined type errors in e2e specs - tests/tsconfig.json: add tests-specific tsconfig with node types Co-Authored-By: Claude Sonnet 4.6 --- src/components/AskAsh/index.ts | 4 ++-- src/components/DashboardLayout.tsx | 2 +- src/components/admin/AiCreditsAdmin.tsx | 2 +- src/components/admin/DashboardDesignPreview.tsx | 2 +- src/components/dashboard/CreditsPage.tsx | 8 ++++---- src/components/dashboard/ExploreServicesPage.tsx | 14 +++++--------- src/components/dashboard/MyDashboardPage.tsx | 1 - src/components/dashboard/PortfolioPage.tsx | 2 +- .../dashboard/widgets/PortfolioWidget.tsx | 2 +- src/global.d.ts | 7 +++++++ src/hooks/useAiCredits.ts | 4 ++-- src/middleware.ts | 7 +++---- src/routes/about.tsx | 2 +- src/routes/contact.tsx | 2 +- src/routes/dashboard/wallet/invoices/[id].tsx | 3 ++- src/routes/dashboard/wallet/invoices/index.tsx | 2 +- src/routes/help-center/article/[slug].tsx | 3 ++- src/routes/help-center/index.tsx | 4 ++-- src/routes/signup/index.tsx | 6 +++--- src/test/setup.ts | 13 ++++++++----- tests/e2e/ai-chat-widget.spec.ts | 2 +- tests/e2e/api.spec.ts | 4 ++-- tests/e2e/company-admin-e2e.spec.ts | 6 +++--- ...ompany-complete-e2e-with-admin-approval.spec.ts | 6 +++--- tests/e2e/company-e2e-complete-flow.spec.ts | 6 +++--- tests/e2e/full-e2e-company-jobseeker-admin.spec.ts | 6 +++--- tests/e2e/signup-verification-submission.spec.ts | 2 +- tests/tsconfig.json | 8 ++++++++ tests/vitest/components/AiChatWidget.test.tsx | 6 +++--- vite.config.ts | 2 +- 30 files changed, 76 insertions(+), 62 deletions(-) create mode 100644 tests/tsconfig.json diff --git a/src/components/AskAsh/index.ts b/src/components/AskAsh/index.ts index 7641131..847b02e 100644 --- a/src/components/AskAsh/index.ts +++ b/src/components/AskAsh/index.ts @@ -12,7 +12,7 @@ export { generateContent, purchaseCredits, CREDIT_COSTS, -} from '../hooks/useAiCredits'; +} from '~/hooks/useAiCredits'; // Types export type { @@ -22,4 +22,4 @@ export type { PurchaseRequest, PurchaseResponse, ApiError, -} from '../hooks/useAiCredits'; +} from '~/hooks/useAiCredits'; diff --git a/src/components/DashboardLayout.tsx b/src/components/DashboardLayout.tsx index 19078eb..2d3c528 100644 --- a/src/components/DashboardLayout.tsx +++ b/src/components/DashboardLayout.tsx @@ -65,7 +65,7 @@ export default function DashboardLayout(props: ParentProps) { return; } - const storageKeys = [ + const storageKeys: [string, Storage][] = [ ["nxtgauge_signup_profile_v1", localStorage], ["nxtgauge_auth_user", localStorage], ["nxtgauge_user", localStorage], diff --git a/src/components/admin/AiCreditsAdmin.tsx b/src/components/admin/AiCreditsAdmin.tsx index dacdca4..6128626 100644 --- a/src/components/admin/AiCreditsAdmin.tsx +++ b/src/components/admin/AiCreditsAdmin.tsx @@ -477,7 +477,7 @@ export default function AiCreditsAdmin() { setLedgerPage(p => p + 1); handleFetchLedger(); }} - disabled={ledgerData()?.data.length === 0 || ledgerData()?.data.length < 20} + disabled={ledgerData()?.data.length === 0 || (ledgerData()?.data.length ?? 0) < 20} style={{ padding: '8px 16px', border: '1px solid #D1D5DB', diff --git a/src/components/admin/DashboardDesignPreview.tsx b/src/components/admin/DashboardDesignPreview.tsx index 4520121..4b309dd 100644 --- a/src/components/admin/DashboardDesignPreview.tsx +++ b/src/components/admin/DashboardDesignPreview.tsx @@ -5599,7 +5599,7 @@ export default function DashboardDesignPreview(props: {
Total Amount - ₹{((appliedCoupon() ? appliedCoupon().final_price_inr : Number(pkg.price_paise)) / 100).toLocaleString('en-IN')} + ₹{((appliedCoupon()?.final_price_inr ?? Number(pkg.price_paise)) / 100).toLocaleString('en-IN')}
diff --git a/src/components/dashboard/CreditsPage.tsx b/src/components/dashboard/CreditsPage.tsx index 84ed8b2..4e23437 100644 --- a/src/components/dashboard/CreditsPage.tsx +++ b/src/components/dashboard/CreditsPage.tsx @@ -1272,7 +1272,7 @@ export default function CreditsPage(props: Props) { "justify-content": "center", }} > - 🪙 + 🪙

); - })} + })()} @@ -1727,7 +1727,7 @@ export default function CreditsPage(props: Props) { {formatDate(item.created_at)}

-
+

-
+

({})); if (res.ok) { const roles: string[] = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : []; - setActiveRoles(roles.map((r) => String(typeof r === "string" ? r : r.role_key || r.key || "").toUpperCase())); + setActiveRoles(roles.map((r) => String(typeof r === "string" ? r : (r as any).role_key || (r as any).key || "").toUpperCase())); } else { setActiveRoles([]); } @@ -496,13 +496,11 @@ export default function ExploreServicesPage() { "font-size": "12px", "font-weight": "700", cursor: - busyRoleKey() === card.key || - card.action === "Current Role" + busyRoleKey() === card.key ? "not-allowed" : "pointer", opacity: - busyRoleKey() === card.key || - card.action === "Current Role" + busyRoleKey() === card.key ? "0.6" : "1", "margin-top": "auto", @@ -517,13 +515,11 @@ export default function ExploreServicesPage() { "font-size": "12px", "font-weight": "700", cursor: - busyRoleKey() === card.key || - card.action === "Current Role" + busyRoleKey() === card.key ? "not-allowed" : "pointer", opacity: - busyRoleKey() === card.key || - card.action === "Current Role" + busyRoleKey() === card.key ? "0.6" : "1", "margin-top": "auto", diff --git a/src/components/dashboard/MyDashboardPage.tsx b/src/components/dashboard/MyDashboardPage.tsx index 290bc34..a45e28f 100644 --- a/src/components/dashboard/MyDashboardPage.tsx +++ b/src/components/dashboard/MyDashboardPage.tsx @@ -416,7 +416,6 @@ export default function MyDashboardPage(props: Props) { if (customizeMode() && !visibleWidgets().has(key)) return null; return (

{ diff --git a/src/components/dashboard/PortfolioPage.tsx b/src/components/dashboard/PortfolioPage.tsx index e7a64fa..6e32707 100644 --- a/src/components/dashboard/PortfolioPage.tsx +++ b/src/components/dashboard/PortfolioPage.tsx @@ -726,7 +726,7 @@ export default function PortfolioPage(props: Props) { }; const tabs = runtimePortfolioTabs(); const fieldsByTab = runtimeFieldsByTab(); - const activeTab = () => tabs.includes(jobSeekerTab()) ? jobSeekerTab() : tabs[0]; + const activeTab = () => (tabs as string[]).includes(jobSeekerTab()) ? jobSeekerTab() : tabs[0]; const activeFields = () => fieldsByTab[activeTab()] || []; const isLongField = (field: string) => { const key = normalizeToken(field); diff --git a/src/components/dashboard/widgets/PortfolioWidget.tsx b/src/components/dashboard/widgets/PortfolioWidget.tsx index d49295f..9f1c9b8 100644 --- a/src/components/dashboard/widgets/PortfolioWidget.tsx +++ b/src/components/dashboard/widgets/PortfolioWidget.tsx @@ -64,7 +64,7 @@ export default function PortfolioWidget(props: Props) { - {([label, value, color]) => } + {([label, value, color]) => } diff --git a/src/global.d.ts b/src/global.d.ts index 24b77cd..fec335b 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -1,4 +1,11 @@ /// /// +declare global { + interface Window { + __captchaCode?: string; + __testMode?: boolean; + } +} + export {}; diff --git a/src/hooks/useAiCredits.ts b/src/hooks/useAiCredits.ts index 56be39b..892d95b 100644 --- a/src/hooks/useAiCredits.ts +++ b/src/hooks/useAiCredits.ts @@ -1,4 +1,4 @@ -import { createSignal, createResource, createEffect, Accessor } from 'solid-js'; +import { createSignal, createResource, createEffect, Accessor, Resource } from 'solid-js'; // API base URL - can be configured via environment const API_BASE = import.meta.env.VITE_API_BASE_URL || ''; @@ -185,7 +185,7 @@ export interface UseAiCreditsReturn { // Credit data credits: Accessor; tracecoinBalance: Accessor; - creditResource: ReturnType>; + creditResource: Resource; // Loading states isLoading: Accessor; diff --git a/src/middleware.ts b/src/middleware.ts index 3fdd98a..9a70278 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -23,7 +23,7 @@ import { createMiddleware } from "@solidjs/start/middleware"; const GATEWAY_URL = ( - process.env.GATEWAY_URL || "http://nxtgauge-rust-gateway:9100" + process.env.GATEWAY_URL || "http://localhost:9100" ).replace(/\/+$/, ""); const PUBLIC_API_URL = ( @@ -202,8 +202,7 @@ export default createMiddleware({ return handlePayuReturn(fetchEvent); } - // Everything else under /api/* — let it fall through. - // Returning undefined tells the framework to continue. - return; + // All other /api/* paths — proxy to the gateway + return proxyToGateway(fetchEvent, path); }, }); diff --git a/src/routes/about.tsx b/src/routes/about.tsx index cfc7a16..a8e20bb 100644 --- a/src/routes/about.tsx +++ b/src/routes/about.tsx @@ -386,7 +386,7 @@ export default function AboutPage() { ? `perspective(980px) rotateX(${builtTilt().x}deg) rotateY(${builtTilt().y}deg) scale(1)` : 'perspective(980px) rotateX(0deg) rotateY(0deg) scale(0.95)', filter: builtVisible() && !reduceMotion() ? 'blur(0px)' : 'blur(3px)', - transitionDelay: builtVisible() ? '150ms' : '0ms', + "transition-delay": builtVisible() ? '150ms' : '0ms', }} > diff --git a/src/routes/contact.tsx b/src/routes/contact.tsx index b97c9d1..838c3a1 100644 --- a/src/routes/contact.tsx +++ b/src/routes/contact.tsx @@ -267,7 +267,7 @@ export default function ContactPage() { {values().attachment - ? values().attachment.name + ? values().attachment?.name : "Upload pdf/png/jpg (max 10MB)"} - + {(d: InvoiceResponse) => (
diff --git a/src/routes/dashboard/wallet/invoices/index.tsx b/src/routes/dashboard/wallet/invoices/index.tsx index d72fdd2..04adeb4 100644 --- a/src/routes/dashboard/wallet/invoices/index.tsx +++ b/src/routes/dashboard/wallet/invoices/index.tsx @@ -61,7 +61,7 @@ export default function InvoicesPage() {
- + {(d) => ( 0} diff --git a/src/routes/help-center/article/[slug].tsx b/src/routes/help-center/article/[slug].tsx index a9f48d5..b6151e7 100644 --- a/src/routes/help-center/article/[slug].tsx +++ b/src/routes/help-center/article/[slug].tsx @@ -6,6 +6,7 @@ import PublicBackground from "~/components/PublicBackground"; import PublicHeader from "~/components/PublicHeader"; import PublicFooter from "~/components/PublicFooter"; import ArticleContent from "~/components/ArticleContent"; +import type { ContentBlock } from "~/data/help-center-seed"; function categoryTitle(input: string) { return input @@ -326,7 +327,7 @@ export default function HelpCenterArticlePage() { when={a().content} fallback={

No content available.

} > - +
{/* Categories - Glass Cards */} -
+

{/* Articles - Glass Cards */} -
+

{ - const intent = search.intent?.toLowerCase() || ""; + const intent = (typeof search.intent === 'string' ? search.intent : Array.isArray(search.intent) ? search.intent[0] : '')?.toLowerCase() || ""; if (intent.includes("company")) { navigate("/signup/company", { replace: true }); @@ -35,8 +35,8 @@ export default function SignupIndexRoute() { return (
diff --git a/src/test/setup.ts b/src/test/setup.ts index 216d079..44b40b2 100644 --- a/src/test/setup.ts +++ b/src/test/setup.ts @@ -1,3 +1,4 @@ +/// import "@testing-library/jest-dom"; import { beforeAll, afterEach, afterAll } from "vitest"; import { setupServer } from "msw/node"; @@ -35,14 +36,16 @@ Object.defineProperty(window, "matchMedia", { }); // Mock IntersectionObserver -global.IntersectionObserver = class IntersectionObserver { +(global as any).IntersectionObserver = class IntersectionObserver { + root = null; + rootMargin = ''; + scrollMargin = ''; + thresholds: number[] = []; constructor() {} disconnect() {} observe() {} - takeRecords() { - return []; - } - trigger() {} + unobserve() {} + takeRecords() { return []; } }; // Mock ResizeObserver diff --git a/tests/e2e/ai-chat-widget.spec.ts b/tests/e2e/ai-chat-widget.spec.ts index 537097c..3b181c4 100644 --- a/tests/e2e/ai-chat-widget.spec.ts +++ b/tests/e2e/ai-chat-widget.spec.ts @@ -95,7 +95,7 @@ test.describe("AI Chat Widget", () => { const results = await new AxeBuilder({ page }).analyze(); const criticalViolations = results.violations.filter( - (v: { impact: string }) => v.impact === "critical" + (v: { impact?: string | null }) => v.impact === "critical" ); expect(criticalViolations).toEqual([]); }); diff --git a/tests/e2e/api.spec.ts b/tests/e2e/api.spec.ts index 9f0dd08..f706c01 100644 --- a/tests/e2e/api.spec.ts +++ b/tests/e2e/api.spec.ts @@ -44,8 +44,8 @@ test.describe("AI API Endpoints", () => { if (!companyToken) test.skip(); await page.goto(`${API_BASE}/`); - await page.evaluate((t: string) => { - window.sessionStorage.setItem("nxtgauge_access_token", t); + await page.evaluate((t: string | null) => { + window.sessionStorage.setItem("nxtgauge_access_token", t ?? ""); }, companyToken); const ctx = await request.newContext(); diff --git a/tests/e2e/company-admin-e2e.spec.ts b/tests/e2e/company-admin-e2e.spec.ts index 88e938c..89bdf2e 100644 --- a/tests/e2e/company-admin-e2e.spec.ts +++ b/tests/e2e/company-admin-e2e.spec.ts @@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise { // Get OTP from Redis await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId); + const otpCode = await getOTPFromRedis(user.userId!); if (!otpCode) throw new Error("Could not get OTP from Redis"); console.log(` ✅ OTP retrieved: ${otpCode}`); @@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise { const loginData = await loginResponse.json(); if (!loginData.access_token) throw new Error("Login failed"); user.accessToken = loginData.access_token; - console.log(` ✅ Logged in, token length: ${user.accessToken.length}`); + console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; } @@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) { selectedProfessionalRole: role, name, fullName: name, id: userId })); sessionStorage.setItem("nxtgauge_access_token", token); - }, { token: user.accessToken, email: user.email, userId: user.userId, role, name }); + }, { token: user.accessToken!, email: user.email, userId: user.userId, role, name }); } async function takeScreenshot(page: Page, name: string) { diff --git a/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts b/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts index 76429d5..bd71187 100644 --- a/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts +++ b/tests/e2e/company-complete-e2e-with-admin-approval.spec.ts @@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise { // Get OTP from Redis await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId); + const otpCode = await getOTPFromRedis(user.userId!); if (!otpCode) throw new Error("Could not get OTP from Redis"); console.log(` ✅ OTP retrieved: ${otpCode}`); @@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise { const loginData = await loginResponse.json(); if (!loginData.access_token) throw new Error("Login failed"); user.accessToken = loginData.access_token; - console.log(` ✅ Logged in, token length: ${user.accessToken.length}`); + console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; } @@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) { selectedProfessionalRole: role, name, fullName: name, id: userId })); sessionStorage.setItem("nxtgauge_access_token", token); - }, { token: user.accessToken, email: user.email, userId: user.userId, role, name }); + }, { token: user.accessToken!, email: user.email, userId: user.userId, role, name }); } async function takeScreenshot(page: Page, name: string) { diff --git a/tests/e2e/company-e2e-complete-flow.spec.ts b/tests/e2e/company-e2e-complete-flow.spec.ts index 5986993..f9048ce 100644 --- a/tests/e2e/company-e2e-complete-flow.spec.ts +++ b/tests/e2e/company-e2e-complete-flow.spec.ts @@ -74,7 +74,7 @@ async function registerUser(user: TestUser): Promise { // Wait for OTP to be generated await new Promise(r => setTimeout(r, 1000)); - const otpCode = await getOTPFromRedis(user.userId); + const otpCode = await getOTPFromRedis(user.userId!); if (!otpCode) throw new Error("Could not get OTP from Redis"); console.log(` ✅ OTP retrieved: ${otpCode}`); @@ -96,7 +96,7 @@ async function registerUser(user: TestUser): Promise { const loginData = await loginResponse.json(); if (!loginData.access_token) throw new Error("Login failed"); user.accessToken = loginData.access_token; - console.log(` ✅ Logged in, token length: ${user.accessToken.length}`); + console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; } @@ -116,7 +116,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) { selectedProfessionalRole: role, name, fullName: name, id: userId })); sessionStorage.setItem("nxtgauge_access_token", token); - }, { token: user.accessToken, email: user.email, userId: user.userId, role, name }); + }, { token: user.accessToken!, email: user.email, userId: user.userId, role, name }); } async function takeScreenshot(page: Page, name: string) { diff --git a/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts b/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts index 1b30b9f..c3ba8c6 100644 --- a/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts +++ b/tests/e2e/full-e2e-company-jobseeker-admin.spec.ts @@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise { // Get OTP from Redis await new Promise(r => setTimeout(r, 500)); - const otpCode = await getOTPFromRedis(user.userId); + const otpCode = await getOTPFromRedis(user.userId!); if (!otpCode) throw new Error("Could not get OTP from Redis"); console.log(` ✅ OTP retrieved: ${otpCode}`); @@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise { const loginData = await loginResponse.json(); if (!loginData.access_token) throw new Error("Login failed"); user.accessToken = loginData.access_token; - console.log(` ✅ Logged in, token length: ${user.accessToken.length}`); + console.log(` ✅ Logged in, token length: ${user.accessToken!.length}`); return user; } @@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) { selectedProfessionalRole: role, name, fullName: name, id: userId })); sessionStorage.setItem("nxtgauge_access_token", token); - }, { token: user.accessToken, email: user.email, userId: user.userId, role, name }); + }, { token: user.accessToken!, email: user.email, userId: user.userId, role, name }); } async function takeScreenshot(page: Page, name: string) { diff --git a/tests/e2e/signup-verification-submission.spec.ts b/tests/e2e/signup-verification-submission.spec.ts index 58476ea..889adef 100644 --- a/tests/e2e/signup-verification-submission.spec.ts +++ b/tests/e2e/signup-verification-submission.spec.ts @@ -209,7 +209,7 @@ async function runSignup(page: any, scenario: SignupScenario, email: string) { async function seedSession(page: any, scenario: SignupScenario, email: string) { await page.evaluate( - ({ role, mail }) => { + ({ role, mail }: { role: string; mail: string }) => { sessionStorage.setItem("nxtgauge_access_token", "pw-test-token"); const payload = { email: mail, diff --git a/tests/tsconfig.json b/tests/tsconfig.json new file mode 100644 index 0000000..9965729 --- /dev/null +++ b/tests/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "moduleResolution": "node", + "types": ["vite/client", "node"] + }, + "include": ["./**/*.ts", "./**/*.tsx"] +} diff --git a/tests/vitest/components/AiChatWidget.test.tsx b/tests/vitest/components/AiChatWidget.test.tsx index 2e5f8bf..589cab1 100644 --- a/tests/vitest/components/AiChatWidget.test.tsx +++ b/tests/vitest/components/AiChatWidget.test.tsx @@ -1,6 +1,6 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; import { render, screen, fireEvent, waitFor } from "@solidjs/testing-library"; -import { AiChatWidget } from "../../src/components/AiChatWidget"; +import { AiChatWidget } from "~/components/AiChatWidget"; global.fetch = vi.fn(); @@ -51,7 +51,7 @@ describe("AiChatWidget", () => { fireEvent.click(button); await waitFor(() => { - const input = screen.getByPlaceholder(/Ask/i); + const input = screen.getByPlaceholderText(/Ask/i); fireEvent.change(input, { target: { value: "Test" } }); fireEvent.keyDown(input, { key: "Enter" }); }); @@ -77,7 +77,7 @@ describe("AiChatWidget", () => { fireEvent.click(button); await waitFor(() => { - const input = screen.getByPlaceholder(/Ask/i); + const input = screen.getByPlaceholderText(/Ask/i); fireEvent.change(input, { target: { value: "Hello" } }); fireEvent.keyDown(input, { key: "Enter" }); }); diff --git a/vite.config.ts b/vite.config.ts index b38229b..85ce786 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -13,7 +13,7 @@ export default defineConfig({ "/api/gateway": { target: "http://localhost:9100", changeOrigin: true, - rewrite: (path) => + rewrite: (path: string) => path .replace(/^\/api\/gateway\/api(\/|$)/, "/api$1") .replace(/^\/api\/gateway(\/|$)/, "/api$1"),