ProjectDropdown.test.tsx169 lines · main
1import { screen } from '@testing-library/react'
2import { beforeEach, describe, expect, it, vi } from 'vitest'
3
4import { ProjectDropdown } from './ProjectDropdown'
5import { MANAGED_BY } from '@/lib/constants/infrastructure'
6import { createMockOrganization, render } from '@/tests/helpers'
7
8const { mockSelectedOrganization, mockSelectedProject, mockSelectorProject } = vi.hoisted(() => ({
9 mockSelectedOrganization: vi.fn(),
10 mockSelectedProject: vi.fn(),
11 mockSelectorProject: vi.fn(),
12}))
13
14const mockPush = vi.hoisted(() => vi.fn())
15
16vi.mock('common', async (importOriginal) => {
17 const original = (await importOriginal()) as typeof import('common')
18 return {
19 ...original,
20 useParams: () => ({ ref: 'proj_1' }),
21 }
22})
23
24vi.mock('next/router', () => ({
25 useRouter: () => ({
26 route: '/project/[ref]/settings',
27 query: { ref: 'proj_1' },
28 push: mockPush,
29 }),
30}))
31
32vi.mock('@/lib/constants', async (importOriginal) => {
33 const original = (await importOriginal()) as typeof import('@/lib/constants')
34 return { ...original, IS_PLATFORM: true }
35})
36
37vi.mock('@/hooks/misc/useSelectedProject', () => ({
38 useSelectedProjectQuery: () => ({
39 data: mockSelectedProject(),
40 isPending: false,
41 }),
42}))
43
44vi.mock('@/data/projects/project-detail-query', () => ({
45 useProjectDetailQuery: () => ({ data: undefined, isPending: false }),
46}))
47
48vi.mock('@/hooks/misc/useSelectedOrganization', () => ({
49 useSelectedOrganizationQuery: () => ({ data: mockSelectedOrganization() }),
50}))
51
52vi.mock('@/hooks/misc/useIsFeatureEnabled', () => ({
53 useIsFeatureEnabled: () => false,
54}))
55
56vi.mock('@/components/ui/OrganizationProjectSelector', () => ({
57 OrganizationProjectSelector: ({ renderRow, renderTrigger }: any) => (
58 <div>
59 <div data-testid="project-selector-trigger">{renderTrigger?.({})}</div>
60 <div data-testid="project-selector-row">{renderRow?.(mockSelectorProject())}</div>
61 </div>
62 ),
63}))
64
65vi.mock('@/components/ui/PartnerIcon', () => ({
66 default: ({ organization }: { organization: { managed_by: string } }) =>
67 organization.managed_by === MANAGED_BY.BRIVEN ? null : (
68 <div data-testid="partner-icon" data-managed-by={organization.managed_by} />
69 ),
70}))
71
72describe('ProjectDropdown', () => {
73 beforeEach(() => {
74 vi.clearAllMocks()
75 mockSelectedOrganization.mockReturnValue(
76 createMockOrganization({
77 slug: 'briven-org',
78 managed_by: MANAGED_BY.BRIVEN,
79 })
80 )
81 mockSelectedProject.mockReturnValue({
82 ref: 'proj_1',
83 name: 'Hammer',
84 parentRef: 'proj_1',
85 parent_project_ref: undefined,
86 integration_source: 'stripe_projects',
87 })
88 mockSelectorProject.mockReturnValue({
89 ref: 'proj_1',
90 name: 'Hammer',
91 status: 'ACTIVE_HEALTHY',
92 integration_source: 'stripe_projects',
93 })
94 })
95
96 it('uses project detail integration_source for the selected pill and project row data for selector rows', () => {
97 mockSelectedOrganization.mockReturnValue(
98 createMockOrganization({
99 slug: 'stripe-org',
100 billing_partner: null,
101 integration_source: 'stripe_projects',
102 managed_by: MANAGED_BY.STRIPE_PROJECTS,
103 })
104 )
105
106 render(<ProjectDropdown />)
107
108 expect(screen.getAllByTestId('partner-icon')).toHaveLength(2)
109 })
110
111 it('does not fall back to org-level integration state when matching project data is unavailable', () => {
112 mockSelectedOrganization.mockReturnValue(
113 createMockOrganization({
114 slug: 'stripe-org',
115 billing_partner: null,
116 integration_source: 'stripe_projects',
117 managed_by: MANAGED_BY.STRIPE_PROJECTS,
118 })
119 )
120 mockSelectedProject.mockReturnValue({
121 ref: 'proj_2',
122 name: 'Mallet',
123 parentRef: 'proj_2',
124 parent_project_ref: undefined,
125 integration_source: null,
126 })
127 mockSelectorProject.mockReturnValue({
128 ref: 'proj_2',
129 name: 'Mallet',
130 status: 'ACTIVE_HEALTHY',
131 integration_source: null,
132 })
133
134 render(<ProjectDropdown />)
135
136 expect(screen.queryAllByTestId('partner-icon')).toHaveLength(0)
137 })
138
139 it('falls back to the organization-level state for partner-billed orgs', () => {
140 mockSelectedOrganization.mockReturnValue(
141 createMockOrganization({
142 slug: 'partner-org',
143 billing_partner: 'vercel_marketplace',
144 managed_by: MANAGED_BY.VERCEL_MARKETPLACE,
145 })
146 )
147 mockSelectedProject.mockReturnValue({
148 ref: 'proj_2',
149 name: 'Mallet',
150 parentRef: 'proj_2',
151 parent_project_ref: undefined,
152 integration_source: null,
153 })
154 mockSelectorProject.mockReturnValue({
155 ref: 'proj_2',
156 name: 'Mallet',
157 status: 'ACTIVE_HEALTHY',
158 integration_source: null,
159 })
160
161 render(<ProjectDropdown />)
162
163 expect(screen.getAllByTestId('partner-icon')).toHaveLength(1)
164 expect(screen.getByTestId('partner-icon')).toHaveAttribute(
165 'data-managed-by',
166 MANAGED_BY.VERCEL_MARKETPLACE
167 )
168 })
169})