committed by
GitHub
17 changed files with 1083 additions and 463 deletions
File diff suppressed because it is too large
@ -0,0 +1,152 @@ |
|||||
|
import { MessageInput } from '@components/PageComponents/Messages/MessageInput.tsx'; |
||||
|
import { useDevice } from "@core/stores/deviceStore.ts"; |
||||
|
import { vi, describe, it, expect, beforeEach, Mock } from 'vitest'; |
||||
|
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; |
||||
|
import userEvent from '@testing-library/user-event'; |
||||
|
|
||||
|
vi.mock("@core/stores/deviceStore.ts", () => ({ |
||||
|
useDevice: vi.fn(), |
||||
|
})); |
||||
|
|
||||
|
vi.mock("@core/utils/debounce.ts", () => ({ |
||||
|
debounce: (fn: () => void) => fn, |
||||
|
})); |
||||
|
|
||||
|
vi.mock("@components/UI/Button.tsx", () => ({ |
||||
|
Button: ({ children, ...props }: { children: React.ReactNode }) => <button {...props}>{children}</button> |
||||
|
})); |
||||
|
|
||||
|
vi.mock("@components/UI/Input.tsx", () => ({ |
||||
|
Input: (props: any) => <input {...props} /> |
||||
|
})); |
||||
|
|
||||
|
vi.mock("lucide-react", () => ({ |
||||
|
SendIcon: () => <div data-testid="send-icon">Send</div> |
||||
|
})); |
||||
|
|
||||
|
// TODO: getting an error with this test
|
||||
|
describe('MessageInput Component', () => { |
||||
|
const mockProps = { |
||||
|
to: "broadcast" as const, |
||||
|
channel: 0 as const, |
||||
|
maxBytes: 100, |
||||
|
}; |
||||
|
|
||||
|
const mockSetMessageDraft = vi.fn(); |
||||
|
const mockSetMessageState = vi.fn(); |
||||
|
const mockSendText = vi.fn().mockResolvedValue(123); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
vi.clearAllMocks(); |
||||
|
|
||||
|
(useDevice as Mock).mockReturnValue({ |
||||
|
connection: { |
||||
|
sendText: mockSendText, |
||||
|
}, |
||||
|
setMessageState: mockSetMessageState, |
||||
|
messageDraft: "", |
||||
|
setMessageDraft: mockSetMessageDraft, |
||||
|
hardware: { |
||||
|
myNodeNum: 1234567890, |
||||
|
}, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('renders correctly with initial state', () => { |
||||
|
render(<MessageInput {...mockProps} />); |
||||
|
|
||||
|
expect(screen.getByPlaceholderText('Enter Message')).toBeInTheDocument(); |
||||
|
expect(screen.getByTestId('send-icon')).toBeInTheDocument(); |
||||
|
|
||||
|
expect(screen.getByText('0/100')).toBeInTheDocument(); |
||||
|
}); |
||||
|
|
||||
|
it('updates local draft and byte count when typing', () => { |
||||
|
render(<MessageInput {...mockProps} />); |
||||
|
|
||||
|
const inputField = screen.getByPlaceholderText('Enter Message'); |
||||
|
fireEvent.change(inputField, { target: { value: 'Hello' } }) |
||||
|
|
||||
|
expect(screen.getByText('5/100')).toBeInTheDocument(); |
||||
|
expect(inputField).toHaveValue('Hello'); |
||||
|
expect(mockSetMessageDraft).toHaveBeenCalledWith('Hello'); |
||||
|
}); |
||||
|
|
||||
|
it.skip('does not allow input exceeding max bytes', () => { |
||||
|
render(<MessageInput {...mockProps} maxBytes={5} />); |
||||
|
|
||||
|
const inputField = screen.getByPlaceholderText('Enter Message'); |
||||
|
|
||||
|
expect(screen.getByText('0/100')).toBeInTheDocument(); |
||||
|
|
||||
|
userEvent.type(inputField, 'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis p') |
||||
|
|
||||
|
expect(screen.getByText('100/100')).toBeInTheDocument(); |
||||
|
expect(inputField).toHaveValue('Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean m'); |
||||
|
}); |
||||
|
|
||||
|
it('sends message and resets form when submitting', async () => { |
||||
|
try { |
||||
|
render(<MessageInput {...mockProps} />); |
||||
|
|
||||
|
const inputField = screen.getByPlaceholderText('Enter Message'); |
||||
|
const submitButton = screen.getByText('Send'); |
||||
|
|
||||
|
fireEvent.change(inputField, { target: { value: 'Test Message' } }); |
||||
|
fireEvent.click(submitButton); |
||||
|
|
||||
|
const form = screen.getByRole('form'); |
||||
|
fireEvent.submit(form); |
||||
|
|
||||
|
expect(mockSendText).toHaveBeenCalledWith('Test message', 'broadcast', true, 0); |
||||
|
|
||||
|
await waitFor(() => { |
||||
|
expect(mockSetMessageState).toHaveBeenCalledWith( |
||||
|
'broadcast', |
||||
|
0, |
||||
|
'broadcast', |
||||
|
1234567890, |
||||
|
123, |
||||
|
'ack' |
||||
|
); |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
expect(inputField).toHaveValue(''); |
||||
|
expect(screen.getByText('0/100')).toBeInTheDocument(); |
||||
|
expect(mockSetMessageDraft).toHaveBeenCalledWith(''); |
||||
|
} catch (e) { |
||||
|
console.error(e); |
||||
|
} |
||||
|
}); |
||||
|
it('prevents sending empty messages', () => { |
||||
|
render(<MessageInput {...mockProps} />); |
||||
|
|
||||
|
const form = screen.getByPlaceholderText('Enter Message') |
||||
|
fireEvent.submit(form); |
||||
|
|
||||
|
expect(mockSendText).not.toHaveBeenCalled(); |
||||
|
}); |
||||
|
|
||||
|
it('initializes with existing message draft', () => { |
||||
|
(useDevice as Mock).mockReturnValue({ |
||||
|
connection: { |
||||
|
sendText: mockSendText, |
||||
|
}, |
||||
|
setMessageState: mockSetMessageState, |
||||
|
messageDraft: "Existing draft", |
||||
|
setMessageDraft: mockSetMessageDraft, |
||||
|
isQueueingMessages: false, |
||||
|
queueStatus: { free: 10 }, |
||||
|
hardware: { |
||||
|
myNodeNum: 1234567890, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
render(<MessageInput {...mockProps} />); |
||||
|
|
||||
|
const inputField = screen.getByRole('textbox'); |
||||
|
|
||||
|
expect(inputField).toHaveValue('Existing draft'); |
||||
|
}); |
||||
|
}); |
||||
@ -1,7 +1,18 @@ |
|||||
import "@testing-library/jest-dom"; |
// Try this import style instead
|
||||
|
import { expect, afterEach } from 'vitest'; |
||||
|
import { cleanup } from '@testing-library/react'; |
||||
|
import * as matchers from '@testing-library/jest-dom/matchers'; |
||||
|
|
||||
globalThis.ResizeObserver = class { |
// Add the matchers (should work with * as import)
|
||||
|
expect.extend(matchers); |
||||
|
|
||||
|
// Mock ResizeObserver
|
||||
|
global.ResizeObserver = class { |
||||
observe() { } |
observe() { } |
||||
unobserve() { } |
unobserve() { } |
||||
disconnect() { } |
disconnect() { } |
||||
}; |
}; |
||||
|
|
||||
|
afterEach(() => { |
||||
|
cleanup(); |
||||
|
}); |
||||
@ -0,0 +1,24 @@ |
|||||
|
import path from "node:path"; |
||||
|
import react from '@vitejs/plugin-react'; |
||||
|
import { defineConfig } from 'vitest/config' |
||||
|
|
||||
|
export default defineConfig({ |
||||
|
plugins: [ |
||||
|
react(), |
||||
|
], |
||||
|
resolve: { |
||||
|
alias: { |
||||
|
'@app': path.resolve(process.cwd(), './src'), |
||||
|
'@pages': path.resolve(process.cwd(), './src/pages'), |
||||
|
'@components': path.resolve(process.cwd(), './src/components'), |
||||
|
'@core': path.resolve(process.cwd(), './src/core'), |
||||
|
'@layouts': path.resolve(process.cwd(), './src/layouts'), |
||||
|
}, |
||||
|
}, |
||||
|
test: { |
||||
|
globals: true, |
||||
|
include: ['src/**/*.test.tsx', 'src/**/*.test.ts'], |
||||
|
setupFiles: ['src/tests/setupTests.ts'], |
||||
|
environment: 'happy-dom', |
||||
|
}, |
||||
|
}) |
||||
Loading…
Reference in new issue