123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- import {AvailableNotificationActionsFixture} from 'sentry-fixture/availableNotificationActions';
- import {ProjectFixture} from 'getsentry-test/fixtures/project';
- import {SubscriptionFixture} from 'getsentry-test/fixtures/subscription';
- import {initializeOrg} from 'sentry-test/initializeOrg';
- import {
- cleanup,
- render,
- renderGlobalModal,
- screen,
- userEvent,
- } from 'sentry-test/reactTestingLibrary';
- import type {Project} from 'sentry/types/project';
- import SubscriptionStore from 'getsentry/stores/subscriptionStore';
- import {SPIKE_PROTECTION_OPTION_DISABLED} from 'getsentry/views/spikeProtection/constants';
- import SpikeProtectionProjects from 'getsentry/views/spikeProtection/spikeProtectionProjects';
- describe('project renders and toggles', () => {
- const projects = [
- ProjectFixture({
- id: '1',
- slug: 'project1',
- // If the project option is True, the feature is disabled
- options: {[SPIKE_PROTECTION_OPTION_DISABLED]: true},
- access: ['project:read'],
- }),
- ProjectFixture({
- id: '2',
- slug: 'project2',
- // If the project option is False, the feature is Enabled
- options: {[SPIKE_PROTECTION_OPTION_DISABLED]: false},
- access: ['project:read', 'project:write', 'project:admin'],
- }),
- ];
- let organization: any,
- router: any,
- mockGet: any,
- mockPost: any,
- mockDelete: any,
- mockDeleteAll: any,
- mockPostAll: any,
- mockGetProjectNotificationActions: any;
- beforeEach(() => {
- MockApiClient.clearMockResponses();
- const newData = initializeOrg({
- projects,
- organization: {
- features: ['global-views'],
- openMembership: true,
- access: ['org:write'],
- },
- });
- organization = newData.organization;
- router = newData.router;
- SubscriptionStore.set(organization.slug, SubscriptionFixture({organization}));
- mockGet = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/`,
- method: 'GET',
- body: projects,
- statusCode: 200,
- });
- mockPost = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/spike-protections/`,
- method: 'POST',
- body: [],
- statusCode: 200,
- });
- mockPostAll = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/spike-protections/?projectSlug=$all`,
- method: 'POST',
- body: [],
- statusCode: 200,
- });
- mockDelete = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/spike-protections/`,
- method: 'DELETE',
- body: [],
- statusCode: 200,
- });
- mockDeleteAll = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/spike-protections/?projectSlug=$all`,
- method: 'DELETE',
- body: [],
- statusCode: 200,
- });
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/notifications/available-actions/`,
- method: 'GET',
- body: AvailableNotificationActionsFixture(),
- statusCode: 200,
- });
- mockGetProjectNotificationActions = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/notifications/actions/`,
- match: [
- MockApiClient.matchQuery({
- triggerType: 'spike-protection',
- project: projects[0]!.id,
- }),
- ],
- method: 'GET',
- body: [
- {
- id: 2,
- organizationId: parseInt(organization.id, 10),
- integrationId: null,
- sentryAppId: null,
- projects: [parseInt(projects[0]!.id, 10)],
- serviceType: 'sentry_notification',
- triggerType: 'spike-protection',
- targetType: 'specific',
- targetIdentifier: 'default',
- targetDisplay: 'default',
- },
- ],
- statusCode: 200,
- });
- });
- afterEach(() => {
- cleanup();
- });
- async function validateComponents(project: Project, isEnabled: boolean) {
- const toggle = await screen.findByTestId(`${project.slug}-spike-protection-toggle`);
- if (isEnabled) {
- expect(toggle).toBeChecked();
- } else {
- expect(toggle).not.toBeChecked();
- }
- return {toggle};
- }
- it('renders projects table even with no projects', async () => {
- const mockGetNoProjects = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/`,
- method: 'GET',
- body: [],
- statusCode: 200,
- });
- render(<SpikeProtectionProjects />, {router});
- expect(mockGetNoProjects).toHaveBeenCalled();
- expect(await screen.findByText('There are no items to display')).toBeInTheDocument();
- });
- it('renders projects accessible to user under closed membership', async () => {
- organization.openMembership = false;
- render(<SpikeProtectionProjects />, {organization});
- expect(await screen.findByText('project1')).toBeInTheDocument();
- const requestOptions = mockGet.mock.calls[0][1];
- expect(requestOptions.query).toEqual(
- expect.objectContaining({query: ' is_member:1'})
- );
- });
- it('renders all projects for superuser', async () => {
- // even under closed membership
- organization.access = ['org:superuser'];
- render(<SpikeProtectionProjects />, {organization});
- expect(await screen.findByText('project1')).toBeInTheDocument();
- const requestOptions = mockGet.mock.calls[0][1];
- expect(requestOptions.query).toEqual(
- expect.not.objectContaining({query: ' is_member:1'})
- );
- });
- it('renders all projects for owner', async () => {
- // even under closed membership
- organization.access = ['org:admin'];
- render(<SpikeProtectionProjects />, {organization});
- expect(await screen.findByText('project1')).toBeInTheDocument();
- const requestOptions = mockGet.mock.calls[0][1];
- expect(requestOptions.query).toEqual(
- expect.not.objectContaining({query: ' is_member:1'})
- );
- });
- it('renders toggles', async () => {
- const project = projects[0]!;
- render(<SpikeProtectionProjects />, {router});
- expect(mockGet).toHaveBeenCalled();
- const {toggle} = await validateComponents(project, false);
- await userEvent.click(toggle);
- await validateComponents(project, true);
- expect(mockPost).toHaveBeenCalled();
- });
- it('renders enabled/disabled toggles from team-roles', async () => {
- const projectDisabled = projects[0]!;
- const projectEnabled = projects[1]!;
- organization.access = [];
- render(<SpikeProtectionProjects />, {organization});
- const toggleDisabled = await screen.findByTestId(
- `${projectDisabled.slug}-spike-protection-toggle`
- );
- expect(toggleDisabled).toBeDisabled();
- const toggleEnabled = await screen.findByTestId(
- `${projectEnabled.slug}-spike-protection-toggle`
- );
- expect(toggleEnabled).toBeEnabled();
- });
- it('renders default value for toggles', async () => {
- const newProjects = [
- ProjectFixture({
- id: '1',
- slug: 'project1',
- options: {[SPIKE_PROTECTION_OPTION_DISABLED]: true},
- }),
- ProjectFixture({
- id: '2',
- slug: 'project2',
- options: {[SPIKE_PROTECTION_OPTION_DISABLED]: false},
- }),
- ];
- MockApiClient.clearMockResponses();
- MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/`,
- method: 'GET',
- body: [...newProjects],
- statusCode: 200,
- });
- MockApiClient.addMockResponse({
- url: `/organizations/org-slug/notifications/available-actions/`,
- method: 'GET',
- body: AvailableNotificationActionsFixture(),
- statusCode: 200,
- });
- MockApiClient.addMockResponse({
- url: `/organizations/org-slug/notifications/actions/`,
- match: [MockApiClient.matchQuery({triggerType: 'spike-protection'})],
- method: 'GET',
- body: [],
- statusCode: 200,
- });
- render(<SpikeProtectionProjects />, {router});
- await validateComponents(newProjects[0]!, false);
- await validateComponents(newProjects[1]!, true);
- });
- it('responds to successful toggles', async () => {
- const project = projects[0]!;
- render(<SpikeProtectionProjects />, {router});
- expect(mockGet).toHaveBeenCalled();
- const {toggle} = await validateComponents(project, false);
- await userEvent.click(toggle);
- expect(mockPost).toHaveBeenCalled();
- await validateComponents(project, true);
- // toggle off spike protection
- await userEvent.click(toggle);
- expect(mockDelete).toHaveBeenCalled();
- await validateComponents(project, false);
- });
- it('responds to unsuccessful toggle', async () => {
- const mockPostFailed = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/spike-protections/`,
- method: 'POST',
- statusCode: 403,
- });
- const project = projects[0]!;
- render(<SpikeProtectionProjects />, {router});
- expect(mockGet).toHaveBeenCalled();
- const {toggle} = await validateComponents(project, false);
- await userEvent.click(toggle);
- expect(mockPostFailed).toHaveBeenCalled();
- await validateComponents(project, false);
- });
- it('searches successfully', async () => {
- const projectSlug = projects[0]!.slug;
- render(<SpikeProtectionProjects />, {organization});
- const projectSearch = await screen.findByPlaceholderText('Search projects');
- await userEvent.click(projectSearch);
- await userEvent.paste(projectSlug);
- expect(projectSearch).toHaveValue(projectSlug);
- expect(mockGet).toHaveBeenCalledTimes(2);
- const requestOptions = mockGet.mock.calls[1][1];
- expect(requestOptions.query).toEqual(expect.objectContaining({query: projectSlug}));
- });
- it('response to successful toggle for all projects', async () => {
- renderGlobalModal();
- render(<SpikeProtectionProjects />, {router});
- await validateComponents(projects[0]!, false);
- await validateComponents(projects[1]!, true);
- const enableAll = await screen.findByTestId(`sp-enable-all`);
- await userEvent.click(enableAll);
- await userEvent.click(await screen.findByTestId('confirm-button'));
- expect(mockPostAll).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({data: {projects: []}})
- );
- for (const project of projects) {
- await validateComponents(project, true);
- }
- });
- it('response to successful disable toggle for all projects', async () => {
- renderGlobalModal();
- render(<SpikeProtectionProjects />, {router});
- await validateComponents(projects[0]!, false);
- await validateComponents(projects[1]!, true);
- const disableAll = await screen.findByTestId(`sp-disable-all`);
- await userEvent.click(disableAll);
- await userEvent.click(await screen.findByTestId('confirm-button'));
- expect(mockDeleteAll).toHaveBeenCalledWith(
- expect.anything(),
- expect.objectContaining({data: {projects: []}})
- );
- for (const project of projects) {
- await validateComponents(project, false);
- }
- });
- it('fetches notification actions upon accordion opening', async () => {
- const project = projects[0]!;
- mockGet = MockApiClient.addMockResponse({
- url: `/organizations/${organization.slug}/projects/`,
- method: 'GET',
- body: [project],
- statusCode: 200,
- });
- render(<SpikeProtectionProjects />, {organization});
- await userEvent.click(
- await screen.findByTestId(`${project.slug}-spike-protection-toggle`)
- );
- await userEvent.click(screen.getByTestId('accordion-title'));
- expect(mockGetProjectNotificationActions).toHaveBeenCalled();
- expect(await screen.findByTestId('sentry_notification-action')).toBeInTheDocument();
- });
- it('closes the accordion upon disable', async () => {
- const project = projects[0]!;
- render(<SpikeProtectionProjects />, {organization});
- await userEvent.click(
- await screen.findByTestId(`${project.slug}-spike-protection-toggle`)
- );
- await userEvent.click(screen.getByTestId(`${project.slug}-spike-protection-toggle`));
- expect(
- screen.getByTestId(`${project.slug}-accordion-row-disabled`)
- ).toBeInTheDocument();
- });
- });
|