Commit ed0c2f66 authored by Tom Quirk's avatar Tom Quirk

Create sign_in_button for Jira Connect App

In preparation for a layout re-organisation, this commit
abstracts the sign in button into its
own Vue component. Relevant tests have been
updated.

There are no user-facing changes in this MR.
parent 19d9aa5f
<script> <script>
import { GlAlert, GlButton, GlLink, GlSprintf } from '@gitlab/ui'; import { GlAlert, GlLink, GlSprintf } from '@gitlab/ui';
import { mapState, mapMutations } from 'vuex'; import { mapState, mapMutations } from 'vuex';
import { retrieveAlert, getLocation } from '~/jira_connect/subscriptions/utils'; import { retrieveAlert } from '~/jira_connect/subscriptions/utils';
import { SET_ALERT } from '../store/mutation_types'; import { SET_ALERT } from '../store/mutation_types';
import SubscriptionsList from './subscriptions_list.vue'; import SubscriptionsList from './subscriptions_list.vue';
import AddNamespaceButton from './add_namespace_button.vue'; import AddNamespaceButton from './add_namespace_button.vue';
import SignInButton from './sign_in_button.vue';
export default { export default {
name: 'JiraConnectApp', name: 'JiraConnectApp',
components: { components: {
GlAlert, GlAlert,
GlButton,
GlLink, GlLink,
GlSprintf, GlSprintf,
SubscriptionsList, SubscriptionsList,
AddNamespaceButton, AddNamespaceButton,
SignInButton,
}, },
inject: { inject: {
usersPath: { usersPath: {
default: '', default: '',
}, },
}, },
data() {
return {
location: '',
};
},
computed: { computed: {
...mapState(['alert']), ...mapState(['alert']),
usersPathWithReturnTo() {
if (this.location) {
return `${this.usersPath}?return_to=${this.location}`;
}
return this.usersPath;
},
shouldShowAlert() { shouldShowAlert() {
return Boolean(this.alert?.message); return Boolean(this.alert?.message);
}, },
userSignedIn() {
return Boolean(!this.usersPath);
},
}, },
created() { created() {
this.setInitialAlert(); this.setInitialAlert();
this.setLocation();
}, },
methods: { methods: {
...mapMutations({ ...mapMutations({
setAlert: SET_ALERT, setAlert: SET_ALERT,
}), }),
async setLocation() {
this.location = await getLocation();
},
setInitialAlert() { setInitialAlert() {
const { linkUrl, title, message, variant } = retrieveAlert() || {}; const { linkUrl, title, message, variant } = retrieveAlert() || {};
this.setAlert({ linkUrl, title, message, variant }); this.setAlert({ linkUrl, title, message, variant });
...@@ -82,15 +70,7 @@ export default { ...@@ -82,15 +70,7 @@ export default {
<div class="jira-connect-app-body gl-my-7 gl-px-5 gl-pb-4"> <div class="jira-connect-app-body gl-my-7 gl-px-5 gl-pb-4">
<div class="gl-display-flex gl-justify-content-end"> <div class="gl-display-flex gl-justify-content-end">
<gl-button <sign-in-button v-if="!userSignedIn" :users-path="usersPath" />
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
>
<add-namespace-button v-else /> <add-namespace-button v-else />
</div> </div>
......
<script>
import { GlButton } from '@gitlab/ui';
import { getLocation } from '~/jira_connect/subscriptions/utils';
import { objectToQuery } from '~/lib/utils/url_utility';
export default {
components: {
GlButton,
},
props: {
usersPath: {
type: String,
required: true,
},
},
data() {
return {
location: '',
};
},
computed: {
usersPathWithReturnTo() {
if (this.location) {
const queryParams = {
return_to: this.location,
};
return `${this.usersPath}?${objectToQuery(queryParams)}`;
}
return this.usersPath;
},
},
created() {
this.setLocation();
},
methods: {
async setLocation() {
this.location = await getLocation();
},
},
};
</script>
<template>
<gl-button category="primary" variant="info" :href="usersPathWithReturnTo" target="_blank">
<slot>
{{ s__('Integrations|Sign in to add namespaces') }}
</slot>
</gl-button>
</template>
...@@ -11,6 +11,9 @@ import { getLocation, sizeToParent } from './utils'; ...@@ -11,6 +11,9 @@ import { getLocation, sizeToParent } from './utils';
const store = createStore(); const store = createStore();
/**
* Add `return_to` query param to all HAML-defined GitLab sign in links.
*/
const updateSignInLinks = async () => { const updateSignInLinks = async () => {
const location = await getLocation(); const location = await getLocation();
Array.from(document.querySelectorAll('.js-jira-connect-sign-in')).forEach((el) => { Array.from(document.querySelectorAll('.js-jira-connect-sign-in')).forEach((el) => {
......
import { GlAlert, GlButton, GlLink } from '@gitlab/ui'; import { GlAlert, GlLink } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { mount, shallowMount } from '@vue/test-utils';
import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue'; import JiraConnectApp from '~/jira_connect/subscriptions/components/app.vue';
import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue'; import AddNamespaceButton from '~/jira_connect/subscriptions/components/add_namespace_button.vue';
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
import createStore from '~/jira_connect/subscriptions/store'; import createStore from '~/jira_connect/subscriptions/store';
import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types'; import { SET_ALERT } from '~/jira_connect/subscriptions/store/mutation_types';
import { __ } from '~/locale'; import { __ } from '~/locale';
jest.mock('~/jira_connect/subscriptions/utils', () => ({ jest.mock('~/jira_connect/subscriptions/utils', () => ({
retrieveAlert: jest.fn().mockReturnValue({ message: 'error message' }), retrieveAlert: jest.fn().mockReturnValue({ message: 'error message' }),
getLocation: jest.fn(),
})); }));
describe('JiraConnectApp', () => { describe('JiraConnectApp', () => {
...@@ -18,7 +18,7 @@ describe('JiraConnectApp', () => { ...@@ -18,7 +18,7 @@ describe('JiraConnectApp', () => {
const findAlert = () => wrapper.findComponent(GlAlert); const findAlert = () => wrapper.findComponent(GlAlert);
const findAlertLink = () => findAlert().findComponent(GlLink); const findAlertLink = () => findAlert().findComponent(GlLink);
const findGlButton = () => wrapper.findComponent(GlButton); const findSignInButton = () => wrapper.findComponent(SignInButton);
const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton); const findAddNamespaceButton = () => wrapper.findComponent(AddNamespaceButton);
const createComponent = ({ provide, mountFn = shallowMount } = {}) => { const createComponent = ({ provide, mountFn = shallowMount } = {}) => {
...@@ -35,28 +35,25 @@ describe('JiraConnectApp', () => { ...@@ -35,28 +35,25 @@ describe('JiraConnectApp', () => {
}); });
describe('template', () => { describe('template', () => {
describe('when user is not logged in', () => { describe.each`
scenario | usersPath | expectSignInButton | expectNamespaceButton
${'user is not signed in'} | ${'/users'} | ${true} | ${false}
${'user is signed in'} | ${undefined} | ${false} | ${true}
`('when $scenario', ({ usersPath, expectSignInButton, expectNamespaceButton }) => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
provide: { provide: {
usersPath: '/users', usersPath,
}, },
}); });
}); });
it('renders "Sign in" button', () => { it('renders sign in button as expected', () => {
expect(findGlButton().text()).toBe('Sign in to add namespaces'); expect(findSignInButton().exists()).toBe(expectSignInButton);
expect(findAddNamespaceButton().exists()).toBe(false);
});
});
describe('when user is logged in', () => {
beforeEach(() => {
createComponent();
}); });
it('renders "Add namespace" button ', () => { it('renders "Add Namespace" button as expected', () => {
expect(findAddNamespaceButton().exists()).toBe(true); expect(findAddNamespaceButton().exists()).toBe(expectNamespaceButton);
}); });
}); });
......
import { GlButton } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { getLocation } from '~/jira_connect/subscriptions/utils';
import SignInButton from '~/jira_connect/subscriptions/components/sign_in_button.vue';
import waitForPromises from 'helpers/wait_for_promises';
const MOCK_USERS_PATH = '/user';
jest.mock('~/jira_connect/subscriptions/utils');
describe('SignInButton', () => {
let wrapper;
const createComponent = () => {
wrapper = shallowMount(SignInButton, {
propsData: {
usersPath: MOCK_USERS_PATH,
},
});
};
const findButton = () => wrapper.findComponent(GlButton);
afterEach(() => {
wrapper.destroy();
});
it('displays a button', () => {
createComponent();
expect(findButton().exists()).toBe(true);
});
describe.each`
getLocationValue | expectedHref
${''} | ${MOCK_USERS_PATH}
${undefined} | ${MOCK_USERS_PATH}
${'https://test.jira.com'} | ${`${MOCK_USERS_PATH}?return_to=${encodeURIComponent('https://test.jira.com')}`}
`('when getLocation resolves with `$getLocationValue`', ({ getLocationValue, expectedHref }) => {
it(`sets button href to ${expectedHref}`, async () => {
getLocation.mockResolvedValue(getLocationValue);
createComponent();
expect(getLocation).toHaveBeenCalled();
await waitForPromises();
expect(findButton().attributes('href')).toBe(expectedHref);
});
});
});
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