BigW Consortium Gitlab
Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
G
gitlab-ce
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Forest Godfrey
gitlab-ce
Commits
332aa756
Commit
332aa756
authored
Jan 26, 2018
by
Eric Eastwood
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor CI variable list code for usage with CI/CD settings page secret variables
Part of
https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4110
parent
03f386c2
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
660 additions
and
346 deletions
+660
-346
secret_values.js
app/assets/javascripts/behaviors/secret_values.js
+5
-3
ci_variable_list.js
app/assets/javascripts/ci_variable_list/ci_variable_list.js
+205
-0
native_form_variable_list.js
...javascripts/ci_variable_list/native_form_variable_list.js
+26
-0
polyfills.js
app/assets/javascripts/commons/polyfills.js
+2
-0
pipeline_schedule_form_bundle.js
...ripts/pipeline_schedules/pipeline_schedule_form_bundle.js
+5
-2
setup_pipeline_variable_list.js
...cripts/pipeline_schedules/setup_pipeline_variable_list.js
+0
-73
toggle_buttons.js
app/assets/javascripts/toggle_buttons.js
+4
-4
framework.scss
app/assets/stylesheets/framework.scss
+1
-0
buttons.scss
app/assets/stylesheets/framework/buttons.scss
+5
-0
ci_variable_list.scss
app/assets/stylesheets/framework/ci_variable_list.scss
+88
-0
variables.scss
app/assets/stylesheets/framework/variables.scss
+2
-2
pipeline_schedules.scss
app/assets/stylesheets/pages/pipeline_schedules.scss
+0
-81
_variable_row.html.haml
app/views/ci/variables/_variable_row.html.haml
+49
-0
_form.html.haml
app/views/projects/pipeline_schedules/_form.html.haml
+11
-5
_variable_row.html.haml
...views/projects/pipeline_schedules/_variable_row.html.haml
+0
-17
refactor-ci-variable-list-for-future-usage-in-4110.yml
...ed/refactor-ci-variable-list-for-future-usage-in-4110.yml
+5
-0
pipeline_schedules_spec.rb
spec/features/projects/pipeline_schedules_spec.rb
+16
-14
ci_variable_list_spec.js
spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+163
-0
native_form_variable_list_spec.js
...cripts/ci_variable_list/native_form_variable_list_spec.js
+30
-0
pipeline_schedules.rb
spec/javascripts/fixtures/pipeline_schedules.rb
+43
-0
setup_pipeline_variable_list_spec.js
...s/pipeline_schedules/setup_pipeline_variable_list_spec.js
+0
-145
No files found.
app/assets/javascripts/behaviors/secret_values.js
View file @
332aa756
...
...
@@ -15,10 +15,12 @@ export default class SecretValues {
init
()
{
this
.
revealButton
=
this
.
container
.
querySelector
(
'.js-secret-value-reveal-button'
);
const
isRevealed
=
convertPermissionToBoolean
(
this
.
revealButton
.
dataset
.
secretRevealStatus
);
this
.
updateDom
(
isRevealed
);
if
(
this
.
revealButton
)
{
const
isRevealed
=
convertPermissionToBoolean
(
this
.
revealButton
.
dataset
.
secretRevealStatus
);
this
.
updateDom
(
isRevealed
);
this
.
revealButton
.
addEventListener
(
'click'
,
this
.
onRevealButtonClicked
.
bind
(
this
));
this
.
revealButton
.
addEventListener
(
'click'
,
this
.
onRevealButtonClicked
.
bind
(
this
));
}
}
onRevealButtonClicked
()
{
...
...
app/assets/javascripts/ci_variable_list/ci_variable_list.js
0 → 100644
View file @
332aa756
import
$
from
'jquery'
;
import
{
convertPermissionToBoolean
}
from
'../lib/utils/common_utils'
;
import
{
s__
}
from
'../locale'
;
import
setupToggleButtons
from
'../toggle_buttons'
;
import
CreateItemDropdown
from
'../create_item_dropdown'
;
import
SecretValues
from
'../behaviors/secret_values'
;
const
ALL_ENVIRONMENTS_STRING
=
s__
(
'CiVariable|All environments'
);
function
createEnvironmentItem
(
value
)
{
return
{
title
:
value
===
'*'
?
ALL_ENVIRONMENTS_STRING
:
value
,
id
:
value
,
text
:
value
,
};
}
export
default
class
VariableList
{
constructor
({
container
,
formField
,
})
{
this
.
$container
=
$
(
container
);
this
.
formField
=
formField
;
this
.
environmentDropdownMap
=
new
WeakMap
();
this
.
inputMap
=
{
id
:
{
selector
:
'.js-ci-variable-input-id'
,
default
:
''
,
},
key
:
{
selector
:
'.js-ci-variable-input-key'
,
default
:
''
,
},
value
:
{
selector
:
'.js-ci-variable-input-value'
,
default
:
''
,
},
protected
:
{
selector
:
'.js-ci-variable-input-protected'
,
default
:
'true'
,
},
environment
:
{
// We can't use a `.js-` class here because
// gl_dropdown replaces the <input> and doesn't copy over the class
// See https://gitlab.com/gitlab-org/gitlab-ce/issues/42458
selector
:
`input[name="
${
this
.
formField
}
[variables_attributes][][environment]"]`
,
default
:
'*'
,
},
_destroy
:
{
selector
:
'.js-ci-variable-input-destroy'
,
default
:
''
,
},
};
this
.
secretValues
=
new
SecretValues
({
container
:
this
.
$container
[
0
],
valueSelector
:
'.js-row:not(:last-child) .js-secret-value'
,
placeholderSelector
:
'.js-row:not(:last-child) .js-secret-value-placeholder'
,
});
}
init
()
{
this
.
bindEvents
();
this
.
secretValues
.
init
();
}
bindEvents
()
{
this
.
$container
.
find
(
'.js-row'
).
each
((
index
,
rowEl
)
=>
{
this
.
initRow
(
rowEl
);
});
this
.
$container
.
on
(
'click'
,
'.js-row-remove-button'
,
(
e
)
=>
{
e
.
preventDefault
();
this
.
removeRow
(
$
(
e
.
currentTarget
).
closest
(
'.js-row'
));
});
const
inputSelector
=
Object
.
keys
(
this
.
inputMap
)
.
map
(
name
=>
this
.
inputMap
[
name
].
selector
)
.
join
(
','
);
// Remove any empty rows except the last row
this
.
$container
.
on
(
'blur'
,
inputSelector
,
(
e
)
=>
{
const
$row
=
$
(
e
.
currentTarget
).
closest
(
'.js-row'
);
if
(
$row
.
is
(
':not(:last-child)'
)
&&
!
this
.
checkIfRowTouched
(
$row
))
{
this
.
removeRow
(
$row
);
}
});
// Always make sure there is an empty last row
this
.
$container
.
on
(
'input trigger-change'
,
inputSelector
,
()
=>
{
const
$lastRow
=
this
.
$container
.
find
(
'.js-row'
).
last
();
if
(
this
.
checkIfRowTouched
(
$lastRow
))
{
this
.
insertRow
(
$lastRow
);
}
});
}
initRow
(
rowEl
)
{
const
$row
=
$
(
rowEl
);
setupToggleButtons
(
$row
[
0
]);
const
$environmentSelect
=
$row
.
find
(
'.js-variable-environment-toggle'
);
if
(
$environmentSelect
.
length
)
{
const
createItemDropdown
=
new
CreateItemDropdown
({
$dropdown
:
$environmentSelect
,
defaultToggleLabel
:
ALL_ENVIRONMENTS_STRING
,
fieldName
:
`
${
this
.
formField
}
[variables_attributes][][environment]`
,
getData
:
(
term
,
callback
)
=>
callback
(
this
.
getEnvironmentValues
()),
createNewItemFromValue
:
createEnvironmentItem
,
onSelect
:
()
=>
{
// Refresh the other dropdowns in the variable list
// so they have the new value we just picked
this
.
refreshDropdownData
();
$row
.
find
(
this
.
inputMap
.
environment
.
selector
).
trigger
(
'trigger-change'
);
},
});
// Clear out any data that might have been left-over from the row clone
createItemDropdown
.
clearDropdown
();
this
.
environmentDropdownMap
.
set
(
$row
[
0
],
createItemDropdown
);
}
}
insertRow
(
$row
)
{
const
$rowClone
=
$row
.
clone
();
$rowClone
.
removeAttr
(
'data-is-persisted'
);
// Reset the inputs to their defaults
Object
.
keys
(
this
.
inputMap
).
forEach
((
name
)
=>
{
const
entry
=
this
.
inputMap
[
name
];
$rowClone
.
find
(
entry
.
selector
).
val
(
entry
.
default
);
});
this
.
initRow
(
$rowClone
);
$row
.
after
(
$rowClone
);
}
removeRow
(
$row
)
{
const
isPersisted
=
convertPermissionToBoolean
(
$row
.
attr
(
'data-is-persisted'
));
if
(
isPersisted
)
{
$row
.
hide
();
$row
// eslint-disable-next-line no-underscore-dangle
.
find
(
this
.
inputMap
.
_destroy
.
selector
)
.
val
(
true
);
}
else
{
$row
.
remove
();
}
}
checkIfRowTouched
(
$row
)
{
return
Object
.
keys
(
this
.
inputMap
).
some
((
name
)
=>
{
const
entry
=
this
.
inputMap
[
name
];
const
$el
=
$row
.
find
(
entry
.
selector
);
return
$el
.
length
&&
$el
.
val
()
!==
entry
.
default
;
});
}
getAllData
()
{
// Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems.
const
validRows
=
this
.
$container
.
find
(
'.js-row'
).
toArray
().
slice
(
0
,
-
1
);
return
validRows
.
map
((
rowEl
)
=>
{
const
resultant
=
{};
Object
.
keys
(
this
.
inputMap
).
forEach
((
name
)
=>
{
const
entry
=
this
.
inputMap
[
name
];
const
$input
=
$
(
rowEl
).
find
(
entry
.
selector
);
if
(
$input
.
length
)
{
resultant
[
name
]
=
$input
.
val
();
}
});
return
resultant
;
});
}
getEnvironmentValues
()
{
const
valueMap
=
this
.
$container
.
find
(
this
.
inputMap
.
environment
.
selector
).
toArray
()
.
reduce
((
prevValueMap
,
envInput
)
=>
({
...
prevValueMap
,
[
envInput
.
value
]:
envInput
.
value
,
}),
{});
return
Object
.
keys
(
valueMap
).
map
(
createEnvironmentItem
);
}
refreshDropdownData
()
{
this
.
$container
.
find
(
'.js-row'
).
each
((
index
,
rowEl
)
=>
{
const
environmentDropdown
=
this
.
environmentDropdownMap
.
get
(
rowEl
);
if
(
environmentDropdown
)
{
environmentDropdown
.
refreshData
();
}
});
}
}
app/assets/javascripts/ci_variable_list/native_form_variable_list.js
0 → 100644
View file @
332aa756
import
VariableList
from
'./ci_variable_list'
;
// Used for the variable list on scheduled pipeline edit page
export
default
function
setupNativeFormVariableList
({
container
,
formField
=
'variables'
,
})
{
const
$container
=
$
(
container
);
const
variableList
=
new
VariableList
({
container
:
$container
,
formField
,
});
variableList
.
init
();
// Clear out the names in the empty last row so it
// doesn't get submitted and throw validation errors
$container
.
closest
(
'form'
).
on
(
'submit trigger-submit'
,
()
=>
{
const
$lastRow
=
$container
.
find
(
'.js-row'
).
last
();
const
isTouched
=
variableList
.
checkIfRowTouched
(
$lastRow
);
if
(
!
isTouched
)
{
$lastRow
.
find
(
'input, textarea'
).
attr
(
'name'
,
''
);
}
});
}
app/assets/javascripts/commons/polyfills.js
View file @
332aa756
...
...
@@ -8,6 +8,8 @@ import 'core-js/fn/promise';
import
'core-js/fn/string/code-point-at'
;
import
'core-js/fn/string/from-code-point'
;
import
'core-js/fn/symbol'
;
import
'core-js/es6/map'
;
import
'core-js/es6/weak-map'
;
// Browser polyfills
import
'classlist-polyfill'
;
...
...
app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js
View file @
332aa756
...
...
@@ -4,7 +4,7 @@ import GlFieldErrors from '../gl_field_errors';
import
intervalPatternInput
from
'./components/interval_pattern_input.vue'
;
import
TimezoneDropdown
from
'./components/timezone_dropdown'
;
import
TargetBranchDropdown
from
'./components/target_branch_dropdown'
;
import
{
setupPipelineVariableList
}
from
'./setup_pipeline
_variable_list'
;
import
setupNativeFormVariableList
from
'../ci_variable_list/native_form
_variable_list'
;
Vue
.
use
(
Translate
);
...
...
@@ -42,5 +42,8 @@ document.addEventListener('DOMContentLoaded', () => {
gl
.
targetBranchDropdown
=
new
TargetBranchDropdown
();
gl
.
pipelineScheduleFieldErrors
=
new
GlFieldErrors
(
formElement
);
setupPipelineVariableList
(
$
(
'.js-pipeline-variable-list'
));
setupNativeFormVariableList
({
container
:
$
(
'.js-ci-variable-list-section'
),
formField
:
'schedule'
,
});
});
app/assets/javascripts/pipeline_schedules/setup_pipeline_variable_list.js
deleted
100644 → 0
View file @
03f386c2
import
{
convertPermissionToBoolean
}
from
'../lib/utils/common_utils'
;
function
insertRow
(
$row
)
{
const
$rowClone
=
$row
.
clone
();
$rowClone
.
removeAttr
(
'data-is-persisted'
);
$rowClone
.
find
(
'input, textarea'
).
val
(
''
);
$row
.
after
(
$rowClone
);
}
function
removeRow
(
$row
)
{
const
isPersisted
=
convertPermissionToBoolean
(
$row
.
attr
(
'data-is-persisted'
));
if
(
isPersisted
)
{
$row
.
hide
();
$row
.
find
(
'.js-destroy-input'
)
.
val
(
1
);
}
else
{
$row
.
remove
();
}
}
function
checkIfRowTouched
(
$row
)
{
return
$row
.
find
(
'.js-user-input'
).
toArray
().
some
(
el
=>
$
(
el
).
val
().
length
>
0
);
}
function
setupPipelineVariableList
(
parent
=
document
)
{
const
$parent
=
$
(
parent
);
$parent
.
on
(
'click'
,
'.js-row-remove-button'
,
(
e
)
=>
{
const
$row
=
$
(
e
.
currentTarget
).
closest
(
'.js-row'
);
removeRow
(
$row
);
e
.
preventDefault
();
});
// Remove any empty rows except the last r
$parent
.
on
(
'blur'
,
'.js-user-input'
,
(
e
)
=>
{
const
$row
=
$
(
e
.
currentTarget
).
closest
(
'.js-row'
);
const
isTouched
=
checkIfRowTouched
(
$row
);
if
(
$row
.
is
(
':not(:last-child)'
)
&&
!
isTouched
)
{
removeRow
(
$row
);
}
});
// Always make sure there is an empty last row
$parent
.
on
(
'input'
,
'.js-user-input'
,
()
=>
{
const
$lastRow
=
$parent
.
find
(
'.js-row'
).
last
();
const
isTouched
=
checkIfRowTouched
(
$lastRow
);
if
(
isTouched
)
{
insertRow
(
$lastRow
);
}
});
// Clear out the empty last row so it
// doesn't get submitted and throw validation errors
$parent
.
closest
(
'form'
).
on
(
'submit'
,
()
=>
{
const
$lastRow
=
$parent
.
find
(
'.js-row'
).
last
();
const
isTouched
=
checkIfRowTouched
(
$lastRow
);
if
(
!
isTouched
)
{
$lastRow
.
find
(
'input, textarea'
).
attr
(
'name'
,
''
);
}
});
}
export
{
setupPipelineVariableList
,
insertRow
,
removeRow
,
};
app/assets/javascripts/toggle_buttons.js
View file @
332aa756
...
...
@@ -13,7 +13,7 @@ import { convertPermissionToBoolean } from './lib/utils/common_utils';
```
*/
function
update
t
oggle
(
toggle
,
isOn
)
{
function
update
T
oggle
(
toggle
,
isOn
)
{
toggle
.
classList
.
toggle
(
'is-checked'
,
isOn
);
}
...
...
@@ -21,7 +21,7 @@ function onToggleClicked(toggle, input, clickCallback) {
const
previousIsOn
=
convertPermissionToBoolean
(
input
.
value
);
// Visually change the toggle and start loading
update
t
oggle
(
toggle
,
!
previousIsOn
);
update
T
oggle
(
toggle
,
!
previousIsOn
);
toggle
.
setAttribute
(
'disabled'
,
true
);
toggle
.
classList
.
toggle
(
'is-loading'
,
true
);
...
...
@@ -32,7 +32,7 @@ function onToggleClicked(toggle, input, clickCallback) {
})
.
catch
(()
=>
{
// Revert the visuals if something goes wrong
update
t
oggle
(
toggle
,
previousIsOn
);
update
T
oggle
(
toggle
,
previousIsOn
);
})
.
then
(()
=>
{
// Remove the loading indicator in any case
...
...
@@ -54,7 +54,7 @@ export default function setupToggleButtons(container, clickCallback = () => {})
const
isOn
=
convertPermissionToBoolean
(
input
.
value
);
// Get the visible toggle in sync with the hidden input
update
t
oggle
(
toggle
,
isOn
);
update
T
oggle
(
toggle
,
isOn
);
toggle
.
addEventListener
(
'click'
,
onToggleClicked
.
bind
(
null
,
toggle
,
input
,
clickCallback
));
});
...
...
app/assets/stylesheets/framework.scss
View file @
332aa756
...
...
@@ -60,3 +60,4 @@
@import
"framework/memory_graph"
;
@import
"framework/responsive_tables"
;
@import
"framework/stacked-progress-bar"
;
@import
"framework/ci_variable_list"
;
app/assets/stylesheets/framework/buttons.scss
View file @
332aa756
...
...
@@ -176,6 +176,11 @@
&
.btn-remove
{
@include
btn-outline
(
$white-light
,
$red-500
,
$red-500
,
$red-500
,
$white-light
,
$red-600
,
$red-600
,
$red-700
);
}
&
.btn-primary
,
&
.btn-info
{
@include
btn-outline
(
$white-light
,
$blue-500
,
$blue-500
,
$blue-500
,
$white-light
,
$blue-600
,
$blue-600
,
$blue-700
);
}
}
&
.btn-gray
{
...
...
app/assets/stylesheets/framework/ci_variable_list.scss
0 → 100644
View file @
332aa756
.ci-variable-list
{
margin-left
:
0
;
margin-bottom
:
0
;
padding-left
:
0
;
list-style
:
none
;
clear
:
both
;
}
.ci-variable-row
{
display
:
flex
;
align-items
:
flex-end
;
&
:not
(
:last-child
)
{
margin-bottom
:
$gl-btn-padding
;
@media
(
max-width
:
$screen-xs-max
)
{
margin-bottom
:
3
*
$gl-btn-padding
;
}
}
&
:last-child
{
.ci-variable-body-item
:last-child
{
margin-right
:
$ci-variable-remove-button-width
;
@media
(
max-width
:
$screen-xs-max
)
{
margin-right
:
0
;
}
}
.ci-variable-row-remove-button
{
display
:
none
;
}
@media
(
max-width
:
$screen-xs-max
)
{
.ci-variable-row-body
{
margin-right
:
$ci-variable-remove-button-width
;
}
}
}
}
.ci-variable-row-body
{
display
:
flex
;
width
:
100%
;
@media
(
max-width
:
$screen-xs-max
)
{
display
:
block
;
}
}
.ci-variable-body-item
{
flex
:
1
;
&
:not
(
:last-child
)
{
margin-right
:
$gl-btn-padding
;
@media
(
max-width
:
$screen-xs-max
)
{
margin-right
:
0
;
margin-bottom
:
$gl-btn-padding
;
}
}
}
.ci-variable-protected-item
{
flex
:
0
1
auto
;
display
:
flex
;
align-items
:
center
;
}
.ci-variable-row-remove-button
{
@include
transition
(
color
);
flex-shrink
:
0
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
width
:
$ci-variable-remove-button-width
;
height
:
$input-height
;
padding
:
0
;
background
:
transparent
;
border
:
0
;
color
:
$gl-text-color-secondary
;
&
:hover
,
&
:focus
{
outline
:
none
;
color
:
$gl-text-color
;
}
}
app/assets/stylesheets/framework/variables.scss
View file @
332aa756
...
...
@@ -668,9 +668,9 @@ $pipeline-dropdown-line-height: 20px;
$pipeline-dropdown-status-icon-size
:
18px
;
/*
Pipeline Schedule
s
CI variable list
s
*/
$
pipeline
-variable-remove-button-width
:
calc
(
1em
+
#{
2
*
$gl-padding
}
);
$
ci
-variable-remove-button-width
:
calc
(
1em
+
#{
2
*
$gl-padding
}
);
/*
...
...
app/assets/stylesheets/pages/pipeline_schedules.scss
View file @
332aa756
...
...
@@ -78,84 +78,3 @@
margin-right
:
3px
;
}
}
.pipeline-variable-list
{
margin-left
:
0
;
margin-bottom
:
0
;
padding-left
:
0
;
list-style
:
none
;
clear
:
both
;
}
.pipeline-variable-row
{
display
:
flex
;
align-items
:
flex-end
;
&
:not
(
:last-child
)
{
margin-bottom
:
$gl-btn-padding
;
}
@media
(
max-width
:
$screen-sm-max
)
{
padding-right
:
$gl-col-padding
;
}
&
:last-child
{
.pipeline-variable-row-remove-button
{
display
:
none
;
}
@media
(
max-width
:
$screen-sm-max
)
{
.pipeline-variable-value-input
{
margin-right
:
$pipeline-variable-remove-button-width
;
}
}
@media
(
max-width
:
$screen-xs-max
)
{
.pipeline-variable-row-body
{
margin-right
:
$pipeline-variable-remove-button-width
;
}
}
}
}
.pipeline-variable-row-body
{
display
:
flex
;
width
:
calc
(
75%
-
#{
$gl-col-padding
}
);
padding-left
:
$gl-col-padding
;
@media
(
max-width
:
$screen-sm-max
)
{
width
:
100%
;
}
@media
(
max-width
:
$screen-xs-max
)
{
display
:
block
;
}
}
.pipeline-variable-key-input
{
margin-right
:
$gl-btn-padding
;
@media
(
max-width
:
$screen-xs-max
)
{
margin-bottom
:
$gl-btn-padding
;
}
}
.pipeline-variable-row-remove-button
{
@include
transition
(
color
);
flex-shrink
:
0
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
width
:
$pipeline-variable-remove-button-width
;
height
:
$input-height
;
padding
:
0
;
background
:
transparent
;
border
:
0
;
color
:
$gl-text-color-secondary
;
&
:hover
,
&
:focus
{
outline
:
none
;
color
:
$gl-text-color
;
}
}
app/views/ci/variables/_variable_row.html.haml
0 → 100644
View file @
332aa756
-
form_field
=
local_assigns
.
fetch
(
:form_field
,
nil
)
-
variable
=
local_assigns
.
fetch
(
:variable
,
nil
)
-
only_key_value
=
local_assigns
.
fetch
(
:only_key_value
,
false
)
-
id
=
variable
&
.
id
-
key
=
variable
&
.
key
-
value
=
variable
&
.
value
-
is_protected
=
variable
&&
!
only_key_value
?
variable
.
protected
:
true
-
id_input_name
=
"
#{
form_field
}
[variables_attributes][][id]"
-
destroy_input_name
=
"
#{
form_field
}
[variables_attributes][][_destroy]"
-
key_input_name
=
"
#{
form_field
}
[variables_attributes][][key]"
-
value_input_name
=
"
#{
form_field
}
[variables_attributes][][value]"
-
protected_input_name
=
"
#{
form_field
}
[variables_attributes][][protected]"
%li
.js-row.ci-variable-row
{
data:
{
is_persisted:
"#{!id.nil?}"
}
}
.ci-variable-row-body
%input
.js-ci-variable-input-id
{
type:
"hidden"
,
name:
id_input_name
,
value:
id
}
%input
.js-ci-variable-input-destroy
{
type:
"hidden"
,
name:
destroy_input_name
}
%input
.js-ci-variable-input-key.ci-variable-body-item.form-control
{
type:
"text"
,
name:
key_input_name
,
value:
key
,
placeholder:
s_
(
'CiVariables|Input variable key'
)
}
.ci-variable-body-item
.form-control.js-secret-value-placeholder
{
class:
(
'hide'
unless
id
)
}
=
'*'
*
20
%textarea
.js-ci-variable-input-value.js-secret-value.form-control
{
class:
(
'hide'
if
id
),
rows:
1
,
name:
value_input_name
,
placeholder:
s_
(
'CiVariables|Input variable value'
)
}
=
value
-
unless
only_key_value
.ci-variable-body-item.ci-variable-protected-item
.append-right-default
=
s_
(
"CiVariable|Protected"
)
%button
{
type:
'button'
,
class:
"js-project-feature-toggle project-feature-toggle #{'is-checked' if is_protected}"
,
"aria-label"
:
s_
(
"CiVariable|Toggle protected"
)
}
%input
{
type:
"hidden"
,
class:
'js-ci-variable-input-protected js-project-feature-toggle-input'
,
name:
protected_input_name
,
value:
is_protected
}
%span
.toggle-icon
=
sprite_icon
(
'status_success_borderless'
,
size:
16
,
css_class:
'toggle-icon-svg toggle-status-checked'
)
=
sprite_icon
(
'status_failed_borderless'
,
size:
16
,
css_class:
'toggle-icon-svg toggle-status-unchecked'
)
-# EE-specific start
-# EE-specific end
%button
.js-row-remove-button.ci-variable-row-remove-button
{
type:
'button'
,
'aria-label'
:
s_
(
'CiVariables|Remove variable row'
)
}
=
icon
(
'minus-circle'
)
app/views/projects/pipeline_schedules/_form.html.haml
View file @
332aa756
...
...
@@ -22,14 +22,20 @@
=
f
.
label
:ref
,
_
(
'Target Branch'
),
class:
'label-light'
=
dropdown_tag
(
_
(
"Select target branch"
),
options:
{
toggle_class:
'btn js-target-branch-dropdown'
,
dropdown_class:
'git-revision-dropdown'
,
title:
_
(
"Select target branch"
),
filter:
true
,
placeholder:
s_
(
"OfSearchInADropdown|Filter"
),
data:
{
data:
@project
.
repository
.
branch_names
,
default_branch:
@project
.
default_branch
}
}
)
=
f
.
text_field
:ref
,
value:
@schedule
.
ref
,
id:
'schedule_ref'
,
class:
'hidden'
,
name:
'schedule[ref]'
,
required:
true
.form-group
.form-group
.js-ci-variable-list-section
.col-md-9
%label
.label-light
#{
s_
(
'PipelineSchedules|Variables'
)
}
%ul
.js-pipeline-variable-list.pipeline-variable-list
-
@schedule
.
variables
.
each
do
|
variable
|
=
render
'variable_row'
,
id:
variable
.
id
,
key:
variable
.
key
,
value:
variable
.
value
=
render
'variable_row'
%ul
.ci-variable-list
-
@schedule
.
variables
.
each
do
|
variable
|
=
render
'ci/variables/variable_row'
,
form_field:
'schedule'
,
variable:
variable
,
only_key_value:
true
=
render
'ci/variables/variable_row'
,
form_field:
'schedule'
,
only_key_value:
true
-
if
@schedule
.
variables
.
size
>
0
%button
.btn.btn-info.btn-inverted.prepend-top-10.js-secret-value-reveal-button
{
type:
'button'
,
data:
{
secret_reveal_status:
"#{@schedule.variables.size == 0}"
}
}
-
if
@schedule
.
variables
.
size
==
0
=
n_
(
'Hide value'
,
'Hide values'
,
@schedule
.
variables
.
size
)
-
else
=
n_
(
'Reveal value'
,
'Reveal values'
,
@schedule
.
variables
.
size
)
.form-group
.col-md-9
=
f
.
label
:active
,
s_
(
'PipelineSchedules|Activated'
),
class:
'label-light'
...
...
app/views/projects/pipeline_schedules/_variable_row.html.haml
deleted
100644 → 0
View file @
03f386c2
-
id
=
local_assigns
.
fetch
(
:id
,
nil
)
-
key
=
local_assigns
.
fetch
(
:key
,
""
)
-
value
=
local_assigns
.
fetch
(
:value
,
""
)
%li
.js-row.pipeline-variable-row
{
data:
{
is_persisted:
"#{!id.nil?}"
}
}
.pipeline-variable-row-body
%input
{
type:
"hidden"
,
name:
"schedule[variables_attributes][][id]"
,
value:
id
}
%input
.js-destroy-input
{
type:
"hidden"
,
name:
"schedule[variables_attributes][][_destroy]"
}
%input
.js-user-input.pipeline-variable-key-input.form-control
{
type:
"text"
,
name:
"schedule[variables_attributes][][key]"
,
value:
key
,
placeholder:
s_
(
'PipelineSchedules|Input variable key'
)
}
%textarea
.js-user-input.pipeline-variable-value-input.form-control
{
rows:
1
,
name:
"schedule[variables_attributes][][value]"
,
placeholder:
s_
(
'PipelineSchedules|Input variable value'
)
}
=
value
%button
.js-row-remove-button.pipeline-variable-row-remove-button
{
'aria-label'
:
s_
(
'PipelineSchedules|Remove variable row'
)
}
%i
.fa.fa-minus-circle
{
'aria-hidden'
:
"true"
}
changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml
0 → 100644
View file @
332aa756
---
title
:
Hide variable values on pipeline schedule edit page
merge_request
:
16729
author
:
type
:
changed
spec/features/projects/pipeline_schedules_spec.rb
View file @
332aa756
...
...
@@ -168,11 +168,11 @@ feature 'Pipeline Schedules', :js do
scenario
'user sees the new variable in edit window'
do
find
(
".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']"
).
click
page
.
within
(
'.
pipeline
-variable-list'
)
do
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-key-input
"
).
value
).
to
eq
(
'AAA'
)
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-value-input"
).
value
).
to
eq
(
'AAA123'
)
expect
(
find
(
".
pipeline-variable-row:nth-child(2) .pipeline-variable-key-input
"
).
value
).
to
eq
(
'BBB'
)
expect
(
find
(
".
pipeline-variable-row:nth-child(2) .pipeline-variable-value-input"
).
value
).
to
eq
(
'BBB123'
)
page
.
within
(
'.
ci
-variable-list'
)
do
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-key
"
).
value
).
to
eq
(
'AAA'
)
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-value"
,
visible:
false
).
value
).
to
eq
(
'AAA123'
)
expect
(
find
(
".
ci-variable-row:nth-child(2) .js-ci-variable-input-key
"
).
value
).
to
eq
(
'BBB'
)
expect
(
find
(
".
ci-variable-row:nth-child(2) .js-ci-variable-input-value"
,
visible:
false
).
value
).
to
eq
(
'BBB123'
)
end
end
end
...
...
@@ -185,16 +185,18 @@ feature 'Pipeline Schedules', :js do
visit_pipelines_schedules
find
(
".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']"
).
click
all
(
'[name="schedule[variables_attributes][][key]"]'
)[
0
].
set
(
'foo'
)
all
(
'[name="schedule[variables_attributes][][value]"]'
)[
0
].
set
(
'bar'
)
find
(
'.js-ci-variable-list-section .js-secret-value-reveal-button'
).
click
first
(
'.js-ci-variable-input-key'
).
set
(
'foo'
)
first
(
'.js-ci-variable-input-value'
).
set
(
'bar'
)
click_button
'Save pipeline schedule'
end
scenario
'user sees the updated variable in edit window'
do
find
(
".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']"
).
click
page
.
within
(
'.
pipeline
-variable-list'
)
do
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-key-input
"
).
value
).
to
eq
(
'foo'
)
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-value-input"
).
value
).
to
eq
(
'bar'
)
page
.
within
(
'.
ci
-variable-list'
)
do
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-key
"
).
value
).
to
eq
(
'foo'
)
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-value"
,
visible:
false
).
value
).
to
eq
(
'bar'
)
end
end
end
...
...
@@ -207,15 +209,15 @@ feature 'Pipeline Schedules', :js do
visit_pipelines_schedules
find
(
".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']"
).
click
find
(
'.
pipeline-variable-list .pipeline
-variable-row-remove-button'
).
click
find
(
'.
ci-variable-list .ci
-variable-row-remove-button'
).
click
click_button
'Save pipeline schedule'
end
scenario
'user does not see the removed variable in edit window'
do
find
(
".content-list .pipeline-schedule-table-row:nth-child(1) .btn-group a[title='Edit']"
).
click
page
.
within
(
'.
pipeline
-variable-list'
)
do
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-key-input
"
).
value
).
to
eq
(
''
)
expect
(
find
(
".
pipeline-variable-row:nth-child(1) .pipeline-variable-value-input"
).
value
).
to
eq
(
''
)
page
.
within
(
'.
ci
-variable-list'
)
do
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-key
"
).
value
).
to
eq
(
''
)
expect
(
find
(
".
ci-variable-row:nth-child(1) .js-ci-variable-input-value"
,
visible:
false
).
value
).
to
eq
(
''
)
end
end
end
...
...
spec/javascripts/ci_variable_list/ci_variable_list_spec.js
0 → 100644
View file @
332aa756
import
VariableList
from
'~/ci_variable_list/ci_variable_list'
;
import
getSetTimeoutPromise
from
'../helpers/set_timeout_promise_helper'
;
describe
(
'VariableList'
,
()
=>
{
preloadFixtures
(
'pipeline_schedules/edit.html.raw'
);
preloadFixtures
(
'pipeline_schedules/edit_with_variables.html.raw'
);
let
$wrapper
;
let
variableList
;
describe
(
'with only key/value inputs'
,
()
=>
{
describe
(
'with no variables'
,
()
=>
{
beforeEach
(()
=>
{
loadFixtures
(
'pipeline_schedules/edit.html.raw'
);
$wrapper
=
$
(
'.js-ci-variable-list-section'
);
variableList
=
new
VariableList
({
container
:
$wrapper
,
formField
:
'schedule'
,
});
variableList
.
init
();
});
it
(
'should remove the row when clicking the remove button'
,
()
=>
{
$wrapper
.
find
(
'.js-row-remove-button'
).
trigger
(
'click'
);
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
0
);
});
it
(
'should add another row when editing the last rows key input'
,
()
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row'
);
$row
.
find
(
'.js-ci-variable-input-key'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
// Check for the correct default in the new row
const
$keyInput
=
$wrapper
.
find
(
'.js-row:last-child'
).
find
(
'.js-ci-variable-input-key'
);
expect
(
$keyInput
.
val
()).
toBe
(
''
);
});
it
(
'should add another row when editing the last rows value textarea'
,
()
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row'
);
$row
.
find
(
'.js-ci-variable-input-value'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
// Check for the correct default in the new row
const
$valueInput
=
$wrapper
.
find
(
'.js-row:last-child'
).
find
(
'.js-ci-variable-input-key'
);
expect
(
$valueInput
.
val
()).
toBe
(
''
);
});
it
(
'should remove empty row after blurring'
,
()
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row'
);
$row
.
find
(
'.js-ci-variable-input-key'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
$row
.
find
(
'.js-ci-variable-input-key'
)
.
val
(
''
)
.
trigger
(
'input'
)
.
trigger
(
'blur'
);
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
1
);
});
});
describe
(
'with persisted variables'
,
()
=>
{
beforeEach
(()
=>
{
loadFixtures
(
'pipeline_schedules/edit_with_variables.html.raw'
);
$wrapper
=
$
(
'.js-ci-variable-list-section'
);
variableList
=
new
VariableList
({
container
:
$wrapper
,
formField
:
'schedule'
,
});
variableList
.
init
();
});
it
(
'should have "Reveal values" button initially when there are already variables'
,
()
=>
{
expect
(
$wrapper
.
find
(
'.js-secret-value-reveal-button'
).
text
()).
toBe
(
'Reveal values'
);
});
it
(
'should reveal hidden values'
,
()
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row:first-child'
);
const
$inputValue
=
$row
.
find
(
'.js-ci-variable-input-value'
);
const
$placeholder
=
$row
.
find
(
'.js-secret-value-placeholder'
);
expect
(
$placeholder
.
hasClass
(
'hide'
)).
toBe
(
false
);
expect
(
$inputValue
.
hasClass
(
'hide'
)).
toBe
(
true
);
// Reveal values
$wrapper
.
find
(
'.js-secret-value-reveal-button'
).
click
();
expect
(
$placeholder
.
hasClass
(
'hide'
)).
toBe
(
true
);
expect
(
$inputValue
.
hasClass
(
'hide'
)).
toBe
(
false
);
});
});
});
describe
(
'with all inputs(key, value, protected)'
,
()
=>
{
beforeEach
(()
=>
{
// This markup will be replaced with a fixture when we can render the
// CI/CD settings page with the new dynamic variable list in https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4110
$wrapper
=
$
(
`<form class="js-variable-list">
<ul>
<li class="js-row">
<div class="ci-variable-body-item">
<input class="js-ci-variable-input-key" name="variables[variables_attributes][][key]">
</div>
<div class="ci-variable-body-item">
<textarea class="js-ci-variable-input-value" name="variables[variables_attributes][][value]"></textarea>
</div>
<div class="ci-variable-body-item ci-variable-protected-item">
<button type="button" class="js-project-feature-toggle project-feature-toggle">
<input
type="hidden"
class="js-ci-variable-input-protected js-project-feature-toggle-input"
name="variables[variables_attributes][][protected]"
value="true"
/>
</button>
</div>
<button type="button" class="js-row-remove-button"></button>
</li>
</ul>
<button type="button" class="js-secret-value-reveal-button">
Reveal values
</button>
</form>`
);
variableList
=
new
VariableList
({
container
:
$wrapper
,
formField
:
'variables'
,
});
variableList
.
init
();
});
it
(
'should add another row when editing the last rows protected checkbox'
,
(
done
)
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row:last-child'
);
$row
.
find
(
'.ci-variable-protected-item .js-project-feature-toggle'
).
click
();
getSetTimeoutPromise
()
.
then
(()
=>
{
expect
(
$wrapper
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
// Check for the correct default in the new row
const
$protectedInput
=
$wrapper
.
find
(
'.js-row:last-child'
).
find
(
'.js-ci-variable-input-protected'
);
expect
(
$protectedInput
.
val
()).
toBe
(
'true'
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
});
spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
0 → 100644
View file @
332aa756
import
setupNativeFormVariableList
from
'~/ci_variable_list/native_form_variable_list'
;
describe
(
'NativeFormVariableList'
,
()
=>
{
preloadFixtures
(
'pipeline_schedules/edit.html.raw'
);
let
$wrapper
;
beforeEach
(()
=>
{
loadFixtures
(
'pipeline_schedules/edit.html.raw'
);
$wrapper
=
$
(
'.js-ci-variable-list-section'
);
setupNativeFormVariableList
({
container
:
$wrapper
,
formField
:
'schedule'
,
});
});
describe
(
'onFormSubmit'
,
()
=>
{
it
(
'should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)'
,
()
=>
{
const
$row
=
$wrapper
.
find
(
'.js-row'
);
expect
(
$row
.
find
(
'.js-ci-variable-input-key'
).
attr
(
'name'
)).
toBe
(
'schedule[variables_attributes][][key]'
);
expect
(
$row
.
find
(
'.js-ci-variable-input-value'
).
attr
(
'name'
)).
toBe
(
'schedule[variables_attributes][][value]'
);
$wrapper
.
closest
(
'form'
).
trigger
(
'trigger-submit'
);
expect
(
$row
.
find
(
'.js-ci-variable-input-key'
).
attr
(
'name'
)).
toBe
(
''
);
expect
(
$row
.
find
(
'.js-ci-variable-input-value'
).
attr
(
'name'
)).
toBe
(
''
);
});
});
});
spec/javascripts/fixtures/pipeline_schedules.rb
0 → 100644
View file @
332aa756
require
'spec_helper'
describe
Projects
::
PipelineSchedulesController
,
'(JavaScript fixtures)'
,
type: :controller
do
include
JavaScriptFixturesHelpers
let
(
:admin
)
{
create
(
:admin
)
}
let
(
:namespace
)
{
create
(
:namespace
,
name:
'frontend-fixtures'
)}
let
(
:project
)
{
create
(
:project
,
:public
,
:repository
)
}
let!
(
:pipeline_schedule
)
{
create
(
:ci_pipeline_schedule
,
project:
project
,
owner:
admin
)
}
let!
(
:pipeline_schedule_populated
)
{
create
(
:ci_pipeline_schedule
,
project:
project
,
owner:
admin
)
}
let!
(
:pipeline_schedule_variable1
)
{
create
(
:ci_pipeline_schedule_variable
,
key:
'foo'
,
value:
'foovalue'
,
pipeline_schedule:
pipeline_schedule_populated
)
}
let!
(
:pipeline_schedule_variable2
)
{
create
(
:ci_pipeline_schedule_variable
,
key:
'bar'
,
value:
'barvalue'
,
pipeline_schedule:
pipeline_schedule_populated
)
}
render_views
before
(
:all
)
do
clean_frontend_fixtures
(
'pipeline_schedules/'
)
end
before
do
sign_in
(
admin
)
end
it
'pipeline_schedules/edit.html.raw'
do
|
example
|
get
:edit
,
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
,
id:
pipeline_schedule
.
id
expect
(
response
).
to
be_success
store_frontend_fixture
(
response
,
example
.
description
)
end
it
'pipeline_schedules/edit_with_variables.html.raw'
do
|
example
|
get
:edit
,
namespace_id:
project
.
namespace
.
to_param
,
project_id:
project
,
id:
pipeline_schedule_populated
.
id
expect
(
response
).
to
be_success
store_frontend_fixture
(
response
,
example
.
description
)
end
end
spec/javascripts/pipeline_schedules/setup_pipeline_variable_list_spec.js
deleted
100644 → 0
View file @
03f386c2
import
{
setupPipelineVariableList
,
insertRow
,
removeRow
,
}
from
'~/pipeline_schedules/setup_pipeline_variable_list'
;
describe
(
'Pipeline Variable List'
,
()
=>
{
let
$markup
;
describe
(
'insertRow'
,
()
=>
{
it
(
'should insert another row'
,
()
=>
{
$markup
=
$
(
`<div>
<li class="js-row">
<input>
<textarea></textarea>
</li>
</div>`
);
insertRow
(
$markup
.
find
(
'.js-row'
));
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
});
it
(
'should clear `data-is-persisted` on cloned row'
,
()
=>
{
$markup
=
$
(
`<div>
<li class="js-row" data-is-persisted="true"></li>
</div>`
);
insertRow
(
$markup
.
find
(
'.js-row'
));
const
$lastRow
=
$markup
.
find
(
'.js-row'
).
last
();
expect
(
$lastRow
.
attr
(
'data-is-persisted'
)).
toBe
(
undefined
);
});
it
(
'should clear inputs on cloned row'
,
()
=>
{
$markup
=
$
(
`<div>
<li class="js-row">
<input value="foo">
<textarea>bar</textarea>
</li>
</div>`
);
insertRow
(
$markup
.
find
(
'.js-row'
));
const
$lastRow
=
$markup
.
find
(
'.js-row'
).
last
();
expect
(
$lastRow
.
find
(
'input'
).
val
()).
toBe
(
''
);
expect
(
$lastRow
.
find
(
'textarea'
).
val
()).
toBe
(
''
);
});
});
describe
(
'removeRow'
,
()
=>
{
it
(
'should remove dynamic row'
,
()
=>
{
$markup
=
$
(
`<div>
<li class="js-row">
<input>
<textarea></textarea>
</li>
</div>`
);
removeRow
(
$markup
.
find
(
'.js-row'
));
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
0
);
});
it
(
'should hide and mark to destroy with already persisted rows'
,
()
=>
{
$markup
=
$
(
`<div>
<li class="js-row" data-is-persisted="true">
<input class="js-destroy-input">
</li>
</div>`
);
const
$row
=
$markup
.
find
(
'.js-row'
);
removeRow
(
$row
);
expect
(
$row
.
find
(
'.js-destroy-input'
).
val
()).
toBe
(
'1'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
1
);
});
});
describe
(
'setupPipelineVariableList'
,
()
=>
{
beforeEach
(()
=>
{
$markup
=
$
(
`<form>
<li class="js-row">
<input class="js-user-input" name="schedule[variables_attributes][][key]">
<textarea class="js-user-input" name="schedule[variables_attributes][][value]"></textarea>
<button class="js-row-remove-button"></button>
<button class="js-row-add-button"></button>
</li>
</form>`
);
setupPipelineVariableList
(
$markup
);
});
it
(
'should remove the row when clicking the remove button'
,
()
=>
{
$markup
.
find
(
'.js-row-remove-button'
).
trigger
(
'click'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
0
);
});
it
(
'should add another row when editing the last rows key input'
,
()
=>
{
const
$row
=
$markup
.
find
(
'.js-row'
);
$row
.
find
(
'input.js-user-input'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
});
it
(
'should add another row when editing the last rows value textarea'
,
()
=>
{
const
$row
=
$markup
.
find
(
'.js-row'
);
$row
.
find
(
'textarea.js-user-input'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
});
it
(
'should remove empty row after blurring'
,
()
=>
{
const
$row
=
$markup
.
find
(
'.js-row'
);
$row
.
find
(
'input.js-user-input'
)
.
val
(
'foo'
)
.
trigger
(
'input'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
2
);
$row
.
find
(
'input.js-user-input'
)
.
val
(
''
)
.
trigger
(
'input'
)
.
trigger
(
'blur'
);
expect
(
$markup
.
find
(
'.js-row'
).
length
).
toBe
(
1
);
});
it
(
'should clear out the `name` attribute on the inputs for the last empty row on form submission (avoid BE validation)'
,
()
=>
{
const
$row
=
$markup
.
find
(
'.js-row'
);
expect
(
$row
.
find
(
'input'
).
attr
(
'name'
)).
toBe
(
'schedule[variables_attributes][][key]'
);
expect
(
$row
.
find
(
'textarea'
).
attr
(
'name'
)).
toBe
(
'schedule[variables_attributes][][value]'
);
$markup
.
filter
(
'form'
).
submit
();
expect
(
$row
.
find
(
'input'
).
attr
(
'name'
)).
toBe
(
''
);
expect
(
$row
.
find
(
'textarea'
).
attr
(
'name'
)).
toBe
(
''
);
});
});
});
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