import {browserHistory} from 'react-router';
import moment from 'moment';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
import ProjectsStore from 'sentry/stores/projectsStore';
import AlertRuleDetails from './ruleDetails';
describe('AlertRuleDetails', () => {
const context = initializeOrg({
organization: {features: ['issue-alert-incompatible-rules', 'mute-alerts']},
});
const organization = context.organization;
const project = TestStubs.Project();
const rule = TestStubs.ProjectAlertRule({
lastTriggered: moment().subtract(2, 'day').format(),
});
const member = TestStubs.Member();
const createWrapper = (props: any = {}, newContext?: any, org = organization) => {
const router = newContext ? newContext.router : context.router;
const routerContext = newContext ? newContext.routerContext : context.routerContext;
return render(
,
{context: routerContext, organization: org}
);
};
beforeEach(() => {
browserHistory.push = jest.fn();
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/`,
body: rule,
match: [MockApiClient.matchQuery({expand: 'lastTriggered'})],
});
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/stats/`,
body: [],
});
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/group-history/`,
body: [
{
count: 1,
group: TestStubs.Group(),
lastTriggered: moment('Apr 11, 2019 1:08:59 AM UTC').format(),
eventId: 'eventId',
},
],
headers: {
Link:
'; rel="previous"; results="false"; cursor="0:0:1", ' +
'; rel="next"; results="true"; cursor="0:100:0"',
},
});
MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/projects/`,
body: [project],
});
MockApiClient.addMockResponse({
url: `/organizations/${organization.slug}/users/`,
body: [member],
});
ProjectsStore.init();
ProjectsStore.loadInitialData([project]);
});
afterEach(() => {
ProjectsStore.reset();
MockApiClient.clearMockResponses();
});
it('displays alert rule with list of issues', async () => {
createWrapper();
expect(await screen.findAllByText('My alert rule')).toHaveLength(2);
expect(screen.getByText('RequestError:')).toBeInTheDocument();
expect(screen.getByText('Apr 11, 2019 1:08:59 AM UTC')).toBeInTheDocument();
expect(screen.getByText('RequestError:')).toHaveAttribute(
'href',
expect.stringMatching(
RegExp(
`/organizations/${organization.slug}/issues/${
TestStubs.Group().id
}/events/eventId.*`
)
)
);
});
it('should allow paginating results', async () => {
createWrapper();
expect(await screen.findByLabelText('Next')).toBeEnabled();
await userEvent.click(screen.getByLabelText('Next'));
expect(browserHistory.push).toHaveBeenCalledWith({
pathname: '/mock-pathname/',
query: {
cursor: '0:100:0',
},
});
});
it('should reset pagination cursor on date change', async () => {
createWrapper();
expect(await screen.findByText('Last 7 days')).toBeInTheDocument();
await userEvent.click(screen.getByText('Last 7 days'));
await userEvent.click(screen.getByText('Last 24 hours'));
expect(context.router.push).toHaveBeenCalledWith(
expect.objectContaining({
query: {
pageStatsPeriod: '24h',
cursor: undefined,
pageEnd: undefined,
pageStart: undefined,
pageUtc: undefined,
},
})
);
});
it('should show the time since last triggered in sidebar', async () => {
createWrapper();
expect(await screen.findAllByText('Last Triggered')).toHaveLength(2);
expect(screen.getByText('2 days ago')).toBeInTheDocument();
});
it('renders not found on 404', async () => {
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/`,
statusCode: 404,
body: {},
match: [MockApiClient.matchQuery({expand: 'lastTriggered'})],
});
createWrapper();
expect(
await screen.findByText('The alert rule you were looking for was not found.')
).toBeInTheDocument();
});
it('renders incompatible rule filter', async () => {
const incompatibleRule = TestStubs.ProjectAlertRule({
conditions: [
{id: 'sentry.rules.conditions.first_seen_event.FirstSeenEventCondition'},
{id: 'sentry.rules.conditions.regression_event.RegressionEventCondition'},
],
});
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/`,
body: incompatibleRule,
match: [MockApiClient.matchQuery({expand: 'lastTriggered'})],
});
createWrapper();
expect(
await screen.findByText(
'The conditions in this alert rule conflict and might not be working properly.'
)
).toBeInTheDocument();
});
it('incompatible rule banner hidden for good rule', async () => {
createWrapper();
expect(await screen.findAllByText('My alert rule')).toHaveLength(2);
expect(
screen.queryByText(
'The conditions in this alert rule conflict and might not be working properly.'
)
).not.toBeInTheDocument();
});
it('renders the mute button and can mute/unmute alerts', async () => {
const postRequest = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/snooze/`,
method: 'POST',
});
const deleteRequest = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/snooze/`,
method: 'DELETE',
});
createWrapper();
expect(await screen.findByText('Mute')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: 'Mute'}));
expect(postRequest).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({data: {target: 'me'}})
);
expect(await screen.findByText('Unmute')).toBeInTheDocument();
await userEvent.click(screen.getByRole('button', {name: 'Unmute'}));
expect(deleteRequest).toHaveBeenCalledTimes(1);
});
it('mutes alert if query parameter is set', async () => {
const request = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/snooze/`,
method: 'POST',
});
const contextWithQueryParam = initializeOrg({
organization: {features: ['issue-alert-incompatible-rules', 'mute-alerts']},
router: {
location: {query: {mute: '1'}},
},
});
createWrapper({}, contextWithQueryParam);
expect(await screen.findByText('Unmute')).toBeInTheDocument();
expect(request).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({
data: {target: 'me'},
})
);
});
it('mute button is disabled if no alerts:write permission', async () => {
const orgWithoutAccess = {
features: ['issue-alert-incompatible-rules', 'mute-alerts'],
access: [],
};
const contextWithoutAccess = initializeOrg({
organization: orgWithoutAccess,
});
createWrapper({}, contextWithoutAccess, orgWithoutAccess);
expect(await screen.findByRole('button', {name: 'Mute'})).toBeDisabled();
});
it('inserts user email into rule notify action', async () => {
// Alert rule with "send a notification to member" action
const sendNotificationRule = TestStubs.ProjectAlertRule({
actions: [
{
id: 'sentry.mail.actions.NotifyEmailAction',
name: 'Send a notification to Member and if none can be found then send a notification to ActiveMembers',
targetIdentifier: member.id,
targetType: 'Member',
},
],
});
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/rules/${rule.id}/`,
body: sendNotificationRule,
});
createWrapper();
expect(
await screen.findByText(`Send a notification to ${member.email}`)
).toBeInTheDocument();
});
});