import {act} from 'react-dom/test-utils';
import {selectDropdownMenuItem} from 'sentry-test/dropdownMenu';
import {mountWithTheme} from 'sentry-test/enzyme';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {mountGlobalModal} from 'sentry-test/modal';
import {selectByLabel} from 'sentry-test/select-new';
import {triggerPress} from 'sentry-test/utils';
import GroupStore from 'sentry/stores/groupStore';
import SelectedGroupStore from 'sentry/stores/selectedGroupStore';
import {IssueListActions} from 'sentry/views/issueList/actions';
describe('IssueListActions', function () {
let actions;
let actionsWrapper;
let wrapper;
afterEach(() => {
jest.restoreAllMocks();
});
describe('Bulk', function () {
describe('Total results greater than bulk limit', function () {
beforeAll(function () {
const {routerContext, org} = initializeOrg();
SelectedGroupStore.records = {};
SelectedGroupStore.add(['1', '2', '3']);
wrapper = mountWithTheme(
,
routerContext
);
});
afterAll(() => {
wrapper.unmount();
});
it('after checking "Select all" checkbox, displays bulk select message', function () {
wrapper.find('ActionsCheckbox Checkbox').simulate('change');
expect(wrapper.find('SelectAllNotice')).toSnapshot();
});
it('can bulk select', function () {
wrapper.find('SelectAllNotice').find('a').simulate('click');
expect(wrapper.find('SelectAllNotice')).toSnapshot();
});
it('bulk resolves', async function () {
const apiMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/',
method: 'PUT',
});
wrapper.find('ResolveActions ResolveButton button').simulate('click');
const modal = await mountGlobalModal();
expect(modal.find('Modal')).toSnapshot();
modal.find('Button[priority="primary"]').simulate('click');
expect(apiMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
query: {
project: [1],
},
data: {status: 'resolved', statusDetails: {}},
})
);
await tick();
wrapper.update();
});
});
describe('Total results less than bulk limit', function () {
beforeAll(function () {
SelectedGroupStore.records = {};
SelectedGroupStore.add(['1', '2', '3']);
wrapper = mountWithTheme(
);
});
afterAll(() => {
wrapper.unmount();
});
it('after checking "Select all" checkbox, displays bulk select message', function () {
wrapper.find('ActionsCheckbox Checkbox').simulate('change');
expect(wrapper.find('SelectAllNotice')).toSnapshot();
});
it('can bulk select', function () {
wrapper.find('SelectAllNotice').find('a').simulate('click');
expect(wrapper.find('SelectAllNotice')).toSnapshot();
});
it('bulk resolves', async function () {
const apiMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/',
method: 'PUT',
});
wrapper.find('ResolveActions ResolveButton button').simulate('click');
const modal = await mountGlobalModal();
expect(modal.find('Modal')).toSnapshot();
modal.find('Button[priority="primary"]').simulate('click');
expect(apiMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
query: {
project: [1],
},
data: {status: 'resolved', statusDetails: {}},
})
);
await tick();
wrapper.update();
});
});
describe('Selected on page', function () {
beforeAll(function () {
SelectedGroupStore.records = {};
SelectedGroupStore.add(['1', '2', '3']);
wrapper = mountWithTheme(
);
});
afterAll(() => {
wrapper.unmount();
});
it('resolves selected items', function () {
const apiMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/',
method: 'PUT',
});
jest
.spyOn(SelectedGroupStore, 'getSelectedIds')
.mockImplementation(() => new Set(['3', '6', '9']));
wrapper
.find('IssueListActions')
.setState({allInQuerySelected: false, anySelected: true});
wrapper.find('ResolveActions ResolveButton button').first().simulate('click');
expect(apiMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
query: {
id: ['3', '6', '9'],
project: [1],
},
data: {status: 'resolved', statusDetails: {}},
})
);
});
it('ignores selected items', async function () {
const apiMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/',
method: 'PUT',
});
jest
.spyOn(SelectedGroupStore, 'getSelectedIds')
.mockImplementation(() => new Set(['1']));
wrapper
.find('IssueListActions')
.setState({allInQuerySelected: false, anySelected: true});
await selectDropdownMenuItem({
wrapper,
specifiers: {prefix: 'IgnoreActions'},
triggerSelector: 'DropdownTrigger',
itemKey: ['until-affect', 'until-affect-custom'],
});
const modal = await mountGlobalModal();
modal
.find('CustomIgnoreCountModal input[label="Number of users"]')
.simulate('change', {target: {value: 300}});
selectByLabel(modal, 'per week', {
name: 'window',
});
modal.find('Button[priority="primary"]').simulate('click');
expect(apiMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
query: {
id: ['1'],
project: [1],
},
data: {
status: 'ignored',
statusDetails: {
ignoreUserCount: 300,
ignoreUserWindow: 10080,
},
},
})
);
});
});
});
describe('actionSelectedGroups()', function () {
beforeEach(function () {
jest.spyOn(SelectedGroupStore, 'deselectAll');
actionsWrapper = mountWithTheme(
);
actions = actionsWrapper.instance();
});
afterEach(() => {
actionsWrapper.unmount();
});
describe('for all items', function () {
it("should invoke the callback with 'undefined' and deselect all", function () {
const callback = jest.fn();
actions.state.allInQuerySelected = true;
actions.actionSelectedGroups(callback);
expect(callback).toHaveBeenCalledWith(undefined);
expect(callback).toHaveBeenCalledTimes(1);
expect(SelectedGroupStore.deselectAll).toHaveBeenCalledTimes(1);
// all selected is reset
expect(actions.state.allInQuerySelected).toBe(false);
});
});
describe('for page-selected items', function () {
it('should invoke the callback with an array of selected items and deselect all', function () {
jest
.spyOn(SelectedGroupStore, 'getSelectedIds')
.mockImplementation(() => new Set(['1', '2', '3']));
actions.state.allInQuerySelected = false;
const callback = jest.fn();
actions.actionSelectedGroups(callback);
expect(callback).toHaveBeenCalledWith(['1', '2', '3']);
expect(callback).toHaveBeenCalledTimes(1);
expect(SelectedGroupStore.deselectAll).toHaveBeenCalledTimes(1);
});
});
});
describe('multiple groups from different project', function () {
beforeEach(function () {
jest
.spyOn(SelectedGroupStore, 'getSelectedIds')
.mockImplementation(() => new Set(['1', '2', '3']));
wrapper = mountWithTheme(
);
});
afterEach(() => {
wrapper.unmount();
});
it('should disable resolve dropdown but not resolve action', function () {
const resolve = wrapper.find('ResolveActions').first();
expect(resolve.props().disabled).toBe(false);
expect(resolve.props().disableDropdown).toBe(true);
});
it('should disable merge button', function () {
expect(
wrapper.find('button[aria-label="Merge Selected Issues"]').props()[
'aria-disabled'
]
).toBe(true);
});
});
describe('mark reviewed', function () {
let issuesApiMock;
beforeEach(() => {
SelectedGroupStore.records = {};
const organization = TestStubs.Organization();
wrapper = mountWithTheme(
);
MockApiClient.addMockResponse({
url: '/organizations/org-slug/projects/',
body: [TestStubs.Project({slug: 'earth', platform: 'javascript'})],
});
issuesApiMock = MockApiClient.addMockResponse({
url: '/organizations/org-slug/issues/',
method: 'PUT',
});
});
afterEach(() => {
wrapper.unmount();
});
it('acknowledges group', async function () {
await act(async () => {
wrapper.find('IssueListActions').setState({anySelected: true});
await tick();
wrapper.update();
});
SelectedGroupStore.add(['1', '2', '3']);
SelectedGroupStore.toggleSelectAll();
const inbox = {
date_added: '2020-11-24T13:17:42.248751Z',
reason: 0,
reason_details: null,
};
GroupStore.loadInitialData([
TestStubs.Group({id: '1', inbox}),
TestStubs.Group({id: '2', inbox}),
TestStubs.Group({id: '2', inbox}),
]);
await tick();
wrapper.find('button[aria-label="Mark Reviewed"]').simulate('click');
expect(issuesApiMock).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
data: {inbox: false},
})
);
});
it('mark reviewed disabled for group that is already reviewed', async function () {
await act(async () => {
wrapper.find('IssueListActions').setState({anySelected: true});
await tick();
wrapper.update();
});
SelectedGroupStore.add(['1']);
SelectedGroupStore.toggleSelectAll();
GroupStore.loadInitialData([TestStubs.Group({id: '1', inbox: null})]);
await tick();
expect(
wrapper.find('button[aria-label="Mark Reviewed"]').props()['aria-disabled']
).toBe(true);
});
});
describe('sort', function () {
let onSortChange;
afterEach(() => {
wrapper.unmount();
});
beforeEach(function () {
const organization = TestStubs.Organization();
onSortChange = jest.fn();
wrapper = mountWithTheme(
);
});
it('calls onSortChange with new sort value', async function () {
await act(async () => {
triggerPress(wrapper.find('IssueListSortOptions button'));
await tick();
wrapper.update();
});
wrapper.find('Option').at(3).simulate('click');
expect(onSortChange).toHaveBeenCalledWith('freq');
});
});
});