123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373 |
- import {AutofixCodebaseChangeData} from 'sentry-fixture/autofixCodebaseChangeData';
- import {AutofixStepFixture} from 'sentry-fixture/autofixStep';
- import {
- render,
- renderGlobalModal,
- screen,
- userEvent,
- waitFor,
- within,
- } from 'sentry-test/reactTestingLibrary';
- import {addErrorMessage, addSuccessMessage} from 'sentry/actionCreators/indicator';
- import AutofixMessageBox from 'sentry/components/events/autofix/autofixMessageBox';
- import {AutofixStatus, AutofixStepType} from 'sentry/components/events/autofix/types';
- jest.mock('sentry/actionCreators/indicator');
- describe('AutofixMessageBox', () => {
- const defaultProps = {
- displayText: 'Test display text',
- groupId: '123',
- runId: '456',
- actionText: 'Send',
- allowEmptyMessage: false,
- responseRequired: false,
- step: null,
- onSend: null,
- };
- const changesStepProps = {
- ...defaultProps,
- isChangesStep: true,
- step: AutofixStepFixture({
- type: AutofixStepType.CHANGES,
- changes: [AutofixCodebaseChangeData()],
- }),
- };
- const prCreatedProps = {
- ...changesStepProps,
- step: AutofixStepFixture({
- type: AutofixStepType.CHANGES,
- status: AutofixStatus.COMPLETED,
- changes: [AutofixCodebaseChangeData()],
- }),
- };
- const multiplePRsProps = {
- ...changesStepProps,
- step: AutofixStepFixture({
- type: AutofixStepType.CHANGES,
- status: AutofixStatus.COMPLETED,
- changes: [
- AutofixCodebaseChangeData({
- repo_name: 'example/repo1',
- pull_request: {
- pr_url: 'https://github.com/example/repo1/pull/1',
- pr_number: 1,
- },
- }),
- AutofixCodebaseChangeData({
- repo_name: 'example/repo1',
- pull_request: {
- pr_url: 'https://github.com/example/repo2/pull/2',
- pr_number: 2,
- },
- }),
- ],
- }),
- };
- beforeEach(() => {
- (addSuccessMessage as jest.Mock).mockClear();
- (addErrorMessage as jest.Mock).mockClear();
- MockApiClient.clearMockResponses();
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/setup/?check_write_access=true',
- method: 'GET',
- body: {
- genAIConsent: {ok: true},
- integration: {ok: true},
- },
- });
- });
- it('renders correctly with default props', () => {
- render(<AutofixMessageBox {...defaultProps} />);
- expect(screen.getByText('Test display text')).toBeInTheDocument();
- expect(
- screen.getByPlaceholderText('Share helpful context or directions...')
- ).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Send'})).toBeInTheDocument();
- });
- it('calls onSend when provided and button is clicked', async () => {
- const onSendMock = jest.fn();
- render(<AutofixMessageBox {...defaultProps} onSend={onSendMock} />);
- const input = screen.getByPlaceholderText('Share helpful context or directions...');
- await userEvent.type(input, 'Test message');
- await userEvent.click(screen.getByRole('button', {name: 'Send'}));
- expect(onSendMock).toHaveBeenCalledWith('Test message');
- });
- it('sends interjection message when onSend is not provided', async () => {
- MockApiClient.addMockResponse({
- method: 'POST',
- url: '/issues/123/autofix/update/',
- body: {},
- });
- render(<AutofixMessageBox {...defaultProps} />);
- const input = screen.getByPlaceholderText('Share helpful context or directions...');
- await userEvent.type(input, 'Test message');
- await userEvent.click(screen.getByRole('button', {name: 'Send'}));
- await waitFor(() => {
- expect(addSuccessMessage).toHaveBeenCalledWith('Thanks for the input.');
- });
- });
- it('displays error message when API request fails', async () => {
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/update/',
- method: 'POST',
- body: {
- detail: 'Internal Error',
- },
- statusCode: 500,
- });
- render(<AutofixMessageBox {...defaultProps} />);
- const input = screen.getByPlaceholderText('Share helpful context or directions...');
- await userEvent.type(input, 'Test message');
- await userEvent.click(screen.getByRole('button', {name: 'Send'}));
- await waitFor(() => {
- expect(addErrorMessage).toHaveBeenCalledWith(
- 'Something went wrong when sending Autofix your message.'
- );
- });
- });
- it('renders step icon and title when step is provided', () => {
- const stepProps = {
- ...defaultProps,
- step: AutofixStepFixture(),
- };
- render(<AutofixMessageBox {...stepProps} />);
- expect(screen.getByText(AutofixStepFixture().title)).toBeInTheDocument();
- });
- it('renders required input style when responseRequired is true', () => {
- render(<AutofixMessageBox {...defaultProps} responseRequired />);
- expect(
- screen.getByPlaceholderText('Please answer to continue...')
- ).toBeInTheDocument();
- });
- it('handles suggested root cause selection correctly', async () => {
- const onSendMock = jest.fn();
- render(
- <AutofixMessageBox {...defaultProps} onSend={onSendMock} isRootCauseSelectionStep />
- );
- // Test suggested root cause
- await userEvent.click(screen.getByRole('button', {name: 'Use suggested root cause'}));
- const input = screen.getByPlaceholderText(
- '(Optional) Provide any instructions for the fix...'
- );
- await userEvent.type(input, 'Use this suggestion');
- await userEvent.click(screen.getByRole('button', {name: 'Send'}));
- expect(onSendMock).toHaveBeenCalledWith('Use this suggestion', false);
- });
- it('handles custom root cause selection correctly', async () => {
- const onSendMock = jest.fn();
- render(
- <AutofixMessageBox {...defaultProps} onSend={onSendMock} isRootCauseSelectionStep />
- );
- // Test custom root cause
- await userEvent.click(
- screen.getByRole('button', {name: 'Propose your own root cause'})
- );
- const customInput = screen.getByPlaceholderText('Propose your own root cause...');
- await userEvent.type(customInput, 'Custom root cause');
- await userEvent.click(screen.getByRole('button', {name: 'Send'}));
- expect(onSendMock).toHaveBeenCalledWith('Custom root cause', true);
- });
- it('renders segmented control for changes step', () => {
- render(<AutofixMessageBox {...changesStepProps} />);
- expect(screen.getByRole('button', {name: 'Iterate'})).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Use this code'})).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Add tests'})).toBeInTheDocument();
- });
- it('shows feedback input when "Iterate" is selected', async () => {
- render(<AutofixMessageBox {...changesStepProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Iterate'}));
- expect(
- screen.getByPlaceholderText('Share helpful context or directions...')
- ).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Send'})).toBeInTheDocument();
- });
- it('shows "Draft PR" button when "Approve" is selected', async () => {
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/setup/?check_write_access=true',
- method: 'GET',
- body: {
- genAIConsent: {ok: true},
- integration: {ok: true},
- githubWriteIntegration: {
- repos: [{ok: true, owner: 'owner', name: 'hello-world', id: 100}],
- },
- },
- });
- render(<AutofixMessageBox {...changesStepProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Use this code'}));
- expect(screen.getByText('Push the above changes to a branch?')).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Draft PR'})).toBeInTheDocument();
- });
- it('shows "Draft PRs" button with correct text for multiple changes', async () => {
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/setup/?check_write_access=true',
- method: 'GET',
- body: {
- genAIConsent: {ok: true},
- integration: {ok: true},
- githubWriteIntegration: {
- repos: [{ok: true, owner: 'owner', name: 'hello-world', id: 100}],
- },
- },
- });
- const multipleChangesProps = {
- ...changesStepProps,
- step: {
- ...changesStepProps.step,
- changes: [AutofixCodebaseChangeData(), AutofixCodebaseChangeData()],
- },
- };
- render(<AutofixMessageBox {...multipleChangesProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Use this code'}));
- expect(screen.getByText('Push the above changes to 2 branches?')).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Draft PRs'})).toBeInTheDocument();
- });
- it('shows "View PR" buttons when PRs are created', () => {
- render(<AutofixMessageBox {...prCreatedProps} />);
- expect(screen.getByRole('button', {name: /View PR in/})).toBeInTheDocument();
- expect(screen.getByRole('button', {name: /View PR in/})).toHaveAttribute(
- 'href',
- 'https://github.com/owner/hello-world/pull/200'
- );
- });
- it('shows multiple "View PR" buttons for multiple PRs', () => {
- render(<AutofixMessageBox {...multiplePRsProps} />);
- const viewPRButtons = screen.getAllByRole('button', {name: /View PR in/});
- expect(viewPRButtons).toHaveLength(2);
- expect(viewPRButtons[0]).toHaveAttribute(
- 'href',
- 'https://github.com/example/repo1/pull/1'
- );
- expect(viewPRButtons[1]).toHaveAttribute(
- 'href',
- 'https://github.com/example/repo2/pull/2'
- );
- });
- it('shows "Draft PRs" button that opens setup modal when setup is incomplete', async () => {
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/setup/?check_write_access=true',
- method: 'GET',
- body: {
- genAIConsent: {ok: true},
- integration: {ok: true},
- githubWriteIntegration: {
- repos: [
- {ok: false, provider: 'github', owner: 'owner', name: 'hello-world', id: 100},
- ],
- },
- },
- });
- MockApiClient.addMockResponse({
- url: '/issues/123/autofix/setup/',
- method: 'GET',
- body: {
- genAIConsent: {ok: true},
- integration: {ok: true},
- },
- });
- render(<AutofixMessageBox {...changesStepProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Use this code'}));
- expect(screen.getByText('Push the above changes to a branch?')).toBeInTheDocument();
- const createPRsButton = screen.getByRole('button', {name: 'Draft PR'});
- expect(createPRsButton).toBeInTheDocument();
- renderGlobalModal();
- await userEvent.click(createPRsButton);
- expect(await screen.findByRole('dialog')).toBeInTheDocument();
- expect(
- within(screen.getByRole('dialog')).getByText('Allow Autofix to Make Pull Requests')
- ).toBeInTheDocument();
- });
- it('shows option buttons for changes step', () => {
- render(<AutofixMessageBox {...changesStepProps} />);
- expect(screen.getByRole('button', {name: 'Use this code'})).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Iterate'})).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Add tests'})).toBeInTheDocument();
- });
- it('shows "Test" button and static message when "Test" is selected', async () => {
- render(<AutofixMessageBox {...changesStepProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Add tests'}));
- expect(
- screen.getByText('Write unit tests to make sure the issue is fixed?')
- ).toBeInTheDocument();
- expect(screen.getByRole('button', {name: 'Add Tests'})).toBeInTheDocument();
- });
- it('sends correct message when "Test" is clicked without onSend prop', async () => {
- MockApiClient.addMockResponse({
- method: 'POST',
- url: '/issues/123/autofix/update/',
- body: {},
- });
- render(<AutofixMessageBox {...changesStepProps} />);
- await userEvent.click(screen.getByRole('button', {name: 'Add tests'}));
- await userEvent.click(screen.getByRole('button', {name: 'Add Tests'}));
- await waitFor(() => {
- expect(addSuccessMessage).toHaveBeenCalledWith('Thanks for the input.');
- });
- });
- });
|