mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
251 lines
7.7 KiB
TypeScript
251 lines
7.7 KiB
TypeScript
import selectEvent from 'react-select-event';
|
|
import styled from '@emotion/styled';
|
|
|
|
import {initializeOrg} from 'sentry-test/initializeOrg';
|
|
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';
|
|
|
|
import {addSuccessMessage} from 'sentry/actionCreators/indicator';
|
|
import {makeCloseButton} from 'sentry/components/globalModal/components';
|
|
import type {IssueConfigField} from 'sentry/types';
|
|
import type {IssueAlertRuleAction} from 'sentry/types/alerts';
|
|
import TicketRuleModal from 'sentry/views/alerts/rules/issue/ticketRuleModal';
|
|
|
|
jest.unmock('sentry/utils/recreateRoute');
|
|
jest.mock('sentry/actionCreators/indicator');
|
|
jest.mock('sentry/actionCreators/onboardingTasks');
|
|
|
|
describe('ProjectAlerts -> TicketRuleModal', function () {
|
|
const closeModal = jest.fn();
|
|
const modalElements = {
|
|
Header: p => p.children,
|
|
Body: p => p.children,
|
|
Footer: p => p.children,
|
|
};
|
|
|
|
afterEach(function () {
|
|
closeModal.mockReset();
|
|
MockApiClient.clearMockResponses();
|
|
});
|
|
|
|
const doSubmit = async () =>
|
|
await userEvent.click(screen.getByRole('button', {name: 'Apply Changes'}));
|
|
|
|
const submitSuccess = async () => {
|
|
await doSubmit();
|
|
expect(addSuccessMessage).toHaveBeenCalled();
|
|
expect(closeModal).toHaveBeenCalled();
|
|
};
|
|
|
|
const submitErrors = async errorCount => {
|
|
await doSubmit();
|
|
expect(screen.getAllByText('Field is required')).toHaveLength(errorCount);
|
|
expect(closeModal).toHaveBeenCalledTimes(0);
|
|
};
|
|
|
|
const addMockConfigsAPICall = (otherField = {}) => {
|
|
return MockApiClient.addMockResponse({
|
|
url: '/organizations/org-slug/integrations/1/?ignored=Sprint',
|
|
method: 'GET',
|
|
body: {
|
|
createIssueConfig: [
|
|
{
|
|
name: 'project',
|
|
label: 'Jira Project',
|
|
choices: [['10000', 'TEST']],
|
|
default: '10000',
|
|
type: 'select',
|
|
updatesForm: true,
|
|
},
|
|
{
|
|
name: 'issuetype',
|
|
label: 'Issue Type',
|
|
default: '10001',
|
|
type: 'select',
|
|
choices: [
|
|
['10001', 'Improvement'],
|
|
['10002', 'Task'],
|
|
['10003', 'Sub-task'],
|
|
['10004', 'New Feature'],
|
|
['10005', 'Bug'],
|
|
['10000', 'Epic'],
|
|
],
|
|
updatesForm: true,
|
|
required: true,
|
|
},
|
|
otherField,
|
|
],
|
|
},
|
|
});
|
|
};
|
|
|
|
const renderComponent = (
|
|
props: Partial<IssueAlertRuleAction> = {},
|
|
otherField: IssueConfigField = {
|
|
label: 'Reporter',
|
|
required: true,
|
|
choices: [['a', 'a']],
|
|
type: 'select',
|
|
name: 'reporter',
|
|
}
|
|
) => {
|
|
const {organization, routerContext} = initializeOrg();
|
|
addMockConfigsAPICall(otherField);
|
|
|
|
const body = styled(c => c.children);
|
|
return render(
|
|
<TicketRuleModal
|
|
{...modalElements}
|
|
CloseButton={makeCloseButton(() => {})}
|
|
closeModal={closeModal}
|
|
Body={body()}
|
|
Footer={body()}
|
|
formFields={{}}
|
|
link=""
|
|
ticketType=""
|
|
instance={{...(props.data || {}), integration: 1}}
|
|
index={0}
|
|
onSubmitAction={() => {}}
|
|
organization={organization}
|
|
/>,
|
|
{context: routerContext}
|
|
);
|
|
};
|
|
|
|
describe('Create Rule', function () {
|
|
it('should render the Ticket Rule modal', function () {
|
|
renderComponent();
|
|
|
|
expect(screen.getByRole('button', {name: 'Apply Changes'})).toBeInTheDocument();
|
|
expect(screen.getByRole('textbox', {name: 'Title'})).toBeInTheDocument();
|
|
expect(screen.getByRole('textbox', {name: 'Description'})).toBeInTheDocument();
|
|
});
|
|
|
|
it('should save the modal data when "Apply Changes" is clicked with valid data', async function () {
|
|
renderComponent();
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should raise validation errors when "Apply Changes" is clicked with invalid data', async function () {
|
|
// This doesn't test anything TicketRules specific but I'm leaving it here as an example.
|
|
renderComponent();
|
|
await submitErrors(1);
|
|
});
|
|
|
|
it('should reload fields when an "updatesForm" field changes', async function () {
|
|
renderComponent();
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a');
|
|
|
|
addMockConfigsAPICall({
|
|
label: 'Assignee',
|
|
required: true,
|
|
choices: [['b', 'b']],
|
|
type: 'select',
|
|
name: 'assignee',
|
|
});
|
|
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Assignee'}), 'b');
|
|
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should ignore error checking when default is empty array', async function () {
|
|
renderComponent(undefined, {
|
|
label: 'Labels',
|
|
required: false,
|
|
choices: [['bug', `bug`]],
|
|
default: undefined,
|
|
type: 'select',
|
|
multiple: true,
|
|
name: 'labels',
|
|
});
|
|
expect(
|
|
screen.queryAllByText(`Could not fetch saved option for Labels. Please reselect.`)
|
|
).toHaveLength(0);
|
|
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Labels'}), 'bug');
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should persist single select values when the modal is reopened', async function () {
|
|
renderComponent({data: {reporter: 'a'}});
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should persist multi select values when the modal is reopened', async function () {
|
|
renderComponent(
|
|
{data: {components: ['a', 'c']}},
|
|
{
|
|
name: 'components',
|
|
label: 'Components',
|
|
default: undefined,
|
|
type: 'select',
|
|
multiple: true,
|
|
required: true,
|
|
choices: [
|
|
['a', 'a'],
|
|
['b', 'b'],
|
|
['c', 'c'],
|
|
],
|
|
}
|
|
);
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should not persist value when unavailable in new choices', async function () {
|
|
renderComponent({data: {reporter: 'a'}});
|
|
|
|
addMockConfigsAPICall({
|
|
label: 'Reporter',
|
|
required: true,
|
|
choices: [['b', 'b']],
|
|
type: 'select',
|
|
name: 'reporter',
|
|
ignorePriorChoices: true,
|
|
});
|
|
|
|
// Switch Issue Type so we refetch the config and update Reporter choices
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
|
|
await expect(
|
|
selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'a')
|
|
).rejects.toThrow();
|
|
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Reporter'}), 'b');
|
|
await submitSuccess();
|
|
});
|
|
|
|
it('should get async options from URL', async function () {
|
|
renderComponent();
|
|
addMockConfigsAPICall({
|
|
label: 'Assignee',
|
|
required: true,
|
|
url: 'http://example.com',
|
|
type: 'select',
|
|
name: 'assignee',
|
|
});
|
|
|
|
await selectEvent.select(screen.getByRole('textbox', {name: 'Issue Type'}), 'Epic');
|
|
|
|
// Component makes 1 request per character typed.
|
|
let txt = '';
|
|
for (const char of 'Joe') {
|
|
txt += char;
|
|
MockApiClient.addMockResponse({
|
|
url: `http://example.com?field=assignee&issuetype=10001&project=10000&query=${txt}`,
|
|
method: 'GET',
|
|
body: [{label: 'Joe', value: 'Joe'}],
|
|
});
|
|
}
|
|
|
|
const menu = screen.getByRole('textbox', {name: 'Assignee'});
|
|
selectEvent.openMenu(menu);
|
|
await userEvent.type(menu, 'Joe{Escape}');
|
|
await selectEvent.select(menu, 'Joe');
|
|
|
|
await submitSuccess();
|
|
});
|
|
});
|
|
});
|