1
0
forked from baron/baron-sso

chore: fix biome lint warnings & update Makefile

This commit is contained in:
2026-05-29 13:32:35 +09:00
parent deed33aad2
commit 00b89c04d6
22 changed files with 99 additions and 121 deletions

View File

@@ -276,18 +276,18 @@ code-check-front-lint:
@echo "==> adminfront biome lint/format check"
rm -rf adminfront/playwright-report adminfront/test-results
cd adminfront && CI=true npx pnpm install --frozen-lockfile --ignore-scripts
cd adminfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
cd adminfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
cd adminfront && npx biome lint .
cd adminfront && npx biome format .
@echo "==> devfront biome lint/format check"
rm -rf devfront/playwright-report devfront/test-results
cd devfront && npm ci --ignore-scripts
cd devfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
cd devfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
cd devfront && npx biome lint .
cd devfront && npx biome format .
@echo "==> orgfront biome lint/format check"
rm -rf orgfront/playwright-report orgfront/test-results
cd orgfront && npm ci --ignore-scripts
cd orgfront && npx biome check . --formatter-enabled=false --organize-imports-enabled=false
cd orgfront && npx biome check . --linter-enabled=false --organize-imports-enabled=false
cd orgfront && npx biome lint .
cd orgfront && npx biome format .
code-check-backend-tests:
@echo "==> backend tests"

View File

@@ -3,7 +3,7 @@ import { createServer } from "node:http";
import { extname, join, normalize, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const rootDir = fileURLToPath(new URL("..", import.meta.url));
const _rootDir = fileURLToPath(new URL("..", import.meta.url));
const distDir = resolve(
process.env.ADMINFRONT_BUILD_OUT_DIR ?? "/tmp/baron-sso-adminfront-dist",
);

View File

@@ -19,7 +19,6 @@ import { TenantProfilePage } from "../features/tenants/routes/TenantProfilePage"
import { TenantSchemaPage } from "../features/tenants/routes/TenantSchemaPage";
import { TenantWorksmobilePage } from "../features/tenants/routes/TenantWorksmobilePage";
import TenantUserGroupsTab from "../features/user-groups/routes/TenantUserGroupsTab";
import { UserGroupDetailPage } from "../features/user-groups/routes/UserGroupDetailPage";
import UserCreatePage from "../features/users/UserCreatePage";
import UserDetailPage from "../features/users/UserDetailPage";
import UserListPage from "../features/users/UserListPage";

View File

@@ -3,7 +3,6 @@ import type { AxiosError } from "axios";
import { Download, NotebookTabs, RefreshCw, Search } from "lucide-react";
import * as React from "react";
import {
formatAuditValue,
parseAuditDetails,
resolveAuditAction,
resolveAuditActor,

View File

@@ -23,7 +23,6 @@ import {
fetchDataIntegrityReport,
type RPUsageDailyMetric,
type RPUsagePeriod,
type TenantSummary,
} from "../../lib/adminApi";
import { t } from "../../lib/i18n";

View File

@@ -5,7 +5,6 @@ import {
Plus,
Search,
ShieldCheck,
Trash2,
UserPlus,
Users,
} from "lucide-react";
@@ -28,7 +27,6 @@ import {
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../components/ui/dialog";
import { Input } from "../../../components/ui/input";
import {
@@ -68,7 +66,7 @@ function mergePendingMembers(
export function TenantAdminsAndOwnersTab() {
const auth = useAuth();
const navigate = useNavigate();
const currentUserId = auth.user?.profile.sub;
const _currentUserId = auth.user?.profile.sub;
const { tenantId: tenantIdParam } = useParams<{ tenantId: string }>();
const tenantId = tenantIdParam ?? "";
const queryClient = useQueryClient();
@@ -187,7 +185,7 @@ export function TenantAdminsAndOwnersTab() {
),
);
},
onError: (err: AxiosError<{ error?: string }>, userId, context) => {
onError: (err: AxiosError<{ error?: string }>, _userId, context) => {
if (context?.previousOwners) {
queryClient.setQueryData(
["tenant-owners", tenantId],
@@ -288,7 +286,7 @@ export function TenantAdminsAndOwnersTab() {
t("msg.admin.tenants.admins.remove_success", "권한이 회수되었습니다."),
);
},
onError: (err: AxiosError<{ error?: string }>, userId, context) => {
onError: (err: AxiosError<{ error?: string }>, _userId, context) => {
if (context?.previousAdmins) {
queryClient.setQueryData(
["tenant-admins", tenantId],
@@ -310,7 +308,7 @@ export function TenantAdminsAndOwnersTab() {
}
};
const handleRemoveOwner = (userId: string, userName: string) => {
const _handleRemoveOwner = (userId: string, userName: string) => {
if (
window.confirm(
t(
@@ -324,7 +322,7 @@ export function TenantAdminsAndOwnersTab() {
}
};
const handleRemoveAdmin = (userId: string, userName: string) => {
const _handleRemoveAdmin = (userId: string, userName: string) => {
if (
window.confirm(
t(

View File

@@ -1,7 +1,6 @@
import { useQuery } from "@tanstack/react-query";
import { Copy } from "lucide-react";
import { Link, Outlet, useLocation, useParams } from "react-router-dom";
import { Badge } from "../../../components/ui/badge";
import { Button } from "../../../components/ui/button";
import { fetchMe, fetchTenant } from "../../../lib/adminApi";
import { t } from "../../../lib/i18n";

View File

@@ -38,7 +38,6 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../components/ui/dialog";
import { Input } from "../../../components/ui/input";
import { Label } from "../../../components/ui/label";
@@ -239,7 +238,7 @@ const UserGroupTreeNode: React.FC<UserGroupTreeNodeProps> = ({
function TenantGroupsPage() {
const params = useParams<{ tenantId: string }>();
const tenantId = params.tenantId ?? "";
const queryClient = useQueryClient();
const _queryClient = useQueryClient();
const [newGroupName, setNewGroupName] = useState("");
const [newGroupDesc, setNewGroupNameDesc] = useState("");

View File

@@ -4,7 +4,6 @@ import {
useMutation,
useQuery,
} from "@tanstack/react-query";
import { useVirtualizer } from "@tanstack/react-virtual";
import type { AxiosError } from "axios";
import {
ArrowDown,
@@ -14,7 +13,6 @@ import {
ChevronDown,
ChevronRight,
Download,
ExternalLink,
FileSpreadsheet,
LayoutDashboard,
List,
@@ -28,22 +26,13 @@ import {
import * as React from "react";
import { Link, useNavigate } from "react-router-dom";
import { PageHeader } from "../../../../../common/core/components/page";
import {
SortableTableHead,
sortableTableHeadBaseClassName,
sortableTableHeaderClassName,
} from "../../../../../common/core/components/sort";
import {
type SortConfig,
type SortResolverMap,
sortItems,
toggleSort,
} from "../../../../../common/core/utils";
import {
commonStickyTableHeaderClass,
commonTableShellClass,
commonTableViewportClass,
} from "../../../../../common/ui/table";
import { commonStickyTableHeaderClass } from "../../../../../common/ui/table";
import { RoleGuard } from "../../../components/auth/RoleGuard";
import { Badge } from "../../../components/ui/badge";
import { Button } from "../../../components/ui/button";
@@ -71,7 +60,6 @@ import {
DropdownMenuTrigger,
} from "../../../components/ui/dropdown-menu";
import { Input } from "../../../components/ui/input";
import { ScrollArea } from "../../../components/ui/scroll-area";
import {
Select,
SelectContent,
@@ -79,7 +67,6 @@ import {
SelectTrigger,
SelectValue,
} from "../../../components/ui/select";
import { Separator } from "../../../components/ui/separator";
import { Switch } from "../../../components/ui/switch";
import {
Table,
@@ -132,10 +119,10 @@ import {
const tenantCSVTemplate =
"name,type,parent_tenant_slug,slug,memo,email_domain,visibility,org_unit_type\n";
const tenantPageSize = 500;
const tenantVirtualizationThreshold = 250;
const tenantEstimatedRowHeight = 73;
const tenantLoadAheadPx = 360;
const tenantLoadAheadRows = 30;
const _tenantVirtualizationThreshold = 250;
const _tenantEstimatedRowHeight = 73;
const _tenantLoadAheadPx = 360;
const _tenantLoadAheadRows = 30;
type TenantSortKey = keyof TenantSummary | "recursiveMemberCount";
type TenantListRow = TenantSummary & { recursiveMemberCount: number };
@@ -301,7 +288,7 @@ function TenantListPage() {
>({});
const [previewOpen, setPreviewOpen] = React.useState(false);
const [selectedBulkStatus, setSelectedBulkStatus] = React.useState("");
const tenantTableScrollRef = React.useRef<HTMLDivElement | null>(null);
const _tenantTableScrollRef = React.useRef<HTMLDivElement | null>(null);
const { data: profile } = useQuery({
queryKey: ["me"],

View File

@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { ArrowRight, Building2, Plus } from "lucide-react";
import { Building2, Plus } from "lucide-react";
import { Link, useNavigate, useParams } from "react-router-dom";
import {
commonStickyTableHeaderClass,

View File

@@ -1,14 +1,6 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import type { AxiosError } from "axios";
import {
Loader2,
Mail,
MoreHorizontal,
Plus,
User,
UserMinus,
UserPlus,
} from "lucide-react";
import { Loader2, Mail, Plus, User, UserPlus } from "lucide-react";
import { Link, useNavigate, useParams } from "react-router-dom";
import { commonStickyTableHeaderClass } from "../../../../../common/ui/table";
import { Badge } from "../../../components/ui/badge";
@@ -19,12 +11,6 @@ import {
CardHeader,
CardTitle,
} from "../../../components/ui/card";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "../../../components/ui/dropdown-menu";
import {
Table,
TableBody,
@@ -80,7 +66,7 @@ function TenantUsersPage() {
},
});
const handleRemoveMember = (userId: string, userName: string) => {
const _handleRemoveMember = (userId: string, userName: string) => {
if (!tenantSlug) return;
if (
window.confirm(

View File

@@ -1,6 +1,5 @@
import { useQuery } from "@tanstack/react-query";
import { Building2, Plus, Users } from "lucide-react";
import { useState } from "react";
import { Link } from "react-router-dom";
import { commonStickyTableHeaderClass } from "../../../../../common/ui/table";
import { Badge } from "../../../components/ui/badge";

View File

@@ -6,7 +6,6 @@ import {
Building2,
ChevronDown,
ChevronRight,
CornerDownRight,
Download,
ExternalLink,
FolderOpen,
@@ -41,7 +40,6 @@ import {
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "../../../components/ui/dialog";
import {
DropdownMenu,
@@ -52,7 +50,6 @@ import {
DropdownMenuTrigger,
} from "../../../components/ui/dropdown-menu";
import { Input } from "../../../components/ui/input";
import { Label } from "../../../components/ui/label";
import { ScrollArea } from "../../../components/ui/scroll-area";
import {
Table,
@@ -62,15 +59,8 @@ import {
TableHeader,
TableRow,
} from "../../../components/ui/table";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "../../../components/ui/tabs";
import { toast } from "../../../components/ui/use-toast";
import {
createUser,
exportTenantsCSV,
fetchAllTenants,
fetchUsers,
@@ -413,7 +403,7 @@ const MemberTable: React.FC<{
function TenantUserGroupsTab() {
const { tenantId } = useParams<{ tenantId: string }>();
const navigate = useNavigate();
const _navigate = useNavigate();
const queryClient = useQueryClient();
const [selectedNodeId, setSelectedNodeId] = useState<string>(tenantId || "");
@@ -855,7 +845,7 @@ const UserAddDialog: React.FC<{
try {
const res = await fetchUsers(20, 0, userSearch);
setSearchResults(res.items);
} catch (err) {
} catch (_err) {
toast.error(t("msg.admin.users.list.fetch_error", "사용자 검색 실패"));
} finally {
setIsSearching(false);

View File

@@ -8,7 +8,6 @@ import {
Plus,
Save,
Trash2,
Mail,
X,
} from "lucide-react";
import * as React from "react";
@@ -22,7 +21,6 @@ import {
CardHeader,
CardTitle,
} from "../../components/ui/card";
import { Checkbox } from "../../components/ui/checkbox";
import {
Dialog,
DialogContent,
@@ -184,7 +182,7 @@ function UserCreatePage() {
if (e.key === "Enter" || e.key === "," || e.key === " ") {
e.preventDefault();
const value = newSubEmail.trim().replace(/,/g, "");
if (value && value.includes("@") && !currentSubEmails.includes(value)) {
if (value?.includes("@") && !currentSubEmails.includes(value)) {
setValue("metadata.sub_email", [...currentSubEmails, value], {
shouldDirty: true,
});
@@ -667,8 +665,7 @@ function UserCreatePage() {
onClick={() => {
const value = newSubEmail.trim().replace(/,/g, "");
if (
value &&
value.includes("@") &&
value?.includes("@") &&
!currentSubEmails.includes(value)
) {
setValue(

View File

@@ -35,7 +35,6 @@ import {
CardHeader,
CardTitle,
} from "../../components/ui/card";
import { Checkbox } from "../../components/ui/checkbox";
import {
Dialog,
DialogContent,
@@ -95,7 +94,10 @@ import { resolvePersonalTenant } from "./utils/personalTenant";
type UserFormValues = Omit<UserUpdateRequest, "metadata"> & {
email: string;
metadata: Record<string, Record<string, string | number | boolean | string[]> | string[] | undefined> & {
metadata: Record<
string,
Record<string, string | number | boolean | string[]> | string[] | undefined
> & {
sub_email?: string[];
};
};
@@ -322,8 +324,8 @@ function UserDetailPage() {
const userId = params.id ?? "";
const navigate = useNavigate();
const queryClient = useQueryClient();
const [error, setError] = React.useState<string | null>(null);
const [successMsg, setSuccessMsg] = React.useState<string | null>(null);
const [_error, _setError] = React.useState<string | null>(null);
const [_successMsg, _setSuccessMsg] = React.useState<string | null>(null);
const [isPasswordResetOpen, setIsPasswordResetOpen] = React.useState(false);
const [generatedPassword, setGeneratedPassword] = React.useState<
string | null
@@ -419,7 +421,7 @@ function UserDetailPage() {
if (e.key === "Enter" || e.key === "," || e.key === " ") {
e.preventDefault();
const value = newSubEmail.trim().replace(/,/g, "");
if (value && value.includes("@") && !currentSubEmails.includes(value)) {
if (value?.includes("@") && !currentSubEmails.includes(value)) {
setValue("metadata.sub_email", [...currentSubEmails, value], {
shouldDirty: true,
});
@@ -595,7 +597,7 @@ function UserDetailPage() {
);
};
const setPrimaryAppointment = (targetIndex: number) => {
const _setPrimaryAppointment = (targetIndex: number) => {
setAdditionalAppointments((current) =>
current.map((appointment, index) => ({
...appointment,
@@ -793,11 +795,13 @@ function UserDetailPage() {
sub_email: rawSubEmail,
...safeMetadata
} = cleanMetadata;
// Parse sub_email
let sub_email: string[] = [];
if (Array.isArray(rawSubEmail)) {
sub_email = rawSubEmail.filter(e => typeof e === 'string' && e.includes("@"));
sub_email = rawSubEmail.filter(
(e) => typeof e === "string" && e.includes("@"),
);
} else if (typeof rawSubEmail === "string" && rawSubEmail.trim() !== "") {
sub_email = rawSubEmail
.split(/[;,\n\r\t]/)
@@ -816,7 +820,7 @@ function UserDetailPage() {
};
// email cannot be updated directly via this API in current backend implementation,
// so we delete it from payload if it spread
// @ts-ignore
// @ts-expect-error
delete payload.email;
payload.role = undefined;
@@ -992,16 +996,21 @@ function UserDetailPage() {
<Mail size={14} className="text-primary/70" />
{user.email}
</div>
{Boolean(user.metadata?.sub_email &&
Array.isArray(user.metadata.sub_email) &&
user.metadata.sub_email.length > 0) && (
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
<Mail size={14} className="text-primary/40" />
<span className="text-[10px] font-bold">
+{Array.isArray(user.metadata?.sub_email) ? user.metadata.sub_email.length : 0}
</span>
</div>
)}
{Boolean(
user.metadata?.sub_email &&
Array.isArray(user.metadata.sub_email) &&
user.metadata.sub_email.length > 0,
) && (
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
<Mail size={14} className="text-primary/40" />
<span className="text-[10px] font-bold">
+
{Array.isArray(user.metadata?.sub_email)
? user.metadata.sub_email.length
: 0}
</span>
</div>
)}
{user.phone && (
<div className="flex items-center gap-1.5 bg-background px-2.5 py-1 rounded-full border">
<Shield size={14} className="text-primary/70" />
@@ -1170,8 +1179,7 @@ function UserDetailPage() {
onClick={() => {
const value = newSubEmail.trim().replace(/,/g, "");
if (
value &&
value.includes("@") &&
value?.includes("@") &&
!currentSubEmails.includes(value)
) {
setValue(

View File

@@ -1,5 +1,5 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { fireEvent, render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";
import { beforeEach, describe, expect, it, vi } from "vitest";
import { createI18nMock } from "../../test/i18nMock";

View File

@@ -13,7 +13,6 @@ import {
ChevronDown,
ChevronLeft,
ChevronRight,
Download,
FileDown,
FileSpreadsheet,
LayoutDashboard,
@@ -268,7 +267,7 @@ const UserListSearchControls = React.memo(function UserListSearchControls({
});
function UserListPage() {
const navigate = useNavigate();
const _navigate = useNavigate();
const [page, setPage] = React.useState(1);
const [search, setSearch] = React.useState("");
const [selectedCompany, setSelectedCompany] = React.useState<string>("");
@@ -563,7 +562,7 @@ function UserListPage() {
},
});
const handleApplyBulkStatus = () => {
const _handleApplyBulkStatus = () => {
if (selectedUserIds.length === 0 || !selectedBulkStatus) return;
bulkUpdateMutation.mutate({
userIds: selectedUserIds,
@@ -571,7 +570,7 @@ function UserListPage() {
});
};
const handleApplyBulkPermission = () => {
const _handleApplyBulkPermission = () => {
if (selectedUserIds.length === 0 || !selectedBulkPermission) return;
bulkUpdateMutation.mutate({
userIds: selectedUserIds,
@@ -594,7 +593,7 @@ function UserListPage() {
}
};
const handleDelete = (userId: string, userName: string) => {
const _handleDelete = (userId: string, userName: string) => {
if (
!window.confirm(
t(

View File

@@ -19,8 +19,6 @@ import {
bulkUpdateUsers,
fetchAllTenants,
fetchGroups,
type GroupSummary,
type TenantSummary,
type UserSummary,
} from "../../../lib/adminApi";
import { t } from "../../../lib/i18n";
@@ -49,7 +47,7 @@ export function UserBulkMoveGroupModal({
const [searchTerm, setSearchTerm] = React.useState("");
const [acknowledgeWarning, setAcknowledgeWarning] = React.useState(false);
const queryClient = useQueryClient();
const _queryClient = useQueryClient();
const { data: tenantsData } = useQuery({
queryKey: ["tenants", "all"],

View File

@@ -102,7 +102,7 @@ export function isHanmacFamilyTenant<T extends TenantFilterTarget>(
tenants: T[],
hanmacFamilyTenantId?: string,
) {
if (!tenant || !tenant.id) return false;
if (!tenant?.id) return false;
const rootTenantId = resolveHanmacFamilyTenantId(
tenants,

View File

@@ -24,7 +24,7 @@ export function buildTenantFullTree(
});
}
const visitedDuringBuild = new Set<string>();
const _visitedDuringBuild = new Set<string>();
// Build initial children relations and prevent simple cycles
for (const t of allTenants) {
if (t.parentId && t.parentId !== t.id) {

View File

@@ -258,7 +258,7 @@ test.describe("Tenants Management", () => {
page,
}) => {
await page.setViewportSize({ width: 900, height: 700 });
let requestCount = 0;
let _requestCount = 0;
await page.route("**/api/v1/admin/tenants**", async (route) => {
if (route.request().method() !== "GET") {
@@ -266,7 +266,7 @@ test.describe("Tenants Management", () => {
}
const url = new URL(route.request().url());
const cursor = url.searchParams.get("cursor");
requestCount += 1;
_requestCount += 1;
if (!cursor) {
return route.fulfill({

View File

@@ -5,7 +5,9 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
await page.addInitScript(() => {
window.localStorage.setItem("locale", "ko");
window.localStorage.setItem("admin_session", "fake-token");
(window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean })._IS_TEST_MODE = true;
(
window as Window & typeof globalThis & { _IS_TEST_MODE?: boolean }
)._IS_TEST_MODE = true;
const authData = {
access_token: "fake-token",
@@ -13,12 +15,20 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
profile: { sub: "admin-user", name: "Admin", role: "super_admin" },
expires_at: Math.floor(Date.now() / 1000) + 36000,
};
window.localStorage.setItem("oidc.user:http://localhost:5000/oidc:adminfront", JSON.stringify(authData));
window.localStorage.setItem(
"oidc.user:http://localhost:5000/oidc:adminfront",
JSON.stringify(authData),
);
});
await page.route("**/api/v1/user/me", async (route) => {
return route.fulfill({
json: { id: "admin-user", name: "Admin", role: "super_admin", manageableTenants: [] },
json: {
id: "admin-user",
name: "Admin",
role: "super_admin",
manageableTenants: [],
},
headers: { "Access-Control-Allow-Origin": "*" },
});
});
@@ -31,7 +41,7 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
});
await page.route("**/api/v1/admin/users*", async (route) => {
if(route.request().url().includes("/bulk")) {
if (route.request().url().includes("/bulk")) {
return route.continue();
}
return route.fulfill({
@@ -45,14 +55,20 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
});
});
test("should parse secondary_emails and send to backend", async ({ page }) => {
test("should parse secondary_emails and send to backend", async ({
page,
}) => {
let bulkPayload: any = null;
await page.route("**/api/v1/admin/users/bulk", async (route) => {
if (route.request().method() === "POST") {
bulkPayload = route.request().postDataJSON();
return route.fulfill({
json: { results: [{ email: "test@example.com", success: true, userId: "u-1" }] },
json: {
results: [
{ email: "test@example.com", success: true, userId: "u-1" },
],
},
headers: { "Access-Control-Allow-Origin": "*" },
});
}
@@ -60,21 +76,26 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
});
await page.goto("/users");
await expect(page.getByTestId("page-title")).toContainText(/사용자|Users/i, { timeout: 20000 });
await expect(page.getByTestId("page-title")).toContainText(
/사용자|Users/i,
{ timeout: 20000 },
);
await page.getByTestId("user-data-mgmt-btn").click();
await page.getByRole("menuitem", { name: /일괄 임포트|일괄 등록|Bulk Import/i }).click();
await page
.getByRole("menuitem", { name: /일괄 임포트|일괄 등록|Bulk Import/i })
.click();
// Create a mock CSV with secondary_emails
const csvContent = `email,sub_email,name,phone,role,tenant_slug\ntest@example.com,sub1@test.com;sub2@test.com,홍길동,010-1234-5678,user,tenant-slug`;
const fileChooserPromise = page.waitForEvent('filechooser');
const fileChooserPromise = page.waitForEvent("filechooser");
await page.getByText(/파일 선택|Change file|Select file/i).click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles({
name: 'users_with_secondary.csv',
mimeType: 'text/csv',
name: "users_with_secondary.csv",
mimeType: "text/csv",
buffer: Buffer.from(csvContent),
});
@@ -87,9 +108,9 @@ test.describe("Users Bulk Upload Secondary Emails", () => {
expect(bulkPayload).not.toBeNull();
expect(bulkPayload.users).toHaveLength(1);
// The most important check - does it parse to the metadata
expect(bulkPayload.users[0].metadata.sub_email).toContain("sub1@test.com");
expect(bulkPayload.users[0].metadata.sub_email).toContain("sub2@test.com");
});
});
});