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
a3ceca88
Commit
a3ceca88
authored
Apr 03, 2020
by
Kushal Pandya
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add support for creating Requirements
Adds UI support for creating requirements from Requirements List page.
parent
20b5232c
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
447 additions
and
22 deletions
+447
-22
ee/app/assets/javascripts/requirements/components/requirement_form.vue
.../javascripts/requirements/components/requirement_form.vue
+85
-0
ee/app/assets/javascripts/requirements/components/requirements_root.vue
...javascripts/requirements/components/requirements_root.vue
+56
-4
ee/app/assets/javascripts/requirements/queries/createRequirement.mutation.graphql
...s/requirements/queries/createRequirement.mutation.graphql
+6
-0
ee/app/assets/javascripts/requirements/requirements_bundle.js
...pp/assets/javascripts/requirements/requirements_bundle.js
+0
-14
ee/app/assets/stylesheets/pages/requirements.scss
ee/app/assets/stylesheets/pages/requirements.scss
+9
-0
ee/spec/features/projects/requirements/requirements_list_spec.rb
.../features/projects/requirements/requirements_list_spec.rb
+36
-4
ee/spec/frontend/requirements/components/requirement_form_spec.js
...frontend/requirements/components/requirement_form_spec.js
+137
-0
ee/spec/frontend/requirements/components/requirements_root_spec.js
...rontend/requirements/components/requirements_root_spec.js
+106
-0
locale/gitlab.pot
locale/gitlab.pot
+12
-0
No files found.
ee/app/assets/javascripts/requirements/components/requirement_form.vue
0 → 100644
View file @
a3ceca88
<
script
>
import
{
GlFormGroup
,
GlFormTextarea
,
GlDeprecatedButton
}
from
'
@gitlab/ui
'
;
import
{
isEmpty
}
from
'
lodash
'
;
import
{
__
}
from
'
~/locale
'
;
export
default
{
components
:
{
GlFormGroup
,
GlFormTextarea
,
GlDeprecatedButton
,
},
props
:
{
requirement
:
{
type
:
Object
,
required
:
false
,
default
:
null
,
},
requirementRequestActive
:
{
type
:
Boolean
,
required
:
true
,
},
},
data
()
{
return
{
isCreate
:
isEmpty
(
this
.
requirement
),
title
:
this
.
requirement
?.
title
||
''
,
};
},
computed
:
{
fieldLabel
()
{
return
this
.
isCreate
?
__
(
'
New requirement
'
)
:
__
(
'
Requirement
'
);
},
saveButtonLabel
()
{
return
this
.
isCreate
?
__
(
'
Create requirement
'
)
:
__
(
'
Save changes
'
);
},
disableSaveButton
()
{
return
this
.
title
===
''
||
this
.
requirementRequestActive
;
},
},
methods
:
{
handleSave
()
{
if
(
this
.
isCreate
)
{
this
.
$emit
(
'
save
'
,
this
.
title
);
}
else
{
this
.
$emit
(
'
save
'
,
{
iid
:
this
.
requirement
.
iid
,
title
:
this
.
title
,
});
}
},
},
};
</
script
>
<
template
>
<div
class=
"requirement-form"
:class=
"
{ 'p-3 border-bottom': isCreate }">
<gl-form-group
:label=
"fieldLabel"
label-for=
"requirementTitle"
>
<gl-form-textarea
id=
"requirementTitle"
v-model.trim=
"title"
autofocus
resize
:disabled=
"requirementRequestActive"
:placeholder=
"__('Describe the requirement here')"
max-rows=
"25"
class=
"requirement-form-textarea"
@
keyup.escape.exact=
"$emit('cancel')"
/>
</gl-form-group>
<div
class=
"d-flex requirement-form-actions"
>
<gl-deprecated-button
:disabled=
"disableSaveButton"
:loading=
"requirementRequestActive"
category=
"primary"
variant=
"success"
class=
"mr-auto js-requirement-save"
@
click=
"handleSave"
>
{{
saveButtonLabel
}}
</gl-deprecated-button
>
<gl-deprecated-button
class=
"js-requirement-cancel"
@
click=
"$emit('cancel')"
>
{{
__
(
'
Cancel
'
)
}}
</gl-deprecated-button>
</div>
</div>
</
template
>
ee/app/assets/javascripts/requirements/components/requirements_root.vue
View file @
a3ceca88
...
@@ -9,7 +9,10 @@ import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
...
@@ -9,7 +9,10 @@ import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import
RequirementsLoading
from
'
./requirements_loading.vue
'
;
import
RequirementsLoading
from
'
./requirements_loading.vue
'
;
import
RequirementsEmptyState
from
'
./requirements_empty_state.vue
'
;
import
RequirementsEmptyState
from
'
./requirements_empty_state.vue
'
;
import
RequirementItem
from
'
./requirement_item.vue
'
;
import
RequirementItem
from
'
./requirement_item.vue
'
;
import
RequirementForm
from
'
./requirement_form.vue
'
;
import
projectRequirements
from
'
../queries/projectRequirements.query.graphql
'
;
import
projectRequirements
from
'
../queries/projectRequirements.query.graphql
'
;
import
createRequirement
from
'
../queries/createRequirement.mutation.graphql
'
;
import
{
FilterState
,
DEFAULT_PAGE_SIZE
}
from
'
../constants
'
;
import
{
FilterState
,
DEFAULT_PAGE_SIZE
}
from
'
../constants
'
;
...
@@ -20,6 +23,7 @@ export default {
...
@@ -20,6 +23,7 @@ export default {
RequirementsLoading
,
RequirementsLoading
,
RequirementsEmptyState
,
RequirementsEmptyState
,
RequirementItem
,
RequirementItem
,
RequirementForm
,
},
},
props
:
{
props
:
{
projectPath
:
{
projectPath
:
{
...
@@ -50,10 +54,6 @@ export default {
...
@@ -50,10 +54,6 @@ export default {
required
:
false
,
required
:
false
,
default
:
''
,
default
:
''
,
},
},
showCreateRequirement
:
{
type
:
Boolean
,
required
:
true
,
},
emptyStatePath
:
{
emptyStatePath
:
{
type
:
String
,
type
:
String
,
required
:
true
,
required
:
true
,
...
@@ -105,6 +105,8 @@ export default {
...
@@ -105,6 +105,8 @@ export default {
},
},
data
()
{
data
()
{
return
{
return
{
showCreateForm
:
false
,
createRequirementRequestActive
:
false
,
currentPage
:
this
.
page
,
currentPage
:
this
.
page
,
prevPageCursor
:
this
.
prev
,
prevPageCursor
:
this
.
prev
,
nextPageCursor
:
this
.
next
,
nextPageCursor
:
this
.
next
,
...
@@ -136,6 +138,16 @@ export default {
...
@@ -136,6 +138,16 @@ export default {
return
nextPage
>
Math
.
ceil
(
this
.
totalRequirements
/
DEFAULT_PAGE_SIZE
)
?
null
:
nextPage
;
return
nextPage
>
Math
.
ceil
(
this
.
totalRequirements
/
DEFAULT_PAGE_SIZE
)
?
null
:
nextPage
;
},
},
},
},
mounted
()
{
document
.
querySelector
(
'
.js-new-requirement
'
)
.
addEventListener
(
'
click
'
,
this
.
handleNewRequirementClick
);
},
beforeDestroy
()
{
document
.
querySelector
(
'
.js-new-requirement
'
)
.
removeEventListener
(
'
click
'
,
this
.
handleNewRequirementClick
);
},
methods
:
{
methods
:
{
/**
/**
* Update browser URL with updated query-param values
* Update browser URL with updated query-param values
...
@@ -166,6 +178,40 @@ export default {
...
@@ -166,6 +178,40 @@ export default {
replace
:
true
,
replace
:
true
,
});
});
},
},
handleNewRequirementClick
()
{
this
.
showCreateForm
=
true
;
},
handleNewRequirementSave
(
title
)
{
this
.
createRequirementRequestActive
=
true
;
return
this
.
$apollo
.
mutate
({
mutation
:
createRequirement
,
variables
:
{
createRequirementInput
:
{
projectPath
:
this
.
projectPath
,
title
,
},
},
})
.
then
(({
data
})
=>
{
if
(
!
data
.
createRequirement
.
errors
.
length
)
{
this
.
showCreateForm
=
false
;
this
.
$apollo
.
queries
.
requirements
.
refetch
();
}
else
{
throw
new
Error
(
`Error creating a requirement`
);
}
})
.
catch
(
e
=>
{
createFlash
(
__
(
'
Something went wrong while creating a requirement.
'
));
Sentry
.
captureException
(
e
);
})
.
finally
(()
=>
{
this
.
createRequirementRequestActive
=
false
;
});
},
handleNewRequirementCancel
()
{
this
.
showCreateForm
=
false
;
},
handlePageChange
(
page
)
{
handlePageChange
(
page
)
{
const
{
startCursor
,
endCursor
}
=
this
.
requirements
.
pageInfo
;
const
{
startCursor
,
endCursor
}
=
this
.
requirements
.
pageInfo
;
...
@@ -202,6 +248,12 @@ export default {
...
@@ -202,6 +248,12 @@ export default {
:current-tab-count=
"totalRequirements"
:current-tab-count=
"totalRequirements"
:current-page=
"currentPage"
:current-page=
"currentPage"
/>
/>
<requirement-form
v-if=
"showCreateForm"
:requirement-request-active=
"createRequirementRequestActive"
@
save=
"handleNewRequirementSave"
@
cancel=
"handleNewRequirementCancel"
/>
<ul
<ul
v-if=
"!requirementsListLoading && !requirementsListEmpty"
v-if=
"!requirementsListLoading && !requirementsListEmpty"
class=
"content-list issuable-list issues-list requirements-list"
class=
"content-list issuable-list issues-list requirements-list"
...
...
ee/app/assets/javascripts/requirements/queries/createRequirement.mutation.graphql
0 → 100644
View file @
a3ceca88
mutation
createRequirement
(
$createRequirementInput
:
CreateRequirementInput
!)
{
createRequirement
(
input
:
$createRequirementInput
)
{
clientMutationId
errors
}
}
ee/app/assets/javascripts/requirements/requirements_bundle.js
View file @
a3ceca88
...
@@ -9,7 +9,6 @@ import { FilterState } from './constants';
...
@@ -9,7 +9,6 @@ import { FilterState } from './constants';
Vue
.
use
(
VueApollo
);
Vue
.
use
(
VueApollo
);
export
default
()
=>
{
export
default
()
=>
{
const
btnNewRequirement
=
document
.
querySelector
(
'
.js-new-requirement
'
);
const
el
=
document
.
getElementById
(
'
js-requirements-app
'
);
const
el
=
document
.
getElementById
(
'
js-requirements-app
'
);
if
(
!
el
)
{
if
(
!
el
)
{
...
@@ -43,7 +42,6 @@ export default () => {
...
@@ -43,7 +42,6 @@ export default () => {
const
ARCHIVED
=
parseInt
(
archived
,
10
);
const
ARCHIVED
=
parseInt
(
archived
,
10
);
return
{
return
{
showCreateRequirement
:
false
,
filterBy
:
stateFilterBy
,
filterBy
:
stateFilterBy
,
requirementsCount
:
{
requirementsCount
:
{
OPENED
,
OPENED
,
...
@@ -57,17 +55,6 @@ export default () => {
...
@@ -57,17 +55,6 @@ export default () => {
projectPath
,
projectPath
,
};
};
},
},
mounted
()
{
btnNewRequirement
.
addEventListener
(
'
click
'
,
this
.
handleClickNewRequirement
);
},
beforeDestroy
()
{
btnNewRequirement
.
removeEventListener
(
'
click
'
,
this
.
handleClickNewRequirement
);
},
methods
:
{
handleClickNewRequirement
()
{
this
.
showCreateRequirement
=
!
this
.
showCreateRequirement
;
},
},
render
(
createElement
)
{
render
(
createElement
)
{
return
createElement
(
'
requirements-root
'
,
{
return
createElement
(
'
requirements-root
'
,
{
props
:
{
props
:
{
...
@@ -77,7 +64,6 @@ export default () => {
...
@@ -77,7 +64,6 @@ export default () => {
page
:
parseInt
(
this
.
page
,
10
)
||
1
,
page
:
parseInt
(
this
.
page
,
10
)
||
1
,
prev
:
this
.
prev
,
prev
:
this
.
prev
,
next
:
this
.
next
,
next
:
this
.
next
,
showCreateRequirement
:
this
.
showCreateRequirement
,
emptyStatePath
:
this
.
emptyStatePath
,
emptyStatePath
:
this
.
emptyStatePath
,
},
},
});
});
...
...
ee/app/assets/stylesheets/pages/requirements.scss
View file @
a3ceca88
...
@@ -11,6 +11,15 @@
...
@@ -11,6 +11,15 @@
}
}
}
}
}
}
.requirement-form
{
.requirement-form-textarea
{
line-height
:
$gl-line-height-24
;
// We need `!important` here as GlFormTextarea (based on `BFormTextarea`)
// somehow applies inline styles ¯\_(ツ)_/¯.
overflow-y
:
auto
!
important
;
}
}
}
}
.requirements-list-container
{
.requirements-list-container
{
...
...
ee/spec/features/projects/requirements/requirements_list_spec.rb
View file @
a3ceca88
...
@@ -45,6 +45,7 @@ describe 'Requirements list', :js do
...
@@ -45,6 +45,7 @@ describe 'Requirements list', :js do
end
end
end
end
context
'new requirement'
do
it
'shows button "New requirement"'
do
it
'shows button "New requirement"'
do
page
.
within
(
'.nav-controls'
)
do
page
.
within
(
'.nav-controls'
)
do
expect
(
page
).
to
have_selector
(
'button.js-new-requirement'
)
expect
(
page
).
to
have_selector
(
'button.js-new-requirement'
)
...
@@ -52,6 +53,37 @@ describe 'Requirements list', :js do
...
@@ -52,6 +53,37 @@ describe 'Requirements list', :js do
end
end
end
end
it
'shows requirement create form when "New requirement" button is clicked'
do
page
.
within
(
'.nav-controls'
)
do
find
(
'button.js-new-requirement'
).
click
end
page
.
within
(
'.requirements-list-container'
)
do
expect
(
page
).
to
have_selector
(
'.requirement-form'
)
end
end
it
'creates new requirement'
do
page
.
within
(
'.nav-controls'
)
do
find
(
'button.js-new-requirement'
).
click
end
page
.
within
(
'.requirements-list-container'
)
do
requirement_title
=
'Foobar'
find
(
'textarea#requirementTitle'
).
native
.
send_keys
requirement_title
find
(
'button.js-requirement-save'
).
click
wait_for_all_requests
expect
(
page
).
to
have_selector
(
'li.requirement'
,
count:
4
)
page
.
within
(
'.requirements-list li.requirement'
,
match: :first
)
do
expect
(
page
.
find
(
'.issue-title-text'
)).
to
have_content
(
requirement_title
)
end
end
end
end
context
'open tab'
do
context
'open tab'
do
it
'shows list of all open requirements'
do
it
'shows list of all open requirements'
do
page
.
within
(
'.requirements-list-container .requirements-list'
)
do
page
.
within
(
'.requirements-list-container .requirements-list'
)
do
...
...
ee/spec/frontend/requirements/components/requirement_form_spec.js
0 → 100644
View file @
a3ceca88
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlFormGroup
,
GlFormTextarea
}
from
'
@gitlab/ui
'
;
import
RequirementForm
from
'
ee/requirements/components/requirement_form.vue
'
;
import
{
mockRequirementsOpen
}
from
'
../mock_data
'
;
const
createComponent
=
({
requirement
=
null
,
requirementRequestActive
=
false
}
=
{})
=>
shallowMount
(
RequirementForm
,
{
propsData
:
{
requirement
,
requirementRequestActive
,
},
});
describe
(
'
RequirementForm
'
,
()
=>
{
let
wrapper
;
let
wrapperWithRequirement
;
beforeEach
(()
=>
{
wrapper
=
createComponent
();
wrapperWithRequirement
=
createComponent
({
requirement
:
mockRequirementsOpen
[
0
],
});
});
afterEach
(()
=>
{
wrapper
.
destroy
();
wrapperWithRequirement
.
destroy
();
});
describe
(
'
computed
'
,
()
=>
{
describe
(
'
fieldLabel
'
,
()
=>
{
it
(
'
returns string "New requirement" when `requirement` prop is null
'
,
()
=>
{
expect
(
wrapper
.
vm
.
fieldLabel
).
toBe
(
'
New requirement
'
);
});
it
(
'
returns string "Requirement" when `requirement` prop is defined
'
,
()
=>
{
expect
(
wrapperWithRequirement
.
vm
.
fieldLabel
).
toBe
(
'
Requirement
'
);
});
});
describe
(
'
saveButtonLabel
'
,
()
=>
{
it
(
'
returns string "Create requirement" when `requirement` prop is null
'
,
()
=>
{
expect
(
wrapper
.
vm
.
saveButtonLabel
).
toBe
(
'
Create requirement
'
);
});
it
(
'
returns string "Save changes" when `requirement` prop is defined
'
,
()
=>
{
expect
(
wrapperWithRequirement
.
vm
.
saveButtonLabel
).
toBe
(
'
Save changes
'
);
});
});
});
describe
(
'
methods
'
,
()
=>
{
describe
(
'
handleSave
'
,
()
=>
{
it
(
'
emits `save` event on component with `title` as param when form is in create mode
'
,
()
=>
{
wrapper
.
setData
({
title
:
'
foo
'
,
});
wrapper
.
vm
.
handleSave
();
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
emitted
(
'
save
'
)).
toBeTruthy
();
expect
(
wrapper
.
emitted
(
'
save
'
)[
0
]).
toEqual
([
'
foo
'
]);
});
});
it
(
'
emits `save` event on component with object as param containing `iid` & `title` when form is in update mode
'
,
()
=>
{
wrapperWithRequirement
.
vm
.
handleSave
();
return
wrapperWithRequirement
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapperWithRequirement
.
emitted
(
'
save
'
)).
toBeTruthy
();
expect
(
wrapperWithRequirement
.
emitted
(
'
save
'
)[
0
]).
toEqual
([
{
iid
:
mockRequirementsOpen
[
0
].
iid
,
title
:
mockRequirementsOpen
[
0
].
title
,
},
]);
});
});
});
});
describe
(
'
template
'
,
()
=>
{
it
(
'
renders component container element with classes `p-3 border-bottom` when form is in create mode
'
,
()
=>
{
const
wrapperClasses
=
wrapper
.
classes
();
expect
(
wrapperClasses
).
toContain
(
'
p-3
'
);
expect
(
wrapperClasses
).
toContain
(
'
border-bottom
'
);
});
it
(
'
renders component container element without classes `p-3 border-bottom` when form is in edit mode
'
,
()
=>
{
const
wrapperClasses
=
wrapperWithRequirement
.
classes
();
expect
(
wrapperClasses
).
not
.
toContain
(
'
p-3
'
);
expect
(
wrapperClasses
).
not
.
toContain
(
'
border-bottom
'
);
});
it
(
'
renders gl-form-group component
'
,
()
=>
{
const
glFormGroup
=
wrapper
.
find
(
GlFormGroup
);
expect
(
glFormGroup
.
exists
()).
toBe
(
true
);
expect
(
glFormGroup
.
attributes
(
'
label
'
)).
toBe
(
'
New requirement
'
);
expect
(
glFormGroup
.
attributes
(
'
label-for
'
)).
toBe
(
'
requirementTitle
'
);
});
it
(
'
renders gl-form-textarea component
'
,
()
=>
{
const
glFormTextarea
=
wrapper
.
find
(
GlFormTextarea
);
expect
(
glFormTextarea
.
exists
()).
toBe
(
true
);
expect
(
glFormTextarea
.
attributes
(
'
id
'
)).
toBe
(
'
requirementTitle
'
);
expect
(
glFormTextarea
.
attributes
(
'
placeholder
'
)).
toBe
(
'
Describe the requirement here
'
);
expect
(
glFormTextarea
.
attributes
(
'
max-rows
'
)).
toBe
(
'
25
'
);
});
it
(
'
renders gl-form-textarea component populated with `requirement.title` when `requirement` prop is defined
'
,
()
=>
{
expect
(
wrapperWithRequirement
.
find
(
GlFormTextarea
).
attributes
(
'
value
'
)).
toBe
(
mockRequirementsOpen
[
0
].
title
,
);
});
it
(
'
renders save button component
'
,
()
=>
{
const
saveButton
=
wrapper
.
find
(
'
.js-requirement-save
'
);
expect
(
saveButton
.
exists
()).
toBe
(
true
);
expect
(
saveButton
.
text
()).
toBe
(
'
Create requirement
'
);
});
it
(
'
renders cancel button component
'
,
()
=>
{
const
cancelButton
=
wrapper
.
find
(
'
.js-requirement-cancel
'
);
expect
(
cancelButton
.
exists
()).
toBe
(
true
);
expect
(
cancelButton
.
text
()).
toBe
(
'
Cancel
'
);
});
});
});
ee/spec/frontend/requirements/components/requirements_root_spec.js
View file @
a3ceca88
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlPagination
}
from
'
@gitlab/ui
'
;
import
{
GlPagination
}
from
'
@gitlab/ui
'
;
import
createFlash
from
'
~/flash
'
;
import
RequirementsRoot
from
'
ee/requirements/components/requirements_root.vue
'
;
import
RequirementsRoot
from
'
ee/requirements/components/requirements_root.vue
'
;
import
RequirementsLoading
from
'
ee/requirements/components/requirements_loading.vue
'
;
import
RequirementsLoading
from
'
ee/requirements/components/requirements_loading.vue
'
;
import
RequirementsEmptyState
from
'
ee/requirements/components/requirements_empty_state.vue
'
;
import
RequirementsEmptyState
from
'
ee/requirements/components/requirements_empty_state.vue
'
;
import
RequirementItem
from
'
ee/requirements/components/requirement_item.vue
'
;
import
RequirementItem
from
'
ee/requirements/components/requirement_item.vue
'
;
import
RequirementForm
from
'
ee/requirements/components/requirement_form.vue
'
;
import
createRequirement
from
'
ee/requirements/queries/createRequirement.mutation.graphql
'
;
import
{
import
{
FilterState
,
FilterState
,
...
@@ -17,6 +22,8 @@ jest.mock('ee/requirements/constants', () => ({
...
@@ -17,6 +22,8 @@ jest.mock('ee/requirements/constants', () => ({
DEFAULT_PAGE_SIZE
:
2
,
DEFAULT_PAGE_SIZE
:
2
,
}));
}));
jest
.
mock
(
'
~/flash
'
);
const
createComponent
=
({
const
createComponent
=
({
projectPath
=
'
gitlab-org/gitlab-shell
'
,
projectPath
=
'
gitlab-org/gitlab-shell
'
,
filterBy
=
FilterState
.
opened
,
filterBy
=
FilterState
.
opened
,
...
@@ -41,8 +48,10 @@ const createComponent = ({
...
@@ -41,8 +48,10 @@ const createComponent = ({
list
:
[],
list
:
[],
pageInfo
:
{},
pageInfo
:
{},
count
:
{},
count
:
{},
refetch
:
jest
.
fn
(),
},
},
},
},
mutate
:
jest
.
fn
(),
},
},
},
},
});
});
...
@@ -51,6 +60,7 @@ describe('RequirementsRoot', () => {
...
@@ -51,6 +60,7 @@ describe('RequirementsRoot', () => {
let
wrapper
;
let
wrapper
;
beforeEach
(()
=>
{
beforeEach
(()
=>
{
setFixtures
(
'
<button class="js-new-requirement">New requirement</button>
'
);
wrapper
=
createComponent
();
wrapper
=
createComponent
();
});
});
...
@@ -148,6 +158,92 @@ describe('RequirementsRoot', () => {
...
@@ -148,6 +158,92 @@ describe('RequirementsRoot', () => {
});
});
});
});
describe
(
'
handleNewRequirementClick
'
,
()
=>
{
it
(
'
sets `showCreateForm` prop to `true`
'
,
()
=>
{
wrapper
.
vm
.
handleNewRequirementClick
();
expect
(
wrapper
.
vm
.
showCreateForm
).
toBe
(
true
);
});
});
describe
(
'
handleNewRequirementSave
'
,
()
=>
{
const
mockMutationResult
=
{
data
:
{
createRequirement
:
{
errors
:
[],
},
},
};
it
(
'
sets `createRequirementRequestActive` prop to `true`
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
)
.
mockReturnValue
(
Promise
.
resolve
(
mockMutationResult
));
wrapper
.
vm
.
handleNewRequirementSave
(
'
foo
'
);
expect
(
wrapper
.
vm
.
createRequirementRequestActive
).
toBe
(
true
);
});
it
(
'
calls `$apollo.mutate` with createRequirement mutation and `projectPath` & `title` as variables
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
)
.
mockReturnValue
(
Promise
.
resolve
(
mockMutationResult
));
wrapper
.
vm
.
handleNewRequirementSave
(
'
foo
'
);
expect
(
wrapper
.
vm
.
$apollo
.
mutate
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
mutation
:
createRequirement
,
variables
:
{
createRequirementInput
:
{
projectPath
:
'
gitlab-org/gitlab-shell
'
,
title
:
'
foo
'
,
},
},
}),
);
});
it
(
'
sets `showCreateForm` and `createRequirementRequestActive` props to `false` and calls `$apollo.queries.requirements.refetch()` when request is successful
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
)
.
mockReturnValue
(
Promise
.
resolve
(
mockMutationResult
));
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
.
queries
.
requirements
,
'
refetch
'
)
.
mockImplementation
(
jest
.
fn
());
return
wrapper
.
vm
.
handleNewRequirementSave
(
'
foo
'
).
then
(()
=>
{
expect
(
wrapper
.
vm
.
showCreateForm
).
toBe
(
false
);
expect
(
wrapper
.
vm
.
$apollo
.
queries
.
requirements
.
refetch
).
toHaveBeenCalled
();
expect
(
wrapper
.
vm
.
createRequirementRequestActive
).
toBe
(
false
);
});
});
it
(
'
sets `createRequirementRequestActive` prop to `false` and calls `createFlash` when `$apollo.mutate` request fails
'
,
()
=>
{
jest
.
spyOn
(
wrapper
.
vm
.
$apollo
,
'
mutate
'
).
mockReturnValue
(
Promise
.
reject
(
new
Error
()));
return
wrapper
.
vm
.
handleNewRequirementSave
(
'
foo
'
).
then
(()
=>
{
expect
(
createFlash
).
toHaveBeenCalledWith
(
'
Something went wrong while creating a requirement.
'
,
);
expect
(
wrapper
.
vm
.
createRequirementRequestActive
).
toBe
(
false
);
});
});
});
describe
(
'
handleNewRequirementCancel
'
,
()
=>
{
it
(
'
sets `showCreateForm` prop to `false`
'
,
()
=>
{
wrapper
.
setData
({
showCreateForm
:
true
,
});
wrapper
.
vm
.
handleNewRequirementCancel
();
expect
(
wrapper
.
vm
.
showCreateForm
).
toBe
(
false
);
});
});
describe
(
'
handlePageChange
'
,
()
=>
{
describe
(
'
handlePageChange
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
jest
.
spyOn
(
wrapper
.
vm
,
'
updateUrl
'
).
mockImplementation
(
jest
.
fn
());
jest
.
spyOn
(
wrapper
.
vm
,
'
updateUrl
'
).
mockImplementation
(
jest
.
fn
());
...
@@ -208,6 +304,16 @@ describe('RequirementsRoot', () => {
...
@@ -208,6 +304,16 @@ describe('RequirementsRoot', () => {
wrapperLoading
.
destroy
();
wrapperLoading
.
destroy
();
});
});
it
(
'
renders requirement-form component when `showCreateForm` prop is `true`
'
,
()
=>
{
wrapper
.
setData
({
showCreateForm
:
true
,
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
RequirementForm
).
exists
()).
toBe
(
true
);
});
});
it
(
'
renders requirement items for all the requirements
'
,
()
=>
{
it
(
'
renders requirement items for all the requirements
'
,
()
=>
{
wrapper
.
setData
({
wrapper
.
setData
({
requirements
:
{
requirements
:
{
...
...
locale/gitlab.pot
View file @
a3ceca88
...
@@ -5931,6 +5931,9 @@ msgstr ""
...
@@ -5931,6 +5931,9 @@ msgstr ""
msgid "Create project label"
msgid "Create project label"
msgstr ""
msgstr ""
msgid "Create requirement"
msgstr ""
msgid "Create wildcard: %{searchTerm}"
msgid "Create wildcard: %{searchTerm}"
msgstr ""
msgstr ""
...
@@ -6846,6 +6849,9 @@ msgstr ""
...
@@ -6846,6 +6849,9 @@ msgstr ""
msgid "Describe the goal of the changes and what reviewers should be aware of."
msgid "Describe the goal of the changes and what reviewers should be aware of."
msgstr ""
msgstr ""
msgid "Describe the requirement here"
msgstr ""
msgid "Description"
msgid "Description"
msgstr ""
msgstr ""
...
@@ -16973,6 +16979,9 @@ msgstr ""
...
@@ -16973,6 +16979,9 @@ msgstr ""
msgid "Require users to prove ownership of custom domains"
msgid "Require users to prove ownership of custom domains"
msgstr ""
msgstr ""
msgid "Requirement"
msgstr ""
msgid "Requirements"
msgid "Requirements"
msgstr ""
msgstr ""
...
@@ -18632,6 +18641,9 @@ msgstr ""
...
@@ -18632,6 +18641,9 @@ msgstr ""
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr ""
msgstr ""
msgid "Something went wrong while creating a requirement."
msgstr ""
msgid "Something went wrong while deleting description changes. Please try again."
msgid "Something went wrong while deleting description changes. Please try again."
msgstr ""
msgstr ""
...
...
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