import {GroupFixture} from 'sentry-fixture/group';
import {OrganizationFixture} from 'sentry-fixture/organization';
import {RouterFixture} from 'sentry-fixture/routerFixture';
import {render, screen} from 'sentry-test/reactTestingLibrary';
import {GroupRelatedIssues} from 'sentry/views/issueDetails/groupRelatedIssues';
describe('Related Issues View', function () {
let sameRootIssuesMock: jest.Mock;
let traceIssuesMock: jest.Mock;
let issuesMock: jest.Mock;
const organization = OrganizationFixture();
const groupId = '12345678';
const group = GroupFixture({id: groupId});
const router = RouterFixture({
params: {groupId: group.id},
});
const orgSlug = organization.slug;
const group1 = '15';
const group2 = '20';
// query=issue.id:[15,20] -> query=issue.id%3A%5B15%2C20%5D
const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id%3A%5B${group1}%2C${group2}%5D`;
const errorType = 'RuntimeError';
const onlySameRootData = {
type: 'same_root_cause',
data: [group1, group2],
};
const onlyTraceConnectedData = {
type: 'trace_connected',
data: [group1, group2],
meta: {
event_id: 'abcd',
trace_id: '1234',
},
};
const issuesData = [
{
id: group1,
shortId: `EARTH-${group1}`,
project: {id: '3', name: 'Earth', slug: 'earth', platform: null},
type: 'error',
metadata: {
type: errorType,
},
issueCategory: 'error',
lastSeen: '2024-03-15T20:15:30Z',
},
{
id: group2,
shortId: `EARTH-${group2}`,
project: {id: '3', name: 'Earth', slug: 'earth', platform: null},
type: 'error',
metadata: {
type: errorType,
},
issueCategory: 'error',
lastSeen: '2024-03-16T20:15:30Z',
},
];
beforeEach(function () {
// GroupList calls this but we don't need it for this test
MockApiClient.addMockResponse({
url: `/organizations/${orgSlug}/users/`,
body: {},
});
MockApiClient.addMockResponse({
url: `/organizations/org-slug/issues/${group.id}/`,
body: group,
});
});
afterEach(() => {
MockApiClient.clearMockResponses();
jest.clearAllMocks();
});
it('renders with same root issues', async function () {
sameRootIssuesMock = MockApiClient.addMockResponse({
url: `/issues/${groupId}/related-issues/?type=same_root_cause`,
body: onlySameRootData,
});
MockApiClient.addMockResponse({
url: `/issues/${groupId}/related-issues/?type=trace_connected`,
body: [],
});
issuesMock = MockApiClient.addMockResponse({
url: orgIssuesEndpoint,
body: issuesData,
});
render(, {router, organization});
// Wait for the issues showing up on the table
expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument();
expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument();
expect(sameRootIssuesMock).toHaveBeenCalled();
expect(issuesMock).toHaveBeenCalled();
const linkButton = screen.getByRole('button', {name: /open in issues/i});
expect(linkButton).toHaveAttribute(
'href',
// Opening in Issues needs to include the group we are currently viewing
`/organizations/org-slug/issues/?project=-1&query=issue.id:[${groupId},${group1},${group2}]`
);
});
it('renders with trace connected issues', async function () {
MockApiClient.addMockResponse({
url: `/issues/${groupId}/related-issues/?type=same_root_cause`,
body: [],
});
traceIssuesMock = MockApiClient.addMockResponse({
url: `/issues/${groupId}/related-issues/?type=trace_connected`,
body: onlyTraceConnectedData,
});
issuesMock = MockApiClient.addMockResponse({
url: orgIssuesEndpoint,
body: issuesData,
});
render(, {router, organization});
// Wait for the issues showing up on the table
expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument();
expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument();
expect(traceIssuesMock).toHaveBeenCalled();
expect(issuesMock).toHaveBeenCalled();
const linkElement = screen.getByRole('link', {name: /this trace/i});
expect(linkElement).toHaveAttribute(
'href',
'/organizations/org-slug/performance/trace/1234/?node=error-abcd'
);
const linkButton = screen.getByRole('button', {name: /open in issues/i});
// The Issue search supports using `trace` as a parameter
expect(linkButton).toHaveAttribute(
'href',
`/organizations/org-slug/issues/?project=-1&query=trace:1234`
);
});
});