123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455 |
- import {mountWithTheme} from 'sentry-test/enzyme';
- import {selectByValue} from 'sentry-test/select-new';
- import {Client} from 'sentry/api';
- import JsonForm from 'sentry/components/forms/jsonForm';
- import PermissionsObserver from 'sentry/views/settings/organizationDeveloperSettings/permissionsObserver';
- import SentryApplicationDetails from 'sentry/views/settings/organizationDeveloperSettings/sentryApplicationDetails';
- describe('Sentry Application Details', function () {
- let org;
- let orgId;
- let sentryApp;
- let token;
- let wrapper;
- let createAppRequest;
- let editAppRequest;
- const verifyInstallToggle = 'Switch[name="verifyInstall"]';
- const redirectUrlInput = 'Input[name="redirectUrl"]';
- const maskedValue = '*'.repeat(64);
- beforeEach(() => {
- Client.clearMockResponses();
- org = TestStubs.Organization({features: ['sentry-app-logo-upload']});
- orgId = org.slug;
- });
- describe('Creating a new public Sentry App', () => {
- beforeEach(() => {
- createAppRequest = Client.addMockResponse({
- url: '/sentry-apps/',
- method: 'POST',
- body: [],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{orgId}} route={{path: 'new-public/'}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('has inputs for redirectUrl and verifyInstall', () => {
- expect(wrapper.exists(verifyInstallToggle)).toBeTruthy();
- expect(wrapper.exists(redirectUrlInput)).toBeTruthy();
- });
- it('shows empty scopes and no credentials', function () {
- // new app starts off with no scopes selected
- expect(wrapper.find('PermissionsObserver').prop('scopes')).toEqual([]);
- expect(
- wrapper.find('PanelHeader').findWhere(h => h.text() === 'Permissions')
- ).toBeDefined();
- });
- it('does not show logo upload fields', function () {
- expect(wrapper.find('PanelHeader').at(1).text()).not.toContain('Logo');
- expect(wrapper.find('PanelHeader').at(2).text()).not.toContain('Small Icon');
- expect(wrapper.exists('AvatarChooser')).toBe(false);
- });
- it('saves', function () {
- wrapper
- .find('Input[name="name"]')
- .simulate('change', {target: {value: 'Test App'}});
- wrapper
- .find('Input[name="author"]')
- .simulate('change', {target: {value: 'Sentry'}});
- wrapper
- .find('Input[name="webhookUrl"]')
- .simulate('change', {target: {value: 'https://webhook.com'}});
- wrapper
- .find(redirectUrlInput)
- .simulate('change', {target: {value: 'https://webhook.com/setup'}});
- wrapper.find('TextArea[name="schema"]').simulate('change', {target: {value: '{}'}});
- wrapper.find('Switch[name="isAlertable"]').simulate('click');
- selectByValue(wrapper, 'admin', {name: 'Member--permission'});
- selectByValue(wrapper, 'admin', {name: 'Event--permission'});
- wrapper
- .find('Checkbox')
- .first()
- .simulate('change', {target: {checked: true}});
- wrapper.find('form').simulate('submit');
- const data = {
- name: 'Test App',
- author: 'Sentry',
- organization: org.slug,
- redirectUrl: 'https://webhook.com/setup',
- webhookUrl: 'https://webhook.com',
- scopes: expect.arrayContaining([
- 'member:read',
- 'member:admin',
- 'event:read',
- 'event:admin',
- ]),
- events: ['issue'],
- isInternal: false,
- verifyInstall: true,
- isAlertable: true,
- allowedOrigins: [],
- schema: {},
- };
- expect(createAppRequest).toHaveBeenCalledWith(
- '/sentry-apps/',
- expect.objectContaining({
- data,
- method: 'POST',
- })
- );
- });
- });
- describe('Creating a new internal Sentry App', () => {
- beforeEach(() => {
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{orgId}} route={{path: 'new-internal/'}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('does not show logo upload fields', function () {
- expect(wrapper.find('PanelHeader').at(1).text()).not.toContain('Logo');
- expect(wrapper.find('PanelHeader').at(2).text()).not.toContain('Small Icon');
- expect(wrapper.exists('AvatarChooser')).toBe(false);
- });
- it('no inputs for redirectUrl and verifyInstall', () => {
- expect(wrapper.exists(verifyInstallToggle)).toBeFalsy();
- expect(wrapper.exists(redirectUrlInput)).toBeFalsy();
- });
- });
- describe('Renders public app', function () {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp();
- sentryApp.events = ['issue'];
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('shows logo upload fields', function () {
- expect(wrapper.find('PanelHeader').at(1).text()).toContain('Logo');
- expect(wrapper.find('PanelHeader').at(2).text()).toContain('Small Icon');
- expect(wrapper.find('AvatarChooser')).toHaveLength(2);
- });
- it('has inputs for redirectUrl and verifyInstall', () => {
- expect(wrapper.exists(verifyInstallToggle)).toBeTruthy();
- expect(wrapper.exists(redirectUrlInput)).toBeTruthy();
- });
- it('shows application data', function () {
- // data should be filled out
- expect(wrapper.find('PermissionsObserver').prop('scopes')).toEqual([
- 'project:read',
- ]);
- });
- it('renders clientId and clientSecret for public apps', function () {
- expect(wrapper.find('#clientId').exists()).toBe(true);
- expect(wrapper.find('#clientSecret').exists()).toBe(true);
- });
- });
- describe('Renders for internal apps', () => {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp({
- status: 'internal',
- });
- token = TestStubs.SentryAppToken();
- sentryApp.events = ['issue'];
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [token],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('no inputs for redirectUrl and verifyInstall', () => {
- expect(wrapper.exists(verifyInstallToggle)).toBeFalsy();
- expect(wrapper.exists(redirectUrlInput)).toBeFalsy();
- });
- it('shows logo upload fields', function () {
- expect(wrapper.find('PanelHeader').at(1).text()).toContain('Logo');
- expect(wrapper.find('PanelHeader').at(2).text()).toContain('Small Icon');
- expect(wrapper.find('AvatarChooser')).toHaveLength(2);
- });
- it('shows tokens', function () {
- expect(wrapper.find('PanelHeader').at(5).text()).toContain('Tokens');
- expect(wrapper.find('TokenItem').exists()).toBe(true);
- });
- it('shows just clientSecret', function () {
- expect(wrapper.find('#clientSecret').exists()).toBe(true);
- expect(wrapper.find('#clientId').exists()).toBe(false);
- });
- });
- describe('Renders masked values', () => {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp({
- status: 'internal',
- clientSecret: maskedValue,
- });
- token = TestStubs.SentryAppToken({token: maskedValue, refreshToken: maskedValue});
- sentryApp.events = ['issue'];
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [token],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('shows masked tokens', function () {
- expect(wrapper.find('TextCopyInput input').first().prop('value')).toBe(maskedValue);
- });
- it('shows masked clientSecret', function () {
- expect(wrapper.find('#clientSecret input').prop('value')).toBe(maskedValue);
- });
- });
- describe('Editing internal app tokens', () => {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp({
- status: 'internal',
- isAlertable: true,
- });
- token = TestStubs.SentryAppToken();
- sentryApp.events = ['issue'];
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [token],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('adding token to list', async function () {
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- method: 'POST',
- body: [
- TestStubs.SentryAppToken({
- token: '392847329',
- dateCreated: '2018-03-02T18:30:26Z',
- }),
- ],
- });
- wrapper.find('Button[data-test-id="token-add"]').simulate('click');
- await tick();
- wrapper.update();
- const tokenItems = wrapper.find('TokenItem');
- expect(tokenItems).toHaveLength(2);
- });
- it('removing token from list', async function () {
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/${token.token}/`,
- method: 'DELETE',
- body: {},
- });
- wrapper.find('Button[data-test-id="token-delete"]').simulate('click');
- await tick();
- wrapper.update();
- expect(wrapper.find('EmptyMessage').exists()).toBe(true);
- });
- it('removing webhookURL unsets isAlertable and changes webhookDisabled to true', () => {
- expect(wrapper.find(PermissionsObserver).prop('webhookDisabled')).toBe(false);
- expect(wrapper.find('Switch[name="isAlertable"]').prop('isActive')).toBe(true);
- wrapper.find('Input[name="webhookUrl"]').simulate('change', {target: {value: ''}});
- expect(wrapper.find('Switch[name="isAlertable"]').prop('isActive')).toBe(false);
- expect(wrapper.find(PermissionsObserver).prop('webhookDisabled')).toBe(true);
- expect(wrapper.find(JsonForm).prop('additionalFieldProps')).toEqual({
- webhookDisabled: true,
- });
- });
- });
- describe('Editing an existing public Sentry App', () => {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp();
- sentryApp.events = ['issue'];
- editAppRequest = Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- method: 'PUT',
- body: [],
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('updates app with correct data', function () {
- wrapper
- .find(redirectUrlInput)
- .simulate('change', {target: {value: 'https://hello.com/'}});
- wrapper.find('TextArea[name="schema"]').simulate('change', {target: {value: '{}'}});
- wrapper
- .find('Checkbox')
- .first()
- .simulate('change', {target: {checked: false}});
- wrapper.find('form').simulate('submit');
- expect(editAppRequest).toHaveBeenCalledWith(
- `/sentry-apps/${sentryApp.slug}/`,
- expect.objectContaining({
- data: expect.objectContaining({
- redirectUrl: 'https://hello.com/',
- events: [],
- }),
- method: 'PUT',
- })
- );
- });
- it('submits with no-access for event subscription when permission is revoked', () => {
- wrapper
- .find('Checkbox')
- .first()
- .simulate('change', {target: {checked: true}});
- wrapper.find('TextArea[name="schema"]').simulate('change', {target: {value: '{}'}});
- selectByValue(wrapper, 'no-access', {name: 'Event--permission'});
- wrapper.find('form').simulate('submit');
- expect(editAppRequest).toHaveBeenCalledWith(
- `/sentry-apps/${sentryApp.slug}/`,
- expect.objectContaining({
- data: expect.objectContaining({
- events: [],
- }),
- method: 'PUT',
- })
- );
- });
- });
- describe('Editing an existing public Sentry App with a scope error', () => {
- beforeEach(() => {
- sentryApp = TestStubs.SentryApp();
- editAppRequest = Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- method: 'PUT',
- statusCode: 400,
- body: {
- scopes: [
- "Requested permission of member:write exceeds requester's permission. Please contact an administrator to make the requested change.",
- "Requested permission of member:admin exceeds requester's permission. Please contact an administrator to make the requested change.",
- ],
- },
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/`,
- body: sentryApp,
- });
- Client.addMockResponse({
- url: `/sentry-apps/${sentryApp.slug}/api-tokens/`,
- body: [],
- });
- wrapper = mountWithTheme(
- <SentryApplicationDetails params={{appSlug: sentryApp.slug, orgId}} />,
- TestStubs.routerContext([{organization: org}])
- );
- });
- it('renders the error', async () => {
- wrapper.find('form').simulate('submit');
- await tick();
- wrapper.update();
- expect(wrapper.find('div FieldErrorReason').text()).toEqual(
- "Requested permission of member:admin exceeds requester's permission. Please contact an administrator to make the requested change."
- );
- });
- });
- });
|