make supabase client singleton

This commit is contained in:
Naytitorn Chaovirachot 2024-11-26 00:45:18 +07:00
parent 42a4fc4293
commit 8a65012044
8 changed files with 2085 additions and 30 deletions

View File

@ -1,3 +1,3 @@
{ {
"presets": ["next/babel"] "presets": ["next/babel"]
} }

View File

@ -2,8 +2,9 @@ import { render } from "@testing-library/react";
// Pages - Admin // Pages - Admin
import AdminPage from "@/app/admin/page"; import AdminPage from "@/app/admin/page";
import BusinesssApplicationAdminPage from "@/app/admin/business/page"; import BusinessApplicationAdminPage from "@/app/admin/business/page";
import ProjectAdminPage from "@/app/admin/business/[businessId]/projects/page"; import ProjectAdminPage from "@/app/admin/business/[businessId]/projects/page";
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
// Mock Cookies // Mock Cookies
jest.mock("next/headers", () => ({ jest.mock("next/headers", () => ({
@ -13,15 +14,31 @@ jest.mock("next/headers", () => ({
})), })),
})); }));
jest.mock('next/navigation', () => ({
redirect: jest.fn(),
}));
// Admin Pages // Admin Pages
describe("Admin Pages", () => { describe("Admin Pages", () => {
it("Admin Page should render without crashing", async () => { it("Admin Page should render without crashing", async () => {
render(await AdminPage()); render(await AdminPage());
}); });
// it("Business Application Admin Page should render without crashing", async () => { it("Business Application Admin Page should render without crashing", async () => {
// render(await BusinesssApplicationAdminPage()); const mockGetUser = jest.spyOn(createSupabaseClient().auth, 'getUser');
// });
// Mock the getUser method's implementation to return a full User object
mockGetUser.mockResolvedValue({
data: {
user: {
id: '4d31cfe0-8ebd-4bc8-915e-d301ecaffb33',
}
},
error: null,
});
render(await BusinessApplicationAdminPage());
});
it("Project Admin Page should render without crashing", async () => { it("Project Admin Page should render without crashing", async () => {
render(await ProjectAdminPage({ params: { businessId: "1" } })); render(await ProjectAdminPage({ params: { businessId: "1" } }));

View File

@ -1,3 +1,4 @@
import React from 'react';
import { render } from "@testing-library/react"; import { render } from "@testing-library/react";
// Pages - Business // Pages - Business
@ -26,8 +27,10 @@ jest.mock('next/navigation', () => ({
})); }));
// Business Pages // Business Pages
// describe("Business Pages", () => { describe("Business Pages", () => {
// it("Apply Business Page should render without crashing", async () => { // it("Apply Business Page should render without crashing", async () => {
// render(await ApplyBusiness()); // jest.spyOn(React, 'useRef').mockReturnValue({ current: false });
// }); // jest.spyOn(React, 'useEffect').mockImplementation((fn) => fn());
// }); // render(await ApplyBusiness());
// });
});

View File

@ -13,7 +13,10 @@ const config: Config = {
transformIgnorePatterns: [ transformIgnorePatterns: [
'/node_modules/(?!(flat|@supabase-cache-helpers|react-markdown)/)' '/node_modules/(?!(flat|@supabase-cache-helpers|react-markdown)/)'
], ],
setupFiles: ["jest-canvas-mock"] setupFiles: ["jest-canvas-mock"],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // Resolves @/ to src/
},
// Add more setup options before each test is run // Add more setup options before each test is run
// setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'], // setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
} }

2017
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -76,6 +76,8 @@
"zod": "^3.23.8" "zod": "^3.23.8"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.26.0", "@babel/preset-typescript": "^7.26.0",
"@eslint/js": "^9.13.0", "@eslint/js": "^9.13.0",
"@playwright/test": "^1.47.2", "@playwright/test": "^1.47.2",
@ -94,6 +96,7 @@
"@types/react-file-icon": "^1.0.4", "@types/react-file-icon": "^1.0.4",
"@types/react-lottie": "^1.2.10", "@types/react-lottie": "^1.2.10",
"@types/react-select-country-list": "^2.2.3", "@types/react-select-country-list": "^2.2.3",
"babel-jest": "^29.7.0",
"eslint": "^8.57.1", "eslint": "^8.57.1",
"eslint-config-next": "14.2.5", "eslint-config-next": "14.2.5",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",

View File

@ -81,13 +81,14 @@ function ApplicationTable({ applications }: { applications: ApplicationData[] })
)); ));
} }
export default async function BusinesssApplicationAdminPage() { export default async function BusinessApplicationAdminPage() {
const client = createSupabaseClient(); const client = createSupabaseClient();
const { data: userData, error: userDataError } = await client.auth.getUser(); const { data: userData, error: userDataError } = await client.auth.getUser();
if (userDataError) { if (userDataError) {
redirect("/"); redirect("/");
} }
const uid = userData.user!.id; const uid = userData.user!.id;
const { data: roleData, error: roleDataError } = await getUserRole(client, uid); const { data: roleData, error: roleDataError } = await getUserRole(client, uid);
@ -104,7 +105,7 @@ export default async function BusinesssApplicationAdminPage() {
const pendingApplications = businessApplicationData?.filter((app) => app.status === "pending") || []; const pendingApplications = businessApplicationData?.filter((app) => app.status === "pending") || [];
const approvedApplications = businessApplicationData?.filter((app) => app.status === "approve") || []; const approvedApplications = businessApplicationData?.filter((app) => app.status === "approve") || [];
const rejectedApplications = businessApplicationData?.filter((app) => app.status === "rejecte") || []; const rejectedApplications = businessApplicationData?.filter((app) => app.status === "rejected") || [];
return ( return (
<div className="container max-w-screen-xl my-4"> <div className="container max-w-screen-xl my-4">

View File

@ -1,21 +1,32 @@
import { createServerClient } from "@supabase/ssr"; import { createServerClient } from "@supabase/ssr";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
export function createSupabaseClient() { let supabaseClient: ReturnType<typeof createServerClient> | null = null;
const cookieStore = cookies();
return createServerClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { // This is singleton
cookies: { export function createSupabaseClient() {
getAll() { if (!supabaseClient) {
return cookieStore.getAll(); const cookieStore = cookies();
},
setAll(cookiesToSet) { supabaseClient = createServerClient(
try { process.env.NEXT_PUBLIC_SUPABASE_URL!,
cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options)); process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
} catch (error) { {
console.error("Error setting cookies:", error); cookies: {
} getAll() {
}, return cookieStore.getAll();
}, },
}); setAll(cookiesToSet) {
} try {
cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options));
} catch (error) {
console.error("Error setting cookies:", error);
}
},
},
}
);
}
return supabaseClient;
}