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
1
Merge Requests
1
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
nexedi
gitlab-ce
Commits
4d482938
Commit
4d482938
authored
Mar 04, 2021
by
Nicolò Maria Mezzopera
Committed by
Natalia Tepluhina
Mar 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new vuex store for merge conflicts
- state - getters - mutations - actions
parent
27a3c00c
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
819 additions
and
0 deletions
+819
-0
app/assets/javascripts/merge_conflicts/constants.js
app/assets/javascripts/merge_conflicts/constants.js
+20
-0
app/assets/javascripts/merge_conflicts/store/actions.js
app/assets/javascripts/merge_conflicts/store/actions.js
+120
-0
app/assets/javascripts/merge_conflicts/store/getters.js
app/assets/javascripts/merge_conflicts/store/getters.js
+117
-0
app/assets/javascripts/merge_conflicts/store/index.js
app/assets/javascripts/merge_conflicts/store/index.js
+16
-0
app/assets/javascripts/merge_conflicts/store/mutation_types.js
...ssets/javascripts/merge_conflicts/store/mutation_types.js
+8
-0
app/assets/javascripts/merge_conflicts/store/mutations.js
app/assets/javascripts/merge_conflicts/store/mutations.js
+40
-0
app/assets/javascripts/merge_conflicts/store/state.js
app/assets/javascripts/merge_conflicts/store/state.js
+13
-0
app/assets/javascripts/merge_conflicts/utils.js
app/assets/javascripts/merge_conflicts/utils.js
+228
-0
spec/frontend/merge_conflicts/store/actions_spec.js
spec/frontend/merge_conflicts/store/actions_spec.js
+257
-0
No files found.
app/assets/javascripts/merge_conflicts/constants.js
0 → 100644
View file @
4d482938
import
{
s__
}
from
'
~/locale
'
;
export
const
CONFLICT_TYPES
=
{
TEXT
:
'
text
'
,
TEXT_EDITOR
:
'
text-editor
'
,
};
export
const
VIEW_TYPES
=
{
INLINE
:
'
inline
'
,
PARALLEL
:
'
parallel
'
,
};
export
const
EDIT_RESOLVE_MODE
=
'
edit
'
;
export
const
INTERACTIVE_RESOLVE_MODE
=
'
interactive
'
;
export
const
DEFAULT_RESOLVE_MODE
=
INTERACTIVE_RESOLVE_MODE
;
export
const
HEAD_HEADER_TEXT
=
s__
(
'
MergeConflict|HEAD//our changes
'
);
export
const
ORIGIN_HEADER_TEXT
=
s__
(
'
MergeConflict|origin//their changes
'
);
export
const
HEAD_BUTTON_TITLE
=
s__
(
'
MergeConflict|Use ours
'
);
export
const
ORIGIN_BUTTON_TITLE
=
s__
(
'
MergeConflict|Use theirs
'
);
app/assets/javascripts/merge_conflicts/store/actions.js
0 → 100644
View file @
4d482938
import
Cookies
from
'
js-cookie
'
;
import
createFlash
from
'
~/flash
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
INTERACTIVE_RESOLVE_MODE
,
EDIT_RESOLVE_MODE
}
from
'
../constants
'
;
import
{
decorateFiles
,
restoreFileLinesState
,
markLine
}
from
'
../utils
'
;
import
*
as
types
from
'
./mutation_types
'
;
export
const
fetchConflictsData
=
async
({
commit
,
dispatch
},
conflictsPath
)
=>
{
commit
(
types
.
SET_LOADING_STATE
,
true
);
try
{
const
{
data
}
=
await
axios
.
get
(
conflictsPath
);
if
(
data
.
type
===
'
error
'
)
{
commit
(
types
.
SET_FAILED_REQUEST
,
data
.
message
);
}
else
{
dispatch
(
'
setConflictsData
'
,
data
);
}
}
catch
(
e
)
{
commit
(
types
.
SET_FAILED_REQUEST
);
}
commit
(
types
.
SET_LOADING_STATE
,
false
);
};
export
const
setConflictsData
=
async
({
commit
},
data
)
=>
{
const
files
=
decorateFiles
(
data
.
files
);
commit
(
types
.
SET_CONFLICTS_DATA
,
{
...
data
,
files
});
};
export
const
submitResolvedConflicts
=
async
({
commit
,
getters
},
resolveConflictsPath
)
=>
{
commit
(
types
.
SET_SUBMIT_STATE
,
true
);
try
{
const
{
data
}
=
await
axios
.
post
(
resolveConflictsPath
,
getters
.
getCommitData
);
window
.
location
.
assign
(
data
.
redirect_to
);
}
catch
(
e
)
{
commit
(
types
.
SET_SUBMIT_STATE
,
false
);
createFlash
({
message
:
__
(
'
Failed to save merge conflicts resolutions. Please try again!
'
)
});
}
};
export
const
setLoadingState
=
({
commit
},
isLoading
)
=>
{
commit
(
types
.
SET_LOADING_STATE
,
isLoading
);
};
export
const
setErrorState
=
({
commit
},
hasError
)
=>
{
commit
(
types
.
SET_ERROR_STATE
,
hasError
);
};
export
const
setFailedRequest
=
({
commit
},
message
)
=>
{
commit
(
types
.
SET_FAILED_REQUEST
,
message
);
};
export
const
setViewType
=
({
commit
},
viewType
)
=>
{
commit
(
types
.
SET_VIEW_TYPE
,
viewType
);
Cookies
.
set
(
'
diff_view
'
,
viewType
);
};
export
const
setSubmitState
=
({
commit
},
isSubmitting
)
=>
{
commit
(
types
.
SET_SUBMIT_STATE
,
isSubmitting
);
};
export
const
updateCommitMessage
=
({
commit
},
commitMessage
)
=>
{
commit
(
types
.
UPDATE_CONFLICTS_DATA
,
{
commitMessage
});
};
export
const
setFileResolveMode
=
({
commit
,
state
,
getters
},
{
file
,
mode
})
=>
{
const
index
=
getters
.
getFileIndex
(
file
);
const
updated
=
{
...
state
.
conflictsData
.
files
[
index
]
};
if
(
mode
===
INTERACTIVE_RESOLVE_MODE
)
{
updated
.
showEditor
=
false
;
}
else
if
(
mode
===
EDIT_RESOLVE_MODE
)
{
// Restore Interactive mode when switching to Edit mode
updated
.
showEditor
=
true
;
updated
.
loadEditor
=
true
;
updated
.
resolutionData
=
{};
const
{
inlineLines
,
parallelLines
}
=
restoreFileLinesState
(
updated
);
updated
.
parallelLines
=
parallelLines
;
updated
.
inlineLines
=
inlineLines
;
}
updated
.
resolveMode
=
mode
;
commit
(
types
.
UPDATE_FILE
,
{
file
:
updated
,
index
});
};
export
const
setPromptConfirmationState
=
(
{
commit
,
state
,
getters
},
{
file
,
promptDiscardConfirmation
},
)
=>
{
const
index
=
getters
.
getFileIndex
(
file
);
const
updated
=
{
...
state
.
conflictsData
.
files
[
index
],
promptDiscardConfirmation
};
commit
(
types
.
UPDATE_FILE
,
{
file
:
updated
,
index
});
};
export
const
handleSelected
=
({
commit
,
state
,
getters
},
{
file
,
line
:
{
id
,
section
}
})
=>
{
const
index
=
getters
.
getFileIndex
(
file
);
const
updated
=
{
...
state
.
conflictsData
.
files
[
index
]
};
updated
.
resolutionData
=
{
...
updated
.
resolutionData
,
[
id
]:
section
};
updated
.
inlineLines
=
file
.
inlineLines
.
map
((
line
)
=>
{
if
(
id
===
line
.
id
&&
(
line
.
hasConflict
||
line
.
isHeader
))
{
return
markLine
(
line
,
section
);
}
return
line
;
});
updated
.
parallelLines
=
file
.
parallelLines
.
map
((
lines
)
=>
{
let
left
=
{
...
lines
[
0
]
};
let
right
=
{
...
lines
[
1
]
};
const
hasSameId
=
right
.
id
===
id
||
left
.
id
===
id
;
const
isLeftMatch
=
left
.
hasConflict
||
left
.
isHeader
;
const
isRightMatch
=
right
.
hasConflict
||
right
.
isHeader
;
if
(
hasSameId
&&
(
isLeftMatch
||
isRightMatch
))
{
left
=
markLine
(
left
,
section
);
right
=
markLine
(
right
,
section
);
}
return
[
left
,
right
];
});
commit
(
types
.
UPDATE_FILE
,
{
file
:
updated
,
index
});
};
app/assets/javascripts/merge_conflicts/store/getters.js
0 → 100644
View file @
4d482938
import
{
s__
}
from
'
~/locale
'
;
import
{
CONFLICT_TYPES
,
EDIT_RESOLVE_MODE
,
INTERACTIVE_RESOLVE_MODE
}
from
'
../constants
'
;
export
const
getConflictsCount
=
(
state
)
=>
{
if
(
!
state
.
conflictsData
.
files
.
length
)
{
return
0
;
}
const
{
files
}
=
state
.
conflictsData
;
let
count
=
0
;
files
.
forEach
((
file
)
=>
{
if
(
file
.
type
===
CONFLICT_TYPES
.
TEXT
)
{
file
.
sections
.
forEach
((
section
)
=>
{
if
(
section
.
conflict
)
{
count
+=
1
;
}
});
}
else
{
count
+=
1
;
}
});
return
count
;
};
export
const
getConflictsCountText
=
(
state
,
getters
)
=>
{
const
count
=
getters
.
getConflictsCount
;
const
text
=
count
>
1
?
s__
(
'
MergeConflict|conflicts
'
)
:
s__
(
'
MergeConflict|conflict
'
);
return
`
${
count
}
${
text
}
`
;
};
export
const
isReadyToCommit
=
(
state
)
=>
{
const
{
files
}
=
state
.
conflictsData
;
const
hasCommitMessage
=
state
.
conflictsData
.
commitMessage
.
trim
().
length
;
let
unresolved
=
0
;
for
(
let
i
=
0
,
l
=
files
.
length
;
i
<
l
;
i
+=
1
)
{
const
file
=
files
[
i
];
if
(
file
.
resolveMode
===
INTERACTIVE_RESOLVE_MODE
)
{
let
numberConflicts
=
0
;
const
resolvedConflicts
=
Object
.
keys
(
file
.
resolutionData
).
length
;
// We only check for conflicts type 'text'
// since conflicts `text_editor` can´t be resolved in interactive mode
if
(
file
.
type
===
CONFLICT_TYPES
.
TEXT
)
{
for
(
let
j
=
0
,
k
=
file
.
sections
.
length
;
j
<
k
;
j
+=
1
)
{
if
(
file
.
sections
[
j
].
conflict
)
{
numberConflicts
+=
1
;
}
}
if
(
resolvedConflicts
!==
numberConflicts
)
{
unresolved
+=
1
;
}
}
}
else
if
(
file
.
resolveMode
===
EDIT_RESOLVE_MODE
)
{
// Unlikely to happen since switching to Edit mode saves content automatically.
// Checking anyway in case the save strategy changes in the future
if
(
!
file
.
content
)
{
unresolved
+=
1
;
// eslint-disable-next-line no-continue
continue
;
}
}
}
return
!
state
.
isSubmitting
&&
hasCommitMessage
&&
!
unresolved
;
};
export
const
getCommitButtonText
=
(
state
)
=>
{
const
initial
=
s__
(
'
MergeConflict|Commit to source branch
'
);
const
inProgress
=
s__
(
'
MergeConflict|Committing...
'
);
return
state
.
isSubmitting
?
inProgress
:
initial
;
};
export
const
getCommitData
=
(
state
)
=>
{
let
commitData
=
{};
commitData
=
{
commit_message
:
state
.
conflictsData
.
commitMessage
,
files
:
[],
};
state
.
conflictsData
.
files
.
forEach
((
file
)
=>
{
const
addFile
=
{
old_path
:
file
.
old_path
,
new_path
:
file
.
new_path
,
};
if
(
file
.
type
===
CONFLICT_TYPES
.
TEXT
)
{
// Submit only one data for type of editing
if
(
file
.
resolveMode
===
INTERACTIVE_RESOLVE_MODE
)
{
addFile
.
sections
=
file
.
resolutionData
;
}
else
if
(
file
.
resolveMode
===
EDIT_RESOLVE_MODE
)
{
addFile
.
content
=
file
.
content
;
}
}
else
if
(
file
.
type
===
CONFLICT_TYPES
.
TEXT_EDITOR
)
{
addFile
.
content
=
file
.
content
;
}
commitData
.
files
.
push
(
addFile
);
});
return
commitData
;
};
export
const
fileTextTypePresent
=
(
state
)
=>
{
return
state
.
conflictsData
?.
files
.
some
((
f
)
=>
f
.
type
===
CONFLICT_TYPES
.
TEXT
);
};
export
const
getFileIndex
=
(
state
)
=>
({
blobPath
})
=>
{
return
state
.
conflictsData
.
files
.
findIndex
((
f
)
=>
f
.
blobPath
===
blobPath
);
};
app/assets/javascripts/merge_conflicts/store/index.js
0 → 100644
View file @
4d482938
import
Vue
from
'
vue
'
;
import
Vuex
from
'
vuex
'
;
import
*
as
actions
from
'
./actions
'
;
import
*
as
getters
from
'
./getters
'
;
import
mutations
from
'
./mutations
'
;
import
state
from
'
./state
'
;
Vue
.
use
(
Vuex
);
export
const
createStore
=
()
=>
new
Vuex
.
Store
({
state
,
getters
,
actions
,
mutations
,
});
app/assets/javascripts/merge_conflicts/store/mutation_types.js
0 → 100644
View file @
4d482938
export
const
SET_LOADING_STATE
=
'
SET_LOADING_STATE
'
;
export
const
SET_ERROR_STATE
=
'
SET_ERROR_STATE
'
;
export
const
SET_FAILED_REQUEST
=
'
SET_FAILED_REQUEST
'
;
export
const
SET_VIEW_TYPE
=
'
SET_VIEW_TYPE
'
;
export
const
SET_SUBMIT_STATE
=
'
SET_SUBMIT_STATE
'
;
export
const
SET_CONFLICTS_DATA
=
'
SET_CONFLICTS_DATA
'
;
export
const
UPDATE_FILE
=
'
UPDATE_FILE
'
;
export
const
UPDATE_CONFLICTS_DATA
=
'
UPDATE_CONFLICTS_DATA
'
;
app/assets/javascripts/merge_conflicts/store/mutations.js
0 → 100644
View file @
4d482938
import
{
VIEW_TYPES
}
from
'
../constants
'
;
import
*
as
types
from
'
./mutation_types
'
;
export
default
{
[
types
.
SET_LOADING_STATE
]:
(
state
,
value
)
=>
{
state
.
isLoading
=
value
;
},
[
types
.
SET_ERROR_STATE
]:
(
state
,
value
)
=>
{
state
.
hasError
=
value
;
},
[
types
.
SET_FAILED_REQUEST
]:
(
state
,
value
)
=>
{
state
.
hasError
=
true
;
state
.
conflictsData
.
errorMessage
=
value
;
},
[
types
.
SET_VIEW_TYPE
]:
(
state
,
value
)
=>
{
state
.
diffView
=
value
;
state
.
isParallel
=
value
===
VIEW_TYPES
.
PARALLEL
;
},
[
types
.
SET_SUBMIT_STATE
]:
(
state
,
value
)
=>
{
state
.
isSubmitting
=
value
;
},
[
types
.
SET_CONFLICTS_DATA
]:
(
state
,
data
)
=>
{
state
.
conflictsData
=
{
files
:
data
.
files
,
commitMessage
:
data
.
commit_message
,
sourceBranch
:
data
.
source_branch
,
targetBranch
:
data
.
target_branch
,
shortCommitSha
:
data
.
commit_sha
.
slice
(
0
,
7
),
};
},
[
types
.
UPDATE_CONFLICTS_DATA
]:
(
state
,
payload
)
=>
{
state
.
conflictsData
=
{
...
state
.
conflictsData
,
...
payload
,
};
},
[
types
.
UPDATE_FILE
]:
(
state
,
{
file
,
index
})
=>
{
state
.
conflictsData
.
files
.
splice
(
index
,
1
,
file
);
},
};
app/assets/javascripts/merge_conflicts/store/state.js
0 → 100644
View file @
4d482938
import
Cookies
from
'
js-cookie
'
;
import
{
VIEW_TYPES
}
from
'
../constants
'
;
const
diffViewType
=
Cookies
.
get
(
'
diff_view
'
);
export
default
()
=>
({
isLoading
:
true
,
hasError
:
false
,
isSubmitting
:
false
,
isParallel
:
diffViewType
===
VIEW_TYPES
.
PARALLEL
,
diffViewType
,
conflictsData
:
{},
});
app/assets/javascripts/merge_conflicts/utils.js
0 → 100644
View file @
4d482938
import
{
ORIGIN_HEADER_TEXT
,
ORIGIN_BUTTON_TITLE
,
HEAD_HEADER_TEXT
,
HEAD_BUTTON_TITLE
,
DEFAULT_RESOLVE_MODE
,
CONFLICT_TYPES
,
}
from
'
./constants
'
;
export
const
getFilePath
=
(
file
)
=>
{
const
{
old_path
,
new_path
}
=
file
;
// eslint-disable-next-line babel/camelcase
return
old_path
===
new_path
?
new_path
:
`
${
old_path
}
→
${
new_path
}
`
;
};
export
const
checkLineLengths
=
({
left
,
right
})
=>
{
const
wLeft
=
[...
left
];
const
wRight
=
[...
right
];
if
(
left
.
length
!==
right
.
length
)
{
if
(
left
.
length
>
right
.
length
)
{
const
diff
=
left
.
length
-
right
.
length
;
for
(
let
i
=
0
;
i
<
diff
;
i
+=
1
)
{
wRight
.
push
({
lineType
:
'
emptyLine
'
,
richText
:
''
});
}
}
else
{
const
diff
=
right
.
length
-
left
.
length
;
for
(
let
i
=
0
;
i
<
diff
;
i
+=
1
)
{
wLeft
.
push
({
lineType
:
'
emptyLine
'
,
richText
:
''
});
}
}
}
return
{
left
:
wLeft
,
right
:
wRight
};
};
export
const
getHeadHeaderLine
=
(
id
)
=>
{
return
{
id
,
richText
:
HEAD_HEADER_TEXT
,
buttonTitle
:
HEAD_BUTTON_TITLE
,
type
:
'
new
'
,
section
:
'
head
'
,
isHeader
:
true
,
isHead
:
true
,
isSelected
:
false
,
isUnselected
:
false
,
};
};
export
const
decorateLineForInlineView
=
(
line
,
id
,
conflict
)
=>
{
const
{
type
}
=
line
;
return
{
id
,
hasConflict
:
conflict
,
isHead
:
type
===
'
new
'
,
isOrigin
:
type
===
'
old
'
,
hasMatch
:
type
===
'
match
'
,
richText
:
line
.
rich_text
,
isSelected
:
false
,
isUnselected
:
false
,
};
};
export
const
getLineForParallelView
=
(
line
,
id
,
lineType
,
isHead
)
=>
{
const
{
old_line
,
new_line
,
rich_text
}
=
line
;
const
hasConflict
=
lineType
===
'
conflict
'
;
return
{
id
,
lineType
,
hasConflict
,
isHead
:
hasConflict
&&
isHead
,
isOrigin
:
hasConflict
&&
!
isHead
,
hasMatch
:
lineType
===
'
match
'
,
// eslint-disable-next-line babel/camelcase
lineNumber
:
isHead
?
new_line
:
old_line
,
section
:
isHead
?
'
head
'
:
'
origin
'
,
richText
:
rich_text
,
isSelected
:
false
,
isUnselected
:
false
,
};
};
export
const
getOriginHeaderLine
=
(
id
)
=>
{
return
{
id
,
richText
:
ORIGIN_HEADER_TEXT
,
buttonTitle
:
ORIGIN_BUTTON_TITLE
,
type
:
'
old
'
,
section
:
'
origin
'
,
isHeader
:
true
,
isOrigin
:
true
,
isSelected
:
false
,
isUnselected
:
false
,
};
};
export
const
setInlineLine
=
(
file
)
=>
{
const
inlineLines
=
[];
file
.
sections
.
forEach
((
section
)
=>
{
let
currentLineType
=
'
new
'
;
const
{
conflict
,
lines
,
id
}
=
section
;
if
(
conflict
)
{
inlineLines
.
push
(
getHeadHeaderLine
(
id
));
}
lines
.
forEach
((
line
)
=>
{
const
{
type
}
=
line
;
if
((
type
===
'
new
'
||
type
===
'
old
'
)
&&
currentLineType
!==
type
)
{
currentLineType
=
type
;
inlineLines
.
push
({
lineType
:
'
emptyLine
'
,
richText
:
''
});
}
const
decoratedLine
=
decorateLineForInlineView
(
line
,
id
,
conflict
);
inlineLines
.
push
(
decoratedLine
);
});
if
(
conflict
)
{
inlineLines
.
push
(
getOriginHeaderLine
(
id
));
}
});
return
inlineLines
;
};
export
const
setParallelLine
=
(
file
)
=>
{
const
parallelLines
=
[];
let
linesObj
=
{
left
:
[],
right
:
[]
};
file
.
sections
.
forEach
((
section
)
=>
{
const
{
conflict
,
lines
,
id
}
=
section
;
if
(
conflict
)
{
linesObj
.
left
.
push
(
getOriginHeaderLine
(
id
));
linesObj
.
right
.
push
(
getHeadHeaderLine
(
id
));
}
lines
.
forEach
((
line
)
=>
{
const
{
type
}
=
line
;
if
(
conflict
)
{
if
(
type
===
'
old
'
)
{
linesObj
.
left
.
push
(
getLineForParallelView
(
line
,
id
,
'
conflict
'
));
}
else
if
(
type
===
'
new
'
)
{
linesObj
.
right
.
push
(
getLineForParallelView
(
line
,
id
,
'
conflict
'
,
true
));
}
}
else
{
const
lineType
=
type
||
'
context
'
;
linesObj
.
left
.
push
(
getLineForParallelView
(
line
,
id
,
lineType
));
linesObj
.
right
.
push
(
getLineForParallelView
(
line
,
id
,
lineType
,
true
));
}
});
linesObj
=
checkLineLengths
(
linesObj
);
});
for
(
let
i
=
0
,
len
=
linesObj
.
left
.
length
;
i
<
len
;
i
+=
1
)
{
parallelLines
.
push
([
linesObj
.
right
[
i
],
linesObj
.
left
[
i
]]);
}
return
parallelLines
;
};
export
const
decorateFiles
=
(
files
)
=>
{
return
files
.
map
((
file
)
=>
{
const
f
=
{
...
file
};
f
.
content
=
''
;
f
.
resolutionData
=
{};
f
.
promptDiscardConfirmation
=
false
;
f
.
resolveMode
=
DEFAULT_RESOLVE_MODE
;
f
.
filePath
=
getFilePath
(
file
);
f
.
blobPath
=
f
.
blob_path
;
if
(
f
.
type
===
CONFLICT_TYPES
.
TEXT
)
{
f
.
showEditor
=
false
;
f
.
loadEditor
=
false
;
f
.
inlineLines
=
setInlineLine
(
file
);
f
.
parallelLines
=
setParallelLine
(
file
);
}
else
if
(
f
.
type
===
CONFLICT_TYPES
.
TEXT_EDITOR
)
{
f
.
showEditor
=
true
;
f
.
loadEditor
=
true
;
}
return
f
;
});
};
export
const
restoreFileLinesState
=
(
file
)
=>
{
const
inlineLines
=
file
.
inlineLines
.
map
((
line
)
=>
{
if
(
line
.
hasConflict
||
line
.
isHeader
)
{
return
{
...
line
,
isSelected
:
false
,
isUnselected
:
false
};
}
return
{
...
line
};
});
const
parallelLines
=
file
.
parallelLines
.
map
((
lines
)
=>
{
const
left
=
{
...
lines
[
0
]
};
const
right
=
{
...
lines
[
1
]
};
const
isLeftMatch
=
left
.
hasConflict
||
left
.
isHeader
;
const
isRightMatch
=
right
.
hasConflict
||
right
.
isHeader
;
if
(
isLeftMatch
||
isRightMatch
)
{
left
.
isSelected
=
false
;
left
.
isUnselected
=
false
;
right
.
isSelected
=
false
;
right
.
isUnselected
=
false
;
}
return
[
left
,
right
];
});
return
{
inlineLines
,
parallelLines
};
};
export
const
markLine
=
(
line
,
selection
)
=>
{
const
updated
=
{
...
line
};
if
(
selection
===
'
head
'
&&
line
.
isHead
)
{
updated
.
isSelected
=
true
;
updated
.
isUnselected
=
false
;
}
else
if
(
selection
===
'
origin
'
&&
updated
.
isOrigin
)
{
updated
.
isSelected
=
true
;
updated
.
isUnselected
=
false
;
}
else
{
updated
.
isSelected
=
false
;
updated
.
isUnselected
=
true
;
}
return
updated
;
};
spec/frontend/merge_conflicts/store/actions_spec.js
0 → 100644
View file @
4d482938
import
axios
from
'
axios
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
{
useMockLocationHelper
}
from
'
helpers/mock_window_location_helper
'
;
import
testAction
from
'
helpers/vuex_action_helper
'
;
import
createFlash
from
'
~/flash
'
;
import
{
INTERACTIVE_RESOLVE_MODE
,
EDIT_RESOLVE_MODE
}
from
'
~/merge_conflicts/constants
'
;
import
*
as
actions
from
'
~/merge_conflicts/store/actions
'
;
import
*
as
types
from
'
~/merge_conflicts/store/mutation_types
'
;
import
{
restoreFileLinesState
,
markLine
,
decorateFiles
}
from
'
~/merge_conflicts/utils
'
;
jest
.
mock
(
'
~/flash.js
'
);
jest
.
mock
(
'
~/merge_conflicts/utils
'
);
describe
(
'
merge conflicts actions
'
,
()
=>
{
let
mock
;
const
files
=
[
{
blobPath
:
'
a
'
,
},
{
blobPath
:
'
b
'
},
];
beforeEach
(()
=>
{
mock
=
new
MockAdapter
(
axios
);
});
afterEach
(()
=>
{
mock
.
restore
();
});
describe
(
'
fetchConflictsData
'
,
()
=>
{
const
conflictsPath
=
'
conflicts/path/mock
'
;
it
(
'
on success dispatches setConflictsData
'
,
(
done
)
=>
{
mock
.
onGet
(
conflictsPath
).
reply
(
200
,
{});
testAction
(
actions
.
fetchConflictsData
,
conflictsPath
,
{},
[
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
true
},
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
false
},
],
[{
type
:
'
setConflictsData
'
,
payload
:
{}
}],
done
,
);
});
it
(
'
when data has type equal to error
'
,
(
done
)
=>
{
mock
.
onGet
(
conflictsPath
).
reply
(
200
,
{
type
:
'
error
'
,
message
:
'
error message
'
});
testAction
(
actions
.
fetchConflictsData
,
conflictsPath
,
{},
[
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
true
},
{
type
:
types
.
SET_FAILED_REQUEST
,
payload
:
'
error message
'
},
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
false
},
],
[],
done
,
);
});
it
(
'
when request fails
'
,
(
done
)
=>
{
mock
.
onGet
(
conflictsPath
).
reply
(
400
);
testAction
(
actions
.
fetchConflictsData
,
conflictsPath
,
{},
[
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
true
},
{
type
:
types
.
SET_FAILED_REQUEST
},
{
type
:
types
.
SET_LOADING_STATE
,
payload
:
false
},
],
[],
done
,
);
});
});
describe
(
'
submitResolvedConflicts
'
,
()
=>
{
useMockLocationHelper
();
const
resolveConflictsPath
=
'
resolve/conflicts/path/mock
'
;
it
(
'
on success reloads the page
'
,
(
done
)
=>
{
mock
.
onPost
(
resolveConflictsPath
).
reply
(
200
,
{
redirect_to
:
'
hrefPath
'
});
testAction
(
actions
.
submitResolvedConflicts
,
resolveConflictsPath
,
{},
[{
type
:
types
.
SET_SUBMIT_STATE
,
payload
:
true
}],
[],
()
=>
{
expect
(
window
.
location
.
assign
).
toHaveBeenCalledWith
(
'
hrefPath
'
);
done
();
},
);
});
it
(
'
on errors shows flash
'
,
(
done
)
=>
{
mock
.
onPost
(
resolveConflictsPath
).
reply
(
400
);
testAction
(
actions
.
submitResolvedConflicts
,
resolveConflictsPath
,
{},
[
{
type
:
types
.
SET_SUBMIT_STATE
,
payload
:
true
},
{
type
:
types
.
SET_SUBMIT_STATE
,
payload
:
false
},
],
[],
()
=>
{
expect
(
createFlash
).
toHaveBeenCalledWith
({
message
:
'
Failed to save merge conflicts resolutions. Please try again!
'
,
});
done
();
},
);
});
});
describe
(
'
setConflictsData
'
,
()
=>
{
it
(
'
INTERACTIVE_RESOLVE_MODE updates the correct file
'
,
(
done
)
=>
{
decorateFiles
.
mockReturnValue
([{
bar
:
'
baz
'
}]);
testAction
(
actions
.
setConflictsData
,
{
files
,
foo
:
'
bar
'
},
{},
[
{
type
:
types
.
SET_CONFLICTS_DATA
,
payload
:
{
foo
:
'
bar
'
,
files
:
[{
bar
:
'
baz
'
}]
},
},
],
[],
done
,
);
});
});
describe
(
'
setFileResolveMode
'
,
()
=>
{
it
(
'
INTERACTIVE_RESOLVE_MODE updates the correct file
'
,
(
done
)
=>
{
testAction
(
actions
.
setFileResolveMode
,
{
file
:
files
[
0
],
mode
:
INTERACTIVE_RESOLVE_MODE
},
{
conflictsData
:
{
files
},
getFileIndex
:
()
=>
0
},
[
{
type
:
types
.
UPDATE_FILE
,
payload
:
{
file
:
{
...
files
[
0
],
showEditor
:
false
,
resolveMode
:
INTERACTIVE_RESOLVE_MODE
},
index
:
0
,
},
},
],
[],
done
,
);
});
it
(
'
EDIT_RESOLVE_MODE updates the correct file
'
,
(
done
)
=>
{
restoreFileLinesState
.
mockReturnValue
([]);
const
file
=
{
...
files
[
0
],
showEditor
:
true
,
loadEditor
:
true
,
resolutionData
:
{},
resolveMode
:
EDIT_RESOLVE_MODE
,
};
testAction
(
actions
.
setFileResolveMode
,
{
file
:
files
[
0
],
mode
:
EDIT_RESOLVE_MODE
},
{
conflictsData
:
{
files
},
getFileIndex
:
()
=>
0
},
[
{
type
:
types
.
UPDATE_FILE
,
payload
:
{
file
,
index
:
0
,
},
},
],
[],
()
=>
{
expect
(
restoreFileLinesState
).
toHaveBeenCalledWith
(
file
);
done
();
},
);
});
});
describe
(
'
setPromptConfirmationState
'
,
()
=>
{
it
(
'
updates the correct file
'
,
(
done
)
=>
{
testAction
(
actions
.
setPromptConfirmationState
,
{
file
:
files
[
0
],
promptDiscardConfirmation
:
true
},
{
conflictsData
:
{
files
},
getFileIndex
:
()
=>
0
},
[
{
type
:
types
.
UPDATE_FILE
,
payload
:
{
file
:
{
...
files
[
0
],
promptDiscardConfirmation
:
true
},
index
:
0
,
},
},
],
[],
done
,
);
});
});
describe
(
'
handleSelected
'
,
()
=>
{
const
file
=
{
...
files
[
0
],
inlineLines
:
[{
id
:
1
,
hasConflict
:
true
},
{
id
:
2
}],
parallelLines
:
[
[{
id
:
1
,
hasConflict
:
true
},
{
id
:
1
}],
[{
id
:
2
},
{
id
:
3
}],
],
};
it
(
'
updates the correct file
'
,
(
done
)
=>
{
const
marLikeMockReturn
=
{
foo
:
'
bar
'
};
markLine
.
mockReturnValue
(
marLikeMockReturn
);
testAction
(
actions
.
handleSelected
,
{
file
,
line
:
{
id
:
1
,
section
:
'
baz
'
}
},
{
conflictsData
:
{
files
},
getFileIndex
:
()
=>
0
},
[
{
type
:
types
.
UPDATE_FILE
,
payload
:
{
file
:
{
...
file
,
resolutionData
:
{
1
:
'
baz
'
},
inlineLines
:
[
marLikeMockReturn
,
{
id
:
2
}],
parallelLines
:
[
[
marLikeMockReturn
,
marLikeMockReturn
],
[{
id
:
2
},
{
id
:
3
}],
],
},
index
:
0
,
},
},
],
[],
()
=>
{
expect
(
markLine
).
toHaveBeenCalledTimes
(
3
);
done
();
},
);
});
});
});
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