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
20f7e1a0
Commit
20f7e1a0
authored
Apr 17, 2020
by
Kushal Pandya
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '207463-edit-form-connected' into 'master'
Snippet edit form See merge request gitlab-org/gitlab!28600
parents
a792e9ed
78f1d2fb
Changes
22
Show whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
660 additions
and
71 deletions
+660
-71
app/assets/javascripts/blob/components/blob_edit_content.vue
app/assets/javascripts/blob/components/blob_edit_content.vue
+4
-5
app/assets/javascripts/snippet/snippet_edit.js
app/assets/javascripts/snippet/snippet_edit.js
+10
-3
app/assets/javascripts/snippets/components/edit.vue
app/assets/javascripts/snippets/components/edit.vue
+211
-0
app/assets/javascripts/snippets/components/snippet_description_edit.vue
...ascripts/snippets/components/snippet_description_edit.vue
+1
-1
app/assets/javascripts/snippets/index.js
app/assets/javascripts/snippets/index.js
+7
-2
app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql
...scripts/snippets/mutations/createSnippet.mutation.graphql
+8
-0
app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql
...scripts/snippets/mutations/updateSnippet.mutation.graphql
+8
-0
app/assets/javascripts/vue_shared/components/form/title.vue
app/assets/javascripts/vue_shared/components/form/title.vue
+1
-1
app/views/shared/snippets/_form.html.haml
app/views/shared/snippets/_form.html.haml
+58
-54
changelogs/unreleased/207463-edit-form-connected.yml
changelogs/unreleased/207463-edit-form-connected.yml
+5
-0
locale/gitlab.pot
locale/gitlab.pot
+6
-0
spec/features/projects/snippets/create_snippet_spec.rb
spec/features/projects/snippets/create_snippet_spec.rb
+1
-0
spec/features/projects/snippets/user_updates_snippet_spec.rb
spec/features/projects/snippets/user_updates_snippet_spec.rb
+1
-0
spec/features/snippets/spam_snippets_spec.rb
spec/features/snippets/spam_snippets_spec.rb
+1
-0
spec/features/snippets/user_creates_snippet_spec.rb
spec/features/snippets/user_creates_snippet_spec.rb
+1
-0
spec/features/snippets/user_edits_snippet_spec.rb
spec/features/snippets/user_edits_snippet_spec.rb
+1
-0
spec/frontend/blob/components/blob_edit_content_spec.js
spec/frontend/blob/components/blob_edit_content_spec.js
+1
-1
spec/frontend/helpers/dom_events_helper.js
spec/frontend/helpers/dom_events_helper.js
+10
-0
spec/frontend/snippet/snippet_edit_spec.js
spec/frontend/snippet/snippet_edit_spec.js
+45
-0
spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap
...nents/__snapshots__/snippet_description_edit_spec.js.snap
+0
-1
spec/frontend/snippets/components/edit_spec.js
spec/frontend/snippets/components/edit_spec.js
+279
-0
spec/frontend/vue_shared/components/form/__snapshots__/title_spec.js.snap
...e_shared/components/form/__snapshots__/title_spec.js.snap
+1
-3
No files found.
app/assets/javascripts/blob/components/blob_edit_content.vue
View file @
20f7e1a0
<
script
>
import
{
initEditorLite
}
from
'
~/blob/utils
'
;
import
{
debounce
}
from
'
lodash
'
;
export
default
{
props
:
{
...
...
@@ -32,16 +33,14 @@ export default {
});
},
methods
:
{
triggerFileChange
()
{
triggerFileChange
:
debounce
(
function
debouncedFileChange
()
{
this
.
$emit
(
'
input
'
,
this
.
editor
.
getValue
());
},
},
250
),
},
};
</
script
>
<
template
>
<div
class=
"file-content code"
>
<pre
id=
"editor"
ref=
"editor"
data-editor-loading
@
focusout=
"triggerFileChange"
>
{{
value
}}
</pre>
<pre
id=
"editor"
ref=
"editor"
data-editor-loading
@
keyup=
"triggerFileChange"
>
{{
value
}}
</pre>
</div>
</
template
>
app/assets/javascripts/snippet/snippet_edit.js
View file @
20f7e1a0
...
...
@@ -2,6 +2,7 @@ import $ from 'jquery';
import
initSnippet
from
'
~/snippet/snippet_bundle
'
;
import
ZenMode
from
'
~/zen_mode
'
;
import
GLForm
from
'
~/gl_form
'
;
import
{
SnippetEditInit
}
from
'
~/snippets
'
;
document
.
addEventListener
(
'
DOMContentLoaded
'
,
()
=>
{
const
form
=
document
.
querySelector
(
'
.snippet-form
'
);
...
...
@@ -17,9 +18,15 @@ document.addEventListener('DOMContentLoaded', () => {
const
projectSnippetOptions
=
{};
const
options
=
form
.
dataset
.
snippetType
===
'
project
'
?
projectSnippetOptions
:
personalSnippetOptions
;
form
.
dataset
.
snippetType
===
'
project
'
||
form
.
dataset
.
projectPath
?
projectSnippetOptions
:
personalSnippetOptions
;
if
(
gon
?.
features
?.
snippetsEditVue
)
{
SnippetEditInit
();
}
else
{
initSnippet
();
new
ZenMode
();
// eslint-disable-line no-new
new
GLForm
(
$
(
form
),
options
);
// eslint-disable-line no-new
}
new
ZenMode
();
// eslint-disable-line no-new
});
app/assets/javascripts/snippets/components/edit.vue
0 → 100644
View file @
20f7e1a0
<
script
>
import
{
GlButton
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
Flash
from
'
~/flash
'
;
import
{
__
,
sprintf
}
from
'
~/locale
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
TitleField
from
'
~/vue_shared/components/form/title.vue
'
;
import
{
getBaseURL
,
joinPaths
,
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
FormFooterActions
from
'
~/vue_shared/components/form/form_footer_actions.vue
'
;
import
UpdateSnippetMutation
from
'
../mutations/updateSnippet.mutation.graphql
'
;
import
CreateSnippetMutation
from
'
../mutations/createSnippet.mutation.graphql
'
;
import
{
getSnippetMixin
}
from
'
../mixins/snippets
'
;
import
{
SNIPPET_VISIBILITY_PRIVATE
}
from
'
../constants
'
;
import
SnippetBlobEdit
from
'
./snippet_blob_edit.vue
'
;
import
SnippetVisibilityEdit
from
'
./snippet_visibility_edit.vue
'
;
import
SnippetDescriptionEdit
from
'
./snippet_description_edit.vue
'
;
export
default
{
components
:
{
SnippetDescriptionEdit
,
SnippetVisibilityEdit
,
SnippetBlobEdit
,
TitleField
,
FormFooterActions
,
GlButton
,
GlLoadingIcon
,
},
mixins
:
[
getSnippetMixin
],
props
:
{
markdownPreviewPath
:
{
type
:
String
,
required
:
true
,
},
markdownDocsPath
:
{
type
:
String
,
required
:
true
,
},
visibilityHelpLink
:
{
type
:
String
,
default
:
''
,
required
:
false
,
},
projectPath
:
{
type
:
String
,
default
:
''
,
required
:
false
,
},
},
data
()
{
return
{
blob
:
{},
fileName
:
''
,
content
:
''
,
isContentLoading
:
true
,
isUpdating
:
false
,
newSnippet
:
false
,
};
},
computed
:
{
updatePrevented
()
{
return
this
.
snippet
.
title
===
''
||
this
.
content
===
''
||
this
.
isUpdating
;
},
isProjectSnippet
()
{
return
Boolean
(
this
.
projectPath
);
},
apiData
()
{
return
{
id
:
this
.
snippet
.
id
,
title
:
this
.
snippet
.
title
,
description
:
this
.
snippet
.
description
,
visibilityLevel
:
this
.
snippet
.
visibilityLevel
,
fileName
:
this
.
fileName
,
content
:
this
.
content
,
};
},
saveButtonLabel
()
{
if
(
this
.
newSnippet
)
{
return
__
(
'
Create snippet
'
);
}
return
this
.
isUpdating
?
__
(
'
Saving
'
)
:
__
(
'
Save changes
'
);
},
cancelButtonHref
()
{
return
this
.
projectPath
?
`/
${
this
.
projectPath
}
/snippets`
:
`/snippets`
;
},
titleFieldId
()
{
return
`
${
this
.
isProjectSnippet
?
'
project
'
:
'
personal
'
}
_snippet_title`
;
},
descriptionFieldId
()
{
return
`
${
this
.
isProjectSnippet
?
'
project
'
:
'
personal
'
}
_snippet_description`
;
},
},
methods
:
{
updateFileName
(
newName
)
{
this
.
fileName
=
newName
;
},
flashAPIFailure
(
err
)
{
Flash
(
sprintf
(
__
(
"
Can't update snippet: %{err}
"
),
{
err
}));
},
onNewSnippetFetched
()
{
this
.
newSnippet
=
true
;
this
.
snippet
=
this
.
$options
.
newSnippetSchema
;
this
.
blob
=
this
.
snippet
.
blob
;
this
.
isContentLoading
=
false
;
},
onExistingSnippetFetched
()
{
this
.
newSnippet
=
false
;
const
{
blob
}
=
this
.
snippet
;
this
.
blob
=
blob
;
this
.
fileName
=
blob
.
name
;
const
baseUrl
=
getBaseURL
();
const
url
=
joinPaths
(
baseUrl
,
blob
.
rawPath
);
axios
.
get
(
url
)
.
then
(
res
=>
{
this
.
content
=
res
.
data
;
this
.
isContentLoading
=
false
;
})
.
catch
(
e
=>
this
.
flashAPIFailure
(
e
));
},
onSnippetFetch
(
snippetRes
)
{
if
(
snippetRes
.
data
.
snippets
.
edges
.
length
===
0
)
{
this
.
onNewSnippetFetched
();
}
else
{
this
.
onExistingSnippetFetched
();
}
},
handleFormSubmit
()
{
this
.
isUpdating
=
true
;
this
.
$apollo
.
mutate
({
mutation
:
this
.
newSnippet
?
CreateSnippetMutation
:
UpdateSnippetMutation
,
variables
:
{
input
:
{
...
this
.
apiData
,
projectPath
:
this
.
newSnippet
?
this
.
projectPath
:
undefined
,
},
},
})
.
then
(({
data
})
=>
{
const
baseObj
=
this
.
newSnippet
?
data
?.
createSnippet
:
data
?.
updateSnippet
;
const
errors
=
baseObj
?.
errors
;
if
(
errors
.
length
)
{
this
.
flashAPIFailure
(
errors
[
0
]);
}
redirectTo
(
baseObj
.
snippet
.
webUrl
);
})
.
catch
(
e
=>
{
this
.
isUpdating
=
false
;
this
.
flashAPIFailure
(
e
);
});
},
},
newSnippetSchema
:
{
title
:
''
,
description
:
''
,
visibilityLevel
:
SNIPPET_VISIBILITY_PRIVATE
,
blob
:
{},
},
};
</
script
>
<
template
>
<form
class=
"snippet-form js-requires-input js-quick-submit common-note-form"
:data-snippet-type=
"isProjectSnippet ? 'project' : 'personal'"
>
<gl-loading-icon
v-if=
"isLoading"
:label=
"__('Loading snippet')"
size=
"lg"
class=
"loading-animation prepend-top-20 append-bottom-20"
/>
<template
v-else
>
<title-field
:id=
"titleFieldId"
v-model=
"snippet.title"
required
:autofocus=
"true"
/>
<snippet-description-edit
:id=
"descriptionFieldId"
v-model=
"snippet.description"
:markdown-preview-path=
"markdownPreviewPath"
:markdown-docs-path=
"markdownDocsPath"
/>
<snippet-blob-edit
v-model=
"content"
:file-name=
"fileName"
:is-loading=
"isContentLoading"
@
name-change=
"updateFileName"
/>
<snippet-visibility-edit
v-model=
"snippet.visibilityLevel"
:help-link=
"visibilityHelpLink"
:is-project-snippet=
"isProjectSnippet"
/>
<form-footer-actions>
<template
#prepend
>
<gl-button
type=
"submit"
category=
"primary"
variant=
"success"
:disabled=
"updatePrevented"
@
click=
"handleFormSubmit"
>
{{
saveButtonLabel
}}
</gl-button
>
</
template
>
<
template
#append
>
<gl-button
:href=
"cancelButtonHref"
>
{{
__
(
'
Cancel
'
)
}}
</gl-button>
</
template
>
</form-footer-actions>
</template>
</form>
</template>
app/assets/javascripts/snippets/components/snippet_description_edit.vue
View file @
20f7e1a0
...
...
@@ -50,7 +50,6 @@ export default {
:markdown-docs-path="markdownDocsPath"
>
<textarea
id=
"snippet-description"
slot=
"textarea"
class=
"note-textarea js-gfm-input js-autosize markdown-area
qa-description-textarea"
...
...
@@ -59,6 +58,7 @@ export default {
:value=
"value"
:aria-label=
"__('Description')"
:placeholder=
"__('Write a comment or drag your files here…')"
v-bind=
"$attrs"
@
input=
"$emit('input', $event.target.value)"
>
</textarea>
...
...
app/assets/javascripts/snippets/index.js
View file @
20f7e1a0
...
...
@@ -3,7 +3,8 @@ import Translate from '~/vue_shared/translate';
import
VueApollo
from
'
vue-apollo
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
SnippetsApp
from
'
./components/show.vue
'
;
import
SnippetsShow
from
'
./components/show.vue
'
;
import
SnippetsEdit
from
'
./components/edit.vue
'
;
Vue
.
use
(
VueApollo
);
Vue
.
use
(
Translate
);
...
...
@@ -31,7 +32,11 @@ function appFactory(el, Component) {
}
export
const
SnippetShowInit
=
()
=>
{
appFactory
(
document
.
getElementById
(
'
js-snippet-view
'
),
SnippetsApp
);
appFactory
(
document
.
getElementById
(
'
js-snippet-view
'
),
SnippetsShow
);
};
export
const
SnippetEditInit
=
()
=>
{
appFactory
(
document
.
getElementById
(
'
js-snippet-edit
'
),
SnippetsEdit
);
};
export
default
()
=>
{};
app/assets/javascripts/snippets/mutations/createSnippet.mutation.graphql
0 → 100644
View file @
20f7e1a0
mutation
CreateSnippet
(
$input
:
CreateSnippetInput
!)
{
createSnippet
(
input
:
$input
)
{
errors
snippet
{
webUrl
}
}
}
app/assets/javascripts/snippets/mutations/updateSnippet.mutation.graphql
0 → 100644
View file @
20f7e1a0
mutation
UpdateSnippet
(
$input
:
UpdateSnippetInput
!)
{
updateSnippet
(
input
:
$input
)
{
errors
snippet
{
webUrl
}
}
}
app/assets/javascripts/vue_shared/components/form/title.vue
View file @
20f7e1a0
...
...
@@ -10,6 +10,6 @@ export default {
</
script
>
<
template
>
<gl-form-group
:label=
"__('Title')"
label-for=
"title-field-edit"
>
<gl-form-input
id=
"title-field-edit"
v-bind=
"$attrs"
v-on=
"$listeners"
/>
<gl-form-input
v-bind=
"$attrs"
v-on=
"$listeners"
/>
</gl-form-group>
</
template
>
app/views/shared/snippets/_form.html.haml
View file @
20f7e1a0
-
content_for
:page_specific_javascripts
do
-
if
Feature
.
disabled?
(
:monaco_snippets
)
-
content_for
:page_specific_javascripts
do
=
page_specific_javascript_tag
(
'lib/ace.js'
)
.snippet-form-holder
-
if
Feature
.
enabled?
(
:snippets_edit_vue
)
#js-snippet-edit
.snippet-form
{
data:
{
'project_path'
:
@snippet
.
project
&
.
full_path
,
'snippet-gid'
:
@snippet
.
new_record?
?
''
:
@snippet
.
to_global_id
,
'markdown-preview-path'
:
preview_markdown_path
(
parent
),
'markdown-docs-path'
:
help_page_path
(
'user/markdown'
),
'visibility-help-link'
:
help_page_path
(
"public_access/public_access"
)
}
}
-
else
.snippet-form-holder
=
form_for
@snippet
,
url:
url
,
html:
{
class:
"snippet-form js-requires-input js-quick-submit common-note-form"
},
data:
{
"snippet-type"
:
@snippet
.
project_id
?
'project'
:
'personal'
}
do
|
f
|
...
...
changelogs/unreleased/207463-edit-form-connected.yml
0 → 100644
View file @
20f7e1a0
---
title
:
Refactored Snippet edit form to Vue
merge_request
:
28600
author
:
type
:
added
locale/gitlab.pot
View file @
20f7e1a0
...
...
@@ -3408,6 +3408,9 @@ msgstr ""
msgid "Can't scan the code?"
msgstr ""
msgid "Can't update snippet: %{err}"
msgstr ""
msgid "Canary"
msgstr ""
...
...
@@ -6070,6 +6073,9 @@ msgstr ""
msgid "Create requirement"
msgstr ""
msgid "Create snippet"
msgstr ""
msgid "Create wildcard: %{searchTerm}"
msgstr ""
...
...
spec/features/projects/snippets/create_snippet_spec.rb
View file @
20f7e1a0
...
...
@@ -4,6 +4,7 @@ require 'spec_helper'
shared_examples_for
'snippet editor'
do
before
do
stub_feature_flags
(
snippets_edit_vue:
false
)
stub_feature_flags
(
monaco_snippets:
flag
)
end
...
...
spec/features/projects/snippets/user_updates_snippet_spec.rb
View file @
20f7e1a0
...
...
@@ -11,6 +11,7 @@ describe 'Projects > Snippets > User updates a snippet', :js do
before
do
stub_feature_flags
(
snippets_vue:
false
)
stub_feature_flags
(
snippets_edit_vue:
false
)
stub_feature_flags
(
version_snippets:
version_snippet_enabled
)
project
.
add_maintainer
(
user
)
...
...
spec/features/snippets/spam_snippets_spec.rb
View file @
20f7e1a0
...
...
@@ -10,6 +10,7 @@ shared_examples_for 'snippet editor' do
before
do
stub_feature_flags
(
allow_possible_spam:
false
)
stub_feature_flags
(
snippets_vue:
false
)
stub_feature_flags
(
snippets_edit_vue:
false
)
stub_feature_flags
(
monaco_snippets:
flag
)
stub_env
(
'IN_MEMORY_APPLICATION_SETTINGS'
,
'false'
)
...
...
spec/features/snippets/user_creates_snippet_spec.rb
View file @
20f7e1a0
...
...
@@ -5,6 +5,7 @@ require 'spec_helper'
shared_examples_for
'snippet editor'
do
before
do
stub_feature_flags
(
snippets_vue:
false
)
stub_feature_flags
(
snippets_edit_vue:
false
)
stub_feature_flags
(
monaco_snippets:
flag
)
sign_in
(
user
)
visit
new_snippet_path
...
...
spec/features/snippets/user_edits_snippet_spec.rb
View file @
20f7e1a0
...
...
@@ -14,6 +14,7 @@ describe 'User edits snippet', :js do
before
do
stub_feature_flags
(
snippets_vue:
false
)
stub_feature_flags
(
snippets_edit_vue:
false
)
stub_feature_flags
(
version_snippets:
version_snippet_enabled
)
sign_in
(
user
)
...
...
spec/frontend/blob/components/blob_edit_content_spec.js
View file @
20f7e1a0
...
...
@@ -80,7 +80,7 @@ describe('Blob Header Editing', () => {
getValue
:
jest
.
fn
().
mockReturnValue
(
value
),
};
editorEl
.
trigger
(
'
focusout
'
);
editorEl
.
trigger
(
'
keyup
'
);
return
nextTick
().
then
(()
=>
{
expect
(
wrapper
.
emitted
().
input
[
0
]).
toEqual
([
value
]);
...
...
spec/frontend/helpers/dom_events_helper.js
0 → 100644
View file @
20f7e1a0
export
const
triggerDOMEvent
=
type
=>
{
window
.
document
.
dispatchEvent
(
new
Event
(
type
,
{
bubbles
:
true
,
cancelable
:
true
,
}),
);
};
export
default
()
=>
{};
spec/frontend/snippet/snippet_edit_spec.js
0 → 100644
View file @
20f7e1a0
import
'
~/snippet/snippet_edit
'
;
import
{
SnippetEditInit
}
from
'
~/snippets
'
;
import
initSnippet
from
'
~/snippet/snippet_bundle
'
;
import
{
triggerDOMEvent
}
from
'
jest/helpers/dom_events_helper
'
;
jest
.
mock
(
'
~/snippet/snippet_bundle
'
);
jest
.
mock
(
'
~/snippets
'
);
describe
(
'
Snippet edit form initialization
'
,
()
=>
{
const
setFF
=
flag
=>
{
gon
.
features
=
{
snippetsEditVue
:
flag
};
};
let
features
;
beforeEach
(()
=>
{
features
=
gon
.
features
;
setFixtures
(
'
<div class="snippet-form"></div>
'
);
});
afterEach
(()
=>
{
gon
.
features
=
features
;
});
it
.
each
`
name | flag | isVue
${
'
Regular
'
}
|
${
false
}
|
${
false
}
${
'
Vue
'
}
|
${
true
}
|
${
true
}
`
(
'
correctly initializes $name Snippet Edit form
'
,
({
flag
,
isVue
})
=>
{
initSnippet
.
mockClear
();
SnippetEditInit
.
mockClear
();
setFF
(
flag
);
triggerDOMEvent
(
'
DOMContentLoaded
'
);
if
(
isVue
)
{
expect
(
initSnippet
).
not
.
toHaveBeenCalled
();
expect
(
SnippetEditInit
).
toHaveBeenCalled
();
}
else
{
expect
(
initSnippet
).
toHaveBeenCalled
();
expect
(
SnippetEditInit
).
not
.
toHaveBeenCalled
();
}
});
});
spec/frontend/snippets/components/__snapshots__/snippet_description_edit_spec.js.snap
View file @
20f7e1a0
...
...
@@ -39,7 +39,6 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
qa-description-textarea"
data-supports-quick-actions="false"
dir="auto"
id="snippet-description"
placeholder="Write a comment or drag your files here…"
/>
</markdown-field-stub>
...
...
spec/frontend/snippets/components/edit_spec.js
0 → 100644
View file @
20f7e1a0
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
joinPaths
,
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
SnippetEditApp
from
'
~/snippets/components/edit.vue
'
;
import
SnippetDescriptionEdit
from
'
~/snippets/components/snippet_description_edit.vue
'
;
import
SnippetVisibilityEdit
from
'
~/snippets/components/snippet_visibility_edit.vue
'
;
import
SnippetBlobEdit
from
'
~/snippets/components/snippet_blob_edit.vue
'
;
import
TitleField
from
'
~/vue_shared/components/form/title.vue
'
;
import
FormFooterActions
from
'
~/vue_shared/components/form/form_footer_actions.vue
'
;
import
UpdateSnippetMutation
from
'
~/snippets/mutations/updateSnippet.mutation.graphql
'
;
import
CreateSnippetMutation
from
'
~/snippets/mutations/createSnippet.mutation.graphql
'
;
import
AxiosMockAdapter
from
'
axios-mock-adapter
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
{
ApolloMutation
}
from
'
vue-apollo
'
;
jest
.
mock
(
'
~/lib/utils/url_utility
'
,
()
=>
({
getBaseURL
:
jest
.
fn
().
mockReturnValue
(
'
foo/
'
),
redirectTo
:
jest
.
fn
().
mockName
(
'
redirectTo
'
),
joinPaths
:
jest
.
fn
()
.
mockName
(
'
joinPaths
'
)
.
mockReturnValue
(
'
contentApiURL
'
),
}));
let
flashSpy
;
const
contentMock
=
'
Foo Bar
'
;
const
rawPathMock
=
'
/foo/bar
'
;
const
rawProjectPathMock
=
'
/project/path
'
;
const
newlyEditedSnippetUrl
=
'
http://foo.bar
'
;
const
apiError
=
{
message
:
'
Ufff
'
};
const
defaultProps
=
{
snippetGid
:
'
gid://gitlab/PersonalSnippet/42
'
,
markdownPreviewPath
:
'
http://preview.foo.bar
'
,
markdownDocsPath
:
'
http://docs.foo.bar
'
,
};
describe
(
'
Snippet Edit app
'
,
()
=>
{
let
wrapper
;
let
axiosMock
;
const
resolveMutate
=
jest
.
fn
().
mockResolvedValue
({
data
:
{
updateSnippet
:
{
errors
:
[],
snippet
:
{
webUrl
:
newlyEditedSnippetUrl
,
},
},
},
});
const
rejectMutation
=
jest
.
fn
().
mockRejectedValue
(
apiError
);
const
mutationTypes
=
{
RESOLVE
:
resolveMutate
,
REJECT
:
rejectMutation
,
};
function
createComponent
({
props
=
defaultProps
,
data
=
{},
loading
=
false
,
mutationRes
=
mutationTypes
.
RESOLVE
,
}
=
{})
{
const
$apollo
=
{
queries
:
{
snippet
:
{
loading
,
},
},
mutate
:
mutationRes
,
};
wrapper
=
shallowMount
(
SnippetEditApp
,
{
mocks
:
{
$apollo
},
stubs
:
{
FormFooterActions
,
ApolloMutation
,
},
propsData
:
{
...
props
,
},
data
()
{
return
data
;
},
});
flashSpy
=
jest
.
spyOn
(
wrapper
.
vm
,
'
flashAPIFailure
'
);
}
afterEach
(()
=>
{
wrapper
.
destroy
();
});
const
findSubmitButton
=
()
=>
wrapper
.
find
(
'
[type=submit]
'
);
describe
(
'
rendering
'
,
()
=>
{
it
(
'
renders loader while the query is in flight
'
,
()
=>
{
createComponent
({
loading
:
true
});
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
true
);
});
it
(
'
renders all required components
'
,
()
=>
{
createComponent
();
expect
(
wrapper
.
contains
(
TitleField
)).
toBe
(
true
);
expect
(
wrapper
.
contains
(
SnippetDescriptionEdit
)).
toBe
(
true
);
expect
(
wrapper
.
contains
(
SnippetBlobEdit
)).
toBe
(
true
);
expect
(
wrapper
.
contains
(
SnippetVisibilityEdit
)).
toBe
(
true
);
expect
(
wrapper
.
contains
(
FormFooterActions
)).
toBe
(
true
);
});
it
(
'
does not fail if there is no snippet yet (new snippet creation)
'
,
()
=>
{
const
snippetGid
=
''
;
createComponent
({
props
:
{
...
defaultProps
,
snippetGid
,
},
});
expect
(
wrapper
.
props
(
'
snippetGid
'
)).
toBe
(
snippetGid
);
});
it
.
each
`
title | content | expectation
${
''
}
|
${
''
}
|
${
true
}
${
'
foo
'
}
|
${
''
}
|
${
true
}
${
''
}
|
${
'
foo
'
}
|
${
true
}
${
'
foo
'
}
|
${
'
bar
'
}
|
${
false
}
`
(
'
disables submit button unless both title and content are present
'
,
({
title
,
content
,
expectation
})
=>
{
createComponent
({
data
:
{
snippet
:
{
title
},
content
,
},
});
const
isBtnDisabled
=
Boolean
(
findSubmitButton
().
attributes
(
'
disabled
'
));
expect
(
isBtnDisabled
).
toBe
(
expectation
);
},
);
});
describe
(
'
functionality
'
,
()
=>
{
describe
(
'
handling of the data from GraphQL response
'
,
()
=>
{
const
snippet
=
{
blob
:
{
rawPath
:
rawPathMock
,
},
};
const
getResSchema
=
newSnippet
=>
{
return
{
data
:
{
snippets
:
{
edges
:
newSnippet
?
[]
:
[
snippet
],
},
},
};
};
const
bootstrapForExistingSnippet
=
resp
=>
{
createComponent
({
data
:
{
snippet
,
},
});
if
(
resp
===
500
)
{
axiosMock
.
onGet
(
'
contentApiURL
'
).
reply
(
500
);
}
else
{
axiosMock
.
onGet
(
'
contentApiURL
'
).
reply
(
200
,
contentMock
);
}
wrapper
.
vm
.
onSnippetFetch
(
getResSchema
());
};
const
bootstrapForNewSnippet
=
()
=>
{
createComponent
();
wrapper
.
vm
.
onSnippetFetch
(
getResSchema
(
true
));
};
beforeEach
(()
=>
{
axiosMock
=
new
AxiosMockAdapter
(
axios
);
});
afterEach
(()
=>
{
axiosMock
.
restore
();
});
it
(
'
fetches blob content with the additional query
'
,
()
=>
{
bootstrapForExistingSnippet
();
return
waitForPromises
().
then
(()
=>
{
expect
(
joinPaths
).
toHaveBeenCalledWith
(
'
foo/
'
,
rawPathMock
);
expect
(
wrapper
.
vm
.
newSnippet
).
toBe
(
false
);
expect
(
wrapper
.
vm
.
content
).
toBe
(
contentMock
);
});
});
it
(
'
flashes the error message if fetching content fails
'
,
()
=>
{
bootstrapForExistingSnippet
(
500
);
return
waitForPromises
().
then
(()
=>
{
expect
(
flashSpy
).
toHaveBeenCalled
();
expect
(
wrapper
.
vm
.
content
).
toBe
(
''
);
});
});
it
(
'
does not fetch content for new snippet
'
,
()
=>
{
bootstrapForNewSnippet
();
return
waitForPromises
().
then
(()
=>
{
// we keep using waitForPromises to make sure we do not run failed test
expect
(
wrapper
.
vm
.
newSnippet
).
toBe
(
true
);
expect
(
wrapper
.
vm
.
content
).
toBe
(
''
);
expect
(
joinPaths
).
not
.
toHaveBeenCalled
();
expect
(
wrapper
.
vm
.
snippet
).
toEqual
(
wrapper
.
vm
.
$options
.
newSnippetSchema
);
});
});
});
describe
(
'
form submission handling
'
,
()
=>
{
it
.
each
`
newSnippet | projectPath | mutation | mutationName
${
true
}
|
${
rawProjectPathMock
}
|
${
CreateSnippetMutation
}
|
${
'
CreateSnippetMutation with projectPath
'
}
${
true
}
|
${
''
}
|
${
CreateSnippetMutation
}
|
${
'
CreateSnippetMutation without projectPath
'
}
${
false
}
|
${
rawProjectPathMock
}
|
${
UpdateSnippetMutation
}
|
${
'
UpdateSnippetMutation with projectPath
'
}
${
false
}
|
${
''
}
|
${
UpdateSnippetMutation
}
|
${
'
UpdateSnippetMutation without projectPath
'
}
`
(
'
should submit $mutationName correctly
'
,
({
newSnippet
,
projectPath
,
mutation
})
=>
{
createComponent
({
data
:
{
newSnippet
,
},
props
:
{
...
defaultProps
,
projectPath
,
},
});
const
mutationPayload
=
{
mutation
,
variables
:
{
input
:
newSnippet
?
expect
.
objectContaining
({
projectPath
})
:
expect
.
any
(
Object
),
},
};
wrapper
.
vm
.
handleFormSubmit
();
expect
(
resolveMutate
).
toHaveBeenCalledWith
(
mutationPayload
);
});
it
(
'
redirects to snippet view on successful mutation
'
,
()
=>
{
createComponent
();
wrapper
.
vm
.
handleFormSubmit
();
return
waitForPromises
().
then
(()
=>
{
expect
(
redirectTo
).
toHaveBeenCalledWith
(
newlyEditedSnippetUrl
);
});
});
it
(
'
flashes an error if mutation failed
'
,
()
=>
{
createComponent
({
mutationRes
:
mutationTypes
.
REJECT
,
});
wrapper
.
vm
.
handleFormSubmit
();
return
waitForPromises
().
then
(()
=>
{
expect
(
redirectTo
).
not
.
toHaveBeenCalled
();
expect
(
flashSpy
).
toHaveBeenCalledWith
(
apiError
);
});
});
});
});
});
spec/frontend/vue_shared/components/form/__snapshots__/title_spec.js.snap
View file @
20f7e1a0
...
...
@@ -5,8 +5,6 @@ exports[`Title edit field matches the snapshot 1`] = `
label="Title"
label-for="title-field-edit"
>
<gl-form-input-stub
id="title-field-edit"
/>
<gl-form-input-stub />
</gl-form-group-stub>
`;
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