Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/agent/web-routes-and-features.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ App Router 기준 (`packages/web/app/`). 작업 시 이 표와 실제 `app/` 트
| `/admin/seed/post-spots` | 시드 포스트 스팟 |
| `/admin/entities/artists` | 아티스트 관리 (CRUD, paginated, searchable) |
| `/admin/entities/brands` | 브랜드 관리 (CRUD) |
| `/admin/entities/group-members` | 그룹 멤버 관리 |
| `/admin/entities/group-members` | 그룹 멤버 관리 — group별 artist membership 조회·추가·수정·삭제 |
| `/admin/raw-post-sources` | 수집 소스 등록/관리 (Pinterest 등 — #327) |
| `/admin/raw-posts` | **검증 큐** (#333) — assets 의 raw_posts 를 status 탭(COMPLETED/IN_PROGRESS/ERROR/VERIFIED) 으로 필터링, "검증" 버튼으로 prod posts 반영 |
| `/admin/data-pipeline/instagram-accounts` | Instagram tagged account enrichment queue, Gemini grounding quota, scheduler controls, manual entity review. See [`docs/database/entity-enrichment-pipeline.md`](../database/entity-enrichment-pipeline.md). |
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
/**
* @vitest-environment jsdom
*
* GroupMembersPage empty / loading / error state tests.
*
* Group members has no search/status filter, so isEmpty is simply:
* not loading AND not error AND no rows.
* GroupMembersPage group list / member table state tests.
*/
import React from "react";
import { describe, test, expect, vi, beforeEach } from "vitest";
Expand All @@ -19,44 +16,87 @@ vi.mock("next/navigation", () => ({

// --- Mock the data hooks ---
const useGroupMemberListMock = vi.fn();
const mutateMock = vi.fn();

vi.mock("@/lib/api/admin/entities", () => ({
useGroupMemberList: (...args: unknown[]) => useGroupMemberListMock(...args),
useGroupMemberArtistSearch: () => ({ data: { data: [] }, isLoading: false }),
useCreateGroupMember: () => ({ mutate: mutateMock, isPending: false }),
useUpdateGroupMember: () => ({ mutate: mutateMock, isPending: false }),
useDeleteGroupMember: () => ({ mutate: mutateMock, isPending: false }),
}));

import GroupMembersPage from "../page";

beforeEach(() => {
useGroupMemberListMock.mockReset();
mutateMock.mockReset();
});

describe("GroupMembersPage — empty state", () => {
test("renders AdminEmptyState when no data", () => {
useGroupMemberListMock.mockReturnValue({
data: { data: [], pagination: undefined },
data: {
data: [],
groups: [],
selected_group: null,
pagination: undefined,
},
isLoading: false,
isError: false,
});

render(<GroupMembersPage />);

expect(screen.getByText("No group members")).toBeInTheDocument();
expect(screen.getByText("No groups")).toBeInTheDocument();
expect(
screen.getByText(
"Artist–group relationships will appear here once they are seeded."
"Groups created by entity enrichment or manual admin actions will appear here."
)
).toBeInTheDocument();
});

test("renders table when data has rows", () => {
test("renders selected group and member rows", () => {
useGroupMemberListMock.mockReturnValue({
data: {
groups: [
{
id: "group-uuid-5678",
name_en: "BLACKPINK",
name_ko: "블랙핑크",
profile_image_url: null,
primary_instagram_account_id: null,
primary_instagram_account: null,
member_count: 1,
total_member_count: 1,
metadata: null,
},
],
selected_group: {
id: "group-uuid-5678",
name_en: "BLACKPINK",
name_ko: "블랙핑크",
profile_image_url: null,
primary_instagram_account_id: null,
primary_instagram_account: null,
member_count: 1,
total_member_count: 1,
metadata: null,
},
data: [
{
artist_id: "artist-uuid-1234",
group_id: "group-uuid-5678",
is_active: true,
metadata: null,
artist: {
id: "artist-uuid-1234",
name_en: "Jennie",
name_ko: "제니",
profile_image_url: null,
primary_instagram_account_id: null,
primary_instagram_account: { username: "jennierubyjane" },
},
},
],
pagination: {
Expand All @@ -72,9 +112,10 @@ describe("GroupMembersPage — empty state", () => {

render(<GroupMembersPage />);

expect(screen.queryByText("No group members")).not.toBeInTheDocument();
// row data is rendered (truncated artist_id prefix)
expect(screen.getByText("artist-u…")).toBeInTheDocument();
expect(screen.queryByText("No groups")).not.toBeInTheDocument();
expect(screen.getAllByText("BLACKPINK").length).toBeGreaterThan(0);
expect(screen.getByText("Jennie")).toBeInTheDocument();
expect(screen.getByText("@jennierubyjane")).toBeInTheDocument();
});

test("renders error state when fetch fails", () => {
Expand All @@ -87,10 +128,8 @@ describe("GroupMembersPage — empty state", () => {
render(<GroupMembersPage />);

expect(
screen.getByText(
"Failed to load group members. Please try refreshing the page."
)
screen.getByText("Failed to load groups. Please try refreshing the page.")
).toBeInTheDocument();
expect(screen.queryByText("No group members")).not.toBeInTheDocument();
expect(screen.queryByText("No groups")).not.toBeInTheDocument();
});
});
Loading
Loading