import {mountWithTheme} from 'sentry-test/enzyme';
import {initializeOrg} from 'sentry-test/initializeOrg';
import {mockRouterPush} from 'sentry-test/mockRouterPush';
import {Client} from 'sentry/api';
import SentryAppDetailedView from 'sentry/views/organizationIntegrations/sentryAppDetailedView';
const mockResponse = mocks => {
mocks.forEach(([url, body, method = 'GET']) =>
Client.addMockResponse({
url,
body,
method,
})
);
};
describe('SentryAppDetailedView', function () {
const org = TestStubs.Organization();
let wrapper;
const {router} = initializeOrg({
projects: [
{isMember: true, isBookmarked: true},
{isMember: true, slug: 'new-project', id: 3},
],
organization: {
features: ['events'],
},
router: {
location: {
pathname: '/organizations/org-slug/events/',
query: {},
},
},
});
describe('Published Sentry App', function () {
let createRequest;
let deleteRequest;
let sentryAppInteractionRequest;
beforeEach(() => {
Client.clearMockResponses();
sentryAppInteractionRequest = MockApiClient.addMockResponse({
url: `/sentry-apps/clickup/interaction/`,
method: 'POST',
statusCode: 200,
body: {},
});
mockResponse([
[
'/sentry-apps/clickup/',
{
status: 'published',
scopes: [],
isAlertable: false,
clientSecret:
'193583e573d14d61832de96a9efc32ceb64e59a494284f58b50328a656420a55',
overview: null,
verifyInstall: false,
owner: {id: 1, slug: 'sentry'},
slug: 'clickup',
name: 'ClickUp',
uuid: '5d547ecb-7eb8-4ed2-853b-40256177d526',
author: 'Nisanthan',
webhookUrl: 'http://localhost:7000',
clientId: 'c215db1accc040919e0b0dce058e0ecf4ea062bb82174d70aee8eba62351be24',
redirectUrl: null,
allowedOrigins: [],
events: [],
schema: {},
},
],
[
'/sentry-apps/clickup/features/',
[
{
featureGate: 'integrations-api',
description:
'ClickUp can **utilize the Sentry API** to pull data or update resources in Sentry (with permissions granted, of course).',
},
],
],
[`/organizations/${org.slug}/sentry-app-installations/`, []],
]);
createRequest = Client.addMockResponse({
url: `/organizations/${org.slug}/sentry-app-installations/`,
body: {
status: 'installed',
organization: {slug: `${org.slug}`},
app: {uuid: '5d547ecb-7eb8-4ed2-853b-40256177d526', slug: 'clickup'},
code: '1dc8b0a28b7f45959d01bbc99d9bd568',
uuid: '687323fd-9fa4-4f8f-9bee-ca0089224b3e',
},
method: 'POST',
});
deleteRequest = Client.addMockResponse({
url: '/sentry-app-installations/687323fd-9fa4-4f8f-9bee-ca0089224b3e/',
body: {},
method: 'DELETE',
});
wrapper = mountWithTheme(
);
});
it('records interaction request', () => {
expect(sentryAppInteractionRequest).toHaveBeenCalledWith(
`/sentry-apps/clickup/interaction/`,
expect.objectContaining({
method: 'POST',
data: {
tsdbField: 'sentry_app_viewed',
},
})
);
});
it('shows the Integration name and install status', function () {
expect(wrapper.find('Name').props().children).toEqual('ClickUp');
expect(wrapper.find('IntegrationStatus').props().status).toEqual('Not Installed');
});
it('shows the Accept & Install button', function () {
expect(wrapper.find('InstallButton').props().disabled).toEqual(false);
expect(wrapper.find('InstallButton').props().children).toEqual('Accept & Install');
});
describe('onClick:', function () {
let wrapperState;
it('installs app', async function () {
wrapper.find('InstallButton').simulate('click');
await tick();
await tick();
expect(createRequest).toHaveBeenCalled();
wrapper.update();
wrapperState = wrapper;
expect(wrapper.find('IntegrationStatus').props().status).toEqual('Installed');
expect(wrapper.find('StyledUninstallButton').exists()).toEqual(true);
});
it('uninstalls app', async function () {
expect(wrapperState.find('StyledUninstallButton')).toHaveLength(1);
wrapperState.find('StyledUninstallButton').simulate('click');
await tick();
wrapperState.find('Confirm').props().onConfirm();
await tick();
expect(deleteRequest).toHaveBeenCalled();
});
});
});
describe('Internal Sentry App', function () {
beforeEach(() => {
Client.clearMockResponses();
MockApiClient.addMockResponse({
url: `/sentry-apps/my-headband-washer-289499/interaction/`,
method: 'POST',
statusCode: 200,
body: {},
});
mockResponse([
[
'/sentry-apps/my-headband-washer-289499/',
{
status: 'internal',
scopes: [
'project:read',
'team:read',
'team:write',
'project:releases',
'event:read',
'org:read',
'member:read',
'member:write',
],
isAlertable: false,
clientSecret:
'8f47dcef40f7486f9bacfeca257022e092a483add7cf4d619993b9ace9775a79',
overview: null,
verifyInstall: false,
owner: {id: 1, slug: 'sentry'},
slug: 'my-headband-washer-289499',
name: 'My Headband Washer',
uuid: 'a806ab10-9608-4a4f-8dd9-ca6d6c09f9f5',
author: 'Sentry',
webhookUrl: 'https://myheadbandwasher.com',
clientId: 'a6d35972d4164ef18845b1e2ca954fe70ac196e0b20d4d1e8760a38772cf6f1c',
redirectUrl: null,
allowedOrigins: [],
events: [],
schema: {},
},
],
[
'/sentry-apps/my-headband-washer-289499/features/',
[
{
featureGate: 'integrations-api',
description:
'My Headband Washer can **utilize the Sentry API** to pull data or update resources in Sentry (with permissions granted, of course).',
},
],
],
[`/organizations/${org.slug}/sentry-app-installations/`, []],
]);
wrapper = mountWithTheme(
);
mockRouterPush(wrapper, router);
});
it('should get redirected to Developer Settings', () => {
expect(router.push).toHaveBeenLastCalledWith(
`/settings/${org.slug}/developer-settings/my-headband-washer-289499/`
);
});
});
describe('Unpublished Sentry App without Redirect Url', function () {
let createRequest;
beforeEach(() => {
Client.clearMockResponses();
MockApiClient.addMockResponse({
url: `/sentry-apps/la-croix-monitor/interaction/`,
method: 'POST',
statusCode: 200,
body: {},
});
mockResponse([
[
'/sentry-apps/la-croix-monitor/',
{
status: 'unpublished',
scopes: [
'project:read',
'project:write',
'team:read',
'project:releases',
'event:read',
'org:read',
],
isAlertable: false,
clientSecret:
'2b2aeb743c3745ab832e03bf02a7d91851908d379646499f900cd115780e8b2b',
overview: null,
verifyInstall: false,
owner: {id: 1, slug: 'sentry'},
slug: 'la-croix-monitor',
name: 'La Croix Monitor',
uuid: 'a59c8fcc-2f27-49f8-af9e-02661fc3e8d7',
author: 'La Croix',
webhookUrl: 'https://lacroix.com',
clientId: '8cc36458a0f94c93816e06dce7d808f882cbef59af6040d2b9ec4d67092c80f1',
redirectUrl: null,
allowedOrigins: [],
events: [],
schema: {},
},
],
[
'/sentry-apps/la-croix-monitor/features/',
[
{
featureGate: 'integrations-api',
description:
'La Croix Monitor can **utilize the Sentry API** to pull data or update resources in Sentry (with permissions granted, of course).',
},
],
],
[`/organizations/${org.slug}/sentry-app-installations/`, []],
]);
createRequest = Client.addMockResponse({
url: `/organizations/${org.slug}/sentry-app-installations/`,
body: {
status: 'installed',
organization: {slug: 'sentry'},
app: {uuid: 'a59c8fcc-2f27-49f8-af9e-02661fc3e8d7', slug: 'la-croix-monitor'},
code: '21c87231918a4e5c85d9b9e799c07382',
uuid: '258ad77c-7e6c-4cfe-8a40-6171cff30d61',
},
method: 'POST',
});
wrapper = mountWithTheme(
);
});
it('shows the Integration name and install status', function () {
expect(wrapper.find('Name').props().children).toEqual('La Croix Monitor');
expect(wrapper.find('IntegrationStatus').props().status).toEqual('Not Installed');
});
it('shows the Accept & Install button', function () {
expect(wrapper.find('InstallButton').props().disabled).toEqual(false);
expect(wrapper.find('InstallButton').props().children).toEqual('Accept & Install');
});
it('onClick: installs app', async function () {
wrapper.find('InstallButton').simulate('click');
await tick();
expect(createRequest).toHaveBeenCalled();
wrapper.update();
expect(wrapper.find('IntegrationStatus').props().status).toEqual('Installed');
expect(wrapper.find('StyledUninstallButton').exists()).toEqual(true);
});
});
describe('Unpublished Sentry App with Redirect Url', function () {
let createRequest;
beforeEach(() => {
Client.clearMockResponses();
MockApiClient.addMockResponse({
url: `/sentry-apps/go-to-google/interaction/`,
method: 'POST',
statusCode: 200,
body: {},
});
mockResponse([
[
'/sentry-apps/go-to-google/',
{
status: 'unpublished',
scopes: ['project:read', 'team:read'],
isAlertable: false,
clientSecret:
'6405a4a7b8084cdf8dbea53b53e2163983deb428b78e4c6997bc408d44d93878',
overview: null,
verifyInstall: false,
owner: {id: 1, slug: 'sentry'},
slug: 'go-to-google',
name: 'Go to Google',
uuid: 'a4b8f364-4300-41ac-b8af-d8791ad50e77',
author: 'Nisanthan Nanthakumar',
webhookUrl: 'https://www.google.com',
clientId: '0974b5df6b57480b99c2e1f238eef769ef2c27ec156d4791a26903a896d5807e',
redirectUrl: 'https://www.google.com',
allowedOrigins: [],
events: [],
schema: {},
},
],
[
'/sentry-apps/go-to-google/features/',
[
{
featureGate: 'integrations-api',
description:
'Go to Google can **utilize the Sentry API** to pull data or update resources in Sentry (with permissions granted, of course).',
},
],
],
[`/organizations/${org.slug}/sentry-app-installations/`, []],
]);
createRequest = Client.addMockResponse({
url: `/organizations/${org.slug}/sentry-app-installations/`,
body: {
status: 'installed',
organization: {slug: 'sentry'},
app: {uuid: 'a4b8f364-4300-41ac-b8af-d8791ad50e77', slug: 'go-to-google'},
code: '1f0e7c1b99b940abac7a19b86e69bbe1',
uuid: '4d803538-fd42-4278-b410-492f5ab677b5',
},
method: 'POST',
});
wrapper = mountWithTheme(
);
mockRouterPush(wrapper, router);
});
it('shows the Integration name and install status', function () {
expect(wrapper.find('Name').props().children).toEqual('Go to Google');
expect(wrapper.find('IntegrationStatus').props().status).toEqual('Not Installed');
});
it('shows the Accept & Install button', function () {
expect(wrapper.find('InstallButton').props().disabled).toEqual(false);
expect(wrapper.find('InstallButton').props().children).toEqual('Accept & Install');
});
it('onClick: redirects url', async function () {
window.location.assign = jest.fn();
wrapper.find('InstallButton').simulate('click');
await tick();
expect(createRequest).toHaveBeenCalled();
wrapper.update();
expect(window.location.assign).toHaveBeenLastCalledWith(
'https://www.google.com/?code=1f0e7c1b99b940abac7a19b86e69bbe1&installationId=4d803538-fd42-4278-b410-492f5ab677b5&orgSlug=org-slug'
);
});
});
});