Commit 09babc57 authored by Filipa Lacerda's avatar Filipa Lacerda

Moves more mr widget components into vue files

Adds i18n
Adds better test coverage
parent 0f5f77a0
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="loading" :show-disabled-button="true" />
<div class="media-body space-children">
<span class="bold">
Checking ability to merge automatically
</span>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetChecking',
components: {
statusIcon,
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="loading"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span class="bold">
{{ s__("mrWidget|Checking ability to merge automatically") }}
</span>
</div>
</div>
</template>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetClosed',
props: {
mr: { type: Object, required: true },
},
components: {
'mr-widget-author-and-time': mrWidgetAuthorTime,
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon status="warning" />
<div class="media-body">
<mr-widget-author-and-time
actionText="Closed by"
:author="mr.metrics.closedBy"
:dateTitle="mr.metrics.closedAt"
:dateReadable="mr.metrics.readableClosedAt"
/>
<section class="mr-info-list">
<p>
The changes were not merged into
<a
:href="mr.targetBranchPath"
class="label-branch">
{{mr.targetBranch}}</a>
</p>
</section>
</div>
</div>
`,
};
<script>
import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetClosed',
components: {
mrWidgetAuthorTime,
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs metrics and targetBranch */
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
/>
<div class="media-body">
<mr-widget-author-time
:action-text="s__('mrWidget|Closed by')"
:author="mr.metrics.closedBy"
:date-title="mr.metrics.closedAt"
:date-readable="mr.metrics.readableClosedAt"
/>
<section class="mr-info-list">
<p>
{{ s__("mrWidget|The changes were not merged into") }}
<a
:href="mr.targetBranchPath"
class="label-branch"
>
{{ mr.targetBranch }}
</a>
</p>
</section>
</div>
</div>
</template>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetConflicts',
props: {
mr: { type: Object, required: true },
},
components: {
statusIcon,
},
template: `
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true" />
<div class="media-body space-children">
<span
v-if="mr.shouldBeRebased"
class="bold">
Fast-forward merge is not possible.
To merge this request, first rebase locally.
</span>
<template v-else>
<span class="bold">
There are merge conflicts<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
Resolve these conflicts or ask someone with write access to this repository to merge it locally
</span>
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="js-resolve-conflicts-button btn btn-default btn-xs">
Resolve conflicts
</a>
<a
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
href="#modal_merge_info">
Merge locally
</a>
</template>
</div>
</div>
`,
};
<script>
import statusIcon from '../mr_widget_status_icon';
export default {
name: 'MRWidgetConflicts',
components: {
statusIcon,
},
props: {
/* TODO: This is providing all store and service down when it
only needs a few props */
mr: {
type: Object,
required: true,
default: () => ({}),
},
},
};
</script>
<template>
<div class="mr-widget-body media">
<status-icon
status="warning"
:show-disabled-button="true"
/>
<div class="media-body space-children">
<span
v-if="mr.shouldBeRebased"
class="bold"
>
{{ s__(`mrWidget|Fast-forward merge is not possible.
To merge this request, first rebase locally.`) }}
</span>
<template v-else>
<span class="bold">
{{ s__("mrWidget|There are merge conflicts") }}<span v-if="!mr.canMerge">.</span>
<span v-if="!mr.canMerge">
{{ s__(`mrWidget|Resolve these conflicts or ask someone
with write access to this repository to merge it locally`) }}
</span>
</span>
<a
v-if="mr.canMerge && mr.conflictResolutionPath"
:href="mr.conflictResolutionPath"
class="js-resolve-conflicts-button btn btn-default btn-xs"
>
{{ s__("mrWidget|Resolve conflicts") }}
</a>
<button
v-if="mr.canMerge"
class="js-merge-locally-button btn btn-default btn-xs"
data-toggle="modal"
data-target="#modal_merge_info"
>
{{ s__("mrWidget|Merge locally") }}
</button>
</template>
</div>
</div>
</template>
...@@ -18,11 +18,11 @@ export { default as WidgetDeployment } from './components/mr_widget_deployment'; ...@@ -18,11 +18,11 @@ export { default as WidgetDeployment } from './components/mr_widget_deployment';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links'; export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
export { default as MergedState } from './components/states/mr_widget_merged'; export { default as MergedState } from './components/states/mr_widget_merged';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge'; export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge';
export { default as ClosedState } from './components/states/mr_widget_closed'; export { default as ClosedState } from './components/states/mr_widget_closed.vue';
export { default as MergingState } from './components/states/mr_widget_merging'; export { default as MergingState } from './components/states/mr_widget_merging';
export { default as WipState } from './components/states/mr_widget_wip'; export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue'; export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts'; export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge'; export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
export { default as MissingBranchState } from './components/states/mr_widget_missing_branch'; export { default as MissingBranchState } from './components/states/mr_widget_missing_branch';
export { default as NotAllowedState } from './components/states/mr_widget_not_allowed'; export { default as NotAllowedState } from './components/states/mr_widget_not_allowed';
...@@ -34,7 +34,7 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi ...@@ -34,7 +34,7 @@ export { default as PipelineFailedState } from './components/states/mr_widget_pi
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds'; export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue'; export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue'; export { default as AutoMergeFailed } from './components/states/mr_widget_auto_merge_failed.vue';
export { default as CheckingState } from './components/states/mr_widget_checking'; export { default as CheckingState } from './components/states/mr_widget_checking.vue';
export { default as MRWidgetStore } from './stores/mr_widget_store'; export { default as MRWidgetStore } from './stores/mr_widget_store';
export { default as MRWidgetService } from './services/mr_widget_service'; export { default as MRWidgetService } from './services/mr_widget_service';
export { default as eventHub } from './event_hub'; export { default as eventHub } from './event_hub';
......
---
title: Refactors mr widget components into vue files and adds i18n
merge_request:
author:
type: other
import Vue from 'vue'; import Vue from 'vue';
import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking'; import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetChecking', () => { describe('MRWidgetChecking', () => {
describe('template', () => { let Component;
it('should have correct elements', () => { let vm;
const Component = Vue.extend(checkingComponent);
const el = new Component({
el: document.createElement('div'),
}).$el;
expect(el.classList.contains('mr-widget-body')).toBeTruthy(); beforeEach(() => {
expect(el.querySelector('button').classList.contains('btn-success')).toBeTruthy(); Component = Vue.extend(checkingComponent);
expect(el.querySelector('button').disabled).toBeTruthy(); vm = mountComponent(Component);
expect(el.innerText).toContain('Checking ability to merge automatically');
expect(el.querySelector('i')).toBeDefined();
}); });
afterEach(() => {
vm.$destroy();
});
it('renders disabled button', () => {
expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
});
it('renders loading icon', () => {
expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
});
it('renders information about merging', () => {
expect(vm.$el.querySelector('.media-body').textContent.trim()).toEqual('Checking ability to merge automatically');
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed'; import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
const mr = { describe('MRWidgetClosed', () => {
targetBranch: 'good-branch', let vm;
targetBranchPath: '/good-branch',
beforeEach(() => {
const Component = Vue.extend(closedComponent);
vm = mountComponent(Component, { mr: {
metrics: { metrics: {
mergedBy: {}, mergedBy: {},
mergedAt: 'mergedUpdatedAt',
closedBy: { closedBy: {
name: 'Fatih Acet', name: 'Administrator',
username: 'fatihacet', username: 'root',
webUrl: 'http://localhost:3000/root',
avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
}, },
closedAt: 'closedEventUpdatedAt', mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
readableMergedAt: '', readableMergedAt: '',
readableClosedAt: '', readableClosedAt: 'less than a minute ago',
}, },
updatedAt: 'mrUpdatedAt', targetBranchPath: '/twitter/flight/commits/so_long_jquery',
closedAt: '1 day ago', targetBranch: 'so_long_jquery',
}; } });
const createComponent = () => {
const Component = Vue.extend(closedComponent);
return new Component({
el: document.createElement('div'),
propsData: { mr },
}); });
};
describe('MRWidgetClosed', () => {
describe('props', () => {
it('should have props', () => {
const mrProp = closedComponent.props.mr;
expect(mrProp.type instanceof Object).toBeTruthy(); afterEach(() => {
expect(mrProp.required).toBeTruthy(); vm.$destroy();
});
});
describe('components', () => {
it('should have components added', () => {
expect(closedComponent.components['mr-widget-author-and-time']).toBeDefined();
});
}); });
describe('template', () => { it('renders warning icon', () => {
let vm; expect(vm.$el.querySelector('.js-ci-status-icon-warning')).not.toBeNull();
let el;
beforeEach(() => {
vm = createComponent();
el = vm.$el;
}); });
afterEach(() => { it('renders closed by information with author and time', () => {
vm.$destroy(); expect(
vm.$el.querySelector('.js-mr-widget-author').textContent.trim().replace(/\s\s+/g, ' '),
).toContain(
'Closed by Administrator less than a minute ago',
);
}); });
it('should have correct elements', () => { it('links to the user that closed the MR', () => {
expect(el.querySelector('h4').textContent).toContain('Closed by'); expect(vm.$el.querySelector('.author-link').getAttribute('href')).toEqual('http://localhost:3000/root');
expect(el.querySelector('h4').textContent).toContain(mr.metrics.closedBy.name);
expect(el.textContent).toContain('The changes were not merged into');
expect(el.querySelector('.label-branch').getAttribute('href')).toEqual(mr.targetBranchPath);
expect(el.querySelector('.label-branch').textContent).toContain(mr.targetBranch);
}); });
it('should use closedEvent updatedAt as tooltip title', () => { it('renders information about the changes not being merged', () => {
expect( expect(
el.querySelector('time').getAttribute('title'), vm.$el.querySelector('.mr-info-list').textContent.trim().replace(/\s\s+/g, ' '),
).toBe('closedEventUpdatedAt'); ).toContain('The changes were not merged into so_long_jquery');
}); });
it('renders link for target branch', () => {
expect(vm.$el.querySelector('.label-branch').getAttribute('href')).toEqual('/twitter/flight/commits/so_long_jquery');
}); });
}); });
import Vue from 'vue'; import Vue from 'vue';
import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts'; import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper'; import mountComponent from '../../../helpers/vue_mount_component_helper';
const ConflictsComponent = Vue.extend(conflictsComponent);
const path = '/conflicts';
describe('MRWidgetConflicts', () => { describe('MRWidgetConflicts', () => {
describe('props', () => { let Component;
it('should have props', () => { let vm;
const { mr } = conflictsComponent.props; const path = '/conflicts';
expect(mr.type instanceof Object).toBeTruthy(); beforeEach(() => {
expect(mr.required).toBeTruthy(); Component = Vue.extend(conflictsComponent);
}); });
afterEach(() => {
vm.$destroy();
}); });
describe('template', () => {
describe('when allowed to merge', () => { describe('when allowed to merge', () => {
let vm;
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ConflictsComponent, { vm = mountComponent(Component, {
mr: { mr: {
canMerge: true, canMerge: true,
conflictResolutionPath: path, conflictResolutionPath: path,
...@@ -28,10 +25,6 @@ describe('MRWidgetConflicts', () => { ...@@ -28,10 +25,6 @@ describe('MRWidgetConflicts', () => {
}); });
}); });
afterEach(() => {
vm.$destroy();
});
it('should tell you about conflicts without bothering other people', () => { it('should tell you about conflicts without bothering other people', () => {
expect(vm.$el.textContent).toContain('There are merge conflicts'); expect(vm.$el.textContent).toContain('There are merge conflicts');
expect(vm.$el.textContent).not.toContain('ask someone with write access'); expect(vm.$el.textContent).not.toContain('ask someone with write access');
...@@ -56,22 +49,16 @@ describe('MRWidgetConflicts', () => { ...@@ -56,22 +49,16 @@ describe('MRWidgetConflicts', () => {
}); });
describe('when user does not have permission to merge', () => { describe('when user does not have permission to merge', () => {
let vm;
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ConflictsComponent, { vm = mountComponent(Component, {
mr: { mr: {
canMerge: false, canMerge: false,
}, },
}); });
}); });
afterEach(() => {
vm.$destroy();
});
it('should show proper message', () => { it('should show proper message', () => {
expect(vm.$el.textContent).toContain('ask someone with write access'); expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('ask someone with write access');
}); });
it('should not have action buttons', () => { it('should not have action buttons', () => {
...@@ -82,24 +69,17 @@ describe('MRWidgetConflicts', () => { ...@@ -82,24 +69,17 @@ describe('MRWidgetConflicts', () => {
}); });
describe('when fast-forward or semi-linear merge enabled', () => { describe('when fast-forward or semi-linear merge enabled', () => {
let vm;
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ConflictsComponent, { vm = mountComponent(Component, {
mr: { mr: {
shouldBeRebased: true, shouldBeRebased: true,
}, },
}); });
}); });
afterEach(() => {
vm.$destroy();
});
it('should tell you to rebase locally', () => { it('should tell you to rebase locally', () => {
expect(vm.$el.textContent).toContain('Fast-forward merge is not possible.'); expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('Fast-forward merge is not possible.');
expect(vm.$el.textContent).toContain('To merge this request, first rebase locally'); expect(vm.$el.textContent.trim().replace(/\s\s+/g, ' ')).toContain('To merge this request, first rebase locally');
});
}); });
}); });
}); });
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