mirror of https://github.com/langgenius/dify.git
chore: add provider context mock (#29201)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b466d8da92
commit
0cb696b208
|
|
@ -0,0 +1,47 @@
|
|||
import { merge, noop } from 'lodash-es'
|
||||
import { defaultPlan } from '@/app/components/billing/config'
|
||||
import { baseProviderContextValue } from '@/context/provider-context'
|
||||
import type { ProviderContextState } from '@/context/provider-context'
|
||||
import type { Plan, UsagePlanInfo } from '@/app/components/billing/type'
|
||||
|
||||
export const createMockProviderContextValue = (overrides: Partial<ProviderContextState> = {}): ProviderContextState => {
|
||||
const merged = merge({}, baseProviderContextValue, overrides)
|
||||
|
||||
return {
|
||||
...merged,
|
||||
refreshModelProviders: merged.refreshModelProviders ?? noop,
|
||||
onPlanInfoChanged: merged.onPlanInfoChanged ?? noop,
|
||||
refreshLicenseLimit: merged.refreshLicenseLimit ?? noop,
|
||||
}
|
||||
}
|
||||
|
||||
export const createMockPlan = (plan: Plan): ProviderContextState =>
|
||||
createMockProviderContextValue({
|
||||
plan: merge({}, defaultPlan, {
|
||||
type: plan,
|
||||
}),
|
||||
})
|
||||
|
||||
export const createMockPlanUsage = (usage: UsagePlanInfo, ctx: Partial<ProviderContextState>): ProviderContextState =>
|
||||
createMockProviderContextValue({
|
||||
...ctx,
|
||||
plan: merge(ctx.plan, {
|
||||
usage,
|
||||
}),
|
||||
})
|
||||
|
||||
export const createMockPlanTotal = (total: UsagePlanInfo, ctx: Partial<ProviderContextState>): ProviderContextState =>
|
||||
createMockProviderContextValue({
|
||||
...ctx,
|
||||
plan: merge(ctx.plan, {
|
||||
total,
|
||||
}),
|
||||
})
|
||||
|
||||
export const createMockPlanReset = (reset: Partial<ProviderContextState['plan']['reset']>, ctx: Partial<ProviderContextState>): ProviderContextState =>
|
||||
createMockProviderContextValue({
|
||||
...ctx,
|
||||
plan: merge(ctx?.plan, {
|
||||
reset,
|
||||
}),
|
||||
})
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import { render } from '@testing-library/react'
|
||||
import type { UsagePlanInfo } from '@/app/components/billing/type'
|
||||
import { Plan } from '@/app/components/billing/type'
|
||||
import ProviderContextMock from './provider-context-mock'
|
||||
import { createMockPlan, createMockPlanReset, createMockPlanTotal, createMockPlanUsage } from '@/__mocks__/provider-context'
|
||||
|
||||
let mockPlan: Plan = Plan.sandbox
|
||||
const usage: UsagePlanInfo = {
|
||||
vectorSpace: 1,
|
||||
buildApps: 10,
|
||||
teamMembers: 1,
|
||||
annotatedResponse: 1,
|
||||
documentsUploadQuota: 0,
|
||||
apiRateLimit: 0,
|
||||
triggerEvents: 0,
|
||||
}
|
||||
|
||||
const total: UsagePlanInfo = {
|
||||
vectorSpace: 100,
|
||||
buildApps: 100,
|
||||
teamMembers: 10,
|
||||
annotatedResponse: 100,
|
||||
documentsUploadQuota: 0,
|
||||
apiRateLimit: 0,
|
||||
triggerEvents: 0,
|
||||
}
|
||||
|
||||
const reset = {
|
||||
apiRateLimit: 100,
|
||||
triggerEvents: 100,
|
||||
}
|
||||
|
||||
jest.mock('@/context/provider-context', () => ({
|
||||
useProviderContext: () => {
|
||||
const withPlan = createMockPlan(mockPlan)
|
||||
const withUsage = createMockPlanUsage(usage, withPlan)
|
||||
const withTotal = createMockPlanTotal(total, withUsage)
|
||||
const withReset = createMockPlanReset(reset, withTotal)
|
||||
console.log(JSON.stringify(withReset.plan, null, 2))
|
||||
return withReset
|
||||
},
|
||||
}))
|
||||
|
||||
const renderWithPlan = (plan: Plan) => {
|
||||
mockPlan = plan
|
||||
return render(<ProviderContextMock />)
|
||||
}
|
||||
|
||||
describe('ProviderContextMock', () => {
|
||||
beforeEach(() => {
|
||||
mockPlan = Plan.sandbox
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
it('should display sandbox plan type when mocked with sandbox plan', async () => {
|
||||
const { getByTestId } = renderWithPlan(Plan.sandbox)
|
||||
expect(getByTestId('plan-type').textContent).toBe(Plan.sandbox)
|
||||
})
|
||||
it('should display team plan type when mocked with team plan', () => {
|
||||
const { getByTestId } = renderWithPlan(Plan.team)
|
||||
expect(getByTestId('plan-type').textContent).toBe(Plan.team)
|
||||
})
|
||||
it('should provide usage info from mocked plan', () => {
|
||||
const { getByTestId } = renderWithPlan(Plan.team)
|
||||
const buildApps = getByTestId('plan-usage-build-apps').textContent
|
||||
|
||||
expect(Number(buildApps as string)).toEqual(usage.buildApps)
|
||||
})
|
||||
|
||||
it('should provide total info from mocked plan', () => {
|
||||
const { getByTestId } = renderWithPlan(Plan.team)
|
||||
const buildApps = getByTestId('plan-total-build-apps').textContent
|
||||
|
||||
expect(Number(buildApps as string)).toEqual(total.buildApps)
|
||||
})
|
||||
|
||||
it('should provide reset info from mocked plan', () => {
|
||||
const { getByTestId } = renderWithPlan(Plan.team)
|
||||
const apiRateLimit = getByTestId('plan-reset-api-rate-limit').textContent
|
||||
|
||||
expect(Number(apiRateLimit as string)).toEqual(reset.apiRateLimit)
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
const ProviderContextMock: FC = () => {
|
||||
const { plan } = useProviderContext()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div data-testid="plan-type">{plan.type}</div>
|
||||
<div data-testid="plan-usage-build-apps">{plan.usage.buildApps}</div>
|
||||
<div data-testid="plan-total-build-apps">{plan.total.buildApps}</div>
|
||||
<div data-testid="plan-reset-api-rate-limit">{plan.reset.apiRateLimit}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(ProviderContextMock)
|
||||
|
|
@ -30,7 +30,7 @@ import { noop } from 'lodash-es'
|
|||
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
|
||||
import { ZENDESK_FIELD_IDS } from '@/config'
|
||||
|
||||
type ProviderContextState = {
|
||||
export type ProviderContextState = {
|
||||
modelProviders: ModelProvider[]
|
||||
refreshModelProviders: () => void
|
||||
textGenerationModelList: Model[]
|
||||
|
|
@ -66,7 +66,8 @@ type ProviderContextState = {
|
|||
isAllowTransferWorkspace: boolean
|
||||
isAllowPublishAsCustomKnowledgePipelineTemplate: boolean
|
||||
}
|
||||
const ProviderContext = createContext<ProviderContextState>({
|
||||
|
||||
export const baseProviderContextValue: ProviderContextState = {
|
||||
modelProviders: [],
|
||||
refreshModelProviders: noop,
|
||||
textGenerationModelList: [],
|
||||
|
|
@ -96,7 +97,9 @@ const ProviderContext = createContext<ProviderContextState>({
|
|||
refreshLicenseLimit: noop,
|
||||
isAllowTransferWorkspace: false,
|
||||
isAllowPublishAsCustomKnowledgePipelineTemplate: false,
|
||||
})
|
||||
}
|
||||
|
||||
const ProviderContext = createContext<ProviderContextState>(baseProviderContextValue)
|
||||
|
||||
export const useProviderContext = () => useContext(ProviderContext)
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,8 @@ Treat component state as part of the public behavior: confirm the initial render
|
|||
- ✅ Reset shared stores (React context, Zustand, TanStack Query cache) between tests to avoid leaking state. Prefer helper factory functions over module-level singletons in specs.
|
||||
- ✅ For hooks that read from context, use `renderHook` with a custom wrapper that supplies required providers.
|
||||
|
||||
If it's need to mock some common context provider used across many components (for example, `ProviderContext`), put it in __mocks__/context(for example, `__mocks__/context/provider-context`). To dynamically control the mock behavior (for example, toggling plan type), use module-level variables to track state and change them(for example, `context/provier-context-mock.spec.tsx`).
|
||||
|
||||
### 4. Performance Optimization
|
||||
|
||||
Cover memoized callbacks or values only when they influence observable behavior—memoized children, subscription updates, expensive computations. Trigger realistic re-renders and assert the outcomes (avoided rerenders, reused results) instead of inspecting hook internals.
|
||||
|
|
|
|||
Loading…
Reference in New Issue