Skip to content

Commit

Permalink
Add symbol replacer for new lines
Browse files Browse the repository at this point in the history
  • Loading branch information
Dayana Ilieva committed Mar 26, 2024
1 parent c223e8d commit a530037
Show file tree
Hide file tree
Showing 9 changed files with 403 additions and 164 deletions.
219 changes: 219 additions & 0 deletions __tests__/feature/new-rows.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
/* eslint-env jest */
import { act } from 'react-dom/test-utils';

import { uuidV4 } from '../../src/utils';
import { localTearDown } from '../helpers';
import AppBase from '../../src/components/AppBase';
import renderWithProviders from '../../src/utils/storeMockWrapper';
import { setConnected } from '../../src/store/slices/chat';
import { serverSocket } from '../../__mocks__/socket.io-client';
import { Events, Roles, chat as getInitialConfig, initialMessage } from '../../src/config';
import { splitIntoRowChunks } from '../../src/utils/formatting';


const textProbeWithTwoRows = (separator = '\n') => ({ 'type': 'text', 'text': `${faker.lorem.word()}${separator}${faker.lorem.word()}`, 'sequence': 2 });

const serverData = {
"region": faker.location.country(),
"history": [],
"errors": [],
}

jest.useFakeTimers();

let root;
describe('Having special symbols \n \r or <br> makes separates items into new rows', () => {
beforeEach(() => {
serverData.history = [];
serverData.errors = [];
serverData.region = faker.location.country();
});

afterEach(localTearDown);

test('having \n returns multiple elements in last message', async () => {
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows()],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows().text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(2);
});

test('having \r\n returns multiple elements in last message', async () => {
const separator = '\r\n';
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows(separator)],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows(separator).text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(2);
});

test('having <br> returns multiple elements in last message', async () => {
const separator = '<br>';
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows(separator)],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows(separator).text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(2);
});


test('having <br/> returns multiple elements in last message', async () => {
const separator = '<br/>';
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows(separator)],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows(separator).text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(2);
});

test('having \\r\\n returns multiple elements in last message', async () => {
const separator = '\\r\\n';
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows(separator)],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows(separator).text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(2);
});

test('no separator returns single element in last message', async () => {
const separator = '';
act(() => {
root = renderWithProviders(
<div id="chatbot-container">
<AppBase config={getInitialConfig({ id: uuidV4(), purpose: '', close: { visible: true } })} />
</div>
);
root.store.dispatch(setConnected(true));
});

serverData.history = [{
id: uuidV4(),
role: Roles.assistant,
time: new Date().getTime(),
content: [textProbeWithTwoRows(separator)],
}];

// Act
act(() => {
serverSocket.emit(Events.chatHistory, serverData);
jest.advanceTimersByTime((initialMessage.length * 1000) * initialMessage.length - 1);
});

// Assert
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(splitIntoRowChunks(textProbeWithTwoRows(separator).text).length);
expect(root.container.querySelector('[data-e2e="assistant-text"] span').children.length)
.toBe(1);
});
});
292 changes: 149 additions & 143 deletions dist/index.es.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.es.js.map

Large diffs are not rendered by default.

18 changes: 10 additions & 8 deletions dist/index.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.umd.js.map

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions src/assets/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
letter-spacing: 2px;
}

p {
display: inline;
}

.mt-8 {
margin-top: 2rem;
}
Expand All @@ -32,6 +28,10 @@ p {
top: 0;
}

span > p:last-child {
display: inline;
}

@layer utilities {
.tw--translate-z-0 * {
transform: translateZ(0);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Layout/head.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { MouseEvent } from 'react';
import { AllEvents } from '../../config/enums';
import { CLOSE_AFTER_TIMEOUT } from '../../config/env';
import { useAppDispatch, useHeadControls } from '../../hooks';
import { track } from '../../services/tracking';
import { track } from '../../services/tracking';
import { setClosed } from '../../store/slices/chat';
import { IconBtn } from '../Button';
import { Profile } from '../Profile';
Expand Down
18 changes: 13 additions & 5 deletions src/components/Stream/assistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import MarkdownLink from '../Markdown/link';
import { Iframe } from '../Video';
import OptionList from './options';
import { flickerEffect } from './variants';
import { splitIntoRowChunks } from '../../utils/formatting';

const Assistant = ({ message, itemId }: AssistantProps) => {
const { isStreaming } = useAppSelector(getChat);
Expand All @@ -27,13 +28,18 @@ const Assistant = ({ message, itemId }: AssistantProps) => {
<div
key={uuidV4()}
className="tw--flex tw--flex-col tw--space-y-[10px]"
data-e2e="assistant-text"
>
<span className={baseFlicker()}>
<Markdown components={{
a(props) { return <MarkdownLink properties={props} />; }
}}
>{it[it.type]}
</Markdown>
{splitIntoRowChunks(it[it.type] || '').map((line) => (
<Markdown
key={uuidV4()}
components={{
a(props) { return <MarkdownLink properties={props} />; },
}}
>{line}
</Markdown>
))}
</span>
</div>
);
Expand All @@ -50,6 +56,7 @@ const Assistant = ({ message, itemId }: AssistantProps) => {
if (it.type === Definition.video) {
return (
<Iframe
data-e2e="assistant-iframe"
key={uuidV4()}
title={it[it.type]?.title || 'video'}
url={it[it.type]?.url || 'https://www.youtube.com/embed/g4B8Dhl4pxY'}
Expand All @@ -61,6 +68,7 @@ const Assistant = ({ message, itemId }: AssistantProps) => {
return (
<img
key={uuidV4()}
data-e2e="assistant-img"
className="w-full tw--h-auto tw--py-4"
src={it[it.type]?.url}
alt={it[it.type]?.alt || 'chat-image'}
Expand Down
6 changes: 5 additions & 1 deletion src/utils/formatting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const constructLink = (link: string): string => {
searchParams.append(CHAT_SEEN_KEY, 'true');
searchParams.append(CID, localStorage.getItem('__cid') || '');

search = `/?${ searchParams}`;
search = `/?${searchParams}`;
} else {
search = '';
}
Expand All @@ -38,3 +38,7 @@ export const constructLink = (link: string): string => {

return link + search;
};

export const splitIntoRowChunks = (text: string): string[] => {
return text.replaceAll(/\\r\\n|\\n|\r\n|<br>|<br\/>/g, '\n').split('\n');
};

0 comments on commit a530037

Please sign in to comment.