123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506 |
- 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(
- <IssueListActions
- api={new MockApiClient()}
- allResultsVisible={false}
- query=""
- queryCount={1500}
- organization={org}
- projectId="project-slug"
- selection={{
- projects: [1],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- groupIds={['1', '2', '3']}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- />,
- 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(
- <IssueListActions
- api={new MockApiClient()}
- allResultsVisible={false}
- query=""
- queryCount={600}
- organization={TestStubs.Organization()}
- projectId="1"
- selection={{
- projects: [1],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- groupIds={['1', '2', '3']}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- />
- );
- });
- 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(
- <IssueListActions
- api={new MockApiClient()}
- allResultsVisible
- query=""
- queryCount={15}
- organization={TestStubs.Organization()}
- projectId="1"
- selection={{
- projects: [1],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- groupIds={['1', '2', '3', '6', '9']}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- />
- );
- });
- 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(
- <IssueListActions
- api={new MockApiClient()}
- query=""
- organization={TestStubs.Organization()}
- projectId="1"
- selection={{
- projects: [1],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- groupIds={['1', '2', '3']}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- />
- );
- 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(
- <IssueListActions
- api={new MockApiClient()}
- query=""
- organization={TestStubs.Organization()}
- groupIds={['1', '2', '3']}
- selection={{
- projects: [],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- />
- );
- });
- 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(
- <IssueListActions
- api={new MockApiClient()}
- query=""
- organization={organization}
- groupIds={['1', '2', '3']}
- selection={{
- projects: [],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- realtimeActive={false}
- statsPeriod="24h"
- queryCount={100}
- displayCount="3 of 3"
- />
- );
- 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(
- <IssueListActions
- api={new MockApiClient()}
- query=""
- organization={organization}
- groupIds={['1', '2', '3']}
- selection={{
- projects: [],
- environments: [],
- datetime: {start: null, end: null, period: null, utc: true},
- }}
- onRealtimeChange={function () {}}
- onSelectStatsPeriod={function () {}}
- onSortChange={onSortChange}
- realtimeActive={false}
- statsPeriod="24h"
- queryCount={100}
- displayCount="3 of 3"
- sort="date"
- />
- );
- });
- 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');
- });
- });
- });
|