import {GroupFixture} from 'sentry-fixture/group';
import {ProjectFixture} from 'sentry-fixture/project';
import {UserFixture} from 'sentry-fixture/user';
import {
render,
renderGlobalModal,
screen,
userEvent,
} from 'sentry-test/reactTestingLibrary';
import ConfigStore from 'sentry/stores/configStore';
import GroupStore from 'sentry/stores/groupStore';
import ProjectsStore from 'sentry/stores/projectsStore';
import type {GroupActivity} from 'sentry/types/group';
import {GroupActivityType} from 'sentry/types/group';
import StreamlinedActivitySection from 'sentry/views/issueDetails/streamline/sidebar/activitySection';
describe('StreamlinedActivitySection', function () {
const project = ProjectFixture();
const user = UserFixture();
user.options.prefersIssueDetailsStreamlinedUI = true;
ConfigStore.set('user', user);
ProjectsStore.loadInitialData([project]);
GroupStore.init();
const group = GroupFixture({
id: '1337',
activity: [
{
type: GroupActivityType.NOTE,
id: 'note-1',
data: {text: 'Test Note'},
dateCreated: '2020-01-01T00:00:00',
user: user,
project,
},
],
project,
});
GroupStore.add([group]);
beforeEach(() => {
jest.restoreAllMocks();
MockApiClient.clearMockResponses();
});
it('renders the input with a comment button', async function () {
const comment = 'nice work friends';
const postMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/1337/comments/',
method: 'POST',
body: {
id: 'note-2',
user: UserFixture({id: '2'}),
type: 'note',
data: {text: comment},
dateCreated: '2024-10-31T00:00:00.000000Z',
},
});
render();
const commentInput = screen.getByRole('textbox', {name: 'Add a comment'});
expect(commentInput).toBeInTheDocument();
expect(
screen.queryByRole('button', {name: 'Submit comment'})
).not.toBeInTheDocument();
await userEvent.click(commentInput);
// Button appears after input is focused
const submitButton = await screen.findByRole('button', {name: 'Submit comment'});
expect(submitButton).toBeInTheDocument();
expect(submitButton).toBeDisabled();
await userEvent.type(commentInput, comment);
expect(submitButton).toBeEnabled();
await userEvent.click(submitButton);
expect(postMock).toHaveBeenCalled();
});
it('allows submitting the comment field with hotkeys', async function () {
const comment = 'nice work friends';
const postMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/1337/comments/',
method: 'POST',
body: {
id: 'note-3',
user: UserFixture({id: '2'}),
type: 'note',
data: {text: comment},
dateCreated: '2024-10-31T00:00:00.000000Z',
},
});
render();
const commentInput = screen.getByRole('textbox', {name: 'Add a comment'});
await userEvent.type(commentInput, comment);
await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
expect(postMock).toHaveBeenCalled();
});
it('renders note and allows for delete', async function () {
const deleteMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/1337/comments/note-1/',
method: 'DELETE',
});
render();
renderGlobalModal();
expect(await screen.findByText('Test Note')).toBeInTheDocument();
expect(screen.getByRole('button', {name: 'Comment Actions'})).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: 'Comment Actions'}));
await userEvent.click(screen.getByRole('menuitemradio', {name: 'Remove'}));
expect(
screen.getByText('Are you sure you want to remove this comment?')
).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: 'Remove comment'}));
expect(deleteMock).toHaveBeenCalledTimes(1);
expect(screen.queryByText('Test Note')).not.toBeInTheDocument();
});
it('renders note but does not allow for deletion if written by someone else', async function () {
const updatedActivityGroup = GroupFixture({
id: '1338',
activity: [
{
type: GroupActivityType.NOTE,
id: 'note-1',
data: {text: 'Test Note'},
dateCreated: '2020-01-01T00:00:00',
user: UserFixture({id: '2'}),
project,
},
],
project,
});
render();
expect(await screen.findByText('Test Note')).toBeInTheDocument();
expect(
screen.queryByRole('button', {name: 'Comment Actions'})
).not.toBeInTheDocument();
});
it('collapses activity when there are more than 5 items', async function () {
const activities: GroupActivity[] = Array.from({length: 7}, (_, index) => ({
type: GroupActivityType.NOTE,
id: `note-${index + 1}`,
data: {text: `Test Note ${index + 1}`},
dateCreated: '2020-01-01T00:00:00',
user: UserFixture({id: '2'}),
project,
}));
const updatedActivityGroup = GroupFixture({
id: '1338',
activity: activities,
project,
});
render();
expect(await screen.findByText('Test Note 1')).toBeInTheDocument();
expect(await screen.findByText('Test Note 3')).toBeInTheDocument();
expect(screen.queryByText('Test Note 7')).not.toBeInTheDocument();
expect(await screen.findByText('View 4 more')).toBeInTheDocument();
});
it('does not collapse activity when rendered in the drawer', function () {
const activities: GroupActivity[] = Array.from({length: 7}, (_, index) => ({
type: GroupActivityType.NOTE,
id: `note-${index + 1}`,
data: {text: `Test Note ${index + 1}`},
dateCreated: '2020-01-01T00:00:00',
user: UserFixture({id: '2'}),
project,
}));
const updatedActivityGroup = GroupFixture({
id: '1338',
activity: activities,
project,
});
render();
for (const activity of activities) {
expect(
screen.getByText((activity.data as {text: string}).text)
).toBeInTheDocument();
}
expect(screen.queryByRole('button')).not.toBeInTheDocument();
});
});