15 changed files with 460 additions and 274 deletions
@ -1,88 +1,91 @@ |
|||||
import { describe, it, expect, vi, beforeEach } from 'vitest'; |
// deno-lint-ignore-file
|
||||
import { render, screen, fireEvent } from '@testing-library/react'; |
import { render, screen, fireEvent } from "@testing-library/react"; |
||||
import { UnsafeRolesDialog } from '@components/Dialog/UnsafeRolesDialog/UnsafeRolesDialog.tsx'; |
import { describe, it, expect, vi } from "vitest"; |
||||
import { useUnsafeRoles } from '@components/Dialog/UnsafeRolesDialog/useUnsafeRoles.ts'; |
import { UnsafeRolesDialog } from "@components/Dialog/UnsafeRolesDialog/UnsafeRolesDialog.tsx"; |
||||
|
import { eventBus } from "@core/utils/eventBus.ts"; |
||||
vi.mock('@components/Dialog/UnsafeRolesDialog/useUnsafeRoles', () => ({ |
import { DeviceWrapper } from "@app/DeviceWrapper.tsx"; |
||||
useUnsafeRoles: vi.fn() |
|
||||
})); |
describe("UnsafeRolesDialog", () => { |
||||
|
const mockDevice = { |
||||
describe('UnsafeRolesDialog', () => { |
setDialogOpen: vi.fn(), |
||||
const getConfirmStateMock = vi.fn(); |
}; |
||||
const toggleConfirmStateMock = vi.fn(); |
|
||||
const handleCloseDialogMock = vi.fn(); |
const renderWithDeviceContext = (ui: any) => { |
||||
const onOpenChangeMock = vi.fn(); |
return render( |
||||
|
<DeviceWrapper device={mockDevice}> |
||||
beforeEach(() => { |
{ui} |
||||
vi.resetAllMocks(); |
</DeviceWrapper> |
||||
|
); |
||||
getConfirmStateMock.mockReturnValue(false); |
}; |
||||
|
|
||||
(useUnsafeRoles as any).mockReturnValue({ |
it("renders the dialog when open is true", () => { |
||||
getConfirmState: getConfirmStateMock, |
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
toggleConfirmState: toggleConfirmStateMock, |
|
||||
handleCloseDialog: handleCloseDialogMock |
const dialog = screen.getByRole('dialog'); |
||||
}); |
expect(dialog).toBeInTheDocument(); |
||||
|
|
||||
|
expect(screen.getByText(/I have read the/i)).toBeInTheDocument(); |
||||
|
expect(screen.getByText(/understand the implications/i)).toBeInTheDocument(); |
||||
|
|
||||
|
const links = screen.getAllByRole('link'); |
||||
|
expect(links).toHaveLength(2); |
||||
|
expect(links[0]).toHaveTextContent('Device Role Documentation'); |
||||
|
expect(links[1]).toHaveTextContent('Choosing The Right Device Role'); |
||||
}); |
}); |
||||
|
|
||||
it('should not render when open is false', () => { |
it("displays the correct links", () => { |
||||
render(<UnsafeRolesDialog open={false} onOpenChange={onOpenChangeMock} />); |
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
|
|
||||
expect(screen.queryByTestId('dialog')).not.toBeInTheDocument(); |
const docLink = screen.getByRole("link", { name: /Device Role Documentation/i }); |
||||
}); |
const blogLink = screen.getByRole("link", { name: /Choosing The Right Device Role/i }); |
||||
|
|
||||
it('should render when open is true', () => { |
expect(docLink).toHaveAttribute("href", "https://meshtastic.org/docs/configuration/radio/device/"); |
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
expect(blogLink).toHaveAttribute("href", "https://meshtastic.org/blog/choosing-the-right-device-role/"); |
||||
|
|
||||
expect(screen.getByRole('dialog')).toBeInTheDocument(); |
|
||||
expect(screen.getByRole('heading')).toBeInTheDocument(); |
|
||||
expect(screen.getByText('Are you sure?')).toBeInTheDocument(); |
|
||||
expect(screen.getAllByRole('link')).length(2); |
|
||||
expect(screen.getByRole('checkbox')).toBeInTheDocument(); |
|
||||
expect(screen.getByText('Yes, I know what I\'m doing')).toBeInTheDocument(); |
|
||||
expect(screen.getByRole('button', { name: /dismiss/i })).toBeInTheDocument(); |
|
||||
expect(screen.getByRole('button', { name: /confirm/i })).toBeInTheDocument(); |
|
||||
}); |
}); |
||||
|
|
||||
it('should have disabled confirm button when checkbox is unchecked', () => { |
it("does not allow confirmation until checkbox is checked", () => { |
||||
getConfirmStateMock.mockReturnValue(false); |
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
|
|
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
|
||||
|
|
||||
expect(screen.getByRole('button', { name: /confirm/i })).toBeDisabled(); |
const confirmButton = screen.getByRole("button", { name: /confirm/i }); |
||||
}); |
|
||||
|
|
||||
it('should have enabled confirm button when checkbox is checked', () => { |
expect(confirmButton).toBeDisabled(); |
||||
getConfirmStateMock.mockReturnValue(true); |
|
||||
|
|
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
const checkbox = screen.getByRole("checkbox"); |
||||
|
fireEvent.click(checkbox); |
||||
|
|
||||
expect(screen.getByRole('button', { name: /confirm/i })).not.toBeDisabled(); |
expect(confirmButton).toBeEnabled(); |
||||
}); |
}); |
||||
|
|
||||
it('should call toggleConfirmState when checkbox is clicked', () => { |
it("emits the correct event when closing via close button", () => { |
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
const eventSpy = vi.spyOn(eventBus, "emit"); |
||||
|
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
|
|
||||
fireEvent.click(screen.getByRole('checkbox')); |
const dismissButton = screen.getByRole("button", { name: /close/i }); |
||||
|
fireEvent.click(dismissButton); |
||||
|
|
||||
expect(toggleConfirmStateMock).toHaveBeenCalledTimes(1); |
expect(eventSpy).toHaveBeenCalledWith("dialog:unsafeRoles", { action: "dismiss" }); |
||||
}); |
}); |
||||
|
|
||||
it('should call handleCloseDialog with "dismiss" when dismiss button is clicked', () => { |
it("emits the correct event when dismissing", () => { |
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
const eventSpy = vi.spyOn(eventBus, "emit"); |
||||
|
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
|
|
||||
fireEvent.click(screen.getByRole('button', { name: /dismiss/i })); |
const dismissButton = screen.getByRole("button", { name: /dismiss/i }); |
||||
|
fireEvent.click(dismissButton); |
||||
|
|
||||
expect(handleCloseDialogMock).toHaveBeenCalledWith('dismiss'); |
expect(eventSpy).toHaveBeenCalledWith("dialog:unsafeRoles", { action: "dismiss" }); |
||||
}); |
}); |
||||
|
|
||||
it('should call handleCloseDialog with "confirm" when confirm button is clicked', () => { |
it("emits the correct event when confirming", () => { |
||||
getConfirmStateMock.mockReturnValue(true); |
const eventSpy = vi.spyOn(eventBus, "emit"); |
||||
|
renderWithDeviceContext(<UnsafeRolesDialog open={true} onOpenChange={vi.fn()} />); |
||||
|
|
||||
render(<UnsafeRolesDialog open={true} onOpenChange={onOpenChangeMock} />); |
const checkbox = screen.getByRole("checkbox"); |
||||
|
const confirmButton = screen.getByRole("button", { name: /confirm/i }); |
||||
|
|
||||
fireEvent.click(screen.getByRole('button', { name: /confirm/i })); |
fireEvent.click(checkbox); |
||||
|
fireEvent.click(confirmButton); |
||||
|
|
||||
expect(handleCloseDialogMock).toHaveBeenCalledWith("confirm"); |
expect(eventSpy).toHaveBeenCalledWith("dialog:unsafeRoles", { action: "confirm" }); |
||||
}); |
}); |
||||
}); |
}); |
||||
|
|||||
@ -1,102 +0,0 @@ |
|||||
import { describe, it, expect, vi, beforeEach } from 'vitest'; |
|
||||
import { renderHook, act } from '@testing-library/react'; |
|
||||
import { useUnsafeRoles } from './useUnsafeRoles.ts'; |
|
||||
import { useDevice } from '@core/stores/deviceStore.ts'; |
|
||||
import useLocalStorage from '@core/hooks/useLocalStorage.ts'; |
|
||||
|
|
||||
vi.mock('@core/stores/deviceStore', () => ({ |
|
||||
useDevice: vi.fn() |
|
||||
})); |
|
||||
|
|
||||
vi.mock('@core/hooks/useLocalStorage', () => { |
|
||||
return { |
|
||||
default: vi.fn() |
|
||||
}; |
|
||||
}); |
|
||||
|
|
||||
describe('useUnsafeRoles', () => { |
|
||||
const setDialogOpenMock = vi.fn(); |
|
||||
const setAgreedToUnsafeRolesMock = vi.fn(); |
|
||||
|
|
||||
beforeEach(() => { |
|
||||
vi.resetAllMocks(); |
|
||||
|
|
||||
(useDevice as any).mockReturnValue({ |
|
||||
setDialogOpen: setDialogOpenMock |
|
||||
}); |
|
||||
|
|
||||
(useLocalStorage as any).mockReturnValue([ |
|
||||
false, |
|
||||
setAgreedToUnsafeRolesMock |
|
||||
]); |
|
||||
}); |
|
||||
|
|
||||
it('should initialize with correct default values', () => { |
|
||||
const { result } = renderHook(() => useUnsafeRoles()); |
|
||||
|
|
||||
expect(result.current.agreedToUnSafeRoles).toBe(false); |
|
||||
expect(result.current.getConfirmState()).toBe(false); |
|
||||
}); |
|
||||
|
|
||||
it('should toggle confirm state correctly', () => { |
|
||||
const { result } = renderHook(() => useUnsafeRoles()); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.toggleConfirmState(); |
|
||||
}); |
|
||||
|
|
||||
expect(result.current.getConfirmState()).toBe(true); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.toggleConfirmState(); |
|
||||
}); |
|
||||
|
|
||||
expect(result.current.getConfirmState()).toBe(false); |
|
||||
}); |
|
||||
|
|
||||
it('should handle dialog close with dismiss state', () => { |
|
||||
const { result } = renderHook(() => useUnsafeRoles()); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.handleCloseDialog('dismiss'); |
|
||||
}); |
|
||||
|
|
||||
expect(setAgreedToUnsafeRolesMock).toHaveBeenCalledWith(false); |
|
||||
expect(setDialogOpenMock).toHaveBeenCalledWith('unsafeRoles', false); |
|
||||
}); |
|
||||
|
|
||||
it('should handle dialog close with confirm state', () => { |
|
||||
const { result } = renderHook(() => useUnsafeRoles()); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.handleCloseDialog('confirm'); |
|
||||
}); |
|
||||
|
|
||||
expect(setAgreedToUnsafeRolesMock).toHaveBeenCalledWith(true); |
|
||||
expect(setDialogOpenMock).toHaveBeenCalledWith('unsafeRoles', false); |
|
||||
}); |
|
||||
|
|
||||
it('should maintain state consistency across multiple operations', () => { |
|
||||
const { result } = renderHook(() => useUnsafeRoles()); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.toggleConfirmState(); |
|
||||
}); |
|
||||
expect(result.current.getConfirmState()).toBe(true); |
|
||||
|
|
||||
act(() => { |
|
||||
result.current.handleCloseDialog('confirm'); |
|
||||
}); |
|
||||
|
|
||||
expect(result.current.getConfirmState()).toBe(false); |
|
||||
expect(setAgreedToUnsafeRolesMock).toHaveBeenCalledWith(true); |
|
||||
|
|
||||
(useLocalStorage as any).mockReturnValue([ |
|
||||
true, |
|
||||
setAgreedToUnsafeRolesMock |
|
||||
]); |
|
||||
|
|
||||
const { result: newResult } = renderHook(() => useUnsafeRoles()); |
|
||||
expect(newResult.current.agreedToUnSafeRoles).toBe(true); |
|
||||
}); |
|
||||
}); |
|
||||
@ -1,40 +0,0 @@ |
|||||
import { useState, useCallback } from "react"; |
|
||||
import { useDevice } from "@core/stores/deviceStore.ts"; |
|
||||
import useLocalStorage from "@core/hooks/useLocalStorage.ts"; |
|
||||
|
|
||||
export const useUnsafeRoles = () => { |
|
||||
const [agreedToUnSafeRoles, setAgreedToUnsafeRoles] = useLocalStorage("agreeToUnsafeRole", false); |
|
||||
const [_confirmState, _setConfirmState] = useState(false); |
|
||||
const { setDialogOpen } = useDevice(); |
|
||||
|
|
||||
const toggleConfirmState = useCallback(() => { |
|
||||
setConfirmState(!_confirmState); |
|
||||
}, [_confirmState]); |
|
||||
|
|
||||
const setConfirmState = useCallback((state: boolean) => { |
|
||||
_setConfirmState(state); |
|
||||
}, [_setConfirmState]); |
|
||||
|
|
||||
const getConfirmState = useCallback(() => { |
|
||||
return _confirmState; |
|
||||
}, [_confirmState]); |
|
||||
|
|
||||
const handleCloseDialog = useCallback((closeState: "dismiss" | "confirm") => { |
|
||||
if (closeState === "dismiss") { |
|
||||
setAgreedToUnsafeRoles(false); |
|
||||
setConfirmState(false); |
|
||||
} |
|
||||
if (closeState === "confirm") { |
|
||||
setAgreedToUnsafeRoles(true); |
|
||||
setConfirmState(false); |
|
||||
} |
|
||||
setDialogOpen("unsafeRoles", false); |
|
||||
}, [setDialogOpen, setAgreedToUnsafeRoles]); |
|
||||
|
|
||||
return { |
|
||||
getConfirmState, |
|
||||
toggleConfirmState, |
|
||||
handleCloseDialog, |
|
||||
agreedToUnSafeRoles |
|
||||
}; |
|
||||
}; |
|
||||
@ -0,0 +1,117 @@ |
|||||
|
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from 'vitest'; |
||||
|
import { renderHook } from '@testing-library/react'; |
||||
|
import { useUnsafeRolesDialog, UNSAFE_ROLES } from "@components/Dialog/UnsafeRolesDialog/useUnsafeRolesDialog"; |
||||
|
import { eventBus } from "@core/utils/eventBus"; |
||||
|
|
||||
|
vi.mock('@core/utils/eventBus', () => ({ |
||||
|
eventBus: { |
||||
|
on: vi.fn(), |
||||
|
off: vi.fn(), |
||||
|
emit: vi.fn(), |
||||
|
}, |
||||
|
})); |
||||
|
|
||||
|
const mockDevice = { |
||||
|
setDialogOpen: vi.fn(), |
||||
|
}; |
||||
|
|
||||
|
vi.mock('@core/stores/deviceStore', () => ({ |
||||
|
useDevice: () => ({ |
||||
|
setDialogOpen: mockDevice.setDialogOpen, |
||||
|
}), |
||||
|
})); |
||||
|
|
||||
|
describe('useUnsafeRolesDialog', () => { |
||||
|
beforeEach(() => { |
||||
|
vi.resetAllMocks(); |
||||
|
}); |
||||
|
|
||||
|
afterEach(() => { |
||||
|
vi.clearAllMocks(); |
||||
|
}); |
||||
|
|
||||
|
const renderUnsafeRolesHook = () => { |
||||
|
return renderHook(() => useUnsafeRolesDialog()); |
||||
|
}; |
||||
|
|
||||
|
describe('handleCloseDialog', () => { |
||||
|
it('should call setDialogOpen with correct parameters when dialog is closed', () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
|
||||
|
result.current.handleCloseDialog(); |
||||
|
|
||||
|
expect(mockDevice.setDialogOpen).toHaveBeenCalledWith('unsafeRoles', false); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('validateRoleSelection', () => { |
||||
|
it('should resolve with true for safe roles without opening dialog', async () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
const safeRole = 'SAFE_ROLE'; |
||||
|
|
||||
|
const validationResult = await result.current.validateRoleSelection(safeRole); |
||||
|
|
||||
|
expect(validationResult).toBe(true); |
||||
|
expect(mockDevice.setDialogOpen).not.toHaveBeenCalled(); |
||||
|
}); |
||||
|
|
||||
|
it('should open dialog for unsafe roles and resolve with true when confirmed', async () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
|
||||
|
const validationPromise = result.current.validateRoleSelection(UNSAFE_ROLES[0]); |
||||
|
|
||||
|
expect(mockDevice.setDialogOpen).toHaveBeenCalledWith('unsafeRoles', true); |
||||
|
expect(eventBus.on).toHaveBeenCalledWith('dialog:unsafeRoles', expect.any(Function)); |
||||
|
|
||||
|
const onHandler = (eventBus.on as Mock).mock.calls[0][1]; |
||||
|
onHandler({ action: 'confirm' }); |
||||
|
const validationResult = await validationPromise; |
||||
|
|
||||
|
expect(validationResult).toBe(true); |
||||
|
expect(eventBus.off).toHaveBeenCalledWith('dialog:unsafeRoles', onHandler); |
||||
|
}); |
||||
|
|
||||
|
it('should resolve with false when user dismisses the dialog', async () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
const validationPromise = result.current.validateRoleSelection(UNSAFE_ROLES[0]); |
||||
|
const onHandler = (eventBus.on as Mock).mock.calls[0][1]; |
||||
|
onHandler({ action: 'dismiss' }); |
||||
|
|
||||
|
const validationResult = await validationPromise; |
||||
|
expect(validationResult).toBe(false); |
||||
|
expect(eventBus.off).toHaveBeenCalledWith('dialog:unsafeRoles', onHandler); |
||||
|
}); |
||||
|
|
||||
|
it('should clean up event listener after response', async () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
|
||||
|
const validationPromise = result.current.validateRoleSelection(UNSAFE_ROLES[1]); |
||||
|
const onHandler = (eventBus.on as Mock).mock.calls[0][1]; |
||||
|
|
||||
|
onHandler({ action: 'confirm' }); |
||||
|
await validationPromise; |
||||
|
|
||||
|
expect(eventBus.off).toHaveBeenCalledWith('dialog:unsafeRoles', onHandler); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should work with all unsafe roles', async () => { |
||||
|
const { result } = renderUnsafeRolesHook(); |
||||
|
|
||||
|
for (const unsafeRole of UNSAFE_ROLES) { |
||||
|
mockDevice.setDialogOpen.mockClear(); |
||||
|
(eventBus.on as Mock).mockClear(); |
||||
|
|
||||
|
const validationPromise = result.current.validateRoleSelection(unsafeRole); |
||||
|
|
||||
|
expect(mockDevice.setDialogOpen).toHaveBeenCalledWith('unsafeRoles', true); |
||||
|
|
||||
|
const onHandler = (eventBus.on as Mock).mock.calls[0][1]; |
||||
|
onHandler({ action: 'confirm' }); |
||||
|
|
||||
|
const validationResult = await validationPromise; |
||||
|
|
||||
|
expect(validationResult).toBe(true); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,39 @@ |
|||||
|
import { useCallback } from "react"; |
||||
|
import { eventBus } from "@core/utils/eventBus.ts"; |
||||
|
import { useDevice } from "@core/stores/deviceStore.ts"; |
||||
|
|
||||
|
export const UNSAFE_ROLES = ["ROUTER", "REPEATER"]; |
||||
|
export type UnsafeRole = typeof UNSAFE_ROLES[number]; |
||||
|
|
||||
|
export const useUnsafeRolesDialog = () => { |
||||
|
const { setDialogOpen } = useDevice(); |
||||
|
|
||||
|
const handleCloseDialog = useCallback(() => { |
||||
|
setDialogOpen("unsafeRoles", false); |
||||
|
}, [setDialogOpen]); |
||||
|
|
||||
|
const validateRoleSelection = useCallback( |
||||
|
(newRoleKey: string): Promise<boolean> => { |
||||
|
if (!UNSAFE_ROLES.includes(newRoleKey as UnsafeRole)) { |
||||
|
return Promise.resolve(true); |
||||
|
} |
||||
|
|
||||
|
setDialogOpen("unsafeRoles", true); |
||||
|
|
||||
|
return new Promise((resolve) => { |
||||
|
const handleResponse = ({ action }: { action: "confirm" | "dismiss" }) => { |
||||
|
eventBus.off("dialog:unsafeRoles", handleResponse); |
||||
|
resolve(action === "confirm"); |
||||
|
}; |
||||
|
|
||||
|
eventBus.on("dialog:unsafeRoles", handleResponse); |
||||
|
}); |
||||
|
}, |
||||
|
[setDialogOpen] |
||||
|
); |
||||
|
|
||||
|
return { |
||||
|
handleCloseDialog, |
||||
|
validateRoleSelection, |
||||
|
}; |
||||
|
}; |
||||
@ -0,0 +1,46 @@ |
|||||
|
import { describe, it, expect, vi, beforeEach } from 'vitest'; |
||||
|
import { render } from '@testing-library/react'; |
||||
|
import { Device } from '@components/PageComponents/Config/Device/index.tsx'; |
||||
|
import { useDevice } from '@core/stores/deviceStore.ts'; |
||||
|
import { useUnsafeRolesDialog } from '@components/Dialog/UnsafeRolesDialog/useUnsafeRolesDialog.ts'; |
||||
|
|
||||
|
// Mock dependencies
|
||||
|
vi.mock('@core/stores/deviceStore', () => ({ |
||||
|
useDevice: vi.fn() |
||||
|
})); |
||||
|
|
||||
|
vi.mock('@components/Dialog/UnsafeRolesDialog/useUnsafeRolesDialog', () => ({ |
||||
|
useUnsafeRolesDialog: vi.fn() |
||||
|
})); |
||||
|
|
||||
|
describe('Device component with UnsafeRolesDialog', () => { |
||||
|
const setWorkingConfigMock = vi.fn(); |
||||
|
const validateRoleDialogResultMock = vi.fn(); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
vi.resetAllMocks(); |
||||
|
|
||||
|
// Mock useDevice hook
|
||||
|
(useDevice as any).mockReturnValue({ |
||||
|
config: { |
||||
|
device: {} |
||||
|
}, |
||||
|
setWorkingConfig: setWorkingConfigMock |
||||
|
}); |
||||
|
|
||||
|
// Mock useUnsafeRolesDialog hook
|
||||
|
(useUnsafeRolesDialog as any).mockReturnValue({ |
||||
|
validateRoleDialogResult: validateRoleDialogResultMock |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should use the validateRoleDialogResult from the hook', () => { |
||||
|
render(<Device />); |
||||
|
|
||||
|
// Verify the hook was called
|
||||
|
expect(useUnsafeRolesDialog).toHaveBeenCalled(); |
||||
|
|
||||
|
// Verify the form is using the validation function from the hook
|
||||
|
expect(setWorkingConfigMock).not.toHaveBeenCalled(); // Just ensure the component rendered without errors
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,71 @@ |
|||||
|
import { describe, it, expect, vi, beforeEach } from "vitest"; |
||||
|
import { eventBus } from "@core/utils/eventBus.ts"; |
||||
|
|
||||
|
describe("EventBus", () => { |
||||
|
beforeEach(() => { |
||||
|
// Reset event listeners before each test
|
||||
|
(eventBus as any).listeners = {}; |
||||
|
}); |
||||
|
|
||||
|
it("should register an event listener and trigger it on emit", () => { |
||||
|
const mockCallback = vi.fn(); |
||||
|
|
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "confirm" }); |
||||
|
|
||||
|
expect(mockCallback).toHaveBeenCalledWith({ action: "confirm" }); |
||||
|
}); |
||||
|
|
||||
|
it("should remove an event listener with off", () => { |
||||
|
const mockCallback = vi.fn(); |
||||
|
|
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback); |
||||
|
eventBus.off("dialog:unsafeRoles", mockCallback); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "dismiss" }); |
||||
|
|
||||
|
expect(mockCallback).not.toHaveBeenCalled(); |
||||
|
}); |
||||
|
|
||||
|
it("should return an unsubscribe function from on", () => { |
||||
|
const mockCallback = vi.fn(); |
||||
|
const unsubscribe = eventBus.on("dialog:unsafeRoles", mockCallback); |
||||
|
|
||||
|
unsubscribe(); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "confirm" }); |
||||
|
|
||||
|
expect(mockCallback).not.toHaveBeenCalled(); |
||||
|
}); |
||||
|
|
||||
|
it("should allow multiple listeners for the same event", () => { |
||||
|
const mockCallback1 = vi.fn(); |
||||
|
const mockCallback2 = vi.fn(); |
||||
|
|
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback1); |
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback2); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "confirm" }); |
||||
|
|
||||
|
expect(mockCallback1).toHaveBeenCalledWith({ action: "confirm" }); |
||||
|
expect(mockCallback2).toHaveBeenCalledWith({ action: "confirm" }); |
||||
|
}); |
||||
|
|
||||
|
it("should only remove the specific listener when off is called", () => { |
||||
|
const mockCallback1 = vi.fn(); |
||||
|
const mockCallback2 = vi.fn(); |
||||
|
|
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback1); |
||||
|
eventBus.on("dialog:unsafeRoles", mockCallback2); |
||||
|
eventBus.off("dialog:unsafeRoles", mockCallback1); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "dismiss" }); |
||||
|
|
||||
|
expect(mockCallback1).not.toHaveBeenCalled(); |
||||
|
expect(mockCallback2).toHaveBeenCalledWith({ action: "dismiss" }); |
||||
|
}); |
||||
|
|
||||
|
it("should not fail when calling off on a non-existent listener", () => { |
||||
|
const mockCallback = vi.fn(); |
||||
|
eventBus.off("dialog:unsafeRoles", mockCallback); |
||||
|
eventBus.emit("dialog:unsafeRoles", { action: "confirm" }); |
||||
|
|
||||
|
expect(mockCallback).not.toHaveBeenCalled(); // No error should occur
|
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,44 @@ |
|||||
|
export type EventMap = { |
||||
|
'dialog:unsafeRoles': { |
||||
|
action: 'confirm' | 'dismiss'; |
||||
|
}; |
||||
|
// add more events as required
|
||||
|
}; |
||||
|
|
||||
|
export type EventName = keyof EventMap; |
||||
|
export type EventCallback<T extends EventName> = (data: EventMap[T]) => void; |
||||
|
|
||||
|
class EventBus { |
||||
|
private listeners: { [K in EventName]?: Array<EventCallback<K>> } = {}; |
||||
|
|
||||
|
public on<T extends EventName>(event: T, callback: EventCallback<T>): () => void { |
||||
|
if (!this.listeners[event]) { |
||||
|
this.listeners[event] = []; |
||||
|
} |
||||
|
|
||||
|
this.listeners[event]?.push(callback as any); |
||||
|
|
||||
|
return () => { |
||||
|
this.off(event, callback); |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
public off<T extends EventName>(event: T, callback: EventCallback<T>): void { |
||||
|
if (!this.listeners[event]) return; |
||||
|
|
||||
|
const callbackIndex = this.listeners[event]?.indexOf(callback as any); |
||||
|
if (callbackIndex !== undefined && callbackIndex > -1) { |
||||
|
this.listeners[event]?.splice(callbackIndex, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public emit<T extends EventName>(event: T, data: EventMap[T]): void { |
||||
|
if (!this.listeners[event]) return; |
||||
|
|
||||
|
this.listeners[event]?.forEach(callback => { |
||||
|
callback(data); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
export const eventBus = new EventBus(); |
||||
Loading…
Reference in new issue