Commit 9dc6194e authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '321441-gitlab-connect-application-namespace-list-polish' into 'master'

Update Jira subscriptions list to use Vue

See merge request gitlab-org/gitlab!57561
parents 8cc3be68 a88095ae
import axios from 'axios';
export const getJwt = () => {
return new Promise((resolve) => {
AP.context.getToken((token) => {
resolve(token);
});
});
};
export const getLocation = () => {
return new Promise((resolve) => {
if (typeof AP.getLocation !== 'function') {
resolve();
}
AP.getLocation((location) => {
resolve(location);
});
});
};
import { getJwt } from '~/jira_connect/utils';
export const addSubscription = async (addPath, namespace) => {
const jwt = await getJwt();
......
<script>
import { GlAlert, GlButton, GlModal, GlModalDirective, GlLink, GlSprintf } from '@gitlab/ui';
import { GlAlert, GlButton, GlLink, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui';
import { mapState, mapMutations } from 'vuex';
import { getLocation } from '~/jira_connect/api';
import { retrieveAlert, getLocation } from '~/jira_connect/utils';
import { __ } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { SET_ALERT } from '../store/mutation_types';
import { retrieveAlert } from '../utils';
import GroupsList from './groups_list.vue';
import SubscriptionsList from './subscriptions_list.vue';
export default {
name: 'JiraConnectApp',
components: {
GlAlert,
GlButton,
GlModal,
GroupsList,
GlLink,
GlModal,
GlSprintf,
GroupsList,
SubscriptionsList,
},
directives: {
GlModalDirective,
......@@ -91,37 +92,36 @@ export default {
<h2 class="gl-text-center">{{ s__('JiraService|GitLab for Jira Configuration') }}</h2>
<div
class="jira-connect-app-body gl-display-flex gl-justify-content-space-between gl-my-7 gl-pb-4 gl-border-b-solid gl-border-b-1 gl-border-b-gray-200"
>
<h5 class="gl-align-self-center gl-mb-0" data-testid="new-jira-connect-ui-heading">
{{ s__('Integrations|Linked namespaces') }}
</h5>
<gl-button
v-if="usersPath"
category="primary"
variant="info"
class="gl-align-self-center"
:href="usersPathWithReturnTo"
target="_blank"
>{{ s__('Integrations|Sign in to add namespaces') }}</gl-button
>
<template v-else>
<div class="jira-connect-app-body gl-my-7 gl-px-5 gl-pb-4">
<div class="gl-display-flex gl-justify-content-end">
<gl-button
v-gl-modal-directive="'add-namespace-modal'"
v-if="usersPath"
category="primary"
variant="info"
class="gl-align-self-center"
>{{ s__('Integrations|Add namespace') }}</gl-button
>
<gl-modal
modal-id="add-namespace-modal"
:title="s__('Integrations|Link namespaces')"
:action-cancel="$options.modal.cancelProps"
:href="usersPathWithReturnTo"
target="_blank"
>{{ s__('Integrations|Sign in to add namespaces') }}</gl-button
>
<groups-list />
</gl-modal>
</template>
<template v-else>
<gl-button
v-gl-modal-directive="'add-namespace-modal'"
category="primary"
variant="info"
class="gl-align-self-center"
>{{ s__('Integrations|Add namespace') }}</gl-button
>
<gl-modal
modal-id="add-namespace-modal"
:title="s__('Integrations|Link namespaces')"
:action-cancel="$options.modal.cancelProps"
>
<groups-list />
</gl-modal>
</template>
</div>
<subscriptions-list />
</div>
</div>
</template>
<script>
import { GlAvatar, GlIcon } from '@gitlab/ui';
export default {
components: {
GlAvatar,
GlIcon,
},
props: {
group: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="gl-display-flex gl-align-items-center">
<gl-icon name="folder-o" class="gl-mr-3" />
<div class="gl-display-none gl-flex-shrink-0 gl-sm-display-flex gl-mr-3">
<gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatar_url" />
</div>
<div>
<span
class="gl-mr-3 gl-text-gray-900! gl-font-weight-bold"
data-testid="group-list-item-name"
>
{{ group.full_name }}
</span>
<div v-if="group.description" data-testid="group-list-item-description">
<p class="gl-mt-2! gl-mb-0 gl-text-gray-600" v-text="group.description"></p>
</div>
</div>
</div>
</template>
<script>
import { GlAvatar, GlButton, GlIcon } from '@gitlab/ui';
import { GlButton } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import { addSubscription } from '~/jira_connect/api';
import { persistAlert, reloadPage } from '~/jira_connect/utils';
import { s__ } from '~/locale';
import { persistAlert } from '../utils';
import GroupItemName from './group_item_name.vue';
export default {
components: {
GlAvatar,
GlButton,
GlIcon,
GroupItemName,
},
inject: {
subscriptionsPath: {
......@@ -47,7 +47,7 @@ export default {
variant: 'success',
});
AP.navigator.reload();
reloadPage();
})
.catch((error) => {
this.$emit(
......@@ -55,8 +55,6 @@ export default {
error?.response?.data?.error ||
s__('Integrations|Failed to link namespace. Please try again.'),
);
})
.finally(() => {
this.isLoading = false;
});
},
......@@ -67,23 +65,9 @@ export default {
<template>
<li class="gl-border-b-1 gl-border-b-solid gl-border-b-gray-100">
<div class="gl-display-flex gl-align-items-center gl-py-3">
<gl-icon name="folder-o" class="gl-mr-3" />
<div class="gl-display-none gl-flex-shrink-0 gl-sm-display-flex gl-mr-3">
<gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatar_url" />
</div>
<div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center">
<div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1">
<div class="gl-display-flex gl-align-items-center gl-flex-wrap">
<span
class="gl-mr-3 gl-text-gray-900! gl-font-weight-bold"
data-testid="group-list-item-name"
>
{{ group.full_name }}
</span>
</div>
<div v-if="group.description" data-testid="group-list-item-description">
<p class="gl-mt-2! gl-mb-0 gl-text-gray-600" v-text="group.description"></p>
</div>
<group-item-name :group="group" />
</div>
<gl-button
......
<script>
import { GlButton, GlEmptyState, GlTable } from '@gitlab/ui';
import { isEmpty } from 'lodash';
import { removeSubscription } from '~/jira_connect/api';
import { reloadPage } from '~/jira_connect/utils';
import { __, s__ } from '~/locale';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import GroupItemName from './group_item_name.vue';
export default {
components: {
GlButton,
GlEmptyState,
GlTable,
GroupItemName,
TimeagoTooltip,
},
inject: {
subscriptions: {
default: [],
},
},
data() {
return {
loadingItem: null,
};
},
fields: [
{
key: 'name',
label: s__('Integrations|Linked namespaces'),
},
{
key: 'created_at',
label: __('Added'),
tdClass: 'gl-vertical-align-middle! gl-w-20p',
},
{
key: 'actions',
label: '',
tdClass: 'gl-text-right gl-vertical-align-middle! gl-pl-0!',
},
],
i18n: {
emptyTitle: s__('Integrations|No linked namespaces'),
emptyDescription: s__(
'Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.',
),
},
methods: {
isEmpty,
isLoadingItem(item) {
return this.loadingItem === item;
},
unlinkBtnClass(item) {
return this.isLoadingItem(item) ? '' : 'gl-ml-6';
},
onClick(item) {
this.loadingItem = item;
removeSubscription(item.unlink_path)
.then(() => {
reloadPage();
})
.catch(() => {
this.loadingItem = null;
});
},
},
};
</script>
<template>
<div>
<gl-empty-state
v-if="isEmpty(subscriptions)"
:title="$options.i18n.emptyTitle"
:description="$options.i18n.emptyDescription"
/>
<gl-table v-else :items="subscriptions" :fields="$options.fields">
<template #cell(name)="{ item }">
<group-item-name :group="item.group" />
</template>
<template #cell(created_at)="{ item }">
<timeago-tooltip :time="item.created_at" />
</template>
<template #cell(actions)="{ item }">
<gl-button
:class="unlinkBtnClass(item)"
category="secondary"
:loading="isLoadingItem(item)"
@click.prevent="onClick(item)"
>{{ __('Unlink') }}</gl-button
>
</template>
</gl-table>
</div>
</template>
import setConfigs from '@gitlab/ui/dist/config';
import Vue from 'vue';
import { addSubscription, removeSubscription, getLocation } from '~/jira_connect/api';
import { addSubscription, removeSubscription } from '~/jira_connect/api';
import { getLocation, reloadPage, sizeToParent } from '~/jira_connect/utils';
import GlFeatureFlagsPlugin from '~/vue_shared/gl_feature_flags_plugin';
import Translate from '~/vue_shared/translate';
......@@ -10,10 +11,6 @@ import { SET_ALERT } from './store/mutation_types';
const store = createStore();
const reqComplete = () => {
AP.navigator.reload();
};
const reqFailed = (res, fallbackErrorMessage) => {
const { error = fallbackErrorMessage } = res || {};
......@@ -35,7 +32,7 @@ const initRemoveSubscriptionButtonHandlers = () => {
const removePath = e.target.getAttribute('href');
removeSubscription(removePath)
.then(reqComplete)
.then(reloadPage)
.catch((err) =>
reqFailed(err.response.data, 'Failed to remove namespace. Please try again.'),
);
......@@ -56,7 +53,7 @@ const initAddSubscriptionFormHandler = () => {
const namespace = (e.target.querySelector('#namespace-input') || {}).value;
addSubscription(addPath, namespace)
.then(reqComplete)
.then(reloadPage)
.catch((err) => reqFailed(err.response.data, 'Failed to add namespace. Please try again.'));
});
};
......@@ -76,14 +73,15 @@ export async function initJiraConnect() {
Vue.use(Translate);
Vue.use(GlFeatureFlagsPlugin);
const { groupsPath, subscriptionsPath, usersPath } = el.dataset;
AP.sizeToParent();
const { groupsPath, subscriptions, subscriptionsPath, usersPath } = el.dataset;
sizeToParent();
return new Vue({
el,
store,
provide: {
groupsPath,
subscriptions: JSON.parse(subscriptions),
subscriptionsPath,
usersPath,
},
......
import AccessorUtilities from '~/lib/utils/accessor';
import { ALERT_LOCALSTORAGE_KEY } from './constants';
const isFunction = (fn) => typeof fn === 'function';
/**
* Persist alert data to localStorage.
*/
......@@ -31,3 +33,41 @@ export const retrieveAlert = () => {
return JSON.parse(initialAlertJSON);
};
export const getJwt = () => {
return new Promise((resolve) => {
if (isFunction(AP?.context?.getToken)) {
AP.context.getToken((token) => {
resolve(token);
});
} else {
resolve();
}
});
};
export const getLocation = () => {
return new Promise((resolve) => {
if (isFunction(AP?.getLocation)) {
AP.getLocation((location) => {
resolve(location);
});
} else {
resolve();
}
});
};
export const reloadPage = () => {
if (isFunction(AP?.navigator?.reload)) {
AP.navigator.reload();
} else {
window.location.reload();
}
};
export const sizeToParent = () => {
if (isFunction(AP?.sizeToParent)) {
AP.sizeToParent();
}
};
......@@ -15,6 +15,7 @@
@import '@gitlab/ui/src/components/base/loading_icon/loading_icon';
@import '@gitlab/ui/src/components/base/modal/modal';
@import '@gitlab/ui/src/components/base/pagination/pagination';
@import '@gitlab/ui/src/components/base/table/table';
@import '@gitlab/ui/src/components/base/tooltip/tooltip';
@import '@gitlab/ui/src/components/base/search_box_by_type/search_box_by_type';
......@@ -55,7 +56,7 @@ $header-height: 40px;
}
.jira-connect-app-body {
max-width: 600px;
max-width: 768px;
margin-left: auto;
margin-right: auto;
}
......
......@@ -6,8 +6,24 @@ module JiraConnectHelper
{
groups_path: api_v4_groups_path(params: { min_access_level: Gitlab::Access::MAINTAINER, skip_groups: skip_groups }),
subscriptions: subscriptions.map { |s| serialize_subscription(s) }.to_json,
subscriptions_path: jira_connect_subscriptions_path,
users_path: current_user ? nil : jira_connect_users_path
}
end
private
def serialize_subscription(subscription)
{
group: {
name: subscription.namespace.name,
avatar_url: subscription.namespace.avatar_url,
full_name: subscription.namespace.full_name,
description: subscription.namespace.description
},
created_at: subscription.created_at,
unlink_path: jira_connect_subscription_path(subscription)
}
end
end
......@@ -10,7 +10,7 @@
%main.jira-connect-app.gl-px-5.gl-pt-7.gl-mx-auto
- if current_user.blank? && @subscriptions.empty?
.jira-connect-app-body.gl-text-center
.jira-connect-app-body.gl-px-5.gl-text-center
%h2= s_('JiraService|GitLab for Jira Configuration')
%p= s_('JiraService|Sign in to GitLab.com to get started.')
......@@ -22,26 +22,7 @@
- else
.js-jira-connect-app{ data: jira_connect_app_data(@subscriptions) }
.jira-connect-app-body
- if @subscriptions.present?
%table.subscriptions.gl-w-full
%thead
%tr
%th= _('Namespace')
%th= _('Added')
%th
%tbody
- @subscriptions.each do |subscription|
%tr
%td= subscription.namespace.full_path
%td= subscription.created_at
%td= link_to _('Remove'), jira_connect_subscription_path(subscription), class: 'js-jira-connect-remove-subscription'
- else
.gl-text-center
%h4= s_('Integrations|No linked namespaces')
%p= s_('Integrations|Namespaces are the GitLab groups and subgroups you link to this Jira instance.')
%p.jira-connect-app-body.gl-mt-7.gl-font-base.gl-text-center
%p.jira-connect-app-body.gl-px-5.gl-mt-7.gl-font-base.gl-text-center
%strong= s_('Integrations|Browser limitations')
- firefox_link_url = 'https://www.mozilla.org/en-US/firefox/'
- firefox_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: firefox_link_url }
......
---
title: Update Jira subscriptions list to use Vue
merge_request: 57561
author:
type: changed
......@@ -33396,6 +33396,9 @@ msgstr ""
msgid "Unlimited"
msgstr ""
msgid "Unlink"
msgstr ""
msgid "Unlock"
msgstr ""
......
import MockAdapter from 'axios-mock-adapter';
import { addSubscription, removeSubscription, fetchGroups } from '~/jira_connect/api';
import { getJwt } from '~/jira_connect/utils';
import axios from '~/lib/utils/axios_utils';
import httpStatus from '~/lib/utils/http_status';
jest.mock('~/jira_connect/utils', () => ({
getJwt: jest.fn().mockResolvedValue('jwt'),
}));
describe('JiraConnect API', () => {
let mock;
let response;
......@@ -13,14 +18,6 @@ describe('JiraConnect API', () => {
const mockJwt = 'jwt';
const mockResponse = { success: true };
const tokenSpy = jest.fn((callback) => callback(mockJwt));
window.AP = {
context: {
getToken: tokenSpy,
},
};
beforeEach(() => {
mock = new MockAdapter(axios);
});
......@@ -44,7 +41,7 @@ describe('JiraConnect API', () => {
response = await makeRequest();
expect(tokenSpy).toHaveBeenCalled();
expect(getJwt).toHaveBeenCalled();
expect(axios.post).toHaveBeenCalledWith(mockAddPath, {
jwt: mockJwt,
namespace_path: mockNamespace,
......@@ -62,7 +59,7 @@ describe('JiraConnect API', () => {
response = await makeRequest();
expect(tokenSpy).toHaveBeenCalled();
expect(getJwt).toHaveBeenCalled();
expect(axios.delete).toHaveBeenCalledWith(mockRemovePath, {
params: {
jwt: mockJwt,
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`GroupItemName template matches the snapshot 1`] = `
<div
class="gl-display-flex gl-align-items-center"
>
<gl-icon-stub
class="gl-mr-3"
name="folder-o"
size="16"
/>
<div
class="gl-display-none gl-flex-shrink-0 gl-sm-display-flex gl-mr-3"
>
<gl-avatar-stub
alt="avatar"
entityid="0"
entityname="Gitlab Org"
shape="rect"
size="32"
src="avatar.png"
/>
</div>
<div>
<span
class="gl-mr-3 gl-text-gray-900! gl-font-weight-bold"
data-testid="group-list-item-name"
>
Gitlab Org
</span>
<div
data-testid="group-list-item-description"
>
<p
class="gl-mt-2! gl-mb-0 gl-text-gray-600"
>
Open source software to collaborate on code
</p>
</div>
</div>
</div>
`;
import { GlAlert, GlButton, GlModal, GlLink } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import JiraConnectApp from '~/jira_connect/components/app.vue';
import createStore from '~/jira_connect/store';
import { SET_ALERT } from '~/jira_connect/store/mutation_types';
import { persistAlert } from '~/jira_connect/utils';
import { __ } from '~/locale';
jest.mock('~/jira_connect/api');
jest.mock('~/jira_connect/utils', () => ({
retrieveAlert: jest.fn().mockReturnValue({ message: 'error message' }),
getLocation: jest.fn(),
}));
describe('JiraConnectApp', () => {
let wrapper;
let store;
const findAlert = () => wrapper.findComponent(GlAlert);
const findAlertLink = () => findAlert().find(GlLink);
const findAlertLink = () => findAlert().findComponent(GlLink);
const findGlButton = () => wrapper.findComponent(GlButton);
const findGlModal = () => wrapper.findComponent(GlModal);
const findHeader = () => wrapper.findByTestId('new-jira-connect-ui-heading');
const findHeaderText = () => findHeader().text();
const createComponent = ({ provide, mountFn = shallowMount } = {}) => {
store = createStore();
wrapper = extendedWrapper(
mountFn(JiraConnectApp, {
store,
provide,
}),
);
wrapper = mountFn(JiraConnectApp, {
store,
provide,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('template', () => {
it('renders new UI', () => {
createComponent();
expect(findHeader().exists()).toBe(true);
expect(findHeaderText()).toBe('Linked namespaces');
});
describe('when user is not logged in', () => {
beforeEach(() => {
createComponent({
......@@ -128,7 +117,6 @@ describe('JiraConnectApp', () => {
describe('when alert is set in localStoage', () => {
it('renders alert on mount', () => {
persistAlert({ message: 'error message' });
createComponent();
const alert = findAlert();
......
import { shallowMount } from '@vue/test-utils';
import GroupItemName from '~/jira_connect/components/group_item_name.vue';
import { mockGroup1 } from '../mock_data';
describe('GroupItemName', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(GroupItemName, {
propsData: {
group: mockGroup1,
},
});
};
afterEach(() => {
wrapper.destroy();
});
describe('template', () => {
it('matches the snapshot', () => {
createComponent();
expect(wrapper.element).toMatchSnapshot();
});
});
});
import { GlAvatar, GlButton } from '@gitlab/ui';
import { GlButton } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import * as JiraConnectApi from '~/jira_connect/api';
import GroupItemName from '~/jira_connect/components/group_item_name.vue';
import GroupsListItem from '~/jira_connect/components/groups_list_item.vue';
import { persistAlert } from '~/jira_connect/utils';
import { persistAlert, reloadPage } from '~/jira_connect/utils';
import { mockGroup1 } from '../mock_data';
jest.mock('~/jira_connect/utils');
......@@ -14,36 +14,23 @@ describe('GroupsListItem', () => {
let wrapper;
const mockSubscriptionPath = 'subscriptionPath';
const reloadSpy = jest.fn();
global.AP = {
navigator: {
reload: reloadSpy,
},
};
const createComponent = ({ mountFn = shallowMount } = {}) => {
wrapper = extendedWrapper(
mountFn(GroupsListItem, {
propsData: {
group: mockGroup1,
},
provide: {
subscriptionsPath: mockSubscriptionPath,
},
}),
);
wrapper = mountFn(GroupsListItem, {
propsData: {
group: mockGroup1,
},
provide: {
subscriptionsPath: mockSubscriptionPath,
},
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
const findGlAvatar = () => wrapper.find(GlAvatar);
const findGroupName = () => wrapper.findByTestId('group-list-item-name');
const findGroupDescription = () => wrapper.findByTestId('group-list-item-description');
const findLinkButton = () => wrapper.find(GlButton);
const findGroupItemName = () => wrapper.findComponent(GroupItemName);
const findLinkButton = () => wrapper.findComponent(GlButton);
const clickLinkButton = () => findLinkButton().trigger('click');
describe('template', () => {
......@@ -51,17 +38,9 @@ describe('GroupsListItem', () => {
createComponent();
});
it('renders group avatar', () => {
expect(findGlAvatar().exists()).toBe(true);
expect(findGlAvatar().props('src')).toBe(mockGroup1.avatar_url);
});
it('renders group name', () => {
expect(findGroupName().text()).toBe(mockGroup1.full_name);
});
it('renders group description', () => {
expect(findGroupDescription().text()).toBe(mockGroup1.description);
it('renders GroupItemName', () => {
expect(findGroupItemName().exists()).toBe(true);
expect(findGroupItemName().props('group')).toBe(mockGroup1);
});
it('renders Link button', () => {
......@@ -106,7 +85,7 @@ describe('GroupsListItem', () => {
await waitForPromises();
expect(reloadSpy).toHaveBeenCalled();
expect(reloadPage).toHaveBeenCalled();
});
});
......@@ -125,7 +104,7 @@ describe('GroupsListItem', () => {
await waitForPromises();
expect(reloadSpy).not.toHaveBeenCalled();
expect(reloadPage).not.toHaveBeenCalled();
expect(wrapper.emitted('error')[0][0]).toBe(mockErrorMessage);
});
});
......
......@@ -35,12 +35,12 @@ describe('GroupsList', () => {
wrapper.destroy();
});
const findGlAlert = () => wrapper.find(GlAlert);
const findGlLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
const findAllItems = () => wrapper.findAll(GroupsListItem);
const findFirstItem = () => findAllItems().at(0);
const findSecondItem = () => findAllItems().at(1);
const findSearchBox = () => wrapper.find(GlSearchBoxByType);
const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
const findGroupsList = () => wrapper.findByTestId('groups-list');
describe('when groups are loading', () => {
......
......@@ -4,16 +4,13 @@ import { removeSubscription } from '~/jira_connect/api';
jest.mock('~/jira_connect/api', () => ({
removeSubscription: jest.fn().mockResolvedValue(),
}));
jest.mock('~/jira_connect/utils', () => ({
getLocation: jest.fn().mockResolvedValue('test/location'),
}));
describe('initJiraConnect', () => {
window.AP = {
navigator: {
reload: jest.fn(),
},
};
beforeEach(async () => {
setFixtures(`
<a class="js-jira-connect-sign-in" href="https://gitlab.com">Sign In</a>
......
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { useMockLocationHelper } from 'helpers/mock_window_location_helper';
import { ALERT_LOCALSTORAGE_KEY } from '~/jira_connect/constants';
import { persistAlert, retrieveAlert } from '~/jira_connect/utils';
useLocalStorageSpy();
import {
persistAlert,
retrieveAlert,
getJwt,
getLocation,
reloadPage,
sizeToParent,
} from '~/jira_connect/utils';
describe('JiraConnect utils', () => {
describe('alert utils', () => {
useLocalStorageSpy();
it.each`
arg | expectedRetrievedValue
${{ title: 'error' }} | ${{ title: 'error' }}
......@@ -29,4 +37,104 @@ describe('JiraConnect utils', () => {
},
);
});
describe('AP object utils', () => {
afterEach(() => {
global.AP = null;
});
describe('getJwt', () => {
const mockJwt = 'jwt';
const getTokenSpy = jest.fn((callback) => callback(mockJwt));
it('resolves to the function call when AP.context.getToken is a function', async () => {
global.AP = {
context: {
getToken: getTokenSpy,
},
};
const jwt = await getJwt();
expect(getTokenSpy).toHaveBeenCalled();
expect(jwt).toBe(mockJwt);
});
it('resolves to undefined when AP.context.getToken is not a function', async () => {
const jwt = await getJwt();
expect(getTokenSpy).not.toHaveBeenCalled();
expect(jwt).toBeUndefined();
});
});
describe('getLocation', () => {
const mockLocation = 'test/location';
const getLocationSpy = jest.fn((callback) => callback(mockLocation));
it('resolves to the function call when AP.getLocation is a function', async () => {
global.AP = {
getLocation: getLocationSpy,
};
const location = await getLocation();
expect(getLocationSpy).toHaveBeenCalled();
expect(location).toBe(mockLocation);
});
it('resolves to undefined when AP.getLocation is not a function', async () => {
const location = await getLocation();
expect(getLocationSpy).not.toHaveBeenCalled();
expect(location).toBeUndefined();
});
});
describe('reloadPage', () => {
const reloadSpy = jest.fn();
useMockLocationHelper();
it('calls the function when AP.navigator.reload is a function', async () => {
global.AP = {
navigator: {
reload: reloadSpy,
},
};
await reloadPage();
expect(reloadSpy).toHaveBeenCalled();
expect(window.location.reload).not.toHaveBeenCalled();
});
it('calls window.location.reload when AP.navigator.reload is not a function', async () => {
await reloadPage();
expect(reloadSpy).not.toHaveBeenCalled();
expect(window.location.reload).toHaveBeenCalled();
});
});
describe('sizeToParent', () => {
const sizeToParentSpy = jest.fn();
it('calls the function when AP.sizeToParent is a function', async () => {
global.AP = {
sizeToParent: sizeToParentSpy,
};
await sizeToParent();
expect(sizeToParentSpy).toHaveBeenCalled();
});
it('does nothing when AP.navigator.reload is not a function', async () => {
await sizeToParent();
expect(sizeToParentSpy).not.toHaveBeenCalled();
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment