fix: local dev proxy, TypeScript errors, and test quality
All checks were successful
build-and-release / build (push) Successful in 2m15s

- 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 <noreply@anthropic.com>
This commit is contained in:
Tracewebstudio Dev 2026-07-17 00:47:32 +02:00
parent 0b38bc6ca5
commit 4efe848f3a
30 changed files with 76 additions and 62 deletions

View file

@ -12,7 +12,7 @@ export {
generateContent, generateContent,
purchaseCredits, purchaseCredits,
CREDIT_COSTS, CREDIT_COSTS,
} from '../hooks/useAiCredits'; } from '~/hooks/useAiCredits';
// Types // Types
export type { export type {
@ -22,4 +22,4 @@ export type {
PurchaseRequest, PurchaseRequest,
PurchaseResponse, PurchaseResponse,
ApiError, ApiError,
} from '../hooks/useAiCredits'; } from '~/hooks/useAiCredits';

View file

@ -65,7 +65,7 @@ export default function DashboardLayout(props: ParentProps) {
return; return;
} }
const storageKeys = [ const storageKeys: [string, Storage][] = [
["nxtgauge_signup_profile_v1", localStorage], ["nxtgauge_signup_profile_v1", localStorage],
["nxtgauge_auth_user", localStorage], ["nxtgauge_auth_user", localStorage],
["nxtgauge_user", localStorage], ["nxtgauge_user", localStorage],

View file

@ -477,7 +477,7 @@ export default function AiCreditsAdmin() {
setLedgerPage(p => p + 1); setLedgerPage(p => p + 1);
handleFetchLedger(); handleFetchLedger();
}} }}
disabled={ledgerData()?.data.length === 0 || ledgerData()?.data.length < 20} disabled={ledgerData()?.data.length === 0 || (ledgerData()?.data.length ?? 0) < 20}
style={{ style={{
padding: '8px 16px', padding: '8px 16px',
border: '1px solid #D1D5DB', border: '1px solid #D1D5DB',

View file

@ -5599,7 +5599,7 @@ export default function DashboardDesignPreview(props: {
<div style="display:flex;justify-content:space-between;margin-top:12px;padding-top:12px;border-top:2px solid #F3F4F6"> <div style="display:flex;justify-content:space-between;margin-top:12px;padding-top:12px;border-top:2px solid #F3F4F6">
<span style="font-size:15px;font-weight:800;color:#111827">Total Amount</span> <span style="font-size:15px;font-weight:800;color:#111827">Total Amount</span>
<span style="font-size:18px;font-weight:800;color:#FF5E13">{((appliedCoupon() ? appliedCoupon().final_price_inr : Number(pkg.price_paise)) / 100).toLocaleString('en-IN')}</span> <span style="font-size:18px;font-weight:800;color:#FF5E13">{((appliedCoupon()?.final_price_inr ?? Number(pkg.price_paise)) / 100).toLocaleString('en-IN')}</span>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1272,7 +1272,7 @@ export default function CreditsPage(props: Props) {
"justify-content": "center", "justify-content": "center",
}} }}
> >
<span style={{ fontSize: "18px" }}>🪙</span> <span style={{ "font-size": "18px" }}>🪙</span>
</div> </div>
<div> <div>
<p <p
@ -1342,7 +1342,7 @@ export default function CreditsPage(props: Props) {
</For> </For>
</div> </div>
); );
})} })()}
</Show> </Show>
</div> </div>
</Show> </Show>
@ -1727,7 +1727,7 @@ export default function CreditsPage(props: Props) {
{formatDate(item.created_at)} {formatDate(item.created_at)}
</p> </p>
</div> </div>
<div style={{ textAlign: "right" }}> <div style={{ "text-align": "right" }}>
<p <p
style={{ style={{
margin: "0", margin: "0",
@ -1776,7 +1776,7 @@ export default function CreditsPage(props: Props) {
{formatDate(payment.created_at)} {formatDate(payment.created_at)}
</p> </p>
</div> </div>
<div style={{ textAlign: "right" }}> <div style={{ "text-align": "right" }}>
<p <p
style={{ style={{
margin: "0", margin: "0",

View file

@ -132,7 +132,7 @@ export default function ExploreServicesPage() {
const data = await res.json().catch(() => ({})); const data = await res.json().catch(() => ({}));
if (res.ok) { if (res.ok) {
const roles: string[] = Array.isArray(data) ? data : Array.isArray(data?.data) ? data.data : []; 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 { } else {
setActiveRoles([]); setActiveRoles([]);
} }
@ -496,13 +496,11 @@ export default function ExploreServicesPage() {
"font-size": "12px", "font-size": "12px",
"font-weight": "700", "font-weight": "700",
cursor: cursor:
busyRoleKey() === card.key || busyRoleKey() === card.key
card.action === "Current Role"
? "not-allowed" ? "not-allowed"
: "pointer", : "pointer",
opacity: opacity:
busyRoleKey() === card.key || busyRoleKey() === card.key
card.action === "Current Role"
? "0.6" ? "0.6"
: "1", : "1",
"margin-top": "auto", "margin-top": "auto",
@ -517,13 +515,11 @@ export default function ExploreServicesPage() {
"font-size": "12px", "font-size": "12px",
"font-weight": "700", "font-weight": "700",
cursor: cursor:
busyRoleKey() === card.key || busyRoleKey() === card.key
card.action === "Current Role"
? "not-allowed" ? "not-allowed"
: "pointer", : "pointer",
opacity: opacity:
busyRoleKey() === card.key || busyRoleKey() === card.key
card.action === "Current Role"
? "0.6" ? "0.6"
: "1", : "1",
"margin-top": "auto", "margin-top": "auto",

View file

@ -416,7 +416,6 @@ export default function MyDashboardPage(props: Props) {
if (customizeMode() && !visibleWidgets().has(key)) return null; if (customizeMode() && !visibleWidgets().has(key)) return null;
return ( return (
<div <div
key={key}
data-widget-key={key} data-widget-key={key}
draggable={true} draggable={true}
onDragStart={(e) => { onDragStart={(e) => {

View file

@ -726,7 +726,7 @@ export default function PortfolioPage(props: Props) {
}; };
const tabs = runtimePortfolioTabs(); const tabs = runtimePortfolioTabs();
const fieldsByTab = runtimeFieldsByTab(); 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 activeFields = () => fieldsByTab[activeTab()] || [];
const isLongField = (field: string) => { const isLongField = (field: string) => {
const key = normalizeToken(field); const key = normalizeToken(field);

View file

@ -64,7 +64,7 @@ export default function PortfolioWidget(props: Props) {
<For each={[ <For each={[
['Portfolio Items', total(), '#374151'], ['Portfolio Items', total(), '#374151'],
]}> ]}>
{([label, value, color]) => <PortfolioStatRow label={label} value={value} color={color} />} {([label, value, color]) => <PortfolioStatRow label={label as string} value={String(value)} color={color as string} />}
</For> </For>
</Show> </Show>
</DashboardWidget> </DashboardWidget>

7
src/global.d.ts vendored
View file

@ -1,4 +1,11 @@
/// <reference types="vinxi/types/client" /> /// <reference types="vinxi/types/client" />
/// <reference types="vinxi/types/server" /> /// <reference types="vinxi/types/server" />
declare global {
interface Window {
__captchaCode?: string;
__testMode?: boolean;
}
}
export {}; export {};

View file

@ -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 // API base URL - can be configured via environment
const API_BASE = import.meta.env.VITE_API_BASE_URL || ''; const API_BASE = import.meta.env.VITE_API_BASE_URL || '';
@ -185,7 +185,7 @@ export interface UseAiCreditsReturn {
// Credit data // Credit data
credits: Accessor<number>; credits: Accessor<number>;
tracecoinBalance: Accessor<number>; tracecoinBalance: Accessor<number>;
creditResource: ReturnType<typeof createResource<CreditBalance>>; creditResource: Resource<CreditBalance>;
// Loading states // Loading states
isLoading: Accessor<boolean>; isLoading: Accessor<boolean>;

View file

@ -23,7 +23,7 @@
import { createMiddleware } from "@solidjs/start/middleware"; import { createMiddleware } from "@solidjs/start/middleware";
const GATEWAY_URL = ( const GATEWAY_URL = (
process.env.GATEWAY_URL || "http://nxtgauge-rust-gateway:9100" process.env.GATEWAY_URL || "http://localhost:9100"
).replace(/\/+$/, ""); ).replace(/\/+$/, "");
const PUBLIC_API_URL = ( const PUBLIC_API_URL = (
@ -202,8 +202,7 @@ export default createMiddleware({
return handlePayuReturn(fetchEvent); return handlePayuReturn(fetchEvent);
} }
// Everything else under /api/* — let it fall through. // All other /api/* paths — proxy to the gateway
// Returning undefined tells the framework to continue. return proxyToGateway(fetchEvent, path);
return;
}, },
}); });

View file

@ -386,7 +386,7 @@ export default function AboutPage() {
? `perspective(980px) rotateX(${builtTilt().x}deg) rotateY(${builtTilt().y}deg) scale(1)` ? `perspective(980px) rotateX(${builtTilt().x}deg) rotateY(${builtTilt().y}deg) scale(1)`
: 'perspective(980px) rotateX(0deg) rotateY(0deg) scale(0.95)', : 'perspective(980px) rotateX(0deg) rotateY(0deg) scale(0.95)',
filter: builtVisible() && !reduceMotion() ? 'blur(0px)' : 'blur(3px)', filter: builtVisible() && !reduceMotion() ? 'blur(0px)' : 'blur(3px)',
transitionDelay: builtVisible() ? '150ms' : '0ms', "transition-delay": builtVisible() ? '150ms' : '0ms',
}} }}
> >
<span class="about-chapter-two-panel-glow" /> <span class="about-chapter-two-panel-glow" />

View file

@ -267,7 +267,7 @@ export default function ContactPage() {
</span> </span>
<span class="contact-upload-text"> <span class="contact-upload-text">
{values().attachment {values().attachment
? values().attachment.name ? values().attachment?.name
: "Upload pdf/png/jpg (max 10MB)"} : "Upload pdf/png/jpg (max 10MB)"}
</span> </span>
<input <input

View file

@ -39,6 +39,7 @@ interface Invoice {
issued_at: string; issued_at: string;
paid_at: string | null; paid_at: string | null;
voided_at: string | null; voided_at: string | null;
void_reason: string | null;
} }
interface LineItem { interface LineItem {
@ -118,7 +119,7 @@ export default function InvoiceDetailPage() {
</div> </div>
</Show> </Show>
<Show when={data()}> <Show when={data()} keyed>
{(d: InvoiceResponse) => ( {(d: InvoiceResponse) => (
<div class="bg-white border rounded-xl overflow-hidden shadow-sm"> <div class="bg-white border rounded-xl overflow-hidden shadow-sm">
<div class="bg-gray-900 text-white p-6"> <div class="bg-gray-900 text-white p-6">

View file

@ -61,7 +61,7 @@ export default function InvoicesPage() {
</div> </div>
</Show> </Show>
<Show when={data()}> <Show when={data()} keyed>
{(d) => ( {(d) => (
<Show <Show
when={d.invoices.length > 0} when={d.invoices.length > 0}

View file

@ -6,6 +6,7 @@ import PublicBackground from "~/components/PublicBackground";
import PublicHeader from "~/components/PublicHeader"; import PublicHeader from "~/components/PublicHeader";
import PublicFooter from "~/components/PublicFooter"; import PublicFooter from "~/components/PublicFooter";
import ArticleContent from "~/components/ArticleContent"; import ArticleContent from "~/components/ArticleContent";
import type { ContentBlock } from "~/data/help-center-seed";
function categoryTitle(input: string) { function categoryTitle(input: string) {
return input return input
@ -326,7 +327,7 @@ export default function HelpCenterArticlePage() {
when={a().content} when={a().content}
fallback={<p style={{ color: "#6B7280" }}>No content available.</p>} fallback={<p style={{ color: "#6B7280" }}>No content available.</p>}
> >
<ArticleContent blocks={a().content} /> <ArticleContent blocks={a().content as ContentBlock[]} />
</Show> </Show>
<div <div
style={{ style={{

View file

@ -146,7 +146,7 @@ export default function HelpCenterPage() {
<div class="container" style={{ "max-width": "1100px" }}> <div class="container" style={{ "max-width": "1100px" }}>
{/* Categories - Glass Cards */} {/* Categories - Glass Cards */}
<Show when={!query() && !category()}> <Show when={!query() && !category()}>
<div style={{ marginBottom: "60px" }}> <div style={{ "margin-bottom": "60px" }}>
<h2 <h2
style={{ style={{
margin: "0 0 28px", margin: "0 0 28px",
@ -237,7 +237,7 @@ export default function HelpCenterPage() {
</Show> </Show>
{/* Articles - Glass Cards */} {/* Articles - Glass Cards */}
<div style={{ marginTop: !query() && !category() ? "0" : "20px" }}> <div style={{ "margin-top": !query() && !category() ? "0" : "20px" }}>
<h2 <h2
style={{ style={{
margin: "0 0 28px", margin: "0 0 28px",

View file

@ -16,7 +16,7 @@ export default function SignupIndexRoute() {
const [search] = useSearchParams(); const [search] = useSearchParams();
createEffect(() => { createEffect(() => {
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")) { if (intent.includes("company")) {
navigate("/signup/company", { replace: true }); navigate("/signup/company", { replace: true });
@ -35,8 +35,8 @@ export default function SignupIndexRoute() {
return ( return (
<div style={{ <div style={{
display: "flex", display: "flex",
justifyContent: "center", "justify-content": "center",
alignItems: "center", "align-items": "center",
height: "100vh", height: "100vh",
background: "#07051a" background: "#07051a"
}}> }}>

View file

@ -1,3 +1,4 @@
/// <reference types="vitest/globals" />
import "@testing-library/jest-dom"; import "@testing-library/jest-dom";
import { beforeAll, afterEach, afterAll } from "vitest"; import { beforeAll, afterEach, afterAll } from "vitest";
import { setupServer } from "msw/node"; import { setupServer } from "msw/node";
@ -35,14 +36,16 @@ Object.defineProperty(window, "matchMedia", {
}); });
// Mock IntersectionObserver // Mock IntersectionObserver
global.IntersectionObserver = class IntersectionObserver { (global as any).IntersectionObserver = class IntersectionObserver {
root = null;
rootMargin = '';
scrollMargin = '';
thresholds: number[] = [];
constructor() {} constructor() {}
disconnect() {} disconnect() {}
observe() {} observe() {}
takeRecords() { unobserve() {}
return []; takeRecords() { return []; }
}
trigger() {}
}; };
// Mock ResizeObserver // Mock ResizeObserver

View file

@ -95,7 +95,7 @@ test.describe("AI Chat Widget", () => {
const results = await new AxeBuilder({ page }).analyze(); const results = await new AxeBuilder({ page }).analyze();
const criticalViolations = results.violations.filter( const criticalViolations = results.violations.filter(
(v: { impact: string }) => v.impact === "critical" (v: { impact?: string | null }) => v.impact === "critical"
); );
expect(criticalViolations).toEqual([]); expect(criticalViolations).toEqual([]);
}); });

View file

@ -44,8 +44,8 @@ test.describe("AI API Endpoints", () => {
if (!companyToken) test.skip(); if (!companyToken) test.skip();
await page.goto(`${API_BASE}/`); await page.goto(`${API_BASE}/`);
await page.evaluate((t: string) => { await page.evaluate((t: string | null) => {
window.sessionStorage.setItem("nxtgauge_access_token", t); window.sessionStorage.setItem("nxtgauge_access_token", t ?? "");
}, companyToken); }, companyToken);
const ctx = await request.newContext(); const ctx = await request.newContext();

View file

@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
// Get OTP from Redis // Get OTP from Redis
await new Promise(r => setTimeout(r, 500)); 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"); if (!otpCode) throw new Error("Could not get OTP from Redis");
console.log(` ✅ OTP retrieved: ${otpCode}`); console.log(` ✅ OTP retrieved: ${otpCode}`);
@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
const loginData = await loginResponse.json(); const loginData = await loginResponse.json();
if (!loginData.access_token) throw new Error("Login failed"); if (!loginData.access_token) throw new Error("Login failed");
user.accessToken = loginData.access_token; 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; return user;
} }
@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) {
selectedProfessionalRole: role, name, fullName: name, id: userId selectedProfessionalRole: role, name, fullName: name, id: userId
})); }));
sessionStorage.setItem("nxtgauge_access_token", token); 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) { async function takeScreenshot(page: Page, name: string) {

View file

@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
// Get OTP from Redis // Get OTP from Redis
await new Promise(r => setTimeout(r, 500)); 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"); if (!otpCode) throw new Error("Could not get OTP from Redis");
console.log(` ✅ OTP retrieved: ${otpCode}`); console.log(` ✅ OTP retrieved: ${otpCode}`);
@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
const loginData = await loginResponse.json(); const loginData = await loginResponse.json();
if (!loginData.access_token) throw new Error("Login failed"); if (!loginData.access_token) throw new Error("Login failed");
user.accessToken = loginData.access_token; 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; return user;
} }
@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) {
selectedProfessionalRole: role, name, fullName: name, id: userId selectedProfessionalRole: role, name, fullName: name, id: userId
})); }));
sessionStorage.setItem("nxtgauge_access_token", token); 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) { async function takeScreenshot(page: Page, name: string) {

View file

@ -74,7 +74,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
// Wait for OTP to be generated // Wait for OTP to be generated
await new Promise(r => setTimeout(r, 1000)); 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"); if (!otpCode) throw new Error("Could not get OTP from Redis");
console.log(` ✅ OTP retrieved: ${otpCode}`); console.log(` ✅ OTP retrieved: ${otpCode}`);
@ -96,7 +96,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
const loginData = await loginResponse.json(); const loginData = await loginResponse.json();
if (!loginData.access_token) throw new Error("Login failed"); if (!loginData.access_token) throw new Error("Login failed");
user.accessToken = loginData.access_token; 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; return user;
} }
@ -116,7 +116,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) {
selectedProfessionalRole: role, name, fullName: name, id: userId selectedProfessionalRole: role, name, fullName: name, id: userId
})); }));
sessionStorage.setItem("nxtgauge_access_token", token); 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) { async function takeScreenshot(page: Page, name: string) {

View file

@ -55,7 +55,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
// Get OTP from Redis // Get OTP from Redis
await new Promise(r => setTimeout(r, 500)); 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"); if (!otpCode) throw new Error("Could not get OTP from Redis");
console.log(` ✅ OTP retrieved: ${otpCode}`); console.log(` ✅ OTP retrieved: ${otpCode}`);
@ -77,7 +77,7 @@ async function registerUser(user: TestUser): Promise<TestUser> {
const loginData = await loginResponse.json(); const loginData = await loginResponse.json();
if (!loginData.access_token) throw new Error("Login failed"); if (!loginData.access_token) throw new Error("Login failed");
user.accessToken = loginData.access_token; 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; return user;
} }
@ -97,7 +97,7 @@ async function setupFrontendAuth(page: Page, user: TestUser) {
selectedProfessionalRole: role, name, fullName: name, id: userId selectedProfessionalRole: role, name, fullName: name, id: userId
})); }));
sessionStorage.setItem("nxtgauge_access_token", token); 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) { async function takeScreenshot(page: Page, name: string) {

View file

@ -209,7 +209,7 @@ async function runSignup(page: any, scenario: SignupScenario, email: string) {
async function seedSession(page: any, scenario: SignupScenario, email: string) { async function seedSession(page: any, scenario: SignupScenario, email: string) {
await page.evaluate( await page.evaluate(
({ role, mail }) => { ({ role, mail }: { role: string; mail: string }) => {
sessionStorage.setItem("nxtgauge_access_token", "pw-test-token"); sessionStorage.setItem("nxtgauge_access_token", "pw-test-token");
const payload = { const payload = {
email: mail, email: mail,

8
tests/tsconfig.json Normal file
View file

@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"moduleResolution": "node",
"types": ["vite/client", "node"]
},
"include": ["./**/*.ts", "./**/*.tsx"]
}

View file

@ -1,6 +1,6 @@
import { describe, it, expect, vi, beforeEach } from "vitest"; import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent, waitFor } from "@solidjs/testing-library"; import { render, screen, fireEvent, waitFor } from "@solidjs/testing-library";
import { AiChatWidget } from "../../src/components/AiChatWidget"; import { AiChatWidget } from "~/components/AiChatWidget";
global.fetch = vi.fn(); global.fetch = vi.fn();
@ -51,7 +51,7 @@ describe("AiChatWidget", () => {
fireEvent.click(button); fireEvent.click(button);
await waitFor(() => { await waitFor(() => {
const input = screen.getByPlaceholder(/Ask/i); const input = screen.getByPlaceholderText(/Ask/i);
fireEvent.change(input, { target: { value: "Test" } }); fireEvent.change(input, { target: { value: "Test" } });
fireEvent.keyDown(input, { key: "Enter" }); fireEvent.keyDown(input, { key: "Enter" });
}); });
@ -77,7 +77,7 @@ describe("AiChatWidget", () => {
fireEvent.click(button); fireEvent.click(button);
await waitFor(() => { await waitFor(() => {
const input = screen.getByPlaceholder(/Ask/i); const input = screen.getByPlaceholderText(/Ask/i);
fireEvent.change(input, { target: { value: "Hello" } }); fireEvent.change(input, { target: { value: "Hello" } });
fireEvent.keyDown(input, { key: "Enter" }); fireEvent.keyDown(input, { key: "Enter" });
}); });

View file

@ -13,7 +13,7 @@ export default defineConfig({
"/api/gateway": { "/api/gateway": {
target: "http://localhost:9100", target: "http://localhost:9100",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => rewrite: (path: string) =>
path path
.replace(/^\/api\/gateway\/api(\/|$)/, "/api$1") .replace(/^\/api\/gateway\/api(\/|$)/, "/api$1")
.replace(/^\/api\/gateway(\/|$)/, "/api$1"), .replace(/^\/api\/gateway(\/|$)/, "/api$1"),