123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import styled from '@emotion/styled';
- import {SentryAppFixture} from 'sentry-fixture/sentryApp';
- import {SentryAppInstallationFixture} from 'sentry-fixture/sentryAppInstallation';
- import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
- import {makeCloseButton} from 'sentry/components/globalModal/components';
- import SentryAppRuleModal from 'sentry/views/alerts/rules/issue/sentryAppRuleModal';
- import type {
- FieldFromSchema,
- SchemaFormConfig,
- } from 'sentry/views/settings/organizationIntegrations/sentryAppExternalForm';
- describe('SentryAppRuleModal', function () {
- const modalElements = {
- Header: p => p.children,
- Body: p => p.children,
- Footer: p => p.children,
- };
- let sentryApp;
- let sentryAppInstallation;
- beforeEach(function () {
- sentryApp = SentryAppFixture();
- sentryAppInstallation = SentryAppInstallationFixture();
- });
- const _submit = async () => {
- await userEvent.click(screen.getByText('Save Changes'));
- return screen.queryAllByText('Field is required');
- };
- const submitSuccess = async () => {
- const errors = await _submit();
- expect(errors).toHaveLength(0);
- };
- const defaultConfig: SchemaFormConfig = {
- uri: '/integration/test/',
- description: '',
- required_fields: [
- {
- type: 'text',
- label: 'Alert Title',
- name: 'title',
- },
- {
- type: 'textarea',
- label: 'Alert Description',
- name: 'description',
- },
- {
- type: 'select',
- label: 'Team Channel',
- name: 'channel',
- choices: [
- ['valor', 'valor'],
- ['mystic', 'mystic'],
- ['instinct', 'instinct'],
- ],
- },
- ],
- optional_fields: [
- {
- type: 'text',
- label: 'Extra Details',
- name: 'extra',
- },
- {
- type: 'select',
- label: 'Assignee',
- name: 'assignee',
- uri: '/link/assignee/',
- },
- {
- type: 'select',
- label: 'Workspace',
- name: 'workspace',
- uri: '/link/workspace/',
- },
- ],
- };
- const resetValues = {
- settings: [
- {
- name: 'extra',
- value: 'saved details from last edit',
- },
- {
- name: 'assignee',
- value: 'edna-mode',
- label: 'Edna Mode',
- },
- ],
- };
- const createWrapper = (props = {}) => {
- const styledWrapper = styled(c => c.children);
- return render(
- <SentryAppRuleModal
- {...modalElements}
- sentryAppInstallationUuid={sentryAppInstallation.uuid}
- appName={sentryApp.name}
- config={defaultConfig}
- onSubmitSuccess={() => {}}
- resetValues={resetValues}
- closeModal={jest.fn()}
- CloseButton={makeCloseButton(() => {})}
- Body={styledWrapper()}
- Footer={styledWrapper()}
- {...props}
- />
- );
- };
- describe('Create UI Alert Rule', function () {
- it('should render the Alert Rule modal with the config fields', function () {
- createWrapper();
- const {required_fields, optional_fields} = defaultConfig;
- const allFields = [...required_fields!, ...optional_fields!];
- allFields.forEach((field: FieldFromSchema) => {
- if (typeof field.label === 'string') {
- expect(screen.getByText(field.label)).toBeInTheDocument();
- }
- });
- });
- it('submit button shall be disabled if form is incomplete', async function () {
- createWrapper();
- expect(screen.getByRole('button', {name: 'Save Changes'})).toBeDisabled();
- await userEvent.hover(screen.getByRole('button', {name: 'Save Changes'}));
- expect(
- await screen.findByText('Required fields must be filled out')
- ).toBeInTheDocument();
- });
- it('should submit when "Save Changes" is clicked with valid data', async function () {
- createWrapper();
- const titleInput = screen.getByTestId('title');
- await userEvent.type(titleInput, 'some title');
- const descriptionInput = screen.getByTestId('description');
- await userEvent.type(descriptionInput, 'some description');
- const channelInput = screen.getAllByText('Type to search')[0];
- await userEvent.type(channelInput, '{keyDown}');
- await userEvent.click(screen.getByText('valor'));
- // Ensure text fields are persisted on edit
- const savedExtraDetailsInput = screen.getByDisplayValue(
- resetValues.settings[0].value
- );
- expect(savedExtraDetailsInput).toBeInTheDocument();
- // Ensure select fields are persisted with labels on edit
- const savedAssigneeInput = screen.getByText(resetValues.settings[1].label!);
- expect(savedAssigneeInput).toBeInTheDocument();
- // Ensure async select fields filter correctly
- const workspaceChoices = [
- ['WS0', 'Primary Workspace'],
- ['WS1', 'Secondary Workspace'],
- ];
- const workspaceResponse = MockApiClient.addMockResponse({
- url: `/sentry-app-installations/${sentryAppInstallation.uuid}/external-requests/`,
- body: {choices: workspaceChoices},
- });
- const workspaceInput = screen.getByText('Type to search');
- // Search by value
- await userEvent.type(workspaceInput, workspaceChoices[1][0]);
- await waitFor(() => expect(workspaceResponse).toHaveBeenCalled());
- // Select by label
- await userEvent.click(screen.getByText(workspaceChoices[1][1]));
- await submitSuccess();
- });
- it('should load complexity options from backend when column has a default value', async function () {
- const mockApi = MockApiClient.addMockResponse({
- url: `/sentry-app-installations/${sentryAppInstallation.uuid}/external-requests/`,
- body: {
- choices: [
- ['low', 'Low'],
- ['medium', 'Medium'],
- ['high', 'High'],
- ],
- },
- });
- const schema: SchemaFormConfig = {
- uri: '/api/sentry/issue-link/create/',
- required_fields: [
- {
- type: 'text',
- label: 'Task Name',
- name: 'title',
- default: 'issue.title',
- },
- {
- type: 'select',
- label: "What's the status of this task?",
- name: 'column',
- uri: '/api/sentry/options/status/',
- defaultValue: 'ongoing',
- choices: [
- ['ongoing', 'ongoing'],
- ['completed', 'completed'],
- ['pending', 'pending'],
- ['cancelled', 'cancelled'],
- ],
- },
- ],
- optional_fields: [
- {
- type: 'select',
- label: 'What is the estimated complexity?',
- name: 'complexity',
- depends_on: ['column'],
- skip_load_on_open: true,
- uri: '/api/sentry/options/complexity-options/',
- choices: [],
- },
- ],
- };
- createWrapper({config: schema});
- // Wait for component to mount and state to update
- await waitFor(() => expect(mockApi).toHaveBeenCalled());
- // Check if complexity options are loaded
- const complexityInput = screen.getByLabelText('What is the estimated complexity?', {
- selector: 'input#complexity',
- });
- await userEvent.click(complexityInput);
- expect(screen.getByText('Low')).toBeInTheDocument();
- expect(screen.getByText('Medium')).toBeInTheDocument();
- expect(screen.getByText('High')).toBeInTheDocument();
- });
- });
- });
|