Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Boxiang Sun
gitlab-ce
Commits
51c64f3f
Commit
51c64f3f
authored
Mar 21, 2018
by
Phil Hughes
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added staged files state to IDE
Closes
https://gitlab.com/gitlab-org/gitlab-ee/issues/4541
parent
4718f22f
Changes
32
Hide whitespace changes
Inline
Side-by-side
Showing
32 changed files
with
1035 additions
and
231 deletions
+1035
-231
app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue
...javascripts/ide/components/commit_sidebar/empty_state.vue
+87
-0
app/assets/javascripts/ide/components/commit_sidebar/list.vue
...assets/javascripts/ide/components/commit_sidebar/list.vue
+84
-10
app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
...ascripts/ide/components/commit_sidebar/list_collapsed.vue
+38
-15
app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
...s/javascripts/ide/components/commit_sidebar/list_item.vue
+38
-35
app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
...avascripts/ide/components/commit_sidebar/stage_button.vue
+49
-0
app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
...ascripts/ide/components/commit_sidebar/unstage_button.vue
+38
-0
app/assets/javascripts/ide/components/ide_context_bar.vue
app/assets/javascripts/ide/components/ide_context_bar.vue
+0
-42
app/assets/javascripts/ide/components/repo_commit_section.vue
...assets/javascripts/ide/components/repo_commit_section.vue
+32
-69
app/assets/javascripts/ide/stores/actions.js
app/assets/javascripts/ide/stores/actions.js
+15
-0
app/assets/javascripts/ide/stores/actions/file.js
app/assets/javascripts/ide/stores/actions/file.js
+8
-0
app/assets/javascripts/ide/stores/getters.js
app/assets/javascripts/ide/stores/getters.js
+2
-0
app/assets/javascripts/ide/stores/modules/commit/actions.js
app/assets/javascripts/ide/stores/modules/commit/actions.js
+8
-4
app/assets/javascripts/ide/stores/mutation_types.js
app/assets/javascripts/ide/stores/mutation_types.js
+4
-0
app/assets/javascripts/ide/stores/mutations.js
app/assets/javascripts/ide/stores/mutations.js
+5
-0
app/assets/javascripts/ide/stores/mutations/file.js
app/assets/javascripts/ide/stores/mutations/file.js
+28
-0
app/assets/javascripts/ide/stores/state.js
app/assets/javascripts/ide/stores/state.js
+1
-0
app/assets/javascripts/ide/stores/utils.js
app/assets/javascripts/ide/stores/utils.js
+16
-12
app/assets/stylesheets/pages/repo.scss
app/assets/stylesheets/pages/repo.scss
+33
-8
spec/features/projects/tree/create_directory_spec.rb
spec/features/projects/tree/create_directory_spec.rb
+2
-0
spec/features/projects/tree/create_file_spec.rb
spec/features/projects/tree/create_file_spec.rb
+2
-0
spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
...scripts/ide/components/commit_sidebar/empty_state_spec.js
+95
-0
spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js
...ipts/ide/components/commit_sidebar/list_collapsed_spec.js
+46
-4
spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
...vascripts/ide/components/commit_sidebar/list_item_spec.js
+14
-10
spec/javascripts/ide/components/commit_sidebar/list_spec.js
spec/javascripts/ide/components/commit_sidebar/list_spec.js
+41
-1
spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
...cripts/ide/components/commit_sidebar/stage_button_spec.js
+46
-0
spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js
...ipts/ide/components/commit_sidebar/unstage_button_spec.js
+39
-0
spec/javascripts/ide/components/repo_commit_section_spec.js
spec/javascripts/ide/components/repo_commit_section_spec.js
+92
-5
spec/javascripts/ide/stores/actions_spec.js
spec/javascripts/ide/stores/actions_spec.js
+78
-0
spec/javascripts/ide/stores/getters_spec.js
spec/javascripts/ide/stores/getters_spec.js
+3
-11
spec/javascripts/ide/stores/modules/commit/actions_spec.js
spec/javascripts/ide/stores/modules/commit/actions_spec.js
+15
-5
spec/javascripts/ide/stores/mutations/file_spec.js
spec/javascripts/ide/stores/mutations/file_spec.js
+66
-0
spec/javascripts/ide/stores/mutations_spec.js
spec/javascripts/ide/stores/mutations_spec.js
+10
-0
No files found.
app/assets/javascripts/ide/components/commit_sidebar/empty_state.vue
0 → 100644
View file @
51c64f3f
<
script
>
import
{
mapActions
,
mapState
,
mapGetters
}
from
'
vuex
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
Icon
,
},
props
:
{
noChangesStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
committedStateSvgPath
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
...
mapState
([
'
lastCommitMsg
'
,
'
rightPanelCollapsed
'
]),
...
mapGetters
([
'
collapseButtonIcon
'
]),
statusSvg
()
{
return
this
.
lastCommitMsg
?
this
.
committedStateSvgPath
:
this
.
noChangesStateSvgPath
;
},
},
methods
:
{
...
mapActions
([
'
toggleRightPanelCollapsed
'
]),
},
};
</
script
>
<
template
>
<div
class=
"multi-file-commit-panel-section ide-commity-empty-state js-empty-state"
>
<header
class=
"multi-file-commit-panel-header"
:class=
"
{
'is-collapsed': rightPanelCollapsed,
}"
>
<button
type=
"button"
class=
"btn btn-transparent multi-file-commit-panel-collapse-btn"
:aria-label=
"__('Toggle sidebar')"
@
click.stop=
"toggleRightPanelCollapsed"
>
<icon
:name=
"collapseButtonIcon"
:size=
"18"
/>
</button>
</header>
<div
class=
"ide-commit-empty-state-container"
v-if=
"!rightPanelCollapsed"
>
<div
class=
"svg-content svg-80"
>
<img
:src=
"statusSvg"
/>
</div>
<div
class=
"append-right-default prepend-left-default"
>
<div
class=
"text-content text-center"
v-if=
"!lastCommitMsg"
>
<h4>
{{
__
(
'
No changes
'
)
}}
</h4>
<p>
{{
__
(
'
Edit files in the editor and commit changes here
'
)
}}
</p>
</div>
<div
class=
"text-content text-center"
v-else
>
<h4>
{{
__
(
'
All changes are committed
'
)
}}
</h4>
<p
v-html=
"lastCommitMsg"
></p>
</div>
</div>
</div>
</div>
</
template
>
app/assets/javascripts/ide/components/commit_sidebar/list.vue
View file @
51c64f3f
<
script
>
import
{
map
State
}
from
'
vuex
'
;
import
{
map
Actions
,
mapState
,
mapGetters
}
from
'
vuex
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
listItem
from
'
./list_item.vue
'
;
import
listCollapsed
from
'
./list_collapsed.vue
'
;
...
...
@@ -19,20 +19,44 @@
type
:
Array
,
required
:
true
,
},
showToggle
:
{
type
:
Boolean
,
required
:
false
,
default
:
true
,
},
icon
:
{
type
:
String
,
required
:
true
,
},
action
:
{
type
:
String
,
required
:
true
,
},
actionBtnText
:
{
type
:
String
,
required
:
true
,
},
itemActionComponent
:
{
type
:
String
,
required
:
true
,
},
},
computed
:
{
...
mapState
([
'
currentProjectId
'
,
'
currentBranchId
'
,
'
rightPanelCollapsed
'
,
]),
isCommitInfoShown
()
{
return
this
.
rightPanelCollapsed
||
this
.
fileList
.
length
;
}
,
...
mapGetters
([
'
collapseButtonIcon
'
,
])
,
},
methods
:
{
toggleCollapsed
()
{
this
.
$emit
(
'
toggleCollapsed
'
);
...
mapActions
([
'
toggleRightPanelCollapsed
'
,
'
stageAllChanges
'
,
'
unstageAllChanges
'
,
]),
actionBtnClicked
()
{
this
[
this
.
action
]();
},
},
};
...
...
@@ -40,17 +64,60 @@
<
template
>
<div
class=
"ide-commit-list-container"
:class=
"
{
'
multi-file-commit-list': isCommitInfoShown
'
is-collapsed': rightPanelCollapsed,
}"
>
<header
class=
"multi-file-commit-panel-header"
:class=
"
{
'is-collapsed': rightPanelCollapsed,
}"
>
<div
v-if=
"!rightPanelCollapsed"
class=
"multi-file-commit-panel-header-title"
:class=
"
{
'append-right-10': showToggle,
}"
>
<icon
v-once
:name=
"icon"
:size=
"18"
/>
{{
title
}}
<button
type=
"button"
class=
"btn btn-blank btn-link ide-staged-action-btn"
@
click=
"actionBtnClicked"
>
{{
actionBtnText
}}
</button>
</div>
<button
v-if=
"showToggle"
type=
"button"
class=
"btn btn-transparent multi-file-commit-panel-collapse-btn"
:aria-label=
"__('Toggle sidebar')"
@
click.stop=
"toggleRightPanelCollapsed"
>
<icon
:name=
"collapseButtonIcon"
:size=
"18"
/>
</button>
</header>
<list-collapsed
v-if=
"rightPanelCollapsed"
:files=
"fileList"
:icon=
"icon"
/>
<template
v-else
>
<ul
v-if=
"fileList.length"
class=
"list-unstyled append-bottom-0"
class=
"
multi-file-commit-list
list-unstyled append-bottom-0"
>
<li
v-for=
"file in fileList"
...
...
@@ -58,9 +125,16 @@
>
<list-item
:file=
"file"
:action-component=
"itemActionComponent"
/>
</li>
</ul>
<p
v-else
class=
"multi-file-commit-list help-block"
>
{{
__
(
'
No changes
'
)
}}
</p>
</
template
>
</div>
</template>
app/assets/javascripts/ide/components/commit_sidebar/list_collapsed.vue
View file @
51c64f3f
<
script
>
import
{
mapGetters
}
from
'
vuex
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
icon
,
export
default
{
components
:
{
icon
,
},
props
:
{
files
:
{
type
:
Array
,
required
:
true
,
},
computed
:
{
...
mapGetters
([
'
addedFiles
'
,
'
modifiedFiles
'
,
]),
icon
:
{
type
:
String
,
required
:
true
,
},
};
},
computed
:
{
addedFilesLength
()
{
return
this
.
files
.
filter
(
f
=>
f
.
tempFile
).
length
;
},
modifiedFilesLength
()
{
return
this
.
files
.
filter
(
f
=>
!
f
.
tempFile
).
length
;
},
addedFilesIconClass
()
{
return
this
.
addedFilesLength
?
'
multi-file-addition
'
:
''
;
},
modifiedFilesClass
()
{
return
this
.
modifiedFilesLength
?
'
multi-file-modified
'
:
''
;
},
},
};
</
script
>
<
template
>
<div
class=
"multi-file-commit-list-collapsed text-center"
>
<icon
v-once
:name=
"icon"
:size=
"18"
css-classes=
"append-bottom-15"
/>
<icon
name=
"file-addition"
:size=
"18"
css-classes=
"multi-file-addition append-bottom-10
"
:css-classes=
"addedFilesIconClass + 'append-bottom-10'
"
/>
{{
addedFiles
.
l
ength
}}
{{
addedFiles
L
ength
}}
<icon
name=
"file-modified"
:size=
"18"
css-classes=
"multi-file-modified prepend-top-10 append-bottom-10
"
:css-classes=
"modifiedFilesClass + ' prepend-top-10 append-bottom-10'
"
/>
{{
modifiedFiles
.
l
ength
}}
{{
modifiedFiles
L
ength
}}
</div>
</
template
>
app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
View file @
51c64f3f
<
script
>
import
{
mapActions
}
from
'
vuex
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
router
from
'
../../ide_router
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
StageButton
from
'
./stage_button.vue
'
;
import
UnstageButton
from
'
./unstage_button.vue
'
;
import
router
from
'
../../ide_router
'
;
export
default
{
components
:
{
icon
,
export
default
{
components
:
{
Icon
,
StageButton
,
UnstageButton
,
},
props
:
{
file
:
{
type
:
Object
,
required
:
true
,
},
props
:
{
file
:
{
type
:
Object
,
required
:
true
,
},
actionComponent
:
{
type
:
String
,
required
:
true
,
},
computed
:
{
iconName
()
{
return
this
.
file
.
tempFile
?
'
file-addition
'
:
'
file-modified
'
;
},
iconClass
()
{
return
`multi-file-
${
this
.
file
.
tempFile
?
'
addition
'
:
'
modified
'
}
append-right-8`
;
},
},
computed
:
{
iconName
()
{
return
this
.
file
.
tempFile
?
'
file-addition
'
:
'
file-modified
'
;
},
methods
:
{
...
mapActions
([
'
discardFileChanges
'
,
'
updateViewer
'
,
]),
openFileInEditor
(
file
)
{
this
.
updateViewer
(
'
diff
'
);
iconClass
()
{
return
`multi-file-
${
this
.
file
.
tempFile
?
'
addition
'
:
'
modified
'
}
append-right-8`
;
},
},
methods
:
{
...
mapActions
([
'
updateViewer
'
]),
openFileInEditor
(
file
)
{
this
.
updateViewer
(
'
diff
'
);
router
.
push
(
`/project
${
file
.
url
}
`
);
},
router
.
push
(
`/project
${
file
.
url
}
`
);
},
};
},
};
</
script
>
<
template
>
...
...
@@ -49,12 +55,9 @@
/>
{{
file
.
path
}}
</span>
</button>
<button
type=
"button"
class=
"btn btn-blank multi-file-discard-btn"
@
click=
"discardFileChanges(file.path)"
>
Discard
</button>
<component
:is=
"actionComponent"
:file=
"file"
/>
</div>
</
template
>
app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
0 → 100644
View file @
51c64f3f
<
script
>
import
{
mapActions
}
from
'
vuex
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
Icon
,
},
props
:
{
file
:
{
type
:
Object
,
required
:
true
,
},
},
methods
:
{
...
mapActions
([
'
stageChange
'
,
'
discardFileChanges
'
]),
},
};
</
script
>
<
template
>
<div
v-once
class=
"multi-file-discard-btn"
>
<button
type=
"button"
class=
"btn btn-blank append-right-5"
:aria-label=
"__('Stage change')"
@
click.stop=
"stageChange(file)"
>
<icon
name=
"mobile-issue-close"
:size=
"12"
/>
</button>
<button
type=
"button"
class=
"btn btn-blank"
:aria-label=
"__('Discard change')"
@
click.stop=
"discardFileChanges(file)"
>
<icon
name=
"remove"
:size=
"12"
/>
</button>
</div>
</
template
>
app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
0 → 100644
View file @
51c64f3f
<
script
>
import
{
mapActions
}
from
'
vuex
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
export
default
{
components
:
{
Icon
,
},
props
:
{
file
:
{
type
:
Object
,
required
:
true
,
},
},
methods
:
{
...
mapActions
([
'
unstageChange
'
]),
},
};
</
script
>
<
template
>
<div
v-once
class=
"multi-file-discard-btn"
>
<button
type=
"button"
class=
"btn btn-blank"
:aria-label=
"__('Unstage change')"
@
click=
"unstageChange(file)"
>
<icon
name=
"history"
:size=
"12"
/>
</button>
</div>
</
template
>
app/assets/javascripts/ide/components/ide_context_bar.vue
View file @
51c64f3f
<
script
>
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
icon
from
'
~/vue_shared/components/icon.vue
'
;
import
panelResizer
from
'
~/vue_shared/components/panel_resizer.vue
'
;
import
repoCommitSection
from
'
./repo_commit_section.vue
'
;
...
...
@@ -22,13 +21,6 @@ export default {
required
:
true
,
},
},
computed
:
{
...
mapState
([
'
changedFiles
'
,
'
rightPanelCollapsed
'
]),
...
mapGetters
([
'
currentIcon
'
]),
},
methods
:
{
...
mapActions
([
'
setPanelCollapsedStatus
'
]),
},
};
</
script
>
...
...
@@ -41,40 +33,6 @@ export default {
<div
class=
"multi-file-commit-panel-section"
>
<header
class=
"multi-file-commit-panel-header"
:class=
"
{
'is-collapsed': rightPanelCollapsed,
}"
>
<div
class=
"multi-file-commit-panel-header-title"
v-if=
"!rightPanelCollapsed"
>
<div
v-if=
"changedFiles.length"
>
<icon
name=
"list-bulleted"
:size=
"18"
/>
Staged
</div>
</div>
<button
type=
"button"
class=
"btn btn-transparent multi-file-commit-panel-collapse-btn"
@
click.stop=
"setPanelCollapsedStatus(
{
side: 'right',
collapsed: !rightPanelCollapsed,
})"
>
<icon
:name=
"currentIcon"
:size=
"18"
/>
</button>
</header>
<repo-commit-section
:no-changes-state-svg-path=
"noChangesStateSvgPath"
:committed-state-svg-path=
"committedStateSvgPath"
...
...
app/assets/javascripts/ide/components/repo_commit_section.vue
View file @
51c64f3f
...
...
@@ -5,14 +5,16 @@ import icon from '~/vue_shared/components/icon.vue';
import
modal
from
'
~/vue_shared/components/modal.vue
'
;
import
LoadingButton
from
'
~/vue_shared/components/loading_button.vue
'
;
import
commitFilesList
from
'
./commit_sidebar/list.vue
'
;
import
*
as
consts
from
'
../stores/modules/commit/constants
'
;
import
EmptyState
from
'
./commit_sidebar/empty_state.vue
'
;
import
Actions
from
'
./commit_sidebar/actions.vue
'
;
import
*
as
consts
from
'
../stores/modules/commit/constants
'
;
export
default
{
components
:
{
modal
,
icon
,
commitFilesList
,
EmptyState
,
Actions
,
LoadingButton
,
},
...
...
@@ -30,45 +32,26 @@ export default {
},
},
computed
:
{
...
mapState
([
'
currentProjectId
'
,
'
currentBranchId
'
,
'
rightPanelCollapsed
'
,
'
lastCommitMsg
'
,
'
changedFiles
'
,
]),
...
mapState
(
'
commit
'
,
[
'
commitMessage
'
,
'
submitCommitLoading
'
,
]),
...
mapState
([
'
stagedFiles
'
,
'
rightPanelCollapsed
'
]),
...
mapState
(
'
commit
'
,
[
'
commitMessage
'
,
'
submitCommitLoading
'
]),
...
mapGetters
([
'
unstagedFiles
'
]),
...
mapGetters
(
'
commit
'
,
[
'
commitButtonDisabled
'
,
'
discardDraftButtonDisabled
'
,
'
branchName
'
,
]),
statusSvg
()
{
return
this
.
lastCommitMsg
?
this
.
committedStateSvgPath
:
this
.
noChangesStateSvgPath
;
},
},
methods
:
{
...
mapActions
([
'
setPanelCollapsedStatus
'
,
]),
...
mapActions
(
'
commit
'
,
[
'
updateCommitMessage
'
,
'
discardDraft
'
,
'
commitChanges
'
,
'
updateCommitAction
'
,
]),
toggleCollapsed
()
{
this
.
setPanelCollapsedStatus
({
side
:
'
right
'
,
collapsed
:
!
this
.
rightPanelCollapsed
,
});
},
forceCreateNewBranch
()
{
return
this
.
updateCommitAction
(
consts
.
COMMIT_TO_NEW_BRANCH
)
.
then
(()
=>
this
.
commitChanges
());
return
this
.
updateCommitAction
(
consts
.
COMMIT_TO_NEW_BRANCH
).
then
(()
=>
this
.
commitChanges
(),
);
},
},
};
...
...
@@ -77,9 +60,6 @@ export default {
<
template
>
<div
class=
"multi-file-commit-panel-section"
:class=
"
{
'multi-file-commit-empty-state-container': !changedFiles.length
}"
>
<modal
id=
"ide-create-branch-modal"
...
...
@@ -93,15 +73,26 @@ export default {
Would you like to create a new branch?`
)
}}
</
template
>
</modal>
<commit-files-list
title=
"Staged"
:file-list=
"changedFiles"
:collapsed=
"rightPanelCollapsed"
@
toggleCollapsed=
"toggleCollapsed"
/>
<
template
v-if=
"
chan
gedFiles.length"
v-if=
"
unstagedFiles.length || sta
gedFiles.length"
>
<commit-files-list
icon=
"unstaged"
:title=
"__('Unstaged')"
:file-list=
"unstagedFiles"
action=
"stageAllChanges"
:action-btn-text=
"__('Stage all')"
item-action-component=
"stage-button"
/>
<commit-files-list
icon=
"staged"
:title=
"__('Staged')"
:file-list=
"stagedFiles"
action=
"unstageAllChanges"
:action-btn-text=
"__('Unstage all')"
item-action-component=
"unstage-button"
:show-toggle=
"false"
/>
<form
class=
"form-horizontal multi-file-commit-form"
@
submit.prevent.stop=
"commitChanges"
...
...
@@ -137,38 +128,10 @@ export default {
</div>
</form>
</
template
>
<div
v-else-if=
"!rightPanelCollapsed"
class=
"row js-empty-state"
>
<div
class=
"col-xs-10 col-xs-offset-1"
>
<div
class=
"svg-content svg-80"
>
<img
:src=
"statusSvg"
/>
</div>
</div>
<div
class=
"col-xs-10 col-xs-offset-1"
>
<div
class=
"text-content text-center"
v-if=
"!lastCommitMsg"
>
<h4>
{{ __('No changes') }}
</h4>
<p>
{{ __('Edit files in the editor and commit changes here') }}
</p>
</div>
<div
class=
"text-content text-center"
v-else
>
<h4>
{{ __('All changes are committed') }}
</h4>
<p
v-html=
"lastCommitMsg"
>
</p>
</div>
</div>
</div>
<empty-state
v-else
:no-changes-state-svg-path=
"noChangesStateSvgPath"
:committed-state-svg-path=
"committedStateSvgPath"
/>
</div>
</template>
app/assets/javascripts/ide/stores/actions.js
View file @
51c64f3f
...
...
@@ -33,6 +33,13 @@ export const setPanelCollapsedStatus = ({ commit }, { side, collapsed }) => {
}
};
export
const
toggleRightPanelCollapsed
=
({
dispatch
,
state
})
=>
{
dispatch
(
'
setPanelCollapsedStatus
'
,
{
side
:
'
right
'
,
collapsed
:
!
state
.
rightPanelCollapsed
,
});
};
export
const
setResizingStatus
=
({
commit
},
resizing
)
=>
{
commit
(
types
.
SET_RESIZING_STATUS
,
resizing
);
};
...
...
@@ -108,6 +115,14 @@ export const scrollToTab = () => {
});
};
export
const
stageAllChanges
=
({
state
,
commit
})
=>
{
[...
state
.
changedFiles
].
forEach
(
file
=>
commit
(
types
.
STAGE_CHANGE
,
file
));
};
export
const
unstageAllChanges
=
({
state
,
commit
})
=>
{
[...
state
.
stagedFiles
].
forEach
(
file
=>
commit
(
types
.
UNSTAGE_CHANGE
,
file
));
};
export
const
updateViewer
=
({
commit
},
viewer
)
=>
{
commit
(
types
.
UPDATE_VIEWER
,
viewer
);
};
...
...
app/assets/javascripts/ide/stores/actions/file.js
View file @
51c64f3f
...
...
@@ -144,3 +144,11 @@ export const discardFileChanges = ({ state, commit }, path) => {
eventHub
.
$emit
(
`editor.update.model.content.
${
file
.
path
}
`
,
file
.
raw
);
};
export
const
stageChange
=
({
commit
},
file
)
=>
{
commit
(
types
.
STAGE_CHANGE
,
file
);
};
export
const
unstageChange
=
({
commit
},
file
)
=>
{
commit
(
types
.
UNSTAGE_CHANGE
,
file
);
};
app/assets/javascripts/ide/stores/getters.js
View file @
51c64f3f
...
...
@@ -28,3 +28,5 @@ export const currentIcon = state =>
state
.
rightPanelCollapsed
?
'
angle-double-left
'
:
'
angle-double-right
'
;
export
const
hasChanges
=
state
=>
!!
state
.
changedFiles
.
length
;
export
const
unstagedFiles
=
state
=>
state
.
changedFiles
.
filter
(
f
=>
!
f
.
staged
);
app/assets/javascripts/ide/stores/modules/commit/actions.js
View file @
51c64f3f
...
...
@@ -131,9 +131,10 @@ export const updateFilesAfterCommit = (
);
});
commit
(
rootTypes
.
REMOVE_ALL_CHANGES_FILES
,
null
,
{
root
:
true
});
if
(
state
.
commitAction
===
consts
.
COMMIT_TO_NEW_BRANCH
)
{
if
(
state
.
commitAction
===
consts
.
COMMIT_TO_NEW_BRANCH
&&
rootGetters
.
activeFile
)
{
router
.
push
(
`/project/
${
rootState
.
currentProjectId
}
/blob/
${
branch
}
/
${
rootGetters
.
activeFile
.
path
...
...
@@ -186,7 +187,6 @@ export const commitChanges = ({
}
dispatch
(
'
setLastCommitMessage
'
,
data
);
dispatch
(
'
updateCommitMessage
'
,
''
);
if
(
state
.
commitAction
===
consts
.
COMMIT_TO_NEW_BRANCH_MR
)
{
dispatch
(
...
...
@@ -204,6 +204,10 @@ export const commitChanges = ({
branch
:
getters
.
branchName
,
});
}
commit
(
rootTypes
.
CLEAR_STAGED_CHANGES
,
null
,
{
root
:
true
});
dispatch
(
'
discardDraft
'
);
})
.
catch
(
err
=>
{
let
errMsg
=
__
(
'
Error committing changes. Please try again.
'
);
...
...
app/assets/javascripts/ide/stores/mutation_types.js
View file @
51c64f3f
...
...
@@ -41,3 +41,7 @@ export const SET_ENTRIES = 'SET_ENTRIES';
export
const
CREATE_TMP_ENTRY
=
'
CREATE_TMP_ENTRY
'
;
export
const
UPDATE_VIEWER
=
'
UPDATE_VIEWER
'
;
export
const
UPDATE_DELAY_VIEWER_CHANGE
=
'
UPDATE_DELAY_VIEWER_CHANGE
'
;
export
const
CLEAR_STAGED_CHANGES
=
'
CLEAR_STAGED_CHANGES
'
;
export
const
STAGE_CHANGE
=
'
STAGE_CHANGE
'
;
export
const
UNSTAGE_CHANGE
=
'
UNSTAGE_CHANGE
'
;
app/assets/javascripts/ide/stores/mutations.js
View file @
51c64f3f
...
...
@@ -51,6 +51,11 @@ export default {
lastCommitMsg
,
});
},
[
types
.
CLEAR_STAGED_CHANGES
](
state
)
{
Object
.
assign
(
state
,
{
stagedFiles
:
[],
});
},
[
types
.
SET_ENTRIES
](
state
,
entries
)
{
Object
.
assign
(
state
,
{
entries
,
...
...
app/assets/javascripts/ide/stores/mutations/file.js
View file @
51c64f3f
import
*
as
types
from
'
../mutation_types
'
;
import
{
findIndexOfFile
,
findEntry
}
from
'
../utils
'
;
export
default
{
[
types
.
SET_FILE_ACTIVE
](
state
,
{
path
,
active
})
{
...
...
@@ -75,6 +76,33 @@ export default {
changedFiles
:
state
.
changedFiles
.
filter
(
f
=>
f
.
path
!==
path
),
});
},
[
types
.
STAGE_CHANGE
](
state
,
file
)
{
const
stagedFile
=
findEntry
(
state
.
stagedFiles
,
'
blob
'
,
file
.
name
);
Object
.
assign
(
file
,
{
staged
:
true
,
});
if
(
stagedFile
)
{
Object
.
assign
(
stagedFile
,
{
...
file
,
});
}
else
{
state
.
stagedFiles
.
push
({
...
file
,
});
}
},
[
types
.
UNSTAGE_CHANGE
](
state
,
file
)
{
const
indexOfStagedFile
=
findIndexOfFile
(
state
.
stagedFiles
,
file
);
const
changedFile
=
findEntry
(
state
.
changedFiles
,
'
blob
'
,
file
.
name
);
state
.
stagedFiles
.
splice
(
indexOfStagedFile
,
1
);
Object
.
assign
(
changedFile
,
{
staged
:
false
,
});
},
[
types
.
TOGGLE_FILE_CHANGED
](
state
,
{
file
,
changed
})
{
Object
.
assign
(
state
.
entries
[
file
.
path
],
{
changed
,
...
...
app/assets/javascripts/ide/stores/state.js
View file @
51c64f3f
...
...
@@ -2,6 +2,7 @@ export default () => ({
currentProjectId
:
''
,
currentBranchId
:
''
,
changedFiles
:
[],
stagedFiles
:
[],
endpoints
:
{},
lastCommitMsg
:
''
,
lastCommitPath
:
''
,
...
...
app/assets/javascripts/ide/stores/utils.js
View file @
51c64f3f
...
...
@@ -13,6 +13,7 @@ export const dataStructure = () => ({
opened
:
false
,
active
:
false
,
changed
:
false
,
staged
:
false
,
lastCommitPath
:
''
,
lastCommit
:
{
id
:
''
,
...
...
@@ -38,7 +39,7 @@ export const dataStructure = () => ({
eol
:
''
,
});
export
const
decorateData
=
(
entity
)
=>
{
export
const
decorateData
=
entity
=>
{
const
{
id
,
projectId
,
...
...
@@ -57,7 +58,6 @@ export const decorateData = (entity) => {
base64
=
false
,
file_lock
,
}
=
entity
;
return
{
...
...
@@ -80,24 +80,23 @@ export const decorateData = (entity) => {
base64
,
file_lock
,
};
};
export
const
findEntry
=
(
tree
,
type
,
name
,
prop
=
'
name
'
)
=>
tree
.
find
(
f
=>
f
.
type
===
type
&&
f
[
prop
]
===
name
,
);
export
const
findEntry
=
(
tree
,
type
,
name
,
prop
=
'
name
'
)
=>
tree
.
find
(
f
=>
f
.
type
===
type
&&
f
[
prop
]
===
name
);
export
const
findIndexOfFile
=
(
state
,
file
)
=>
state
.
findIndex
(
f
=>
f
.
path
===
file
.
path
);
export
const
findIndexOfFile
=
(
state
,
file
)
=>
state
.
findIndex
(
f
=>
f
.
path
===
file
.
path
);
export
const
setPageTitle
=
(
title
)
=>
{
export
const
setPageTitle
=
title
=>
{
document
.
title
=
title
;
};
export
const
createCommitPayload
=
(
branch
,
newBranch
,
state
,
rootState
)
=>
({
branch
,
commit_message
:
state
.
commitMessage
,
actions
:
rootState
.
chan
gedFiles
.
map
(
f
=>
({
actions
:
rootState
.
sta
gedFiles
.
map
(
f
=>
({
action
:
f
.
tempFile
?
'
create
'
:
'
update
'
,
file_path
:
f
.
path
,
content
:
f
.
content
,
...
...
@@ -120,6 +119,11 @@ const sortTreesByTypeAndName = (a, b) => {
return
0
;
};
export
const
sortTree
=
sortedTree
=>
sortedTree
.
map
(
entity
=>
Object
.
assign
(
entity
,
{
tree
:
entity
.
tree
.
length
?
sortTree
(
entity
.
tree
)
:
[],
})).
sort
(
sortTreesByTypeAndName
);
export
const
sortTree
=
sortedTree
=>
sortedTree
.
map
(
entity
=>
Object
.
assign
(
entity
,
{
tree
:
entity
.
tree
.
length
?
sortTree
(
entity
.
tree
)
:
[],
}),
)
.
sort
(
sortTreesByTypeAndName
);
app/assets/stylesheets/pages/repo.scss
View file @
51c64f3f
...
...
@@ -449,9 +449,13 @@
flex
:
1
;
}
.multi-file-commit-empty-state-container
{
align-items
:
center
;
justify-content
:
center
;
.ide-commity-empty-state
{
padding
:
0
$gl-padding
;
}
.ide-commit-empty-state-container
{
margin-top
:
auto
;
margin-bottom
:
auto
;
}
.multi-file-commit-panel-header
{
...
...
@@ -462,7 +466,8 @@
padding
:
$gl-btn-padding
0
;
&
.is-collapsed
{
border-bottom
:
1px
solid
$white-dark
;
margin-left
:
-
$gl-padding
;
margin-right
:
-
$gl-padding
;
svg
{
margin-left
:
auto
;
...
...
@@ -480,7 +485,6 @@
.multi-file-commit-panel-header-title
{
display
:
flex
;
flex
:
1
;
padding
:
0
$gl-btn-padding
;
svg
{
margin-right
:
$gl-btn-padding
;
...
...
@@ -489,6 +493,7 @@
.multi-file-commit-panel-collapse-btn
{
border-left
:
1px
solid
$white-dark
;
margin-left
:
auto
;
}
.multi-file-commit-list
{
...
...
@@ -502,12 +507,14 @@
display
:
flex
;
padding
:
0
;
align-items
:
center
;
border-radius
:
$border-radius-default
;
.multi-file-discard-btn
{
display
:
none
;
margin-top
:
-2px
;
margin-left
:
auto
;
margin-right
:
$grid-size
;
color
:
$gl-link-color
;
padding
:
0
2px
;
&
:focus
,
&
:hover
{
...
...
@@ -519,7 +526,7 @@
background
:
$white-normal
;
.multi-file-discard-btn
{
display
:
block
;
display
:
flex
;
}
}
}
...
...
@@ -535,10 +542,12 @@
.multi-file-commit-list-collapsed
{
display
:
flex
;
flex-direction
:
column
;
padding
:
$gl-padding
0
;
>
svg
{
margin-left
:
auto
;
margin-right
:
auto
;
color
:
$theme-gray-700
;
}
.file-status-icon
{
...
...
@@ -550,7 +559,7 @@
.multi-file-commit-list-path
{
padding
:
$grid-size
/
2
;
padding-left
:
$g
l-padding
;
padding-left
:
$g
rid-size
;
background
:
none
;
border
:
0
;
text-align
:
left
;
...
...
@@ -740,6 +749,22 @@
}
}
.ide-commit-list-container
{
display
:
flex
;
flex-direction
:
column
;
width
:
100%
;
padding
:
0
16px
;
&
:not
(
.is-collapsed
)
{
flex
:
1
;
}
}
.ide-staged-action-btn
{
margin-left
:
auto
;
color
:
$gl-link-color
;
}
.ide-commit-radios
{
label
{
font-weight
:
normal
;
...
...
spec/features/projects/tree/create_directory_spec.rb
View file @
51c64f3f
...
...
@@ -44,6 +44,8 @@ feature 'Multi-file editor new directory', :js do
wait_for_requests
click_button
'Stage all'
fill_in
(
'commit-message'
,
with:
'commit message ide'
)
click_button
(
'Commit'
)
...
...
spec/features/projects/tree/create_file_spec.rb
View file @
51c64f3f
...
...
@@ -34,6 +34,8 @@ feature 'Multi-file editor new file', :js do
wait_for_requests
click_button
'Stage all'
fill_in
(
'commit-message'
,
with:
'commit message ide'
)
click_button
(
'Commit'
)
...
...
spec/javascripts/ide/components/commit_sidebar/empty_state_spec.js
0 → 100644
View file @
51c64f3f
import
Vue
from
'
vue
'
;
import
store
from
'
~/ide/stores
'
;
import
emptyState
from
'
~/ide/components/commit_sidebar/empty_state.vue
'
;
import
{
createComponentWithStore
}
from
'
../../../helpers/vue_mount_component_helper
'
;
import
{
resetStore
}
from
'
../../helpers
'
;
describe
(
'
IDE commit panel empty state
'
,
()
=>
{
let
vm
;
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
emptyState
);
vm
=
createComponentWithStore
(
Component
,
store
,
{
noChangesStateSvgPath
:
'
no-changes
'
,
committedStateSvgPath
:
'
committed-state
'
,
});
vm
.
$mount
();
});
afterEach
(()
=>
{
vm
.
$destroy
();
resetStore
(
vm
.
$store
);
});
describe
(
'
statusSvg
'
,
()
=>
{
it
(
'
uses noChangesStateSvgPath when commit message is empty
'
,
()
=>
{
expect
(
vm
.
statusSvg
).
toBe
(
'
no-changes
'
);
expect
(
vm
.
$el
.
querySelector
(
'
img
'
).
getAttribute
(
'
src
'
)).
toBe
(
'
no-changes
'
,
);
});
it
(
'
uses committedStateSvgPath when commit message exists
'
,
done
=>
{
vm
.
$store
.
state
.
lastCommitMsg
=
'
testing
'
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
statusSvg
).
toBe
(
'
committed-state
'
);
expect
(
vm
.
$el
.
querySelector
(
'
img
'
).
getAttribute
(
'
src
'
)).
toBe
(
'
committed-state
'
,
);
done
();
});
});
});
it
(
'
renders no changes text when last commit message is empty
'
,
()
=>
{
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
No changes
'
);
});
it
(
'
renders last commit message when it exists
'
,
done
=>
{
vm
.
$store
.
state
.
lastCommitMsg
=
'
testing commit message
'
;
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
testing commit message
'
);
done
();
});
});
describe
(
'
toggle button
'
,
()
=>
{
it
(
'
calls store action
'
,
()
=>
{
spyOn
(
vm
,
'
toggleRightPanelCollapsed
'
);
vm
.
$el
.
querySelector
(
'
.multi-file-commit-panel-collapse-btn
'
).
click
();
expect
(
vm
.
toggleRightPanelCollapsed
).
toHaveBeenCalled
();
});
it
(
'
renders collapsed class
'
,
done
=>
{
vm
.
$el
.
querySelector
(
'
.multi-file-commit-panel-collapse-btn
'
).
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.is-collapsed
'
)).
not
.
toBeNull
();
done
();
});
});
});
describe
(
'
collapsed state
'
,
()
=>
{
beforeEach
(
done
=>
{
vm
.
$store
.
state
.
rightPanelCollapsed
=
true
;
Vue
.
nextTick
(
done
);
});
it
(
'
does not render text & svg
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
img
'
)).
toBeNull
();
expect
(
vm
.
$el
.
textContent
).
not
.
toContain
(
'
No changes
'
);
});
});
});
spec/javascripts/ide/components/commit_sidebar/list_collapsed_spec.js
View file @
51c64f3f
...
...
@@ -10,10 +10,16 @@ describe('Multi-file editor commit sidebar list collapsed', () => {
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
listCollapsed
);
vm
=
createComponentWithStore
(
Component
,
store
);
vm
.
$store
.
state
.
changedFiles
.
push
(
file
(
'
file1
'
),
file
(
'
file2
'
));
vm
.
$store
.
state
.
changedFiles
[
0
].
tempFile
=
true
;
vm
=
createComponentWithStore
(
Component
,
store
,
{
files
:
[
{
...
file
(
'
file1
'
),
tempFile
:
true
,
},
file
(
'
file2
'
),
],
icon
:
'
staged
'
,
});
vm
.
$mount
();
});
...
...
@@ -25,4 +31,40 @@ describe('Multi-file editor commit sidebar list collapsed', () => {
it
(
'
renders added & modified files count
'
,
()
=>
{
expect
(
vm
.
$el
.
textContent
.
replace
(
/
\s
+/g
,
'
'
).
trim
()).
toBe
(
'
1 1
'
);
});
describe
(
'
addedFilesLength
'
,
()
=>
{
it
(
'
returns an length of temp files
'
,
()
=>
{
expect
(
vm
.
addedFilesLength
).
toBe
(
1
);
});
});
describe
(
'
modifiedFilesLength
'
,
()
=>
{
it
(
'
returns an length of modified files
'
,
()
=>
{
expect
(
vm
.
modifiedFilesLength
).
toBe
(
1
);
});
});
describe
(
'
addedFilesIconClass
'
,
()
=>
{
it
(
'
includes multi-file-addition when addedFiles is not empty
'
,
()
=>
{
expect
(
vm
.
addedFilesIconClass
).
toContain
(
'
multi-file-addition
'
);
});
it
(
'
excludes multi-file-addition when addedFiles is empty
'
,
()
=>
{
vm
.
files
=
[];
expect
(
vm
.
addedFilesIconClass
).
not
.
toContain
(
'
multi-file-addition
'
);
});
});
describe
(
'
modifiedFilesClass
'
,
()
=>
{
it
(
'
includes multi-file-modified when addedFiles is not empty
'
,
()
=>
{
expect
(
vm
.
modifiedFilesClass
).
toContain
(
'
multi-file-modified
'
);
});
it
(
'
excludes multi-file-modified when addedFiles is empty
'
,
()
=>
{
vm
.
files
=
[];
expect
(
vm
.
modifiedFilesClass
).
not
.
toContain
(
'
multi-file-modified
'
);
});
});
});
spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
View file @
51c64f3f
import
Vue
from
'
vue
'
;
import
store
from
'
~/ide/stores
'
;
import
listItem
from
'
~/ide/components/commit_sidebar/list_item.vue
'
;
import
router
from
'
~/ide/ide_router
'
;
import
mountComponent
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
file
}
from
'
../../helpers
'
;
import
{
createComponentWithStore
}
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
file
,
resetStore
}
from
'
../../helpers
'
;
describe
(
'
Multi-file editor commit sidebar list item
'
,
()
=>
{
let
vm
;
let
f
;
beforeEach
(
()
=>
{
beforeEach
(
done
=>
{
const
Component
=
Vue
.
extend
(
listItem
);
f
=
file
(
'
test-file
'
);
vm
=
mountComponent
(
Component
,
{
vm
=
createComponentWithStore
(
Component
,
store
,
{
file
:
f
,
actionComponent
:
'
stage-button
'
,
});
vm
.
$mount
();
Vue
.
nextTick
(
done
);
});
afterEach
(()
=>
{
vm
.
$destroy
();
resetStore
(
vm
.
$store
);
});
it
(
'
renders file path
'
,
()
=>
{
...
...
@@ -28,12 +36,8 @@ describe('Multi-file editor commit sidebar list item', () => {
).
toBe
(
f
.
path
);
});
it
(
'
calls discardFileChanges when clicking discard button
'
,
()
=>
{
spyOn
(
vm
,
'
discardFileChanges
'
);
vm
.
$el
.
querySelector
(
'
.multi-file-discard-btn
'
).
click
();
expect
(
vm
.
discardFileChanges
).
toHaveBeenCalled
();
it
(
'
renders actionn button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.multi-file-discard-btn
'
)).
not
.
toBeNull
();
});
it
(
'
opens a closed file in the editor when clicking the file path
'
,
()
=>
{
...
...
spec/javascripts/ide/components/commit_sidebar/list_spec.js
View file @
51c64f3f
...
...
@@ -2,7 +2,7 @@ import Vue from 'vue';
import
store
from
'
~/ide/stores
'
;
import
commitSidebarList
from
'
~/ide/components/commit_sidebar/list.vue
'
;
import
{
createComponentWithStore
}
from
'
spec/helpers/vue_mount_component_helper
'
;
import
{
file
}
from
'
../../helpers
'
;
import
{
file
,
resetStore
}
from
'
../../helpers
'
;
describe
(
'
Multi-file editor commit sidebar list
'
,
()
=>
{
let
vm
;
...
...
@@ -13,6 +13,10 @@ describe('Multi-file editor commit sidebar list', () => {
vm
=
createComponentWithStore
(
Component
,
store
,
{
title
:
'
Staged
'
,
fileList
:
[],
icon
:
'
staged
'
,
action
:
'
stageAllChanges
'
,
actionBtnText
:
'
stage all
'
,
itemActionComponent
:
'
stage-button
'
,
});
vm
.
$store
.
state
.
rightPanelCollapsed
=
false
;
...
...
@@ -22,6 +26,8 @@ describe('Multi-file editor commit sidebar list', () => {
afterEach
(()
=>
{
vm
.
$destroy
();
resetStore
(
vm
.
$store
);
});
describe
(
'
with a list of files
'
,
()
=>
{
...
...
@@ -38,6 +44,12 @@ describe('Multi-file editor commit sidebar list', () => {
});
});
describe
(
'
empty files array
'
,
()
=>
{
it
(
'
renders no changes text when empty
'
,
()
=>
{
expect
(
vm
.
$el
.
textContent
).
toContain
(
'
No changes
'
);
});
});
describe
(
'
collapsed
'
,
()
=>
{
beforeEach
(
done
=>
{
vm
.
$store
.
state
.
rightPanelCollapsed
=
true
;
...
...
@@ -50,4 +62,32 @@ describe('Multi-file editor commit sidebar list', () => {
expect
(
vm
.
$el
.
querySelector
(
'
.help-block
'
)).
toBeNull
();
});
});
describe
(
'
with toggle
'
,
()
=>
{
beforeEach
(
done
=>
{
spyOn
(
vm
,
'
toggleRightPanelCollapsed
'
);
vm
.
showToggle
=
true
;
Vue
.
nextTick
(
done
);
});
it
(
'
calls setPanelCollapsedStatus when clickin toggle
'
,
()
=>
{
vm
.
$el
.
querySelector
(
'
.multi-file-commit-panel-collapse-btn
'
).
click
();
expect
(
vm
.
toggleRightPanelCollapsed
).
toHaveBeenCalled
();
});
});
describe
(
'
action button
'
,
()
=>
{
beforeEach
(()
=>
{
spyOn
(
vm
,
'
stageAllChanges
'
);
});
it
(
'
calls store action when clicked
'
,
()
=>
{
vm
.
$el
.
querySelector
(
'
.ide-staged-action-btn
'
).
click
();
expect
(
vm
.
stageAllChanges
).
toHaveBeenCalled
();
});
});
});
spec/javascripts/ide/components/commit_sidebar/stage_button_spec.js
0 → 100644
View file @
51c64f3f
import
Vue
from
'
vue
'
;
import
store
from
'
~/ide/stores
'
;
import
stageButton
from
'
~/ide/components/commit_sidebar/stage_button.vue
'
;
import
{
createComponentWithStore
}
from
'
../../../helpers/vue_mount_component_helper
'
;
import
{
file
,
resetStore
}
from
'
../../helpers
'
;
describe
(
'
IDE stage file button
'
,
()
=>
{
let
vm
;
let
f
;
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
stageButton
);
f
=
file
();
vm
=
createComponentWithStore
(
Component
,
store
,
{
file
:
f
,
});
spyOn
(
vm
,
'
stageChange
'
);
spyOn
(
vm
,
'
discardFileChanges
'
);
vm
.
$mount
();
});
afterEach
(()
=>
{
vm
.
$destroy
();
resetStore
(
vm
.
$store
);
});
it
(
'
renders button to discard & stage
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.btn
'
).
length
).
toBe
(
2
);
});
it
(
'
calls store with stage button
'
,
()
=>
{
vm
.
$el
.
querySelectorAll
(
'
.btn
'
)[
0
].
click
();
expect
(
vm
.
stageChange
).
toHaveBeenCalledWith
(
f
);
});
it
(
'
calls store with discard button
'
,
()
=>
{
vm
.
$el
.
querySelectorAll
(
'
.btn
'
)[
1
].
click
();
expect
(
vm
.
discardFileChanges
).
toHaveBeenCalledWith
(
f
);
});
});
spec/javascripts/ide/components/commit_sidebar/unstage_button_spec.js
0 → 100644
View file @
51c64f3f
import
Vue
from
'
vue
'
;
import
store
from
'
~/ide/stores
'
;
import
unstageButton
from
'
~/ide/components/commit_sidebar/unstage_button.vue
'
;
import
{
createComponentWithStore
}
from
'
../../../helpers/vue_mount_component_helper
'
;
import
{
file
,
resetStore
}
from
'
../../helpers
'
;
describe
(
'
IDE unstage file button
'
,
()
=>
{
let
vm
;
let
f
;
beforeEach
(()
=>
{
const
Component
=
Vue
.
extend
(
unstageButton
);
f
=
file
();
vm
=
createComponentWithStore
(
Component
,
store
,
{
file
:
f
,
});
spyOn
(
vm
,
'
unstageChange
'
);
vm
.
$mount
();
});
afterEach
(()
=>
{
vm
.
$destroy
();
resetStore
(
vm
.
$store
);
});
it
(
'
renders button to unstage
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.btn
'
).
length
).
toBe
(
1
);
});
it
(
'
calls store with unnstage button
'
,
()
=>
{
vm
.
$el
.
querySelector
(
'
.btn
'
).
click
();
expect
(
vm
.
unstageChange
).
toHaveBeenCalledWith
(
f
);
});
});
spec/javascripts/ide/components/repo_commit_section_spec.js
View file @
51c64f3f
...
...
@@ -28,10 +28,24 @@ describe('RepoCommitSection', () => {
},
};
const
files
=
[
file
(
'
file1
'
),
file
(
'
file2
'
)].
map
(
f
=>
Object
.
assign
(
f
,
{
type
:
'
blob
'
,
}),
);
vm
.
$store
.
state
.
rightPanelCollapsed
=
false
;
vm
.
$store
.
state
.
currentBranch
=
'
master
'
;
vm
.
$store
.
state
.
changedFiles
=
[
file
(
'
file1
'
),
file
(
'
file2
'
)
];
vm
.
$store
.
state
.
changedFiles
=
[
...
files
];
vm
.
$store
.
state
.
changedFiles
.
forEach
(
f
=>
Object
.
assign
(
f
,
{
changed
:
true
,
content
:
'
changedFile testing
'
,
}),
);
vm
.
$store
.
state
.
stagedFiles
=
[{
...
files
[
0
]
},
{
...
files
[
1
]
}];
vm
.
$store
.
state
.
stagedFiles
.
forEach
(
f
=>
Object
.
assign
(
f
,
{
changed
:
true
,
content
:
'
testing
'
,
...
...
@@ -94,20 +108,93 @@ describe('RepoCommitSection', () => {
...
vm
.
$el
.
querySelectorAll
(
'
.multi-file-commit-list li
'
),
];
const
submitCommit
=
vm
.
$el
.
querySelector
(
'
form .btn
'
);
const
allFiles
=
vm
.
$store
.
state
.
changedFiles
.
concat
(
vm
.
$store
.
state
.
stagedFiles
,
);
expect
(
vm
.
$el
.
querySelector
(
'
.multi-file-commit-form
'
)).
not
.
toBeNull
();
expect
(
changedFileElements
.
length
).
toEqual
(
2
);
expect
(
changedFileElements
.
length
).
toEqual
(
4
);
changedFileElements
.
forEach
((
changedFile
,
i
)
=>
{
expect
(
changedFile
.
textContent
.
trim
()).
toContain
(
vm
.
$store
.
state
.
changedFiles
[
i
].
path
,
);
expect
(
changedFile
.
textContent
.
trim
()).
toContain
(
allFiles
[
i
].
path
);
});
expect
(
submitCommit
.
disabled
).
toBeTruthy
();
expect
(
submitCommit
.
querySelector
(
'
.fa-spinner.fa-spin
'
)).
toBeNull
();
});
it
(
'
adds changed files into staged files
'
,
done
=>
{
vm
.
$el
.
querySelector
(
'
.ide-staged-action-btn
'
).
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ide-commit-list-container
'
).
textContent
,
).
toContain
(
'
No changes
'
);
done
();
});
});
it
(
'
stages a single file
'
,
done
=>
{
vm
.
$el
.
querySelector
(
'
.multi-file-discard-btn .btn
'
).
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ide-commit-list-container
'
)
.
querySelectorAll
(
'
li
'
).
length
,
).
toBe
(
1
);
done
();
});
});
it
(
'
discards a single file
'
,
done
=>
{
vm
.
$el
.
querySelectorAll
(
'
.multi-file-discard-btn .btn
'
)[
1
].
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ide-commit-list-container
'
).
textContent
,
).
not
.
toContain
(
'
file1
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.ide-commit-list-container
'
)
.
querySelectorAll
(
'
li
'
).
length
,
).
toBe
(
1
);
done
();
});
});
it
(
'
removes all staged files
'
,
done
=>
{
vm
.
$el
.
querySelectorAll
(
'
.ide-staged-action-btn
'
)[
1
].
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.ide-commit-list-container
'
)[
1
].
textContent
,
).
toContain
(
'
No changes
'
);
done
();
});
});
it
(
'
unstages a single file
'
,
done
=>
{
vm
.
$el
.
querySelectorAll
(
'
.multi-file-discard-btn
'
)[
2
]
.
querySelector
(
'
.btn
'
)
.
click
();
Vue
.
nextTick
(()
=>
{
expect
(
vm
.
$el
.
querySelectorAll
(
'
.ide-commit-list-container
'
)[
1
]
.
querySelectorAll
(
'
li
'
).
length
,
).
toBe
(
1
);
done
();
});
});
it
(
'
updates commitMessage in store on input
'
,
done
=>
{
const
textarea
=
vm
.
$el
.
querySelector
(
'
textarea
'
);
...
...
spec/javascripts/ide/stores/actions_spec.js
View file @
51c64f3f
...
...
@@ -292,6 +292,84 @@ describe('Multi-file store actions', () => {
});
});
describe
(
'
stageAllChanges
'
,
()
=>
{
it
(
'
adds all files from changedFiles to stagedFiles
'
,
done
=>
{
const
f
=
file
();
store
.
state
.
changedFiles
.
push
(
f
);
store
.
state
.
changedFiles
.
push
(
file
(
'
new
'
));
store
.
dispatch
(
'
stageAllChanges
'
)
.
then
(()
=>
{
expect
(
store
.
state
.
stagedFiles
.
length
).
toBe
(
2
);
expect
(
store
.
state
.
stagedFiles
[
0
]).
toEqual
(
f
);
done
();
})
.
catch
(
done
.
fail
);
});
it
(
'
sets all files from changedFiles as staged after adding to stagedFiles
'
,
done
=>
{
store
.
state
.
changedFiles
.
push
(
file
());
store
.
state
.
changedFiles
.
push
(
file
(
'
new
'
));
store
.
dispatch
(
'
stageAllChanges
'
)
.
then
(()
=>
{
expect
(
store
.
state
.
changedFiles
.
length
).
toBe
(
2
);
store
.
state
.
changedFiles
.
forEach
(
f
=>
{
expect
(
f
.
staged
).
toBeTruthy
();
});
done
();
})
.
catch
(
done
.
fail
);
});
});
describe
(
'
unstageAllChanges
'
,
()
=>
{
let
f
;
beforeEach
(()
=>
{
f
=
{
...
file
(),
type
:
'
blob
'
,
staged
:
true
,
};
store
.
state
.
changedFiles
.
push
({
...
f
,
});
});
it
(
'
sets staged to false in changedFiles when unstaging
'
,
done
=>
{
store
.
state
.
stagedFiles
.
push
(
f
);
store
.
dispatch
(
'
unstageAllChanges
'
)
.
then
(()
=>
{
expect
(
store
.
state
.
stagedFiles
.
length
).
toBe
(
0
);
expect
(
store
.
state
.
changedFiles
[
0
].
staged
).
toBeFalsy
();
done
();
})
.
catch
(
done
.
fail
);
});
it
(
'
removes all files from stagedFiles after unstaging
'
,
done
=>
{
store
.
state
.
stagedFiles
.
push
(
file
());
store
.
dispatch
(
'
unstageAllChanges
'
)
.
then
(()
=>
{
expect
(
store
.
state
.
stagedFiles
.
length
).
toBe
(
0
);
done
();
})
.
catch
(
done
.
fail
);
});
});
describe
(
'
updateViewer
'
,
()
=>
{
it
(
'
updates viewer state
'
,
done
=>
{
store
...
...
spec/javascripts/ide/stores/getters_spec.js
View file @
51c64f3f
...
...
@@ -37,19 +37,11 @@ describe('Multi-file store getters', () => {
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
.
changedFiles
.
push
(
file
(
'
added
'
));
localState
.
changedFiles
[
0
].
changed
=
true
;
localState
.
changedFiles
[
0
].
tempFile
=
true
;
it
(
'
returns angle left when collapsed
'
,
()
=>
{
localState
.
rightPanelCollapsed
=
true
;
const
modifiedFiles
=
getters
.
addedFiles
(
localState
);
expect
(
modifiedFiles
.
length
).
toBe
(
1
);
expect
(
modifiedFiles
[
0
].
name
).
toBe
(
'
added
'
);
expect
(
getters
.
collapseButtonIcon
(
localState
)).
toBe
(
'
angle-double-left
'
);
});
});
});
spec/javascripts/ide/stores/modules/commit/actions_spec.js
View file @
51c64f3f
...
...
@@ -359,12 +359,22 @@ describe('IDE commit module actions', () => {
},
},
};
store
.
state
.
changedFiles
.
push
(
file
(
'
changed
'
));
store
.
state
.
changedFiles
[
0
].
active
=
true
;
const
f
=
{
...
file
(
'
changed
'
),
type
:
'
blob
'
,
active
:
true
,
};
store
.
state
.
stagedFiles
.
push
(
f
);
store
.
state
.
changedFiles
=
[
{
...
f
,
},
];
store
.
state
.
openFiles
=
store
.
state
.
changedFiles
;
store
.
state
.
openFiles
.
forEach
(
f
=>
{
store
.
state
.
entries
[
f
.
path
]
=
f
;
store
.
state
.
openFiles
.
forEach
(
localF
=>
{
store
.
state
.
entries
[
localF
.
path
]
=
localF
;
});
store
.
state
.
commit
.
commitAction
=
'
2
'
;
...
...
@@ -444,7 +454,7 @@ describe('IDE commit module actions', () => {
.
catch
(
done
.
fail
);
});
it
(
'
adds commit data to
changed
files
'
,
done
=>
{
it
(
'
adds commit data to files
'
,
done
=>
{
store
.
dispatch
(
'
commit/commitChanges
'
)
.
then
(()
=>
{
...
...
spec/javascripts/ide/stores/mutations/file_spec.js
View file @
51c64f3f
...
...
@@ -144,6 +144,72 @@ describe('Multi-file store file mutations', () => {
});
});
describe
(
'
STAGE_CHANGE
'
,
()
=>
{
it
(
'
adds file into stagedFiles array
'
,
()
=>
{
const
f
=
file
();
mutations
.
STAGE_CHANGE
(
localState
,
f
);
expect
(
localState
.
stagedFiles
.
length
).
toBe
(
1
);
expect
(
localState
.
stagedFiles
[
0
]).
toEqual
(
f
);
});
it
(
'
updates changedFiles file to staged
'
,
()
=>
{
const
f
=
{
...
file
(),
type
:
'
blob
'
,
staged
:
false
,
};
localState
.
changedFiles
.
push
(
f
);
mutations
.
STAGE_CHANGE
(
localState
,
f
);
expect
(
localState
.
changedFiles
[
0
].
staged
).
toBeTruthy
();
});
it
(
'
updates stagedFile if it is already staged
'
,
()
=>
{
const
f
=
file
();
f
.
type
=
'
blob
'
;
mutations
.
STAGE_CHANGE
(
localState
,
f
);
f
.
raw
=
'
testing 123
'
;
mutations
.
STAGE_CHANGE
(
localState
,
f
);
expect
(
localState
.
stagedFiles
.
length
).
toBe
(
1
);
expect
(
localState
.
stagedFiles
[
0
].
raw
).
toEqual
(
'
testing 123
'
);
});
});
describe
(
'
UNSTAGE_CHANGE
'
,
()
=>
{
let
f
;
beforeEach
(()
=>
{
f
=
{
...
file
(),
type
:
'
blob
'
,
staged
:
true
,
};
localState
.
stagedFiles
.
push
(
f
);
localState
.
changedFiles
.
push
(
f
);
});
it
(
'
removes from stagedFiles array
'
,
()
=>
{
mutations
.
UNSTAGE_CHANGE
(
localState
,
f
);
expect
(
localState
.
stagedFiles
.
length
).
toBe
(
0
);
});
it
(
'
updates changedFiles array file to unstaged
'
,
()
=>
{
mutations
.
UNSTAGE_CHANGE
(
localState
,
f
);
expect
(
localState
.
changedFiles
[
0
].
staged
).
toBeFalsy
();
});
});
describe
(
'
TOGGLE_FILE_CHANGED
'
,
()
=>
{
it
(
'
updates file changed status
'
,
()
=>
{
mutations
.
TOGGLE_FILE_CHANGED
(
localState
,
{
...
...
spec/javascripts/ide/stores/mutations_spec.js
View file @
51c64f3f
...
...
@@ -69,6 +69,16 @@ describe('Multi-file store mutations', () => {
});
});
describe
(
'
CLEAR_STAGED_CHANGES
'
,
()
=>
{
it
(
'
clears stagedFiles array
'
,
()
=>
{
localState
.
stagedFiles
.
push
(
'
a
'
);
mutations
.
CLEAR_STAGED_CHANGES
(
localState
);
expect
(
localState
.
stagedFiles
.
length
).
toBe
(
0
);
});
});
describe
(
'
UPDATE_VIEWER
'
,
()
=>
{
it
(
'
sets viewer state
'
,
()
=>
{
mutations
.
UPDATE_VIEWER
(
localState
,
'
diff
'
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment