Skip to content
Snippets Groups Projects
Verified Commit 47d55083 authored by Paul Slaughter's avatar Paul Slaughter :two:
Browse files

Create empty state approvals settings form

**Note:**
- This includes a stubbed service in the FE
- This is an iteration towards https://gitlab.com/gitlab-org/gitlab-ee/issues/1979
parent af1511c1
No related branches found
No related tags found
No related merge requests found
Showing
with 385 additions and 0 deletions
<script>
import { GlButton } from '@gitlab/ui';
import { __ } from '~/locale';
import Callout from '~/vue_shared/components/callout.vue';
const message = __(
'There are no approvers explicitly added for this project. Members who are of Developer role or higher and code owners (if any) are eligible to approve.',
);
export default {
components: {
Callout,
GlButton,
},
message,
};
</script>
<template>
<callout category="info">
<div>{{ $options.message }}</div>
<div class="prepend-top-default">
<gl-button class="btn-info btn-inverted" @click="$emit('submit');">{{
__('Add approvers')
}}</gl-button>
</div>
</callout>
</template>
<script>
import { mapState, mapActions } from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import ApprovalsSettingsEmpty from './approvals_settings_empty.vue';
export default {
components: {
ApprovalsSettingsEmpty,
GlLoadingIcon,
},
created() {
this.fetchRules();
},
computed: {
...mapState(['isLoading', 'rules']),
isEmpty() {
return !this.rules || this.rules.length === 0;
},
},
methods: {
...mapActions(['fetchRules']),
},
};
</script>
<template>
<gl-loading-icon v-if="isLoading" :size="2" />
<approvals-settings-empty v-else-if="isEmpty" />
</template>
import Vue from 'vue';
import Vuex from 'vuex';
import createStore from './stores';
import ApprovalSettingsForm from './components/approvals_settings_form.vue';
Vue.use(Vuex);
export default function mountApprovalsSettings(el) {
if (!el) {
return null;
}
return new Vue({
el,
store: createStore(),
render(h) {
return h(ApprovalSettingsForm);
},
});
}
/**
* This provides a stubbed API for approval rule requests.
*
* **PLEASE NOTE:**
* - This class will be removed when the BE is merged for https://gitlab.com/gitlab-org/gitlab-ee/issues/1979
*/
export function createApprovalsServiceStub() {
const projectApprovalRules = [];
return {
getProjectApprovalRules() {
return Promise.resolve({
data: { rules: projectApprovalRules },
});
},
};
}
export default createApprovalsServiceStub();
import createFlash from '~/flash';
import { __ } from '~/locale';
import * as types from './mutation_types';
import service from '../services/approvals_service_stub';
export const requestRules = ({ commit }) => {
commit(types.SET_LOADING, true);
};
export const receiveRulesSuccess = ({ commit }, { rules }) => {
commit(types.SET_RULES, rules);
commit(types.SET_LOADING, false);
};
export const receiveRulesError = () => {
createFlash(() => __('An error occurred fetching the project merge request rules.'));
};
export const fetchRules = ({ state, dispatch }) => {
if (state.isLoading) {
return;
}
dispatch('requestRules');
service
.getProjectApprovalRules()
.then(response => dispatch('receiveRulesSuccess', response.data))
.catch(() => dispatch('receiveRulesError'));
};
import Vuex from 'vuex';
import state from './state';
import mutations from './mutations';
import * as actions from './actions';
export default () =>
new Vuex.Store({
state: state(),
mutations,
actions,
});
export const SET_LOADING = 'SET_LOADING';
export const SET_RULES = 'SET_RULES';
import * as types from './mutation_types';
export default {
[types.SET_LOADING](state, isLoading) {
state.isLoading = isLoading;
},
[types.SET_RULES](state, rules) {
state.rules = rules;
},
};
export default () => ({
isLoading: false,
rules: [],
});
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import ApprovalsSettingsEmpty from 'ee/approvals/components/approvals_settings_empty.vue';
const localVue = createLocalVue();
describe('EE ApprovalsSettingsEmpty', () => {
let wrapper;
const factory = options => {
wrapper = shallowMount(localVue.extend(ApprovalsSettingsEmpty), {
localVue,
...options,
});
};
afterEach(() => {
wrapper.destroy();
});
it('shows message', () => {
factory();
expect(wrapper.text()).toContain(ApprovalsSettingsEmpty.message);
});
it('shows button', () => {
factory();
expect(wrapper.find(GlButton).exists()).toBe(true);
});
it('emits "submit" on button press', () => {
factory();
expect(wrapper.emittedByOrder().length).toEqual(0);
wrapper.find(GlButton).vm.$emit('click');
expect(wrapper.emittedByOrder().map(x => x.name)).toEqual(['submit']);
});
});
import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlLoadingIcon } from '@gitlab/ui';
import ApprovalsSettingsEmpty from 'ee/approvals/components/approvals_settings_empty.vue';
import ApprovalsSettingsForm from 'ee/approvals/components/approvals_settings_form.vue';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('EE ApprovalsSettingsForm', () => {
let state;
let actions;
let wrapper;
const factory = () => {
const store = new Vuex.Store({
state,
actions,
});
wrapper = shallowMount(localVue.extend(ApprovalsSettingsForm), {
localVue,
store,
});
};
beforeEach(() => {
state = {};
actions = {
fetchRules: jasmine.createSpy('fetchRules'),
};
});
it('dispatches fetchRules action on created', () => {
expect(actions.fetchRules).not.toHaveBeenCalled();
factory();
expect(actions.fetchRules).toHaveBeenCalledTimes(1);
});
it('shows loading icon if loading', () => {
state.isLoading = true;
factory();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
});
it('does not show loading icon if not loading', () => {
state.isLoading = false;
factory();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
});
it('shows ApprovalsSettingsEmpty if empty', () => {
state.rules = [];
factory();
expect(wrapper.find(ApprovalsSettingsEmpty).exists()).toBe(true);
});
it('does not show ApprovalsSettingsEmpty is not empty', () => {
state.rules = [{ id: 1 }];
factory();
expect(wrapper.find(ApprovalsSettingsEmpty).exists()).toBe(false);
});
});
import testAction from 'spec/helpers/vuex_action_helper';
import * as types from 'ee/approvals/stores/mutation_types';
import actionsModule, * as actions from 'ee/approvals/stores/actions';
import service from 'ee/approvals/services/approvals_service_stub';
describe('EE approvals store actions', () => {
let flashSpy;
beforeEach(() => {
flashSpy = spyOnDependency(actionsModule, 'createFlash');
spyOn(service, 'getProjectApprovalRules');
});
describe('requestRules', () => {
it('sets loading', done => {
testAction(
actions.requestRules,
null,
{},
[{ type: types.SET_LOADING, payload: true }],
[],
done,
);
});
});
describe('receiveRulesSuccess', () => {
it('sets rules', done => {
const rules = [{ id: 1 }];
testAction(
actions.receiveRulesSuccess,
{ rules },
{},
[{ type: types.SET_RULES, payload: rules }, { type: types.SET_LOADING, payload: false }],
[],
done,
);
});
});
describe('receiveRulesError', () => {
it('creates a flash', () => {
expect(flashSpy).not.toHaveBeenCalled();
actions.receiveRulesError();
expect(flashSpy).toHaveBeenCalledTimes(1);
});
});
describe('fetchRules', () => {
it('does nothing if loading', done => {
testAction(actions.fetchRules, null, { isLoading: true }, [], [], done);
});
it('dispatches request/receive', done => {
const response = {
data: { rules: [] },
};
service.getProjectApprovalRules.and.returnValue(Promise.resolve(response));
testAction(
actions.fetchRules,
null,
{},
[],
[{ type: 'requestRules' }, { type: 'receiveRulesSuccess', payload: response.data }],
done,
);
});
it('dispatches request/receive on error', done => {
service.getProjectApprovalRules.and.returnValue(Promise.reject());
testAction(
actions.fetchRules,
null,
{},
[],
[{ type: 'requestRules' }, { type: 'receiveRulesError' }],
done,
);
});
});
});
import createState from 'ee/approvals/stores/state';
import * as types from 'ee/approvals/stores/mutation_types';
import mutations from 'ee/approvals/stores/mutations';
describe('EE approvals store mutations', () => {
let state;
beforeEach(() => {
state = createState();
});
describe(types.SET_LOADING, () => {
it('sets isLoading', () => {
state.isLoading = false;
mutations[types.SET_LOADING](state, true);
expect(state.isLoading).toBe(true);
});
});
describe(types.SET_RULES, () => {
it('sets rules', () => {
const newRules = [{ id: 1 }, { id: 2 }];
state.rules = [];
mutations[types.SET_RULES](state, newRules);
expect(state.rules).toEqual(newRules);
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment