Merge branch 'multi-file-editor-css-fixes' into 'master'

Added IDE commit panel

Closes #40041

See merge request gitlab-org/gitlab-ce!15583
This commit is contained in:
Tim Zallmann 2017-11-29 10:40:20 +00:00
commit f57b06ca83
26 changed files with 739 additions and 413 deletions

View file

@ -0,0 +1,89 @@
<script>
import icon from '../../../vue_shared/components/icon.vue';
import listItem from './list_item.vue';
import listCollapsed from './list_collapsed.vue';
export default {
components: {
icon,
listItem,
listCollapsed,
},
props: {
title: {
type: String,
required: true,
},
fileList: {
type: Array,
required: true,
},
collapsed: {
type: Boolean,
required: true,
},
},
methods: {
toggleCollapsed() {
this.$emit('toggleCollapsed');
},
},
};
</script>
<template>
<div class="multi-file-commit-panel-section">
<header
class="multi-file-commit-panel-header"
:class="{
'is-collapsed': collapsed,
}"
>
<icon
name="list-bulleted"
:size="18"
css-classes="append-right-default"
/>
<template v-if="!collapsed">
{{ title }}
<button
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-right"
>
</i>
</button>
</template>
</header>
<div class="multi-file-commit-list">
<list-collapsed
v-if="collapsed"
/>
<template v-else>
<ul
v-if="fileList.length"
class="list-unstyled append-bottom-0"
>
<li
v-for="file in fileList"
:key="file.key"
>
<list-item
:file="file"
/>
</li>
</ul>
<div
v-else
class="help-block prepend-top-0"
>
No changes
</div>
</template>
</div>
</div>
</template>

View file

@ -0,0 +1,35 @@
<script>
import { mapGetters } from 'vuex';
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
computed: {
...mapGetters([
'addedFiles',
'modifiedFiles',
]),
},
};
</script>
<template>
<div
class="multi-file-commit-list-collapsed text-center"
>
<icon
name="file-addition"
:size="18"
css-classes="multi-file-addition append-bottom-10"
/>
{{ addedFiles.length }}
<icon
name="file-modified"
:size="18"
css-classes="multi-file-modified prepend-top-10 append-bottom-10"
/>
{{ modifiedFiles.length }}
</div>
</template>

View file

@ -0,0 +1,36 @@
<script>
import icon from '../../../vue_shared/components/icon.vue';
export default {
components: {
icon,
},
props: {
file: {
type: Object,
required: true,
},
},
computed: {
iconName() {
return this.file.tempFile ? 'file-addition' : 'file-modified';
},
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
},
};
</script>
<template>
<div class="multi-file-commit-list-item">
<icon
:name="iconName"
:size="16"
:css-classes="iconClass"
/>
<span class="multi-file-commit-list-path">
{{ file.path }}
</span>
</div>
</template>

View file

@ -40,20 +40,24 @@ export default {
</script>
<template>
<div class="repository-view">
<div class="tree-content-holder" :class="{'tree-content-holder-mini' : isCollapsed}">
<repo-sidebar/>
<div
v-if="isCollapsed"
class="panel-right"
>
<repo-tabs/>
<component
:is="currentBlobView"
/>
<repo-file-buttons/>
</div>
<div
class="multi-file"
:class="{
'is-collapsed': isCollapsed
}"
>
<repo-sidebar/>
<div
v-if="isCollapsed"
class="multi-file-edit-pane"
>
<repo-tabs />
<component
class="multi-file-edit-pane-content"
:is="currentBlobView"
/>
<repo-file-buttons />
</div>
<repo-commit-section v-if="changedFiles.length" />
<repo-commit-section />
</div>
</template>

View file

@ -1,11 +1,18 @@
<script>
import { mapGetters, mapState, mapActions } from 'vuex';
import tooltip from '../../vue_shared/directives/tooltip';
import icon from '../../vue_shared/components/icon.vue';
import PopupDialog from '../../vue_shared/components/popup_dialog.vue';
import { n__ } from '../../locale';
import commitFilesList from './commit_sidebar/list.vue';
export default {
components: {
PopupDialog,
icon,
commitFilesList,
},
directives: {
tooltip,
},
data() {
return {
@ -13,6 +20,7 @@ export default {
submitCommitsLoading: false,
startNewMR: false,
commitMessage: '',
collapsed: true,
};
},
computed: {
@ -23,10 +31,10 @@ export default {
'changedFiles',
]),
commitButtonDisabled() {
return !this.commitMessage || this.submitCommitsLoading;
return this.commitMessage === '' || this.submitCommitsLoading || !this.changedFiles.length;
},
commitButtonText() {
return n__('Commit %d file', 'Commit %d files', this.changedFiles.length);
commitMessageCount() {
return this.commitMessage.length;
},
},
methods: {
@ -77,12 +85,20 @@ export default {
this.submitCommitsLoading = false;
});
},
toggleCollapsed() {
this.collapsed = !this.collapsed;
},
},
};
</script>
<template>
<div id="commit-area">
<div
class="multi-file-commit-panel"
:class="{
'is-collapsed': collapsed,
}"
>
<popup-dialog
v-if="showNewBranchDialog"
:primary-button-label="__('Create new branch')"
@ -92,78 +108,71 @@ export default {
@toggle="showNewBranchDialog = false"
@submit="makeCommit(true)"
/>
<button
v-if="collapsed"
type="button"
class="btn btn-transparent multi-file-commit-panel-collapse-btn is-collapsed prepend-top-10 append-bottom-10"
@click="toggleCollapsed"
>
<i
aria-hidden="true"
class="fa fa-angle-double-left"
>
</i>
</button>
<commit-files-list
title="Staged"
:file-list="changedFiles"
:collapsed="collapsed"
@toggleCollapsed="toggleCollapsed"
/>
<form
class="form-horizontal"
@submit.prevent="tryCommit()">
<fieldset>
<div class="form-group">
<label class="col-md-4 control-label staged-files">
Staged files ({{changedFiles.length}})
</label>
<div class="col-md-6">
<ul class="list-unstyled changed-files">
<li
v-for="(file, index) in changedFiles"
:key="index">
<span class="help-block">
{{ file.path }}
</span>
</li>
</ul>
</div>
class="form-horizontal multi-file-commit-form"
@submit.prevent="tryCommit"
v-if="!collapsed"
>
<div class="multi-file-commit-fieldset">
<textarea
class="form-control multi-file-commit-message"
name="commit-message"
v-model="commitMessage"
placeholder="Commit message"
>
</textarea>
</div>
<div class="multi-file-commit-fieldset">
<label
v-tooltip
title="Create a new merge request with these changes"
data-container="body"
data-placement="top"
>
<input
type="checkbox"
v-model="startNewMR"
/>
Merge Request
</label>
<button
type="submit"
:disabled="commitButtonDisabled"
class="btn btn-default btn-sm append-right-10 prepend-left-10"
>
<i
v-if="submitCommitsLoading"
class="js-commit-loading-icon fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="loading"
>
</i>
Commit
</button>
<div
class="multi-file-commit-message-count"
>
{{ commitMessageCount }}
</div>
<div class="form-group">
<label
class="col-md-4 control-label"
for="commit-message">
Commit message
</label>
<div class="col-md-6">
<textarea
id="commit-message"
class="form-control"
name="commit-message"
v-model="commitMessage">
</textarea>
</div>
</div>
<div class="form-group target-branch">
<label
class="col-md-4 control-label"
for="target-branch">
Target branch
</label>
<div class="col-md-6">
<span class="help-block">
{{currentBranch}}
</span>
</div>
</div>
<div class="col-md-offset-4 col-md-6">
<button
type="submit"
:disabled="commitButtonDisabled"
class="btn btn-success">
<i
v-if="submitCommitsLoading"
class="js-commit-loading-icon fa fa-spinner fa-spin"
aria-hidden="true"
aria-label="loading">
</i>
<span class="commit-summary">
{{ commitButtonText }}
</span>
</button>
</div>
<div class="col-md-offset-4 col-md-6">
<div class="checkbox">
<label>
<input type="checkbox" v-model="startNewMR">
<span>Start a <strong>new merge request</strong> with these changes</span>
</label>
</div>
</div>
</fieldset>
</div>
</form>
</div>
</template>

View file

@ -55,7 +55,7 @@
class="file"
@click.prevent="clickedTreeRow(file)">
<td
class="multi-file-table-col-name"
class="multi-file-table-name"
:colspan="submoduleColSpan"
>
<i
@ -85,12 +85,11 @@
</td>
<template v-if="!isCollapsed && !isSubmodule">
<td class="hidden-sm hidden-xs">
<td class="multi-file-table-col-commit-message hidden-sm hidden-xs">
<a
v-if="file.lastCommit.message"
@click.stop
:href="file.lastCommit.url"
class="commit-message"
>
{{ file.lastCommit.message }}
</a>

View file

@ -22,12 +22,12 @@ export default {
<template>
<div
v-if="showButtons"
class="repo-file-buttons"
class="multi-file-editor-btn-group"
>
<a
:href="activeFile.rawPath"
target="_blank"
class="btn btn-default raw"
class="btn btn-default btn-sm raw"
rel="noopener noreferrer">
{{ rawDownloadButtonLabel }}
</a>
@ -38,17 +38,17 @@ export default {
aria-label="File actions">
<a
:href="activeFile.blamePath"
class="btn btn-default blame">
class="btn btn-default btn-sm blame">
Blame
</a>
<a
:href="activeFile.commitsPath"
class="btn btn-default history">
class="btn btn-default btn-sm history">
History
</a>
<a
:href="activeFile.permalink"
class="btn btn-default permalink">
class="btn btn-default btn-sm permalink">
Permalink
</a>
</div>

View file

@ -32,10 +32,12 @@ export default {
</script>
<template>
<div class="blob-viewer-container">
<div>
<div
v-if="!activeFile.renderError"
v-html="activeFile.html">
v-html="activeFile.html"
class="multi-file-preview-holder"
>
</div>
<div
v-else-if="activeFile.tempFile"

View file

@ -44,20 +44,16 @@ export default {
</script>
<template>
<div id="sidebar" :class="{'sidebar-mini' : isCollapsed}">
<div class="ide-file-list">
<table class="table">
<thead>
<tr>
<th
v-if="isCollapsed"
class="repo-file-options title"
>
<strong class="clgray">
{{ projectName }}
</strong>
</th>
<template v-else>
<th class="name multi-file-table-col-name">
<th class="name multi-file-table-name">
Name
</th>
<th class="hidden-sm hidden-xs last-commit">
@ -79,7 +75,7 @@ export default {
:key="n"
/>
<repo-file
v-for="(file, index) in treeList"
v-for="file in treeList"
:key="file.key"
:file="file"
/>

View file

@ -36,27 +36,32 @@ export default {
<template>
<li
:class="{ active : tab.active }"
@click="setFileActive(tab)"
>
<button
type="button"
class="close-btn"
class="multi-file-tab-close"
@click.stop.prevent="closeFile({ file: tab })"
:aria-label="closeLabel">
:aria-label="closeLabel"
:class="{
'modified': tab.changed,
}"
:disabled="tab.changed"
>
<i
class="fa"
:class="changedClass"
aria-hidden="true">
aria-hidden="true"
>
</i>
</button>
<a
href="#"
class="repo-tab"
<div
class="multi-file-tab"
:class="{active : tab.active }"
:title="tab.url"
@click.prevent.stop="setFileActive(tab)">
{{tab.name}}
</a>
>
{{ tab.name }}
</div>
</li>
</template>

View file

@ -16,14 +16,12 @@
<template>
<ul
id="tabs"
class="list-unstyled"
class="multi-file-tabs list-unstyled append-bottom-0"
>
<repo-tab
v-for="tab in openFiles"
:key="tab.id"
:tab="tab"
/>
<li class="tabs-divider" />
</ul>
</template>

View file

@ -34,3 +34,7 @@ export const canEditFile = (state) => {
openedFiles.length &&
(currentActiveFile && !currentActiveFile.renderError && !currentActiveFile.binary);
};
export const addedFiles = state => changedFiles(state).filter(f => f.tempFile);
export const modifiedFiles = state => changedFiles(state).filter(f => !f.tempFile);

View file

@ -35,270 +35,243 @@
}
}
.repository-view {
border: 1px solid $border-color;
border-radius: $border-radius-default;
color: $almost-black;
.multi-file {
display: flex;
height: calc(100vh - 145px);
border-top: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
.code.white pre .hll {
background-color: $well-light-border !important;
}
.tree-content-holder {
display: -webkit-flex;
display: flex;
min-height: 300px;
}
.tree-content-holder-mini {
height: 100vh;
}
.panel-right {
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
width: 80%;
height: 100%;
.monaco-editor.vs {
.current-line {
border: 0;
background: $well-light-border;
}
.line-numbers {
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
}
.blob-no-preview {
.vertical-center {
justify-content: center;
width: 100%;
}
}
&.blob-editor-container {
overflow: hidden;
}
.blob-viewer-container {
-webkit-flex: 1;
flex: 1;
overflow: auto;
> div,
.file-content:not(.wiki) {
display: flex;
}
> div,
.file-content,
.blob-viewer,
.line-number,
.blob-content,
.code {
min-height: 100%;
width: 100%;
}
.line-numbers {
min-width: 44px;
}
.blob-content {
flex: 1;
overflow-x: auto;
}
}
#tabs {
position: relative;
flex-shrink: 0;
display: flex;
width: 100%;
padding-left: 0;
margin-bottom: 0;
white-space: nowrap;
overflow-y: hidden;
overflow-x: auto;
li {
position: relative;
background: $gray-normal;
padding: #{$gl-padding / 2} $gl-padding;
border-right: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
cursor: pointer;
&.active {
background: $white-light;
border-bottom: 0;
}
a {
@include str-truncated(100px);
color: $gl-text-color;
vertical-align: middle;
text-decoration: none;
margin-right: 12px;
&:focus {
outline: none;
}
}
.close-btn {
position: absolute;
right: 8px;
top: 50%;
padding: 0;
background: none;
border: 0;
font-size: $gl-font-size;
transform: translateY(-50%);
}
.close-icon:hover {
color: $hint-color;
}
.close-icon,
.unsaved-icon {
color: $gray-darkest;
}
.unsaved-icon {
color: $brand-success;
}
&.tabs-divider {
width: 100%;
background-color: $white-light;
border-right: 0;
border-top-right-radius: 2px;
}
}
}
.repo-file-buttons {
background-color: $white-light;
padding: 5px 10px;
border-top: 1px solid $white-normal;
}
#binary-viewer {
height: 80vh;
overflow: auto;
margin: 0;
.blob-viewer {
padding-top: 20px;
padding-left: 20px;
}
.binary-unknown {
text-align: center;
padding-top: 100px;
background: $gray-light;
height: 100%;
font-size: 17px;
span {
display: block;
}
}
}
}
#commit-area {
background: $gray-light;
padding: 20px;
.help-block {
padding-top: 7px;
margin-top: 0;
}
}
#view-toggler {
height: 41px;
position: relative;
display: block;
border-bottom: 1px solid $white-normal;
background: $white-light;
margin-top: -5px;
}
#binary-viewer {
img {
max-width: 100%;
}
}
#sidebar {
flex: 1;
height: 100%;
&.sidebar-mini {
width: 20%;
border-right: 1px solid $white-normal;
overflow: auto;
}
.table {
margin-bottom: 0;
}
tr {
.repo-file-options {
padding: 2px 16px;
width: 100%;
}
.title {
font-size: 10px;
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: middle;
}
.file-icon {
margin-right: 5px;
}
td {
white-space: nowrap;
}
}
.file {
cursor: pointer;
}
a {
@include str-truncated(250px);
color: $almost-black;
&.is-collapsed {
.ide-file-list {
max-width: 250px;
}
}
}
.render-error {
min-height: calc(100vh - 62px);
.ide-file-list {
flex: 1;
overflow: scroll;
p {
width: 100%;
.file {
cursor: pointer;
}
a {
color: $gl-text-color;
}
th {
position: sticky;
top: 0;
}
}
.multi-file-table-col-name {
.multi-file-table-name,
.multi-file-table-col-commit-message {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 0;
}
.multi-file-table-name {
width: 350px;
}
.multi-file-table-col-commit-message {
width: 50%;
}
.multi-file-edit-pane {
display: flex;
flex-direction: column;
flex: 1;
border-left: 1px solid $white-dark;
overflow: hidden;
}
.multi-file-tabs {
display: flex;
overflow: scroll;
background-color: $white-normal;
box-shadow: inset 0 -1px $white-dark;
> li {
position: relative;
}
}
.multi-file-tab {
@include str-truncated(150px);
padding: ($gl-padding / 2) ($gl-padding + 12) ($gl-padding / 2) $gl-padding;
background-color: $gray-normal;
border-right: 1px solid $white-dark;
border-bottom: 1px solid $white-dark;
cursor: pointer;
&.active {
background-color: $white-light;
border-bottom-color: $white-light;
}
}
.multi-file-tab-close {
position: absolute;
right: 8px;
top: 50%;
padding: 0;
background: none;
border: 0;
font-size: $gl-font-size;
color: $gray-darkest;
transform: translateY(-50%);
&:not(.modified):hover,
&:not(.modified):focus {
color: $hint-color;
}
&.modified {
color: $indigo-700;
}
}
.multi-file-edit-pane-content {
flex: 1;
height: 0;
}
.multi-file-editor-btn-group {
padding: $grid-size;
border-top: 1px solid $white-dark;
}
// Not great, but this is to deal with our current output
.multi-file-preview-holder {
height: 100%;
overflow: scroll;
.blob-viewer {
height: 100%;
}
.file-content.code {
display: flex;
i {
margin-left: -10px;
}
}
.line-numbers {
min-width: 50px;
}
.file-content,
.line-numbers,
.blob-content,
.code {
min-height: 100%;
}
}
.multi-file-commit-panel {
display: flex;
flex-direction: column;
height: 100%;
width: 290px;
padding: $gl-padding;
background-color: $gray-light;
border-left: 1px solid $white-dark;
&.is-collapsed {
width: 60px;
padding: 0;
}
}
.multi-file-commit-panel-section {
display: flex;
flex-direction: column;
flex: 1;
}
.multi-file-commit-panel-header {
display: flex;
align-items: center;
padding: 0 0 12px;
margin-bottom: 12px;
border-bottom: 1px solid $white-dark;
&.is-collapsed {
border-bottom: 1px solid $white-dark;
svg {
margin-left: auto;
margin-right: auto;
}
}
}
.multi-file-commit-panel-collapse-btn {
padding-top: 0;
padding-bottom: 0;
margin-left: auto;
font-size: 20px;
&.is-collapsed {
margin-right: auto;
}
}
.multi-file-commit-list {
flex: 1;
overflow: scroll;
}
.multi-file-commit-list-item {
display: flex;
align-items: center;
}
.multi-file-addition {
fill: $green-500;
}
.multi-file-modified {
fill: $orange-500;
}
.multi-file-commit-list-collapsed {
display: flex;
flex-direction: column;
> svg {
margin-left: auto;
margin-right: auto;
}
}
.multi-file-commit-list-path {
@include str-truncated(100%);
}
.multi-file-commit-form {
padding-top: 12px;
border-top: 1px solid $white-dark;
}
.multi-file-commit-fieldset {
display: flex;
align-items: center;
padding-bottom: 12px;
.btn {
flex: 1;
}
}
.multi-file-commit-message.form-control {
height: 80px;
resize: none;
}

View file

@ -11,6 +11,6 @@
= webpack_bundle_tag 'common_vue'
= webpack_bundle_tag 'repo'
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
%div{ class: [(container_class unless show_new_repo?), ("limit-container-width" unless fluid_layout)] }
= render 'projects/last_push'
= render 'projects/files', commit: @last_commit, project: @project, ref: @ref, content_url: project_tree_path(@project, @id)

View file

@ -1,3 +1,4 @@
- @no_container = true;
#repo{ data: { root: @path.empty?.to_s,
root_url: project_tree_path(project),
url: content_url,

View file

@ -26,9 +26,11 @@ feature 'Multi-file editor new directory', :js do
click_button('Create directory')
end
find('.multi-file-commit-panel-collapse-btn').click
fill_in('commit-message', with: 'commit message')
click_button('Commit 1 file')
click_button('Commit')
expect(page).to have_selector('td', text: 'commit message')
end

View file

@ -26,9 +26,11 @@ feature 'Multi-file editor new file', :js do
click_button('Create file')
end
find('.multi-file-commit-panel-collapse-btn').click
fill_in('commit-message', with: 'commit message')
click_button('Commit 1 file')
click_button('Commit')
expect(page).to have_selector('td', text: 'commit message')
end

View file

@ -26,7 +26,7 @@ feature 'Multi-file editor upload file', :js do
find('.add-to-tree').click
expect(page).to have_selector('.repo-tab', text: 'doc_sample.txt')
expect(page).to have_selector('.multi-file-tab', text: 'doc_sample.txt')
expect(find('.blob-editor-container .lines-content')['innerText']).to have_content(File.open(txt_file, &:readline))
end
@ -39,7 +39,7 @@ feature 'Multi-file editor upload file', :js do
find('.add-to-tree').click
expect(page).to have_selector('.repo-tab', text: 'dk.png')
expect(page).to have_selector('.multi-file-tab', text: 'dk.png')
expect(page).not_to have_selector('.monaco-editor')
expect(page).to have_content('The source could not be displayed for this temporary file.')
end

View file

@ -0,0 +1,33 @@
import Vue from 'vue';
import store from '~/repo/stores';
import listCollapsed from '~/repo/components/commit_sidebar/list_collapsed.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list collapsed', () => {
let vm;
beforeEach(() => {
const Component = Vue.extend(listCollapsed);
vm = createComponentWithStore(Component, store);
vm.$store.state.openFiles.push(file(), file());
vm.$store.state.openFiles[0].tempFile = true;
vm.$store.state.openFiles.forEach((f) => {
Object.assign(f, {
changed: true,
});
});
vm.$mount();
});
afterEach(() => {
vm.$destroy();
});
it('renders added & modified files count', () => {
expect(vm.$el.textContent.replace(/\s+/g, ' ').trim()).toBe('1 1');
});
});

View file

@ -0,0 +1,53 @@
import Vue from 'vue';
import listItem from '~/repo/components/commit_sidebar/list_item.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list item', () => {
let vm;
let f;
beforeEach(() => {
const Component = Vue.extend(listItem);
f = file();
vm = mountComponent(Component, {
file: f,
});
});
afterEach(() => {
vm.$destroy();
});
it('renders file path', () => {
expect(vm.$el.querySelector('.multi-file-commit-list-path').textContent.trim()).toBe(f.path);
});
describe('computed', () => {
describe('iconName', () => {
it('returns modified when not a tempFile', () => {
expect(vm.iconName).toBe('file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
expect(vm.iconName).toBe('file-addition');
});
});
describe('iconClass', () => {
it('returns modified when not a tempFile', () => {
expect(vm.iconClass).toContain('multi-file-modified');
});
it('returns addition when not a tempFile', () => {
f.tempFile = true;
expect(vm.iconClass).toContain('multi-file-addition');
});
});
});
});

View file

@ -0,0 +1,72 @@
import Vue from 'vue';
import store from '~/repo/stores';
import commitSidebarList from '~/repo/components/commit_sidebar/list.vue';
import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list', () => {
let vm;
beforeEach(() => {
const Component = Vue.extend(commitSidebarList);
vm = createComponentWithStore(Component, store, {
title: 'Staged',
fileList: [],
collapsed: false,
}).$mount();
});
afterEach(() => {
vm.$destroy();
});
describe('empty file list', () => {
it('renders no changes text', () => {
expect(vm.$el.querySelector('.help-block').textContent.trim()).toBe('No changes');
});
});
describe('with a list of files', () => {
beforeEach((done) => {
const f = file('file name');
f.changed = true;
vm.fileList.push(f);
Vue.nextTick(done);
});
it('renders list', () => {
expect(vm.$el.querySelectorAll('li').length).toBe(1);
});
});
describe('collapsed', () => {
beforeEach((done) => {
vm.collapsed = true;
Vue.nextTick(done);
});
it('adds collapsed class', () => {
expect(vm.$el.querySelector('.is-collapsed')).not.toBeNull();
});
it('hides list', () => {
expect(vm.$el.querySelector('.list-unstyled')).toBeNull();
expect(vm.$el.querySelector('.help-block')).toBeNull();
});
it('hides collapse button', () => {
expect(vm.$el.querySelector('.multi-file-commit-panel-collapse-btn')).toBeNull();
});
});
it('clicking toggle collapse button emits toggle event', () => {
spyOn(vm, '$emit');
vm.$el.querySelector('.multi-file-commit-panel-collapse-btn').click();
expect(vm.$emit).toHaveBeenCalledWith('toggleCollapsed');
});
});

View file

@ -25,8 +25,12 @@ describe('RepoCommitSection', () => {
return comp.$mount();
}
beforeEach(() => {
beforeEach((done) => {
vm = createComponent();
vm.collapsed = false;
Vue.nextTick(done);
});
afterEach(() => {
@ -36,12 +40,11 @@ describe('RepoCommitSection', () => {
});
it('renders a commit section', () => {
const changedFileElements = [...vm.$el.querySelectorAll('.changed-files > li')];
const submitCommit = vm.$el.querySelector('.btn');
const targetBranch = vm.$el.querySelector('.target-branch');
const changedFileElements = [...vm.$el.querySelectorAll('.multi-file-commit-list li')];
const submitCommit = vm.$el.querySelector('form .btn');
expect(vm.$el.querySelector(':scope > form')).toBeTruthy();
expect(vm.$el.querySelector('.staged-files').textContent.trim()).toEqual('Staged files (2)');
expect(vm.$el.querySelector('.multi-file-commit-form')).not.toBeNull();
expect(vm.$el.querySelector('.multi-file-commit-panel-section header').textContent.trim()).toEqual('Staged');
expect(changedFileElements.length).toEqual(2);
changedFileElements.forEach((changedFile, i) => {
@ -49,10 +52,7 @@ describe('RepoCommitSection', () => {
});
expect(submitCommit.disabled).toBeTruthy();
expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeFalsy();
expect(vm.$el.querySelector('.commit-summary').textContent.trim()).toEqual('Commit 2 files');
expect(targetBranch.querySelector(':scope > label').textContent.trim()).toEqual('Target branch');
expect(targetBranch.querySelector('.help-block').textContent.trim()).toEqual('master');
expect(submitCommit.querySelector('.fa-spinner.fa-spin')).toBeNull();
});
describe('when submitting', () => {
@ -69,7 +69,7 @@ describe('RepoCommitSection', () => {
});
it('allows you to submit', () => {
expect(vm.$el.querySelector('.btn').disabled).toBeTruthy();
expect(vm.$el.querySelector('form .btn').disabled).toBeTruthy();
});
it('submits commit', (done) => {

View file

@ -29,7 +29,6 @@ describe('RepoSidebar', () => {
const thead = vm.$el.querySelector('thead');
const tbody = vm.$el.querySelector('tbody');
expect(vm.$el.id).toEqual('sidebar');
expect(vm.$el.classList.contains('sidebar-mini')).toBeFalsy();
expect(thead.querySelector('.name').textContent.trim()).toEqual('Name');
expect(thead.querySelector('.last-commit').textContent.trim()).toEqual('Last commit');
@ -40,18 +39,6 @@ describe('RepoSidebar', () => {
expect(tbody.querySelector('.file')).toBeTruthy();
});
it('does not render a thead, renders repo-file-options and sets sidebar-mini class if isMini', (done) => {
vm.$store.state.openFiles.push(vm.$store.state.tree[0]);
Vue.nextTick(() => {
expect(vm.$el.classList.contains('sidebar-mini')).toBeTruthy();
expect(vm.$el.querySelector('thead')).toBeTruthy();
expect(vm.$el.querySelector('thead .repo-file-options')).toBeTruthy();
done();
});
});
it('renders 5 loading files if tree is loading', (done) => {
vm.$store.state.tree = [];
vm.$store.state.loading = true;

View file

@ -24,8 +24,8 @@ describe('RepoTab', () => {
tab: file(),
});
vm.$store.state.openFiles.push(vm.tab);
const close = vm.$el.querySelector('.close-btn');
const name = vm.$el.querySelector(`a[title="${vm.tab.url}"]`);
const close = vm.$el.querySelector('.multi-file-tab-close');
const name = vm.$el.querySelector(`[title="${vm.tab.url}"]`);
expect(close.querySelector('.fa-times')).toBeTruthy();
expect(name.textContent.trim()).toEqual(vm.tab.name);
@ -50,7 +50,7 @@ describe('RepoTab', () => {
spyOn(vm, 'closeFile');
vm.$el.querySelector('.close-btn').click();
vm.$el.querySelector('.multi-file-tab-close').click();
expect(vm.closeFile).toHaveBeenCalledWith({ file: vm.tab });
});
@ -62,7 +62,7 @@ describe('RepoTab', () => {
tab,
});
expect(vm.$el.querySelector('.close-btn .fa-circle')).toBeTruthy();
expect(vm.$el.querySelector('.multi-file-tab-close .fa-circle')).not.toBeNull();
});
describe('methods', () => {
@ -77,7 +77,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab);
vm.$el.querySelector('.close-btn').click();
vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => {
expect(tab.opened).toBeTruthy();
@ -95,7 +95,7 @@ describe('RepoTab', () => {
vm.$store.state.openFiles.push(tab);
vm.$store.dispatch('setFileActive', tab);
vm.$el.querySelector('.close-btn').click();
vm.$el.querySelector('.multi-file-tab-close').click();
vm.$nextTick(() => {
expect(tab.opened).toBeFalsy();

View file

@ -25,12 +25,11 @@ describe('RepoTabs', () => {
vm.$store.state.openFiles = openedFiles;
vm.$nextTick(() => {
const tabs = [...vm.$el.querySelectorAll(':scope > li')];
const tabs = [...vm.$el.querySelectorAll('.multi-file-tab')];
expect(tabs.length).toEqual(3);
expect(tabs.length).toEqual(2);
expect(tabs[0].classList.contains('active')).toBeTruthy();
expect(tabs[1].classList.contains('active')).toBeFalsy();
expect(tabs[2].classList.contains('tabs-divider')).toBeTruthy();
done();
});

View file

@ -116,4 +116,31 @@ describe('Multi-file store getters', () => {
expect(getters.canEditFile(localState)).toBeFalsy();
});
});
describe('modifiedFiles', () => {
it('returns a list of modified files', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('changed'));
localState.openFiles[1].changed = true;
const modifiedFiles = getters.modifiedFiles(localState);
expect(modifiedFiles.length).toBe(1);
expect(modifiedFiles[0].name).toBe('changed');
});
});
describe('addedFiles', () => {
it('returns a list of added files', () => {
localState.openFiles.push(file());
localState.openFiles.push(file('added'));
localState.openFiles[1].changed = true;
localState.openFiles[1].tempFile = true;
const modifiedFiles = getters.addedFiles(localState);
expect(modifiedFiles.length).toBe(1);
expect(modifiedFiles[0].name).toBe('added');
});
});
});