Skip to content

Commit

Permalink
Solved issue-148
Browse files Browse the repository at this point in the history
  • Loading branch information
MadhurSaluja committed Nov 28, 2024
1 parent bcec368 commit 0e9034f
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 5 deletions.
116 changes: 116 additions & 0 deletions __tests__/components/ChatBotBody/ChatMessagePrompt.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import "@testing-library/jest-dom";
import ChatMessagePrompt from "../../../src/components/ChatBotBody/ChatMessagePrompt/ChatMessagePrompt";

// Mock contexts
jest.mock("../../../src/context/BotRefsContext", () => ({
useBotRefsContext: jest.fn(() => ({
chatBodyRef: {
current: {
scrollTop: 0,
scrollHeight: 1000,
clientHeight: 400,
},
},
})),
}));

const mockSetIsScrolling = jest.fn();
let unreadCountMock = 0;
let isScrollingMock = false;

jest.mock("../../../src/context/BotStatesContext", () => ({
useBotStatesContext: jest.fn(() => ({
unreadCount: unreadCountMock,
isScrolling: isScrollingMock,
setIsScrolling: mockSetIsScrolling,
})),
}));

jest.mock("../../../src/context/SettingsContext", () => ({
useSettingsContext: jest.fn(() => ({
settings: {
general: { primaryColor: "#000" },
chatWindow: {
showMessagePrompt: true,
messagePromptText: "Scroll to new messages",
},
},
})),
}));

jest.mock("../../../src/context/StylesContext", () => ({
useStylesContext: jest.fn(() => ({
styles: {
chatMessagePromptStyle: { color: "#fff", borderColor: "#ccc" },
chatMessagePromptHoveredStyle: { color: "#000", borderColor: "#000" },
},
})),
}));

describe("ChatMessagePrompt Component", () => {
beforeEach(() => {
jest.clearAllMocks();
jest.useFakeTimers();
});

afterEach(() => {
jest.useRealTimers();
});

const renderComponent = () => render(<ChatMessagePrompt />);

it("renders with the correct message prompt text", () => {
renderComponent();
const messagePrompt = screen.getByText("Scroll to new messages");
expect(messagePrompt).toBeInTheDocument();
});

it("applies visible class when conditions are met", () => {
unreadCountMock = 2;
isScrollingMock = true;

renderComponent();
const messagePrompt = screen.getByText("Scroll to new messages");
expect(messagePrompt.parentElement).toHaveClass("rcb-message-prompt-container visible");
});

it("applies hidden class when conditions are not met", () => {
unreadCountMock = 0;
isScrollingMock = false;

renderComponent();
const messagePromptContainer = screen.queryByText("Scroll to new messages")?.parentElement;
expect(messagePromptContainer).toHaveClass("rcb-message-prompt-container hidden");
});

it("applies hover styles when hovered", () => {
renderComponent();
const messagePrompt = screen.getByText("Scroll to new messages");

// Before hover
expect(messagePrompt).toHaveStyle({ color: "#fff", borderColor: "#ccc" });

// Hover
fireEvent.mouseEnter(messagePrompt);
expect(messagePrompt).toHaveStyle({ color: "#000", borderColor: "#000" });

// Leave hover
fireEvent.mouseLeave(messagePrompt);
expect(messagePrompt).toHaveStyle({ color: "#fff", borderColor: "#ccc" });
});

it("scrolls to the bottom when clicked", () => {
renderComponent();
const messagePrompt = screen.getByText("Scroll to new messages");

fireEvent.mouseDown(messagePrompt);

// Simulate scrolling completion
jest.advanceTimersByTime(600);

// Verify that setIsScrolling was called
expect(mockSetIsScrolling).toHaveBeenCalledWith(false);
});
});
138 changes: 138 additions & 0 deletions __tests__/services/VoiceService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { jest, SpyInstance } from "@jest/globals";
import * as VoiceService from "../../src/services/VoiceService";

describe("VoiceService", () => {
let mockRecognition: jest.Mocked<SpeechRecognition>;
let mockMediaRecorder: jest.Mocked<MediaRecorder>;
let mockStopVoiceRecording: jest.SpyInstance<any, any>;

beforeAll(() => {
// Mock navigator.mediaDevices if undefined
if (!navigator.mediaDevices) {
Object.defineProperty(navigator, "mediaDevices", {
value: {
getUserMedia: jest.fn(),
},
writable: true,
});
}
});

beforeEach(() => {
// Mock SpeechRecognition
mockRecognition = {
start: jest.fn(),
stop: jest.fn(),
onresult: null,
onend: null,
} as unknown as jest.Mocked<SpeechRecognition>;

window.SpeechRecognition = jest.fn(() => mockRecognition) as any;

// Mock MediaRecorder
mockMediaRecorder = {
start: jest.fn(),
stop: jest.fn(),
ondataavailable: null,
onstop: null,
state: "inactive",
} as unknown as jest.Mocked<MediaRecorder>;

global.MediaRecorder = jest.fn(() => mockMediaRecorder) as any;

// Mock getUserMedia
jest.spyOn(navigator.mediaDevices, "getUserMedia").mockResolvedValue({
active: true,
id: "mockStreamId",
onaddtrack: null,
onremovetrack: null,
getTracks: jest.fn(() => [{ kind: "audio", stop: jest.fn() }]),
addTrack: jest.fn(),
removeTrack: jest.fn(),
getAudioTracks: jest.fn(() => []),
getVideoTracks: jest.fn(() => []),
clone: jest.fn(),
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
dispatchEvent: jest.fn(),
} as unknown as MediaStream);

mockStopVoiceRecording = jest.spyOn(VoiceService, "stopVoiceRecording");
});

afterEach(() => {
jest.restoreAllMocks(); // Clean up mocks after each test
});

describe("startVoiceRecording", () => {
it("should start SpeechRecognition when sendAsAudio is false", () => {
const settings = { voice: { sendAsAudio: false, language: "en-US" } } as any;
const mockToggleVoice = jest.fn(() => Promise.resolve());

VoiceService.startVoiceRecording(
settings,
mockToggleVoice,
jest.fn(),
jest.fn(),
jest.fn(),
{ current: [] },
{ current: null }
);

expect(mockRecognition.start).toHaveBeenCalled();
});

it("should start MediaRecorder when sendAsAudio is true", async () => {
const settings = { voice: { sendAsAudio: true } } as any;
const mockToggleVoice = jest.fn(() => Promise.resolve());

await VoiceService.startVoiceRecording(
settings,
mockToggleVoice,
jest.fn(),
jest.fn(),
jest.fn(),
{ current: [] },
{ current: null }
);

expect(navigator.mediaDevices.getUserMedia).toHaveBeenCalledWith({ audio: true });
expect(mockMediaRecorder.start).toHaveBeenCalled();
});
});

describe("stopVoiceRecording", () => {
it("should stop SpeechRecognition and MediaRecorder", () => {
VoiceService.stopVoiceRecording();

expect(mockRecognition.stop).toHaveBeenCalled();
expect(mockMediaRecorder.stop).toHaveBeenCalled();
});
});

describe("syncVoiceWithChatInput", () => {
it("should start MediaRecorder if keepVoiceOn is true and sendAsAudio is enabled", () => {
const settings = { voice: { sendAsAudio: true, disabled: false } } as any;

VoiceService.syncVoiceWithChatInput(true, settings);

expect(mockMediaRecorder.start).toHaveBeenCalled();
});

it("should start SpeechRecognition if keepVoiceOn is true and sendAsAudio is disabled", () => {
const settings = { voice: { sendAsAudio: false, disabled: false } } as any;

VoiceService.syncVoiceWithChatInput(true, settings);

expect(mockRecognition.start).toHaveBeenCalled();
});

it("should stop all voice recording if keepVoiceOn is false", () => {
const settings = { voice: { sendAsAudio: false, disabled: false } } as any;

VoiceService.syncVoiceWithChatInput(false, settings);

expect(mockStopVoiceRecording).toHaveBeenCalled();
});
});
});
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react": "^16.0.1",
"@types/dom-speech-recognition": "^0.0.4",
"@types/jest": "^29.5.13",
"@types/jest": "^29.5.14",
"@types/react": "^18.3.6",
"@types/react-dom": "^18.3.0",
"@typescript-eslint/eslint-plugin": "^5.60.1",
Expand Down

0 comments on commit 0e9034f

Please sign in to comment.