import {SourceMapArchive} from 'sentry-fixture/sourceMapArchive';
import {SourceMapsDebugIDBundles} from 'sentry-fixture/sourceMapsDebugIDBundles';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {
render,
renderGlobalModal,
screen,
userEvent,
waitFor,
within,
} from 'sentry-test/reactTestingLibrary';
import {textWithMarkupMatcher} from 'sentry-test/utils';
import {ProjectSourceMaps} from 'sentry/views/settings/projectSourceMaps/projectSourceMaps';
function renderReleaseBundlesMockRequests({
orgSlug,
projectSlug,
empty,
}: {
orgSlug: string;
projectSlug: string;
empty?: boolean;
}) {
const sourceMaps = MockApiClient.addMockResponse({
url: `/projects/${orgSlug}/${projectSlug}/files/source-maps/`,
body: empty
? []
: [
SourceMapArchive(),
SourceMapArchive({
id: 2,
name: 'abc',
fileCount: 3,
date: '2023-05-06T13:41:00Z',
}),
],
});
return {sourceMaps};
}
function renderDebugIdBundlesMockRequests({
orgSlug,
projectSlug,
empty,
}: {
orgSlug: string;
projectSlug: string;
empty?: boolean;
}) {
const artifactBundles = MockApiClient.addMockResponse({
url: `/projects/${orgSlug}/${projectSlug}/files/artifact-bundles/`,
body: empty ? [] : SourceMapsDebugIDBundles(),
});
const artifactBundlesDeletion = MockApiClient.addMockResponse({
url: `/projects/${orgSlug}/${projectSlug}/files/artifact-bundles/`,
method: 'DELETE',
});
return {artifactBundles, artifactBundlesDeletion};
}
describe('ProjectSourceMaps', function () {
describe('Release Bundles', function () {
it('renders default state', async function () {
const {organization, project, routerContext, routerProps} = initializeOrg({
router: {
location: {
query: {},
pathname: `/settings/${initializeOrg().organization.slug}/projects/${
initializeOrg().project.slug
}/source-maps/release-bundles/`,
},
},
});
const mockRequests = renderReleaseBundlesMockRequests({
orgSlug: organization.slug,
projectSlug: project.slug,
});
render(, {
context: routerContext,
organization,
});
// Title
expect(screen.getByRole('heading', {name: 'Source Maps'})).toBeInTheDocument();
// Active tab
const tabs = screen.getAllByRole('listitem');
expect(tabs).toHaveLength(2);
// Tab 1
expect(tabs[0]).toHaveTextContent('Artifact Bundles');
expect(tabs[0]).not.toHaveClass('active');
expect(within(tabs[0]).getByRole('link')).toHaveAttribute(
'href',
'/settings/org-slug/projects/project-slug/source-maps/artifact-bundles/?'
);
// Tab 2
expect(tabs[1]).toHaveTextContent('Release Bundles');
expect(tabs[1]).toHaveClass('active');
expect(within(tabs[0]).getByRole('link')).toHaveAttribute(
'href',
'/settings/org-slug/projects/project-slug/source-maps/artifact-bundles/?'
);
// Search bar
expect(screen.getByPlaceholderText('Filter by Name')).toBeInTheDocument();
// Date Uploaded can be sorted
await userEvent.hover(screen.getByTestId('icon-arrow'));
expect(await screen.findByText('Switch to ascending order')).toBeInTheDocument();
await userEvent.click(screen.getByTestId('icon-arrow'));
await waitFor(() => {
expect(mockRequests.sourceMaps).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/source-maps/',
expect.objectContaining({
query: expect.objectContaining({
sortBy: 'date_added',
}),
})
);
});
// Active tab contains correct link
expect(screen.getByRole('link', {name: /Release Bundles/})).toHaveAttribute(
'href',
'/settings/org-slug/projects/project-slug/source-maps/release-bundles/?'
);
// Artifact Bundles Tab
expect(screen.getByRole('link', {name: /Artifact Bundles/})).toHaveAttribute(
'href',
'/settings/org-slug/projects/project-slug/source-maps/artifact-bundles/?'
);
// Name
expect(await screen.findByRole('link', {name: '1234'})).toBeInTheDocument();
// Artifacts
expect(screen.getByText('0')).toBeInTheDocument();
// Date
expect(screen.getByText('May 6, 2020 1:41 PM UTC')).toBeInTheDocument();
// Delete buttons (this example renders 2 rows)
expect(screen.getAllByRole('button', {name: 'Remove All Artifacts'})).toHaveLength(
2
);
expect(
screen.getAllByRole('button', {name: 'Remove All Artifacts'})[0]
).toBeEnabled();
renderGlobalModal();
// Delete item displays a confirmation modal
await userEvent.click(
screen.getAllByRole('button', {name: 'Remove All Artifacts'})[0]
);
expect(
await screen.findByText(
'Are you sure you want to remove all artifacts in this archive?'
)
).toBeInTheDocument();
// Close modal
await userEvent.click(screen.getByRole('button', {name: 'Cancel'}));
});
it('renders empty state', async function () {
const {organization, project, routerContext, routerProps} = initializeOrg({
router: {
location: {
query: {},
pathname: `/settings/${initializeOrg().organization.slug}/projects/${
initializeOrg().project.slug
}/source-maps/release-bundles/`,
},
},
});
renderReleaseBundlesMockRequests({
orgSlug: organization.slug,
projectSlug: project.slug,
empty: true,
});
render(, {
context: routerContext,
organization,
});
expect(
await screen.findByText('No release bundles found for this project.')
).toBeInTheDocument();
});
});
describe('Artifact Bundles', function () {
it('renders default state', async function () {
const {organization, project, routerContext, router, routerProps} = initializeOrg({
router: {
location: {
query: {},
pathname: `/settings/${initializeOrg().organization.slug}/projects/${
initializeOrg().project.slug
}/source-maps/artifact-bundles/`,
},
},
});
const mockRequests = renderDebugIdBundlesMockRequests({
orgSlug: organization.slug,
projectSlug: project.slug,
});
render(, {
context: routerContext,
organization,
});
// Title
expect(screen.getByRole('heading', {name: 'Source Maps'})).toBeInTheDocument();
// Active tab
const tabs = screen.getAllByRole('listitem');
expect(tabs).toHaveLength(2);
// Tab 1
expect(tabs[0]).toHaveTextContent('Artifact Bundles');
expect(tabs[0]).toHaveClass('active');
// Tab 2
expect(tabs[1]).toHaveTextContent('Release Bundles');
expect(tabs[1]).not.toHaveClass('active');
// Search bar
expect(
screen.getByPlaceholderText('Filter by Bundle ID, Debug ID or Release')
).toBeInTheDocument();
// Date Uploaded can be sorted
await userEvent.click(screen.getByTestId('date-uploaded-header'));
await userEvent.hover(screen.getByTestId('icon-arrow'));
expect(await screen.findByText('Switch to ascending order')).toBeInTheDocument();
await userEvent.click(screen.getByTestId('icon-arrow'));
await waitFor(() => {
expect(mockRequests.artifactBundles).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/artifact-bundles/',
expect.objectContaining({
query: expect.objectContaining({
sortBy: 'date_added',
}),
})
);
});
// Date Uploaded can be sorted in descending
await userEvent.hover(screen.getByTestId('icon-arrow'));
expect(await screen.findByText('Switch to descending order')).toBeInTheDocument();
await userEvent.click(screen.getByTestId('icon-arrow'));
await waitFor(() => {
expect(mockRequests.artifactBundles).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/artifact-bundles/',
expect.objectContaining({
query: expect.objectContaining({
sortBy: '-date_added',
}),
})
);
});
// Date Modified can be sorted
await userEvent.click(screen.getByTestId('date-modified-header'));
await userEvent.hover(screen.getByTestId('icon-arrow-modified'));
expect(await screen.findByText('Switch to ascending order')).toBeInTheDocument();
await userEvent.click(screen.getByTestId('icon-arrow-modified'));
await waitFor(() => {
expect(mockRequests.artifactBundles).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/artifact-bundles/',
expect.objectContaining({
query: expect.objectContaining({
sortBy: 'date_modified',
}),
})
);
});
// Date Modified can be sorted in descending
await userEvent.hover(screen.getByTestId('icon-arrow-modified'));
expect(await screen.findByText('Switch to descending order')).toBeInTheDocument();
await userEvent.click(screen.getByTestId('icon-arrow-modified'));
await waitFor(() => {
expect(mockRequests.artifactBundles).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/artifact-bundles/',
expect.objectContaining({
query: expect.objectContaining({
sortBy: '-date_modified',
}),
})
);
});
// Artifacts
expect(screen.getByText('39')).toBeInTheDocument();
// Date Modified
expect(screen.getByText('Mar 10, 2023 8:25 AM UTC')).toBeInTheDocument();
// Date Uploaded
expect(screen.getByText('Mar 8, 2023 9:53 AM UTC')).toBeInTheDocument();
// Delete button
expect(screen.getByRole('button', {name: 'Remove All Artifacts'})).toBeEnabled();
// Release information
expect(
await screen.findByText(textWithMarkupMatcher('2 Releases associated'))
).toBeInTheDocument();
await userEvent.hover(screen.getByText('2 Releases'));
expect(
await screen.findByText('frontend@2e318148eac9298ec04a662ae32b4b093b027f0a')
).toBeInTheDocument();
// Click on bundle id
await userEvent.click(
screen.getByRole('link', {name: 'b916a646-2c6b-4e45-af4c-409830a44e0e'})
);
expect(router.push).toHaveBeenLastCalledWith(
'/settings/org-slug/projects/project-slug/source-maps/artifact-bundles/b916a646-2c6b-4e45-af4c-409830a44e0e'
);
renderGlobalModal();
// Delete item displays a confirmation modal
await userEvent.click(screen.getByRole('button', {name: 'Remove All Artifacts'}));
expect(
await screen.findByText(
'Are you sure you want to remove all artifacts in this archive?'
)
).toBeInTheDocument();
// Close modal
await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
await waitFor(() => {
expect(mockRequests.artifactBundlesDeletion).toHaveBeenLastCalledWith(
'/projects/org-slug/project-slug/files/artifact-bundles/',
expect.objectContaining({
query: expect.objectContaining({
bundleId: 'b916a646-2c6b-4e45-af4c-409830a44e0e',
}),
})
);
});
});
it('renders empty state', async function () {
const {organization, project, routerProps, routerContext} = initializeOrg({
router: {
location: {
query: {},
pathname: `/settings/${initializeOrg().organization.slug}/projects/${
initializeOrg().project.slug
}/source-maps/artifact-bundles/`,
},
},
});
renderDebugIdBundlesMockRequests({
orgSlug: organization.slug,
projectSlug: project.slug,
empty: true,
});
render(, {
context: routerContext,
organization,
});
expect(
await screen.findByText('No artifact bundles found for this project.')
).toBeInTheDocument();
});
});
});