mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
1486 lines
43 KiB
TypeScript
1486 lines
43 KiB
TypeScript
import {browserHistory} from 'react-router';
|
|
import merge from 'lodash/merge';
|
|
import {GroupFixture} from 'sentry-fixture/group';
|
|
import {GroupStatsFixture} from 'sentry-fixture/groupStats';
|
|
import {LocationFixture} from 'sentry-fixture/locationFixture';
|
|
import {MemberFixture} from 'sentry-fixture/member';
|
|
import {OrganizationFixture} from 'sentry-fixture/organization';
|
|
import {ProjectFixture} from 'sentry-fixture/project';
|
|
import {SearchFixture} from 'sentry-fixture/search';
|
|
import {TagsFixture} from 'sentry-fixture/tags';
|
|
|
|
import {initializeOrg} from 'sentry-test/initializeOrg';
|
|
import {
|
|
act,
|
|
render,
|
|
screen,
|
|
userEvent,
|
|
waitFor,
|
|
waitForElementToBeRemoved,
|
|
} from 'sentry-test/reactTestingLibrary';
|
|
import {textWithMarkupMatcher} from 'sentry-test/utils';
|
|
|
|
import StreamGroup from 'sentry/components/stream/group';
|
|
import ProjectsStore from 'sentry/stores/projectsStore';
|
|
import TagStore from 'sentry/stores/tagStore';
|
|
import {SavedSearchVisibility} from 'sentry/types';
|
|
import localStorageWrapper from 'sentry/utils/localStorage';
|
|
import * as parseLinkHeader from 'sentry/utils/parseLinkHeader';
|
|
import IssueListWithStores, {IssueListOverview} from 'sentry/views/issueList/overview';
|
|
|
|
// Mock <IssueListActions>
|
|
jest.mock('sentry/views/issueList/actions', () => jest.fn(() => null));
|
|
jest.mock('sentry/components/stream/group', () => jest.fn(() => null));
|
|
|
|
const DEFAULT_LINKS_HEADER =
|
|
'<http://127.0.0.1:8000/api/0/organizations/org-slug/issues/?cursor=1443575731:0:1>; rel="previous"; results="false"; cursor="1443575731:0:1", ' +
|
|
'<http://127.0.0.1:8000/api/0/organizations/org-slug/issues/?cursor=1443575000:0:0>; rel="next"; results="true"; cursor="1443575000:0:0"';
|
|
|
|
const project = ProjectFixture({
|
|
id: '3559',
|
|
name: 'Foo Project',
|
|
slug: 'project-slug',
|
|
firstEvent: new Date().toISOString(),
|
|
});
|
|
|
|
const {organization, router, routerContext} = initializeOrg({
|
|
organization: {
|
|
id: '1337',
|
|
slug: 'org-slug',
|
|
features: ['global-views'],
|
|
access: [],
|
|
},
|
|
router: {
|
|
location: {query: {}, search: ''},
|
|
params: {},
|
|
},
|
|
project,
|
|
});
|
|
|
|
const routerProps = {
|
|
params: router.params,
|
|
location: router.location,
|
|
};
|
|
|
|
describe('IssueList', function () {
|
|
let props;
|
|
|
|
const tags = TagsFixture();
|
|
const group = GroupFixture({project});
|
|
const groupStats = GroupStatsFixture();
|
|
const savedSearch = SearchFixture({
|
|
id: '789',
|
|
query: 'is:unresolved TypeError',
|
|
sort: 'date',
|
|
name: 'Unresolved TypeErrors',
|
|
});
|
|
|
|
let fetchTagsRequest: jest.Mock;
|
|
let fetchMembersRequest: jest.Mock;
|
|
const api = new MockApiClient();
|
|
const parseLinkHeaderSpy = jest.spyOn(parseLinkHeader, 'default');
|
|
|
|
beforeEach(function () {
|
|
// The tests fail because we have a "component update was not wrapped in act" error.
|
|
// It should be safe to ignore this error, but we should remove the mock once we move to react testing library
|
|
// eslint-disable-next-line no-console
|
|
jest.spyOn(console, 'error').mockImplementation(jest.fn());
|
|
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [group],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues-stats/',
|
|
body: [groupStats],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/recent-searches/',
|
|
body: [],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/recent-searches/',
|
|
method: 'POST',
|
|
body: [],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues-count/',
|
|
method: 'GET',
|
|
body: [{}],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/processingissues/',
|
|
method: 'GET',
|
|
body: [
|
|
{
|
|
project: 'test-project',
|
|
numIssues: 1,
|
|
hasIssues: true,
|
|
lastSeen: '2019-01-16T15:39:11.081Z',
|
|
},
|
|
],
|
|
});
|
|
fetchTagsRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/tags/',
|
|
method: 'GET',
|
|
body: tags,
|
|
});
|
|
fetchMembersRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/users/',
|
|
method: 'GET',
|
|
body: [MemberFixture({projects: [project.slug]})],
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/sent-first-event/',
|
|
body: {sentFirstEvent: true},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/projects/',
|
|
body: [project],
|
|
});
|
|
|
|
TagStore.init?.();
|
|
|
|
props = {
|
|
api,
|
|
savedSearchLoading: false,
|
|
savedSearches: [savedSearch],
|
|
useOrgSavedSearches: true,
|
|
selection: {
|
|
projects: [parseInt(organization.projects[0].id, 10)],
|
|
environments: [],
|
|
datetime: {period: '14d'},
|
|
},
|
|
location: {query: {query: 'is:unresolved'}, search: 'query=is:unresolved'},
|
|
params: {},
|
|
organization,
|
|
tags: tags.reduce((acc, tag) => {
|
|
acc[tag.key] = tag;
|
|
|
|
return acc;
|
|
}),
|
|
};
|
|
});
|
|
|
|
afterEach(function () {
|
|
jest.clearAllMocks();
|
|
MockApiClient.clearMockResponses();
|
|
localStorageWrapper.clear();
|
|
});
|
|
|
|
describe('withStores and feature flags', function () {
|
|
let savedSearchesRequest: jest.Mock;
|
|
let recentSearchesRequest: jest.Mock;
|
|
let issuesRequest: jest.Mock;
|
|
|
|
beforeEach(function () {
|
|
jest.mocked(StreamGroup).mockClear();
|
|
|
|
recentSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/recent-searches/',
|
|
method: 'GET',
|
|
body: [],
|
|
});
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch],
|
|
});
|
|
issuesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [group],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
});
|
|
|
|
it('loads group rows with default query (no pinned queries, async and no query in URL)', async function () {
|
|
render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
// Loading saved searches
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
expect(savedSearchesRequest).toHaveBeenCalledTimes(1);
|
|
|
|
await userEvent.click(await screen.findByDisplayValue('is:unresolved'));
|
|
|
|
// auxillary requests being made
|
|
expect(recentSearchesRequest).toHaveBeenCalledTimes(1);
|
|
expect(fetchTagsRequest).toHaveBeenCalledTimes(1);
|
|
expect(fetchMembersRequest).toHaveBeenCalledTimes(1);
|
|
|
|
// primary /issues/ request
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with default query
|
|
data: expect.stringContaining('is%3Aunresolved'),
|
|
})
|
|
);
|
|
|
|
expect(screen.getByDisplayValue('is:unresolved')).toBeInTheDocument();
|
|
|
|
expect(screen.getByRole('button', {name: /custom search/i})).toBeInTheDocument();
|
|
});
|
|
|
|
it('loads with query in URL and pinned queries', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
savedSearch,
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'My Pinned Search',
|
|
isPinned: true,
|
|
query: 'is:resolved',
|
|
}),
|
|
],
|
|
});
|
|
|
|
const location = LocationFixture({query: {query: 'level:foo'}});
|
|
|
|
render(<IssueListWithStores {...merge({}, routerProps, {location})} />, {
|
|
context: routerContext,
|
|
router: {location},
|
|
});
|
|
|
|
await waitFor(() => {
|
|
// Main /issues/ request
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with default query
|
|
data: expect.stringContaining('level%3Afoo'),
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('level:foo')).toBeInTheDocument();
|
|
|
|
// Tab shows "custom search"
|
|
expect(screen.getByRole('button', {name: 'Custom Search'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('loads with a pinned custom query', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
savedSearch,
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'My Pinned Search',
|
|
isPinned: true,
|
|
isGlobal: false,
|
|
query: 'is:resolved',
|
|
}),
|
|
],
|
|
});
|
|
|
|
render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with default query
|
|
data: expect.stringContaining('is%3Aresolved'),
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('is:resolved')).toBeInTheDocument();
|
|
|
|
// Organization saved search selector should have default saved search selected
|
|
expect(screen.getByRole('button', {name: 'My Default Search'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows archived tab', async function () {
|
|
render(<IssueListWithStores {...routerProps} organization={{...organization}} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(issuesRequest).toHaveBeenCalled();
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('is:unresolved')).toBeInTheDocument();
|
|
|
|
// TODO(workflow): remove this test when we remove the feature flag
|
|
expect(screen.getByRole('tab', {name: 'Archived'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('loads with a saved query', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'Assigned to Me',
|
|
isPinned: false,
|
|
isGlobal: true,
|
|
query: 'assigned:me',
|
|
sort: 'priority',
|
|
type: 0,
|
|
}),
|
|
],
|
|
});
|
|
|
|
const localRouter = {params: {searchId: '123'}};
|
|
|
|
render(<IssueListWithStores {...merge({}, routerProps, localRouter)} />, {
|
|
context: routerContext,
|
|
router: localRouter,
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with default query
|
|
data:
|
|
expect.stringContaining('assigned%3Ame') &&
|
|
expect.stringContaining('sort=priority'),
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('assigned:me')).toBeInTheDocument();
|
|
|
|
// Organization saved search selector should have default saved search selected
|
|
expect(screen.getByRole('button', {name: 'Assigned to Me'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('loads with a query in URL', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'Assigned to Me',
|
|
isPinned: false,
|
|
isGlobal: true,
|
|
query: 'assigned:me',
|
|
type: 0,
|
|
}),
|
|
],
|
|
});
|
|
|
|
const localRouter = {location: {query: {query: 'level:error'}}};
|
|
|
|
render(<IssueListWithStores {...merge({}, routerProps, localRouter)} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with default query
|
|
data: expect.stringContaining('level%3Aerror'),
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('level:error')).toBeInTheDocument();
|
|
|
|
// Organization saved search selector should have default saved search selected
|
|
expect(screen.getByRole('button', {name: 'Custom Search'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('loads with an empty query in URL', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'My Pinned Search',
|
|
isPinned: true,
|
|
isGlobal: false,
|
|
query: 'is:resolved',
|
|
}),
|
|
],
|
|
});
|
|
|
|
render(
|
|
<IssueListWithStores
|
|
{...merge({}, routerProps, {location: {query: {query: undefined}}})}
|
|
/>,
|
|
{context: routerContext}
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(issuesRequest).toHaveBeenCalledWith(
|
|
expect.anything(),
|
|
expect.objectContaining({
|
|
// Should be called with empty query
|
|
data: expect.stringContaining(''),
|
|
})
|
|
);
|
|
});
|
|
|
|
expect(screen.getByDisplayValue('is:resolved')).toBeInTheDocument();
|
|
|
|
// Organization saved search selector should have default saved search selected
|
|
expect(screen.getByRole('button', {name: 'My Default Search'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('caches the search results', async function () {
|
|
issuesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [...new Array(25)].map((_, i) => ({id: i})),
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
'X-Hits': '500',
|
|
'X-Max-Hits': '1000',
|
|
},
|
|
});
|
|
|
|
const defaultProps = {
|
|
...props,
|
|
...routerProps,
|
|
useOrgSavedSearches: true,
|
|
selection: {
|
|
projects: [],
|
|
environments: [],
|
|
datetime: {period: '14d'},
|
|
},
|
|
organization: OrganizationFixture({
|
|
features: ['issue-stream-performance'],
|
|
projects: [],
|
|
}),
|
|
};
|
|
const {unmount} = render(<IssueListWithStores {...defaultProps} />, {
|
|
context: routerContext,
|
|
organization: defaultProps.organization,
|
|
});
|
|
|
|
expect(
|
|
await screen.findByText(textWithMarkupMatcher('1-25 of 500'))
|
|
).toBeInTheDocument();
|
|
expect(issuesRequest).toHaveBeenCalledTimes(1);
|
|
unmount();
|
|
|
|
// Mount component again, getting from cache
|
|
render(<IssueListWithStores {...defaultProps} />, {
|
|
context: routerContext,
|
|
organization: defaultProps.organization,
|
|
});
|
|
|
|
expect(
|
|
await screen.findByText(textWithMarkupMatcher('1-25 of 500'))
|
|
).toBeInTheDocument();
|
|
expect(issuesRequest).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('1 search', async function () {
|
|
const localSavedSearch = {...savedSearch, projectId: null};
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [localSavedSearch],
|
|
});
|
|
|
|
render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
await userEvent.click(screen.getByRole('button', {name: /custom search/i}));
|
|
await userEvent.click(screen.getByRole('button', {name: localSavedSearch.name}));
|
|
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/searches/789/',
|
|
})
|
|
);
|
|
});
|
|
|
|
it('clears a saved search when a custom one is entered', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [
|
|
savedSearch,
|
|
SearchFixture({
|
|
id: '123',
|
|
name: 'Pinned search',
|
|
isPinned: true,
|
|
isGlobal: false,
|
|
query: 'is:resolved',
|
|
}),
|
|
],
|
|
});
|
|
|
|
render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
const queryInput = screen.getByDisplayValue('is:resolved');
|
|
await userEvent.clear(queryInput);
|
|
await userEvent.type(queryInput, 'dogs{enter}');
|
|
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
environment: [],
|
|
project: [],
|
|
referrer: 'issue-list',
|
|
sort: '',
|
|
query: 'dogs',
|
|
statsPeriod: '14d',
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
it('pins a custom query', async function () {
|
|
const pinnedSearch = {
|
|
id: '666',
|
|
name: 'My Pinned Search',
|
|
query: 'assigned:me level:fatal',
|
|
sort: 'date',
|
|
isPinned: true,
|
|
visibility: SavedSearchVisibility.ORGANIZATION,
|
|
};
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch],
|
|
});
|
|
|
|
const {rerender} = render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
const queryInput = screen.getByDisplayValue('is:unresolved');
|
|
await userEvent.clear(queryInput);
|
|
await userEvent.type(queryInput, 'assigned:me level:fatal{enter}');
|
|
|
|
expect((browserHistory.push as jest.Mock).mock.calls[0][0]).toEqual(
|
|
expect.objectContaining({
|
|
query: expect.objectContaining({
|
|
query: 'assigned:me level:fatal',
|
|
}),
|
|
})
|
|
);
|
|
|
|
await tick();
|
|
|
|
const routerWithQuery = {location: {query: {query: 'assigned:me level:fatal'}}};
|
|
|
|
rerender(<IssueListWithStores {...merge({}, routerProps, routerWithQuery)} />);
|
|
|
|
expect(screen.getByRole('button', {name: 'Custom Search'})).toBeInTheDocument();
|
|
|
|
MockApiClient.clearMockResponses();
|
|
const createPin = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/pinned-searches/',
|
|
method: 'PUT',
|
|
body: pinnedSearch,
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch, pinnedSearch],
|
|
});
|
|
await userEvent.click(screen.getByLabelText(/Set as Default/i));
|
|
|
|
await waitFor(() => {
|
|
expect(createPin).toHaveBeenCalled();
|
|
expect(browserHistory.replace).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/searches/666/',
|
|
query: {
|
|
referrer: 'search-bar',
|
|
},
|
|
search: '',
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it('unpins a custom query', async function () {
|
|
const pinnedSearch = SearchFixture({
|
|
id: '666',
|
|
name: 'My Pinned Search',
|
|
query: 'assigned:me level:fatal',
|
|
sort: 'date',
|
|
isPinned: true,
|
|
visibility: SavedSearchVisibility.ORGANIZATION,
|
|
});
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [pinnedSearch],
|
|
});
|
|
const deletePin = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/pinned-searches/',
|
|
method: 'DELETE',
|
|
});
|
|
|
|
const routerWithSavedSearch = {
|
|
params: {searchId: pinnedSearch.id},
|
|
};
|
|
|
|
render(<IssueListWithStores {...merge({}, routerProps, routerWithSavedSearch)} />, {
|
|
context: routerContext,
|
|
router: routerWithSavedSearch,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
expect(screen.getByRole('button', {name: 'My Default Search'})).toBeInTheDocument();
|
|
|
|
await userEvent.click(screen.getByLabelText(/Remove Default/i));
|
|
|
|
await waitFor(() => {
|
|
expect(deletePin).toHaveBeenCalled();
|
|
});
|
|
|
|
await waitFor(() => {
|
|
expect(browserHistory.replace).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/',
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it('pins a saved query', async function () {
|
|
const assignedToMe = SearchFixture({
|
|
id: '234',
|
|
name: 'Assigned to Me',
|
|
isPinned: false,
|
|
isGlobal: true,
|
|
query: 'assigned:me',
|
|
sort: 'date',
|
|
type: 0,
|
|
});
|
|
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch, assignedToMe],
|
|
});
|
|
|
|
const createPin = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/pinned-searches/',
|
|
method: 'PUT',
|
|
body: {
|
|
...savedSearch,
|
|
isPinned: true,
|
|
},
|
|
});
|
|
const routerWithSavedSearch = {params: {searchId: '789'}};
|
|
|
|
render(<IssueListWithStores {...merge({}, routerProps, routerWithSavedSearch)} />, {
|
|
context: routerContext,
|
|
router: routerWithSavedSearch,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
expect(screen.getByRole('button', {name: savedSearch.name})).toBeInTheDocument();
|
|
|
|
await userEvent.click(screen.getByLabelText(/set as default/i));
|
|
|
|
await waitFor(() => {
|
|
expect(createPin).toHaveBeenCalled();
|
|
expect(browserHistory.replace).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/searches/789/',
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it('pinning search should keep project selected', async function () {
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [savedSearch],
|
|
});
|
|
|
|
const {routerContext: newRouterContext, router: newRouter} = initializeOrg({
|
|
router: {
|
|
location: {
|
|
query: {
|
|
project: ['123'],
|
|
environment: ['prod'],
|
|
query: 'assigned:me level:fatal',
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
render(
|
|
<IssueListWithStores
|
|
{...newRouter}
|
|
selection={{
|
|
projects: [123],
|
|
environments: ['prod'],
|
|
datetime: {
|
|
end: null,
|
|
period: null,
|
|
start: null,
|
|
utc: null,
|
|
},
|
|
}}
|
|
/>,
|
|
{context: newRouterContext}
|
|
);
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
const createPin = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/pinned-searches/',
|
|
method: 'PUT',
|
|
body: {
|
|
...savedSearch,
|
|
id: '666',
|
|
name: 'My Pinned Search',
|
|
query: 'assigned:me level:fatal',
|
|
sort: 'date',
|
|
isPinned: true,
|
|
},
|
|
});
|
|
|
|
await userEvent.click(screen.getByLabelText(/set as default/i));
|
|
|
|
await waitFor(() => {
|
|
expect(createPin).toHaveBeenCalled();
|
|
expect(browserHistory.replace).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/searches/666/',
|
|
query: expect.objectContaining({
|
|
project: ['123'],
|
|
environment: ['prod'],
|
|
query: 'assigned:me level:fatal',
|
|
referrer: 'search-bar',
|
|
}),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it('unpinning search should keep project selected', async function () {
|
|
const localSavedSearch = {
|
|
...savedSearch,
|
|
id: '666',
|
|
isPinned: true,
|
|
query: 'assigned:me level:fatal',
|
|
};
|
|
savedSearchesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/searches/',
|
|
body: [localSavedSearch],
|
|
});
|
|
const deletePin = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/pinned-searches/',
|
|
method: 'DELETE',
|
|
});
|
|
|
|
const {routerContext: newRouterContext, router: newRouter} = initializeOrg(
|
|
merge({}, router, {
|
|
router: {
|
|
location: {
|
|
query: {
|
|
project: ['123'],
|
|
environment: ['prod'],
|
|
query: 'assigned:me level:fatal',
|
|
},
|
|
},
|
|
params: {searchId: '666'},
|
|
},
|
|
})
|
|
);
|
|
|
|
render(
|
|
<IssueListWithStores
|
|
{...newRouter}
|
|
selection={{
|
|
projects: [123],
|
|
environments: ['prod'],
|
|
datetime: {
|
|
end: null,
|
|
period: null,
|
|
start: null,
|
|
utc: null,
|
|
},
|
|
}}
|
|
savedSearch={localSavedSearch}
|
|
/>,
|
|
{context: newRouterContext}
|
|
);
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
await userEvent.click(screen.getByLabelText(/Remove Default/i));
|
|
|
|
await waitFor(() => {
|
|
expect(deletePin).toHaveBeenCalled();
|
|
expect(browserHistory.replace).toHaveBeenLastCalledWith(
|
|
expect.objectContaining({
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: expect.objectContaining({
|
|
project: ['123'],
|
|
environment: ['prod'],
|
|
query: 'assigned:me level:fatal',
|
|
referrer: 'search-bar',
|
|
}),
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
it('does not allow pagination to "previous" while on first page and resets cursors when navigating back to initial page', async function () {
|
|
const {rerender} = render(<IssueListWithStores {...routerProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
|
|
expect(screen.getByRole('button', {name: 'Previous'})).toBeDisabled();
|
|
|
|
issuesRequest = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [group],
|
|
headers: {
|
|
Link: '<http://127.0.0.1:8000/api/0/organizations/org-slug/issues/?cursor=1443575000:0:0>; rel="previous"; results="true"; cursor="1443575000:0:1", <http://127.0.0.1:8000/api/0/organizations/org-slug/issues/?cursor=1443574000:0:0>; rel="next"; results="true"; cursor="1443574000:0:0"',
|
|
},
|
|
});
|
|
|
|
await userEvent.click(screen.getByRole('button', {name: 'Next'}));
|
|
|
|
let pushArgs = {
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
cursor: '1443575000:0:0',
|
|
page: 1,
|
|
environment: [],
|
|
project: [],
|
|
query: 'is:unresolved',
|
|
statsPeriod: '14d',
|
|
referrer: 'issue-list',
|
|
},
|
|
};
|
|
|
|
await waitFor(() => {
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith(pushArgs);
|
|
});
|
|
|
|
rerender(<IssueListWithStores {...merge({}, routerProps, {location: pushArgs})} />);
|
|
|
|
expect(screen.getByRole('button', {name: 'Previous'})).toBeEnabled();
|
|
|
|
// Click next again
|
|
await userEvent.click(screen.getByRole('button', {name: 'Next'}));
|
|
|
|
pushArgs = {
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
cursor: '1443574000:0:0',
|
|
page: 2,
|
|
environment: [],
|
|
project: [],
|
|
query: 'is:unresolved',
|
|
statsPeriod: '14d',
|
|
referrer: 'issue-list',
|
|
},
|
|
};
|
|
|
|
await waitFor(() => {
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith(pushArgs);
|
|
});
|
|
|
|
rerender(<IssueListWithStores {...merge({}, routerProps, {location: pushArgs})} />);
|
|
|
|
// Click previous
|
|
await userEvent.click(screen.getByRole('button', {name: 'Previous'}));
|
|
|
|
pushArgs = {
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
cursor: '1443575000:0:1',
|
|
page: 1,
|
|
environment: [],
|
|
project: [],
|
|
query: 'is:unresolved',
|
|
statsPeriod: '14d',
|
|
referrer: 'issue-list',
|
|
},
|
|
};
|
|
|
|
await waitFor(() => {
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith(pushArgs);
|
|
});
|
|
|
|
rerender(<IssueListWithStores {...merge({}, routerProps, {location: pushArgs})} />);
|
|
|
|
// Click previous back to initial page
|
|
await userEvent.click(screen.getByRole('button', {name: 'Previous'}));
|
|
|
|
await waitFor(() => {
|
|
// cursor is undefined because "prev" cursor is === initial "next" cursor
|
|
expect(browserHistory.push).toHaveBeenLastCalledWith({
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
cursor: undefined,
|
|
environment: [],
|
|
page: undefined,
|
|
project: [],
|
|
query: 'is:unresolved',
|
|
statsPeriod: '14d',
|
|
referrer: 'issue-list',
|
|
},
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('transitionTo', function () {
|
|
it('pushes to history when query is updated', async function () {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
|
|
render(<IssueListOverview {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
const queryInput = screen.getByDisplayValue('is:unresolved');
|
|
await userEvent.clear(queryInput);
|
|
await userEvent.type(queryInput, 'is:ignored{enter}');
|
|
|
|
expect(browserHistory.push).toHaveBeenCalledWith({
|
|
pathname: '/organizations/org-slug/issues/',
|
|
query: {
|
|
environment: [],
|
|
project: [parseInt(project.id, 10)],
|
|
query: 'is:ignored',
|
|
statsPeriod: '14d',
|
|
referrer: 'issue-list',
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it('fetches tags and members', async function () {
|
|
render(<IssueListOverview {...routerProps} {...props} />, {context: routerContext});
|
|
|
|
await waitFor(() => {
|
|
expect(fetchTagsRequest).toHaveBeenCalled();
|
|
expect(fetchMembersRequest).toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('componentDidUpdate fetching groups', function () {
|
|
let fetchDataMock;
|
|
|
|
beforeEach(function () {
|
|
fetchDataMock = MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [group],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
fetchDataMock.mockReset();
|
|
});
|
|
|
|
it('fetches data on selection change', function () {
|
|
const {rerender} = render(<IssueListOverview {...routerProps} {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
rerender(
|
|
<IssueListOverview
|
|
{...routerProps}
|
|
{...props}
|
|
selection={{projects: [99], environments: [], datetime: {period: '24h'}}}
|
|
/>
|
|
);
|
|
|
|
expect(fetchDataMock).toHaveBeenCalled();
|
|
});
|
|
|
|
it('fetches data on savedSearch change', function () {
|
|
const {rerender} = render(<IssueListOverview {...routerProps} {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
rerender(
|
|
<IssueListOverview
|
|
{...routerProps}
|
|
{...props}
|
|
savedSearch={{id: '1', query: 'is:resolved'}}
|
|
/>
|
|
);
|
|
|
|
expect(fetchDataMock).toHaveBeenCalled();
|
|
});
|
|
|
|
it('uses correct statsPeriod when fetching issues list and no datetime given', function () {
|
|
const {rerender} = render(<IssueListOverview {...routerProps} {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
const selection = {projects: [99], environments: [], datetime: {}};
|
|
rerender(<IssueListOverview {...routerProps} {...props} selection={selection} />);
|
|
|
|
expect(fetchDataMock).toHaveBeenLastCalledWith(
|
|
'/organizations/org-slug/issues/',
|
|
expect.objectContaining({
|
|
data: 'collapse=stats&collapse=unhandled&expand=owners&expand=inbox&limit=25&project=99&query=is%3Aunresolved&savedSearch=1&shortIdLookup=1&statsPeriod=14d',
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('componentDidUpdate fetching members', function () {
|
|
it('fetches memberlist and tags list on project change', function () {
|
|
const {rerender} = render(<IssueListOverview {...routerProps} {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
// Called during componentDidMount
|
|
expect(fetchMembersRequest).toHaveBeenCalledTimes(1);
|
|
expect(fetchTagsRequest).toHaveBeenCalledTimes(1);
|
|
|
|
const selection = {
|
|
projects: [99],
|
|
environments: [],
|
|
datetime: {period: '24h'},
|
|
};
|
|
rerender(<IssueListOverview {...routerProps} {...props} selection={selection} />);
|
|
expect(fetchMembersRequest).toHaveBeenCalledTimes(2);
|
|
expect(fetchTagsRequest).toHaveBeenCalledTimes(2);
|
|
});
|
|
});
|
|
|
|
describe('render states', function () {
|
|
it('displays the loading icon when saved searches are loading', function () {
|
|
render(<IssueListOverview {...routerProps} {...props} savedSearchLoading />, {
|
|
context: routerContext,
|
|
});
|
|
expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays an error when issues fail to load', async function () {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
status: 500,
|
|
statusCode: 500,
|
|
});
|
|
render(<IssueListOverview {...routerProps} {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
expect(await screen.findByTestId('loading-error')).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays congrats robots animation with only is:unresolved query', async function () {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
render(<IssueListOverview {...routerProps} {...props} />, {context: routerContext});
|
|
|
|
expect(
|
|
await screen.findByText(/We couldn't find any issues that matched your filters/i)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it('displays an empty resultset with a non-default query', async function () {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
|
|
render(<IssueListOverview {...routerProps} {...props} />, {context: routerContext});
|
|
|
|
await userEvent.type(
|
|
screen.getByDisplayValue('is:unresolved'),
|
|
' level:error{enter}'
|
|
);
|
|
|
|
expect(
|
|
await screen.findByText(/We couldn't find any issues that matched your filters/i)
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
describe('Error Robot', function () {
|
|
const createWrapper = async moreProps => {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [],
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
},
|
|
});
|
|
|
|
const defaultProps = {
|
|
...props,
|
|
useOrgSavedSearches: true,
|
|
selection: {
|
|
projects: [],
|
|
environments: [],
|
|
datetime: {period: '14d'},
|
|
},
|
|
...merge({}, routerProps, {
|
|
params: {},
|
|
location: {query: {query: 'is:unresolved'}, search: 'query=is:unresolved'},
|
|
}),
|
|
organization: OrganizationFixture({
|
|
projects: [],
|
|
}),
|
|
...moreProps,
|
|
};
|
|
render(<IssueListOverview {...defaultProps} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
await waitForElementToBeRemoved(() => screen.getByTestId('loading-indicator'));
|
|
};
|
|
|
|
it('displays when no projects selected and all projects user is member of, async does not have first event', async function () {
|
|
const projects = [
|
|
ProjectFixture({
|
|
id: '1',
|
|
slug: 'foo',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '2',
|
|
slug: 'bar',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '3',
|
|
slug: 'baz',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
];
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/sent-first-event/',
|
|
query: {
|
|
is_member: true,
|
|
},
|
|
body: {sentFirstEvent: false},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/projects/',
|
|
body: projects,
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/projects/org-slug/foo/issues/',
|
|
body: [],
|
|
});
|
|
|
|
await createWrapper({
|
|
organization: OrganizationFixture({
|
|
projects,
|
|
}),
|
|
});
|
|
|
|
expect(screen.getByTestId('awaiting-events')).toBeInTheDocument();
|
|
});
|
|
|
|
it('does not display when no projects selected and any projects have a first event', async function () {
|
|
const projects = [
|
|
ProjectFixture({
|
|
id: '1',
|
|
slug: 'foo',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '2',
|
|
slug: 'bar',
|
|
isMember: true,
|
|
firstEvent: new Date().toISOString(),
|
|
}),
|
|
ProjectFixture({
|
|
id: '3',
|
|
slug: 'baz',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
];
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/sent-first-event/',
|
|
query: {
|
|
is_member: true,
|
|
},
|
|
body: {sentFirstEvent: true},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/projects/',
|
|
body: projects,
|
|
});
|
|
await createWrapper({
|
|
organization: OrganizationFixture({
|
|
projects,
|
|
}),
|
|
});
|
|
|
|
expect(screen.queryByTestId('awaiting-events')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('displays when all selected projects do not have first event', async function () {
|
|
const projects = [
|
|
ProjectFixture({
|
|
id: '1',
|
|
slug: 'foo',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '2',
|
|
slug: 'bar',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '3',
|
|
slug: 'baz',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
];
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/sent-first-event/',
|
|
query: {
|
|
project: [1, 2],
|
|
},
|
|
body: {sentFirstEvent: false},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/projects/',
|
|
body: projects,
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/projects/org-slug/foo/issues/',
|
|
body: [],
|
|
});
|
|
|
|
await createWrapper({
|
|
selection: {
|
|
projects: [1, 2],
|
|
environments: [],
|
|
datetime: {period: '14d'},
|
|
},
|
|
organization: OrganizationFixture({
|
|
projects,
|
|
}),
|
|
});
|
|
|
|
expect(await screen.findByTestId('awaiting-events')).toBeInTheDocument();
|
|
});
|
|
|
|
it('does not display when any selected projects have first event', async function () {
|
|
const projects = [
|
|
ProjectFixture({
|
|
id: '1',
|
|
slug: 'foo',
|
|
isMember: true,
|
|
firstEvent: null,
|
|
}),
|
|
ProjectFixture({
|
|
id: '2',
|
|
slug: 'bar',
|
|
isMember: true,
|
|
firstEvent: new Date().toISOString(),
|
|
}),
|
|
ProjectFixture({
|
|
id: '3',
|
|
slug: 'baz',
|
|
isMember: true,
|
|
firstEvent: new Date().toISOString(),
|
|
}),
|
|
];
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/sent-first-event/',
|
|
query: {
|
|
project: [1, 2],
|
|
},
|
|
body: {sentFirstEvent: true},
|
|
});
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/projects/',
|
|
body: projects,
|
|
});
|
|
|
|
await createWrapper({
|
|
selection: {
|
|
projects: [1, 2],
|
|
environments: [],
|
|
datetime: {period: '14d'},
|
|
},
|
|
organization: OrganizationFixture({
|
|
projects,
|
|
}),
|
|
});
|
|
|
|
expect(screen.queryByTestId('awaiting-events')).not.toBeInTheDocument();
|
|
});
|
|
});
|
|
|
|
it('displays a count that represents the current page', function () {
|
|
MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/issues/',
|
|
body: [...new Array(25)].map((_, i) => ({id: i})),
|
|
headers: {
|
|
Link: DEFAULT_LINKS_HEADER,
|
|
'X-Hits': '500',
|
|
'X-Max-Hits': '1000',
|
|
},
|
|
});
|
|
|
|
parseLinkHeaderSpy.mockReturnValue({
|
|
next: {
|
|
results: true,
|
|
cursor: '',
|
|
href: '',
|
|
},
|
|
previous: {
|
|
results: false,
|
|
cursor: '',
|
|
href: '',
|
|
},
|
|
});
|
|
props = {
|
|
...props,
|
|
location: {
|
|
query: {
|
|
cursor: 'some cursor',
|
|
page: 1,
|
|
},
|
|
},
|
|
};
|
|
|
|
const {routerContext: newRouterContext} = initializeOrg();
|
|
const {rerender} = render(<IssueListOverview {...props} />, {
|
|
context: newRouterContext,
|
|
});
|
|
|
|
expect(screen.getByText(textWithMarkupMatcher('1-25 of 500'))).toBeInTheDocument();
|
|
|
|
parseLinkHeaderSpy.mockReturnValue({
|
|
next: {
|
|
results: true,
|
|
cursor: '',
|
|
href: '',
|
|
},
|
|
previous: {
|
|
results: true,
|
|
cursor: '',
|
|
href: '',
|
|
},
|
|
});
|
|
rerender(<IssueListOverview {...props} />);
|
|
|
|
expect(screen.getByText(textWithMarkupMatcher('26-50 of 500'))).toBeInTheDocument();
|
|
});
|
|
|
|
describe('project low priority queue alert', function () {
|
|
const {routerContext: newRouterContext} = initializeOrg();
|
|
|
|
beforeEach(function () {
|
|
act(() => ProjectsStore.reset());
|
|
});
|
|
|
|
it('does not render event processing alert', function () {
|
|
act(() => ProjectsStore.loadInitialData([project]));
|
|
|
|
render(<IssueListOverview {...props} />, {
|
|
context: newRouterContext,
|
|
});
|
|
|
|
expect(screen.queryByText(/event processing/i)).not.toBeInTheDocument();
|
|
});
|
|
|
|
describe('renders alert', function () {
|
|
it('for one project', function () {
|
|
act(() =>
|
|
ProjectsStore.loadInitialData([
|
|
{...project, eventProcessing: {symbolicationDegraded: true}},
|
|
])
|
|
);
|
|
|
|
render(<IssueListOverview {...props} />, {
|
|
context: routerContext,
|
|
});
|
|
|
|
expect(
|
|
screen.getByText(/Event Processing for this project is currently degraded/i)
|
|
).toBeInTheDocument();
|
|
});
|
|
|
|
it('for multiple projects', function () {
|
|
const projectBar = ProjectFixture({
|
|
id: '3560',
|
|
name: 'Bar Project',
|
|
slug: 'project-slug-bar',
|
|
});
|
|
|
|
act(() =>
|
|
ProjectsStore.loadInitialData([
|
|
{
|
|
...project,
|
|
slug: 'project-slug',
|
|
eventProcessing: {symbolicationDegraded: true},
|
|
},
|
|
{
|
|
...projectBar,
|
|
slug: 'project-slug-bar',
|
|
eventProcessing: {symbolicationDegraded: true},
|
|
},
|
|
])
|
|
);
|
|
|
|
render(
|
|
<IssueListOverview
|
|
{...props}
|
|
selection={{
|
|
...props.selection,
|
|
projects: [Number(project.id), Number(projectBar.id)],
|
|
}}
|
|
/>,
|
|
{
|
|
context: newRouterContext,
|
|
}
|
|
);
|
|
|
|
expect(
|
|
screen.getByText(
|
|
textWithMarkupMatcher(
|
|
'Event Processing for the project-slug, project-slug-bar projects is currently degraded.'
|
|
)
|
|
)
|
|
).toBeInTheDocument();
|
|
});
|
|
});
|
|
});
|
|
});
|