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
38873a13
Commit
38873a13
authored
Jun 11, 2021
by
Alexander Turinske
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Finish up refactor of policy editor
- ensure everything works properly - clean up code - update/add tests
parent
e6c03ec6
Changes
17
Show whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
365 additions
and
170 deletions
+365
-170
ee/app/assets/javascripts/threat_monitoring/components/policy_drawer/cilium_network_policy.vue
...toring/components/policy_drawer/cilium_network_policy.vue
+2
-5
ee/app/assets/javascripts/threat_monitoring/components/policy_drawer/scan_execution_policy.vue
...toring/components/policy_drawer/scan_execution_policy.vue
+2
-2
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/constants.js
...s/threat_monitoring/components/policy_editor/constants.js
+4
-4
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue
...ts/policy_editor/network_policy/network_policy_editor.vue
+5
-4
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/policy_editor_layout.vue
...itoring/components/policy_editor/policy_editor_layout.vue
+19
-17
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_editor.vue
...or/scan_execution_policy/scan_execution_policy_editor.vue
+25
-16
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/utils.js
...ripts/threat_monitoring/components/policy_editor/utils.js
+0
-9
ee/app/assets/javascripts/threat_monitoring/utils.js
ee/app/assets/javascripts/threat_monitoring/utils.js
+10
-0
ee/config/feature_flags/development/scan_execution_policy_ui.yml
...ig/feature_flags/development/scan_execution_policy_ui.yml
+1
-1
ee/spec/frontend/threat_monitoring/components/policy_editor/network_policy/lib/from_yaml_spec.js
...onents/policy_editor/network_policy/lib/from_yaml_spec.js
+0
-13
ee/spec/frontend/threat_monitoring/components/policy_editor/network_policy/network_policy_editor_spec.js
...olicy_editor/network_policy/network_policy_editor_spec.js
+25
-95
ee/spec/frontend/threat_monitoring/components/policy_editor/policy_editor_layout_spec.js
...ing/components/policy_editor/policy_editor_layout_spec.js
+127
-0
ee/spec/frontend/threat_monitoring/components/policy_editor/policy_editor_spec.js
...monitoring/components/policy_editor/policy_editor_spec.js
+17
-3
ee/spec/frontend/threat_monitoring/components/policy_editor/scan_execution_policy/lib/from_yaml_spec.js
...policy_editor/scan_execution_policy/lib/from_yaml_spec.js
+16
-0
ee/spec/frontend/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_spec.js
...ditor/scan_execution_policy/scan_execution_policy_spec.js
+62
-0
ee/spec/frontend/threat_monitoring/mocks/mock_data.js
ee/spec/frontend/threat_monitoring/mocks/mock_data.js
+37
-0
ee/spec/frontend/threat_monitoring/utils_spec.js
ee/spec/frontend/threat_monitoring/utils_spec.js
+13
-1
No files found.
ee/app/assets/javascripts/threat_monitoring/components/policy_drawer/cilium_network_policy.vue
View file @
38873a13
<
script
>
import
{
fromYaml
,
humanizeNetworkPolicy
,
removeUnnecessaryDashes
,
}
from
'
../policy_editor/network_policy/lib
'
;
import
{
removeUnnecessaryDashes
}
from
'
../../utils
'
;
import
{
fromYaml
,
humanizeNetworkPolicy
}
from
'
../policy_editor/network_policy/lib
'
;
import
PolicyPreview
from
'
../policy_editor/policy_preview.vue
'
;
import
BasePolicy
from
'
./base_policy.vue
'
;
import
PolicyInfoRow
from
'
./policy_info_row.vue
'
;
...
...
ee/app/assets/javascripts/threat_monitoring/components/policy_drawer/scan_execution_policy.vue
View file @
38873a13
<
script
>
import
{
GlLink
}
from
'
@gitlab/ui
'
;
import
{
safeLoad
}
from
'
js-yaml
'
;
import
{
fromYaml
}
from
'
../policy_editor/scan_execution_policy/lib
'
;
import
BasePolicy
from
'
./base_policy.vue
'
;
import
PolicyInfoRow
from
'
./policy_info_row.vue
'
;
...
...
@@ -18,7 +18,7 @@ export default {
},
computed
:
{
policy
()
{
return
safeLoad
(
this
.
value
,
{
json
:
true
}
);
return
fromYaml
(
this
.
value
);
},
},
};
...
...
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/constants.js
View file @
38873a13
import
{
s__
,
__
}
from
'
~/locale
'
;
export
const
E
ditorModeRule
=
'
rule
'
;
export
const
E
ditorMode
YAML
=
'
yaml
'
;
export
const
E
DITOR_MODE_RULE
=
'
rule
'
;
export
const
E
DITOR_MODE_
YAML
=
'
yaml
'
;
export
const
PARSING_ERROR_MESSAGE
=
s__
(
'
NetworkPolicies|Rule mode is unavailable for this policy. In some cases, we cannot parse the YAML file back into the rules editor.
'
,
);
export
const
EDITOR_MODES
=
[
{
value
:
E
ditorModeRule
,
text
:
s__
(
'
NetworkPolicies|Rule mode
'
)
},
{
value
:
E
ditorMode
YAML
,
text
:
s__
(
'
NetworkPolicies|.yaml mode
'
)
},
{
value
:
E
DITOR_MODE_RULE
,
text
:
s__
(
'
NetworkPolicies|Rule mode
'
)
},
{
value
:
E
DITOR_MODE_
YAML
,
text
:
s__
(
'
NetworkPolicies|.yaml mode
'
)
},
];
export
const
POLICY_TYPES
=
{
...
...
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue
View file @
38873a13
<
script
>
import
{
GlFormGroup
,
GlFormInput
,
GlFormTextarea
,
GlToggle
,
GlButton
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
mapState
,
mapActions
}
from
'
vuex
'
;
import
{
removeUnnecessaryDashes
}
from
'
ee/threat_monitoring/utils
'
;
import
{
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
EDITOR_MODES
,
E
ditorMode
YAML
,
PARSING_ERROR_MESSAGE
}
from
'
../constants
'
;
import
{
EDITOR_MODES
,
E
DITOR_MODE_
YAML
,
PARSING_ERROR_MESSAGE
}
from
'
../constants
'
;
import
DimDisableContainer
from
'
../dim_disable_container.vue
'
;
import
PolicyActionPicker
from
'
../policy_action_picker.vue
'
;
import
PolicyAlertPicker
from
'
../policy_alert_picker.vue
'
;
import
PolicyEditorLayout
from
'
../policy_editor_layout.vue
'
;
import
PolicyPreview
from
'
../policy_preview.vue
'
;
import
{
removeUnnecessaryDashes
}
from
'
../utils
'
;
import
{
DEFAULT_NETWORK_POLICY
,
RuleTypeEndpoint
,
...
...
@@ -128,14 +128,14 @@ export default {
}
},
changeEditorMode
(
mode
)
{
if
(
mode
===
E
ditorMode
YAML
&&
!
this
.
hasParsingError
)
{
if
(
mode
===
E
DITOR_MODE_
YAML
&&
!
this
.
hasParsingError
)
{
this
.
yamlEditorValue
=
toYaml
(
this
.
policy
);
}
},
savePolicy
(
mode
)
{
const
saveFn
=
this
.
isEditing
?
this
.
updatePolicy
:
this
.
createPolicy
;
const
policy
=
{
manifest
:
mode
===
E
ditorMode
YAML
?
this
.
yamlEditorValue
:
toYaml
(
this
.
policy
),
manifest
:
mode
===
E
DITOR_MODE_
YAML
?
this
.
yamlEditorValue
:
toYaml
(
this
.
policy
),
};
if
(
this
.
isEditing
)
{
policy
.
name
=
this
.
existingPolicy
.
name
;
...
...
@@ -159,6 +159,7 @@ export default {
<
template
>
<policy-editor-layout
:is-editing=
"isEditing"
:is-removing-policy=
"isRemovingPolicy"
:is-updating-policy=
"isUpdatingPolicy"
:policy-name=
"policy.name"
:yaml-editor-value=
"yamlEditorValue"
...
...
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/policy_editor_layout.vue
View file @
38873a13
<
script
>
import
{
GlButton
,
GlFormGroup
,
GlModal
,
GlModalDirective
,
GlSegmentedControl
}
from
'
@gitlab/ui
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
{
DELETE_MODAL_CONFIG
,
EDITOR_MODES
,
E
ditorModeRule
,
EditorMode
YAML
}
from
'
./constants
'
;
import
{
DELETE_MODAL_CONFIG
,
EDITOR_MODES
,
E
DITOR_MODE_RULE
,
EDITOR_MODE_
YAML
}
from
'
./constants
'
;
export
default
{
i18n
:
{
...
...
@@ -18,15 +18,10 @@ export default {
directives
:
{
GlModal
:
GlModalDirective
},
inject
:
[
'
threatMonitoringPath
'
],
props
:
{
customSaveButtonText
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
defaultEditorMode
:
{
type
:
String
,
required
:
false
,
default
:
E
ditorModeRule
,
default
:
E
DITOR_MODE_RULE
,
},
editorModes
:
{
type
:
Array
,
...
...
@@ -38,6 +33,11 @@ export default {
required
:
false
,
default
:
false
,
},
isRemovingPolicy
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
isUpdatingPolicy
:
{
type
:
Boolean
,
required
:
false
,
...
...
@@ -64,18 +64,15 @@ export default {
return
sprintf
(
s__
(
'
NetworkPolicies|Delete policy: %{policy}
'
),
{
policy
:
this
.
policyName
});
},
saveButtonText
()
{
if
(
this
.
customSaveButtonText
)
{
return
this
.
customSaveButtonText
;
}
return
this
.
isEditing
?
s__
(
'
NetworkPolicies|Save changes
'
)
:
s__
(
'
NetworkPolicies|Create policy
'
);
},
shouldShowRuleEditor
()
{
return
this
.
selectedEditorMode
===
E
ditorModeRule
;
return
this
.
selectedEditorMode
===
E
DITOR_MODE_RULE
;
},
shouldShowYamlEditor
()
{
return
this
.
selectedEditorMode
===
E
ditorMode
YAML
;
return
this
.
selectedEditorMode
===
E
DITOR_MODE_
YAML
;
},
},
methods
:
{
...
...
@@ -89,8 +86,8 @@ export default {
this
.
selectedEditorMode
=
mode
;
this
.
$emit
(
'
update-editor-mode
'
,
mode
);
},
updateYaml
()
{
this
.
$emit
(
'
load-yaml
'
);
updateYaml
(
manifest
)
{
this
.
$emit
(
'
update-yaml
'
,
manifest
);
},
},
};
...
...
@@ -103,7 +100,6 @@ export default {
class=
"gl-px-5 gl-py-3 gl-mb-0 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100 gl-border-b-1"
>
<gl-segmented-control
data-testid=
"editor-mode"
:options=
"editorModes"
:checked=
"selectedEditorMode"
@
input=
"updateEditorMode"
...
...
@@ -111,7 +107,9 @@ export default {
</gl-form-group>
<div
class=
"gl-display-flex gl-sm-flex-direction-column"
>
<section
class=
"gl-w-full gl-p-5 gl-flex-fill-4 policy-table-left"
>
<slot
v-if=
"shouldShowRuleEditor"
name=
"rule-editor"
data-testid=
"rule-editor"
></slot>
<div
v-if=
"shouldShowRuleEditor"
data-testid=
"rule-editor"
>
<slot
name=
"rule-editor"
></slot>
</div>
<policy-yaml-editor
v-if=
"shouldShowYamlEditor"
data-testid=
"policy-yaml-editor"
...
...
@@ -123,6 +121,7 @@ export default {
<section
v-if=
"shouldShowRuleEditor"
class=
"gl-w-30p gl-p-5 gl-border-l-gray-100 gl-border-l-1 gl-border-l-solid gl-flex-fill-2"
data-testid=
"rule-editor-preview"
>
<slot
name=
"rule-editor-preview"
></slot>
</section>
...
...
@@ -134,8 +133,11 @@ export default {
data-testid=
"save-policy"
:loading=
"isUpdatingPolicy"
@
click=
"savePolicy"
>
{{
saveButtonText
}}
</gl-button
>
<slot
name=
"save-button-text"
>
{{
saveButtonText
}}
</slot>
</gl-button>
<gl-button
v-if=
"isEditing"
v-gl-modal=
"'delete-modal'"
...
...
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_editor.vue
View file @
38873a13
<
script
>
import
{
removeUnnecessaryDashes
}
from
'
ee/threat_monitoring/utils
'
;
import
{
__
}
from
'
~/locale
'
;
import
{
EDITOR_MODES
,
E
ditorMode
YAML
}
from
'
../constants
'
;
import
{
EDITOR_MODES
,
E
DITOR_MODE_
YAML
}
from
'
../constants
'
;
import
PolicyEditorLayout
from
'
../policy_editor_layout.vue
'
;
import
{
DEFAULT_SCAN_EXECUTION_POLICY
,
fromYaml
}
from
'
./lib
'
;
export
default
{
DEFAULT_EDITOR_MODE
:
E
ditorMode
YAML
,
DEFAULT_EDITOR_MODE
:
E
DITOR_MODE_
YAML
,
EDITOR_MODES
:
[
EDITOR_MODES
[
1
]],
i18n
:
{
createMergeRequest
:
__
(
'
Create merge request
'
),
...
...
@@ -27,28 +28,30 @@ export default {
:
fromYaml
(
DEFAULT_SCAN_EXECUTION_POLICY
);
const
yamlEditorValue
=
this
.
existingPolicy
?
this
.
existingPolicy
.
manifest
?
removeUnnecessaryDashes
(
this
.
existingPolicy
.
manifest
)
:
DEFAULT_SCAN_EXECUTION_POLICY
;
return
{
editorMode
:
EditorModeYAML
,
yamlEditorValue
,
yamlEditorError
:
policy
.
error
?
true
:
null
,
isRemovingPolicy
:
false
,
isUpdatingPolicy
:
false
,
policy
,
yamlEditorValue
,
};
},
computed
:
{
isCreatingMergeRequest
()
{
// TODO track the graphql mutation status after #333163 is closed
return
false
;
},
isEditing
()
{
return
Boolean
(
this
.
existingPolicy
);
},
},
methods
:
{
createMergeRequest
()
{
// TODO call graphql mutation and redirect to merge request after #333163 is closed
removePolicy
()
{
// TODO call graphql mutation and redirect to merge request after #329422 is closed
},
savePolicy
()
{
// TODO call graphql mutation and redirect to merge request after #329422 is closed
},
updateYaml
(
manifest
)
{
this
.
yamlEditorValue
=
manifest
;
},
},
};
...
...
@@ -56,13 +59,19 @@ export default {
<
template
>
<policy-editor-layout
:custom-save-button-text=
"$options.i18n.createMergeRequest"
:default-editor-mode=
"$options.DEFAULT_EDITOR_MODE"
:editor-modes=
"$options.EDITOR_MODES"
:is-editing=
"isEditing"
:is-updating-policy=
"isCreatingMergeRequest"
:is-removing-policy=
"isRemovingPolicy"
:is-updating-policy=
"isUpdatingPolicy"
:policy-name=
"policy.name"
:yaml-editor-value=
"yamlEditorValue"
@
save-policy=
"createMergeRequest"
/>
@
remove-policy=
"removePolicy"
@
save-policy=
"savePolicy"
@
update-yaml=
"updateYaml"
>
<template
#save-button-text
>
{{
$options
.
i18n
.
createMergeRequest
}}
</
template
>
</policy-editor-layout>
</template>
ee/app/assets/javascripts/threat_monitoring/components/policy_editor/utils.js
deleted
100644 → 0
View file @
e6c03ec6
/**
* Removes inital line dashes from a policy YAML that is received from the API, which
* is not required for the user.
* @param {String} manifest the policy from the API request
* @returns {String} the policy without the initial dashes or the initial string
*/
export
const
removeUnnecessaryDashes
=
(
manifest
)
=>
{
return
manifest
.
replace
(
'
---
\n
'
,
''
);
};
ee/app/assets/javascripts/threat_monitoring/utils.js
View file @
38873a13
...
...
@@ -9,3 +9,13 @@ export const getContentWrapperHeight = (contentWrapperClass) => {
const
wrapperEl
=
document
.
querySelector
(
contentWrapperClass
);
return
wrapperEl
?
`
${
wrapperEl
.
offsetTop
}
px`
:
''
;
};
/**
* Removes inital line dashes from a policy YAML that is received from the API, which
* is not required for the user.
* @param {String} manifest the policy from the API request
* @returns {String} the policy without the initial dashes or the initial string
*/
export
const
removeUnnecessaryDashes
=
(
manifest
)
=>
{
return
manifest
.
replace
(
'
---
\n
'
,
''
);
};
ee/config/feature_flags/development/scan_execution_policy_ui.yml
View file @
38873a13
...
...
@@ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63585
rollout_issue_url
:
https://gitlab.com/gitlab-org/gitlab/-/issues/273791
milestone
:
'
14.0'
type
:
development
group
:
group::container
_
security
group
:
group::container
security
default_enabled
:
false
ee/spec/frontend/threat_monitoring/components/policy_editor/network_policy/lib/from_yaml_spec.js
View file @
38873a13
...
...
@@ -11,7 +11,6 @@ import {
RuleTypeFQDN
,
EntityTypes
,
fromYaml
,
removeUnnecessaryDashes
,
buildRule
,
toYaml
,
}
from
'
ee/threat_monitoring/components/policy_editor/network_policy/lib
'
;
...
...
@@ -332,15 +331,3 @@ spec:
);
});
});
describe
(
'
removeUnnecessaryDashes
'
,
()
=>
{
it
.
each
`
input | output
${
'
---
\n
one
'
}
|
${
'
one
'
}
${
'
two
'
}
|
${
'
two
'
}
${
'
--
\n
three
'
}
|
${
'
--
\n
three
'
}
${
'
four---
\n
'
}
|
${
'
four
'
}
`
(
'
returns $output when used on $input
'
,
({
input
,
output
})
=>
{
expect
(
removeUnnecessaryDashes
(
input
)).
toBe
(
output
);
});
});
ee/spec/frontend/threat_monitoring/components/policy_editor/network_policy/network_policy_editor_spec.js
View file @
38873a13
import
{
Gl
Modal
,
Gl
Toggle
}
from
'
@gitlab/ui
'
;
import
{
E
ditorMode
YAML
}
from
'
ee/threat_monitoring/components/policy_editor/constants
'
;
import
{
GlToggle
}
from
'
@gitlab/ui
'
;
import
{
E
DITOR_MODE_
YAML
}
from
'
ee/threat_monitoring/components/policy_editor/constants
'
;
import
{
RuleDirectionInbound
,
PortMatchModeAny
,
...
...
@@ -12,35 +12,18 @@ import {
import
NetworkPolicyEditor
from
'
ee/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue
'
;
import
PolicyRuleBuilder
from
'
ee/threat_monitoring/components/policy_editor/network_policy/policy_rule_builder.vue
'
;
import
PolicyAlertPicker
from
'
ee/threat_monitoring/components/policy_editor/policy_alert_picker.vue
'
;
import
PolicyEditorLayout
from
'
ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue
'
;
import
PolicyPreview
from
'
ee/threat_monitoring/components/policy_editor/policy_preview.vue
'
;
import
createStore
from
'
ee/threat_monitoring/store
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
redirectTo
}
from
'
~/lib/utils/url_utility
'
;
import
{
mockL3Manifest
,
mockL7Manifest
}
from
'
../../../mocks/mock_data
'
;
jest
.
mock
(
'
~/lib/utils/url_utility
'
);
describe
(
'
NetworkPolicyEditor component
'
,
()
=>
{
let
store
;
let
wrapper
;
const
l7manifest
=
`apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: limit-inbound-ip
spec:
endpointSelector: {}
ingress:
- toPorts:
- ports:
- port: '80'
protocol: TCP
- port: '443'
protocol: TCP
rules:
http:
- headers:
- 'X-Forwarded-For: 192.168.1.1'
fromEntities:
- cluster`
;
const
factory
=
({
propsData
,
provide
=
{},
state
,
data
}
=
{})
=>
{
store
=
createStore
();
...
...
@@ -55,11 +38,11 @@ spec:
wrapper
=
shallowMountExtended
(
NetworkPolicyEditor
,
{
propsData
:
{
threatMonitoringPath
:
'
/threat-monitoring
'
,
projectId
:
'
21
'
,
...
propsData
,
},
provide
:
{
threatMonitoringPath
:
'
/threat-monitoring
'
,
projectId
:
'
21
'
,
...
provide
,
},
store
,
...
...
@@ -68,26 +51,22 @@ spec:
});
};
const
findRuleEditor
=
()
=>
wrapper
.
findByTestId
(
'
rule-editor
'
);
const
findPreview
=
()
=>
wrapper
.
findComponent
(
PolicyPreview
);
const
findAddRuleButton
=
()
=>
wrapper
.
findByTestId
(
'
add-rule
'
);
const
findYAMLParsingAlert
=
()
=>
wrapper
.
findByTestId
(
'
parsing-alert
'
);
const
findPolicyYamlEditor
=
()
=>
wrapper
.
findByTestId
(
'
policy-yaml-editor
'
);
const
findPolicyAlertPicker
=
()
=>
wrapper
.
findComponent
(
PolicyAlertPicker
);
const
findPolicyDescription
=
()
=>
wrapper
.
find
(
"
[id='policyDescription']
"
);
const
findPolicyEnableContainer
=
()
=>
wrapper
.
findByTestId
(
'
policy-enable
'
);
const
findPolicyName
=
()
=>
wrapper
.
find
(
"
[id='policyName']
"
);
const
findPolicyRuleBuilder
=
()
=>
wrapper
.
findComponent
(
PolicyRuleBuilder
);
const
findSavePolicy
=
()
=>
wrapper
.
findByTestId
(
'
save-policy
'
);
const
findDeletePolicy
=
()
=>
wrapper
.
findByTestId
(
'
delete-policy
'
);
const
findEditorModeToggle
=
()
=>
wrapper
.
findByTestId
(
'
editor-mode
'
);
const
findPolicyEditorLayout
=
()
=>
wrapper
.
findComponent
(
PolicyEditorLayout
);
const
modifyPolicyAlert
=
async
({
isAlertEnabled
})
=>
{
const
policyAlertPicker
=
findPolicyAlertPicker
();
policyAlertPicker
.
vm
.
$emit
(
'
update-alert
'
,
isAlertEnabled
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
policyAlertPicker
.
props
(
'
policyAlert
'
)).
toBe
(
isAlertEnabled
);
find
SavePolicy
().
vm
.
$emit
(
'
click
'
);
find
PolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
wrapper
.
vm
.
$nextTick
();
};
...
...
@@ -117,14 +96,10 @@ spec:
it
.
each
`
component | status | findComponent | state
${
'
policy alert picker
'
}
|
${
'
does display
'
}
|
${
findPolicyAlertPicker
}
|
${
true
}
${
'
editor mode toggle
'
}
|
${
'
does display
'
}
|
${
findEditorModeToggle
}
|
${
true
}
${
'
policy name input
'
}
|
${
'
does display
'
}
|
${
findPolicyName
}
|
${
true
}
${
'
rule editor
'
}
|
${
'
does display
'
}
|
${
findRuleEditor
}
|
${
true
}
${
'
add rule button
'
}
|
${
'
does display
'
}
|
${
findAddRuleButton
}
|
${
true
}
${
'
policy preview
'
}
|
${
'
does display
'
}
|
${
findPreview
}
|
${
true
}
${
'
yaml editor
'
}
|
${
'
does not display
'
}
|
${
findPolicyYamlEditor
}
|
${
false
}
${
'
parsing error alert
'
}
|
${
'
does not display
'
}
|
${
findYAMLParsingAlert
}
|
${
false
}
${
'
delete button
'
}
|
${
'
does not display
'
}
|
${
findDeletePolicy
}
|
${
false
}
`
(
'
$status the $component
'
,
async
({
findComponent
,
state
})
=>
{
expect
(
findComponent
().
exists
()).
toBe
(
state
);
});
...
...
@@ -133,38 +108,13 @@ spec:
beforeEach
(()
=>
{
factory
({
data
:
()
=>
({
editorMode
:
E
ditorMode
YAML
,
editorMode
:
E
DITOR_MODE_
YAML
,
}),
});
});
it
.
each
`
component | status | findComponent | state
${
'
editor mode toggle
'
}
|
${
'
does display
'
}
|
${
findEditorModeToggle
}
|
${
true
}
${
'
rule editor
'
}
|
${
'
does not display
'
}
|
${
findRuleEditor
}
|
${
false
}
${
'
yaml editor
'
}
|
${
'
does display
'
}
|
${
findPolicyYamlEditor
}
|
${
true
}
`
(
'
$status the $component
'
,
({
findComponent
,
state
})
=>
{
expect
(
findComponent
().
exists
()).
toBe
(
state
);
});
it
(
'
updates policy on yaml editor value change
'
,
async
()
=>
{
const
manifest
=
`apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
description: test description
metadata:
name: test-policy
labels:
app.gitlab.com/proj: '21'
spec:
endpointSelector:
matchLabels:
network-policy.gitlab.com/disabled_by: gitlab
foo: bar
ingress:
- fromEndpoints:
- matchLabels:
foo: bar`
;
findPolicyYamlEditor
().
vm
.
$emit
(
'
input
'
,
manifest
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
update-yaml
'
,
mockL3Manifest
);
expect
(
wrapper
.
vm
.
policy
).
toMatchObject
({
name
:
'
test-policy
'
,
...
...
@@ -185,16 +135,16 @@ spec:
it
(
'
saves L7 policies
'
,
async
()
=>
{
factory
({
data
:
()
=>
({
editorMode
:
E
ditorMode
YAML
,
yamlEditorValue
:
l7m
anifest
,
editorMode
:
E
DITOR_MODE_
YAML
,
yamlEditorValue
:
mockL7M
anifest
,
}),
});
findSavePolicy
().
vm
.
$emit
(
'
click
'
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
,
EDITOR_MODE_YAML
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
networkPolicies/createPolicy
'
,
{
environmentId
:
-
1
,
policy
:
{
manifest
:
l7m
anifest
},
policy
:
{
manifest
:
mockL7M
anifest
},
});
expect
(
redirectTo
).
toHaveBeenCalledWith
(
'
/threat-monitoring
'
);
});
...
...
@@ -246,13 +196,11 @@ spec:
});
it
(
'
updates yaml editor value on switch to yaml editor
'
,
async
()
=>
{
const
policyEditorLayout
=
findPolicyEditorLayout
();
findPolicyName
().
vm
.
$emit
(
'
input
'
,
'
test-policy
'
);
findEditorModeToggle
().
vm
.
$emit
(
'
input
'
,
EditorMode
YAML
);
policyEditorLayout
.
vm
.
$emit
(
'
update-editor-mode
'
,
EDITOR_MODE_
YAML
);
await
wrapper
.
vm
.
$nextTick
();
const
editor
=
findPolicyYamlEditor
();
expect
(
editor
.
exists
()).
toBe
(
true
);
expect
(
fromYaml
(
editor
.
attributes
(
'
value
'
))).
toMatchObject
({
expect
(
fromYaml
(
policyEditorLayout
.
attributes
(
'
yamleditorvalue
'
))).
toMatchObject
({
name
:
'
test-policy
'
,
});
});
...
...
@@ -296,17 +244,15 @@ spec:
it
(
'
does not update yaml editor value on switch to yaml editor
'
,
async
()
=>
{
findPolicyName
().
vm
.
$emit
(
'
input
'
,
'
test-policy
'
);
findEditorModeToggle
().
vm
.
$emit
(
'
input
'
,
EditorModeYAML
);
const
policyEditorLayout
=
findPolicyEditorLayout
();
policyEditorLayout
.
vm
.
$emit
(
'
update-editor-mode
'
,
EDITOR_MODE_YAML
);
await
wrapper
.
vm
.
$nextTick
();
const
editor
=
findPolicyYamlEditor
();
expect
(
editor
.
exists
()).
toBe
(
true
);
expect
(
editor
.
attributes
(
'
value
'
)).
toEqual
(
''
);
expect
(
policyEditorLayout
.
attributes
(
'
yamleditorvalue
'
)).
toEqual
(
''
);
});
});
it
(
'
creates policy and redirects to a threat monitoring path
'
,
async
()
=>
{
find
SavePolicy
().
vm
.
$emit
(
'
click
'
);
find
PolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
networkPolicies/createPolicy
'
,
{
...
...
@@ -326,8 +272,7 @@ spec:
});
it
(
'
it does not redirect
'
,
async
()
=>
{
findSavePolicy
().
vm
.
$emit
(
'
click
'
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
redirectTo
).
not
.
toHaveBeenCalledWith
(
'
/threat-monitoring
'
);
});
...
...
@@ -354,10 +299,7 @@ spec:
});
it
(
'
updates existing policy and redirects to a threat monitoring path
'
,
async
()
=>
{
const
saveButton
=
findSavePolicy
();
expect
(
saveButton
.
text
()).
toEqual
(
'
Save changes
'
);
saveButton
.
vm
.
$emit
(
'
click
'
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
networkPolicies/updatePolicy
'
,
{
environmentId
:
-
1
,
...
...
@@ -379,26 +321,14 @@ spec:
});
it
(
'
it does not redirect
'
,
async
()
=>
{
findSavePolicy
().
vm
.
$emit
(
'
click
'
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
redirectTo
).
not
.
toHaveBeenCalledWith
(
'
/threat-monitoring
'
);
});
});
it
(
'
renders delete button
'
,
()
=>
{
expect
(
findDeletePolicy
().
exists
()).
toBe
(
true
);
});
it
(
'
it does not trigger deletePolicy on delete button click
'
,
async
()
=>
{
findDeletePolicy
().
vm
.
$emit
(
'
click
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
store
.
dispatch
).
not
.
toHaveBeenCalledWith
(
'
networkPolicies/deletePolicy
'
);
});
it
(
'
removes policy and redirects to a threat monitoring path on secondary modal button click
'
,
async
()
=>
{
wrapper
.
findComponent
(
GlModal
).
vm
.
$emit
(
'
secondar
y
'
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
remove-polic
y
'
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
store
.
dispatch
).
toHaveBeenCalledWith
(
'
networkPolicies/deletePolicy
'
,
{
...
...
ee/spec/frontend/threat_monitoring/components/policy_editor/policy_editor_layout_spec.js
0 → 100644
View file @
38873a13
import
{
GlModal
,
GlSegmentedControl
}
from
'
@gitlab/ui
'
;
import
{
EDITOR_MODE_YAML
}
from
'
ee/threat_monitoring/components/policy_editor/constants
'
;
import
PolicyEditorLayout
from
'
ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
describe
(
'
PolicyEditorLayout component
'
,
()
=>
{
let
wrapper
;
const
threatMonitoringPath
=
'
/threat-monitoring
'
;
const
factory
=
({
propsData
=
{}
}
=
{})
=>
{
wrapper
=
shallowMountExtended
(
PolicyEditorLayout
,
{
propsData
:
{
...
propsData
,
},
provide
:
{
threatMonitoringPath
,
},
stubs
:
{
PolicyYamlEditor
:
true
},
});
};
const
findDeletePolicyButton
=
()
=>
wrapper
.
findByTestId
(
'
delete-policy
'
);
const
findDeletePolicyModal
=
()
=>
wrapper
.
findComponent
(
GlModal
);
const
findEditorModeToggle
=
()
=>
wrapper
.
findComponent
(
GlSegmentedControl
);
const
findYamlModeSection
=
()
=>
wrapper
.
findByTestId
(
'
policy-yaml-editor
'
);
const
findRuleModeSection
=
()
=>
wrapper
.
findByTestId
(
'
rule-editor
'
);
const
findRuleModePreviewSection
=
()
=>
wrapper
.
findByTestId
(
'
rule-editor-preview
'
);
const
findSavePolicyButton
=
()
=>
wrapper
.
findByTestId
(
'
save-policy
'
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
default behavior
'
,
()
=>
{
beforeEach
(()
=>
{
factory
();
});
it
.
each
`
component | status | findComponent | state
${
'
editor mode toggle
'
}
|
${
'
does display
'
}
|
${
findEditorModeToggle
}
|
${
true
}
${
'
delete button
'
}
|
${
'
does not display
'
}
|
${
findDeletePolicyButton
}
|
${
false
}
`
(
'
$status the $component
'
,
async
({
findComponent
,
state
})
=>
{
expect
(
findComponent
().
exists
()).
toBe
(
state
);
});
it
(
'
does display the correct save button text when creating a new policy
'
,
()
=>
{
const
saveButton
=
findSavePolicyButton
();
expect
(
saveButton
.
exists
()).
toBe
(
true
);
expect
(
saveButton
.
text
()).
toBe
(
'
Create policy
'
);
});
it
(
'
emits properly with the current mode when the save button is clicked
'
,
()
=>
{
findSavePolicyButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
save-policy
'
)).
toStrictEqual
([[
'
rule
'
]]);
});
it
(
'
mode changes appropriately when new mode is selected
'
,
async
()
=>
{
expect
(
findRuleModeSection
().
exists
()).
toBe
(
true
);
expect
(
findYamlModeSection
().
exists
()).
toBe
(
false
);
findEditorModeToggle
().
vm
.
$emit
(
'
input
'
,
EDITOR_MODE_YAML
);
await
wrapper
.
vm
.
$nextTick
();
expect
(
findRuleModeSection
().
exists
()).
toBe
(
false
);
expect
(
findYamlModeSection
().
exists
()).
toBe
(
true
);
expect
(
wrapper
.
emitted
(
'
update-editor-mode
'
)).
toStrictEqual
([[
EDITOR_MODE_YAML
]]);
});
it
(
'
does display custom save button text
'
,
()
=>
{
// custom save button text works
const
saveButton
=
findSavePolicyButton
();
expect
(
saveButton
.
exists
()).
toBe
(
true
);
expect
(
saveButton
.
text
()).
toBe
(
'
Create policy
'
);
});
});
describe
(
'
editing a policy
'
,
()
=>
{
beforeEach
(()
=>
{
factory
({
propsData
:
{
isEditing
:
true
}
});
});
it
(
'
does not emit when the delete button is clicked
'
,
()
=>
{
findDeletePolicyButton
().
vm
.
$emit
(
'
click
'
);
expect
(
wrapper
.
emitted
(
'
remove-policy
'
)).
toStrictEqual
(
undefined
);
});
it
(
'
emits properly when the delete modal is closed
'
,
()
=>
{
findDeletePolicyModal
().
vm
.
$emit
(
'
secondary
'
);
expect
(
wrapper
.
emitted
(
'
remove-policy
'
)).
toStrictEqual
([[]]);
});
});
describe
(
'
rule mode
'
,
()
=>
{
beforeEach
(()
=>
{
factory
();
});
it
.
each
`
component | status | findComponent | state
${
'
rule mode section
'
}
|
${
'
does display
'
}
|
${
findRuleModeSection
}
|
${
true
}
${
'
rule mode preview section
'
}
|
${
'
does display
'
}
|
${
findRuleModePreviewSection
}
|
${
true
}
${
'
yaml mode section
'
}
|
${
'
does not display
'
}
|
${
findYamlModeSection
}
|
${
false
}
`
(
'
$status the $component
'
,
async
({
findComponent
,
state
})
=>
{
expect
(
findComponent
().
exists
()).
toBe
(
state
);
});
});
describe
(
'
yaml mode
'
,
()
=>
{
beforeEach
(()
=>
{
factory
({
propsData
:
{
defaultEditorMode
:
EDITOR_MODE_YAML
}
});
});
it
.
each
`
component | status | findComponent | state
${
'
rule mode section
'
}
|
${
'
does not display
'
}
|
${
findRuleModeSection
}
|
${
false
}
${
'
rule mode preview section
'
}
|
${
'
does not display
'
}
|
${
findRuleModePreviewSection
}
|
${
false
}
${
'
yaml mode section
'
}
|
${
'
does display
'
}
|
${
findYamlModeSection
}
|
${
true
}
`
(
'
$status the $component
'
,
async
({
findComponent
,
state
})
=>
{
expect
(
findComponent
().
exists
()).
toBe
(
state
);
});
it
(
'
emits propertly when yaml is updated
'
,
()
=>
{
const
newManifest
=
'
new yaml!
'
;
findYamlModeSection
().
vm
.
$emit
(
'
input
'
,
newManifest
);
expect
(
wrapper
.
emitted
(
'
update-yaml
'
)).
toStrictEqual
([[
newManifest
]]);
});
});
});
ee/spec/frontend/threat_monitoring/components/policy_editor/policy_editor_spec.js
View file @
38873a13
...
...
@@ -14,7 +14,7 @@ describe('PolicyEditor component', () => {
const
findFormSelect
=
()
=>
wrapper
.
findComponent
(
GlFormSelect
);
const
findNeworkPolicyEditor
=
()
=>
wrapper
.
findComponent
(
NetworkPolicyEditor
);
const
factory
=
({
propsData
}
=
{})
=>
{
const
factory
=
({
propsData
=
{},
provide
=
{}
}
=
{})
=>
{
store
=
createStore
();
jest
.
spyOn
(
store
,
'
dispatch
'
).
mockImplementation
(()
=>
Promise
.
resolve
());
...
...
@@ -25,18 +25,19 @@ describe('PolicyEditor component', () => {
projectId
:
'
21
'
,
...
propsData
,
},
provide
,
store
,
stubs
:
{
GlFormSelect
},
});
};
beforeEach
(
factory
);
afterEach
(()
=>
{
wrapper
.
destroy
();
});
describe
(
'
default
'
,
()
=>
{
beforeEach
(
factory
);
it
(
'
renders the environment picker
'
,
()
=>
{
expect
(
findEnvironmentPicker
().
exists
()).
toBe
(
true
);
});
...
...
@@ -45,10 +46,23 @@ describe('PolicyEditor component', () => {
const
formSelect
=
findFormSelect
();
expect
(
formSelect
.
exists
()).
toBe
(
true
);
expect
(
formSelect
.
attributes
(
'
value
'
)).
toBe
(
POLICY_TYPES
.
networkPolicy
.
value
);
expect
(
formSelect
.
attributes
(
'
disabled
'
)).
toBe
(
'
true
'
);
});
it
(
'
renders the "NetworkPolicyEditor" component
'
,
()
=>
{
expect
(
findNeworkPolicyEditor
().
exists
()).
toBe
(
true
);
});
});
describe
(
'
with "scanExecutionPolicyUi" feature flag enabled
'
,
()
=>
{
beforeEach
(()
=>
{
factory
({
provide
:
{
glFeatures
:
{
scanExecutionPolicyUi
:
true
}
}
});
});
it
(
'
renders the form select
'
,
()
=>
{
const
formSelect
=
findFormSelect
();
expect
(
formSelect
.
exists
()).
toBe
(
true
);
expect
(
formSelect
.
attributes
(
'
disabled
'
)).
toBe
(
undefined
);
});
});
});
ee/spec/frontend/threat_monitoring/components/policy_editor/scan_execution_policy/lib/from_yaml_spec.js
0 → 100644
View file @
38873a13
import
{
DEFAULT_SCAN_EXECUTION_POLICY
,
fromYaml
,
}
from
'
ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib
'
;
describe
(
'
fromYaml
'
,
()
=>
{
it
(
'
returns policy object
'
,
()
=>
{
expect
(
fromYaml
(
DEFAULT_SCAN_EXECUTION_POLICY
)).
toMatchObject
({
name
:
''
,
description
:
''
,
enabled
:
false
,
actions
:
[{
scan
:
'
dast
'
,
site_profile
:
''
,
scanner_profile
:
''
}],
rules
:
[{
branches
:
[
'
main
'
],
type
:
'
pipeline
'
}],
});
});
});
ee/spec/frontend/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_spec.js
0 → 100644
View file @
38873a13
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
PolicyEditorLayout
from
'
ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue
'
;
import
{
DEFAULT_SCAN_EXECUTION_POLICY
}
from
'
ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib
'
;
import
ScanExecutionPolicyEditor
from
'
ee/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_editor.vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
describe
(
'
ScanExecutionPolicyEditor
'
,
()
=>
{
let
wrapper
;
const
factory
=
({
propsData
=
{}
}
=
{})
=>
{
wrapper
=
shallowMount
(
ScanExecutionPolicyEditor
,
{
propsData
,
provide
:
{
threatMonitoringPath
:
''
,
projectId
:
1
,
},
});
};
const
findPolicyEditorLayout
=
()
=>
wrapper
.
findComponent
(
PolicyEditorLayout
);
beforeEach
(()
=>
{
factory
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
calls the save policy funtion when "save-policy" is emitted
'
,
async
()
=>
{
const
savePolicySpy
=
jest
.
spyOn
(
wrapper
.
vm
,
'
savePolicy
'
);
expect
(
wrapper
.
vm
.
savePolicy
).
toHaveBeenCalledTimes
(
0
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
save-policy
'
);
await
waitForPromises
();
expect
(
wrapper
.
vm
.
savePolicy
).
toHaveBeenCalledTimes
(
1
);
savePolicySpy
.
mockRestore
();
});
it
(
'
calls the remove policy funtion when "remove-policy" is emitted
'
,
async
()
=>
{
const
removePolicySpy
=
jest
.
spyOn
(
wrapper
.
vm
,
'
removePolicy
'
);
expect
(
wrapper
.
vm
.
removePolicy
).
toHaveBeenCalledTimes
(
0
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
remove-policy
'
);
await
waitForPromises
();
expect
(
wrapper
.
vm
.
removePolicy
).
toHaveBeenCalledTimes
(
1
);
removePolicySpy
.
mockRestore
();
});
it
(
'
updates the policy yaml when "update-yaml" is emitted
'
,
async
()
=>
{
const
updateYamlSpy
=
jest
.
spyOn
(
wrapper
.
vm
,
'
updateYaml
'
);
const
newManifest
=
'
new yaml!
'
;
expect
(
wrapper
.
vm
.
updateYaml
).
toHaveBeenCalledTimes
(
0
);
expect
(
findPolicyEditorLayout
().
attributes
(
'
yamleditorvalue
'
)).
toBe
(
DEFAULT_SCAN_EXECUTION_POLICY
,
);
findPolicyEditorLayout
().
vm
.
$emit
(
'
update-yaml
'
,
newManifest
);
await
waitForPromises
();
expect
(
wrapper
.
vm
.
updateYaml
).
toHaveBeenCalledTimes
(
1
);
expect
(
wrapper
.
vm
.
updateYaml
).
toHaveBeenCalledWith
(
newManifest
);
expect
(
findPolicyEditorLayout
().
attributes
(
'
yaml-editor-value
'
)).
toBe
(
newManifest
);
updateYamlSpy
.
mockRestore
();
});
});
ee/spec/frontend/threat_monitoring/mocks/mock_data.js
View file @
38873a13
...
...
@@ -181,3 +181,40 @@ export const mockAlertDetails = {
title
:
'
dropingress
'
,
monitorTool
:
'
Cilium
'
,
};
export
const
mockL7Manifest
=
`apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: limit-inbound-ip
spec:
endpointSelector: {}
ingress:
- toPorts:
- ports:
- port: '80'
protocol: TCP
- port: '443'
protocol: TCP
rules:
http:
- headers:
- 'X-Forwarded-For: 192.168.1.1'
fromEntities:
- cluster`
;
export
const
mockL3Manifest
=
`apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
description: test description
metadata:
name: test-policy
labels:
app.gitlab.com/proj: '21'
spec:
endpointSelector:
matchLabels:
network-policy.gitlab.com/disabled_by: gitlab
foo: bar
ingress:
- fromEndpoints:
- matchLabels:
foo: bar`
;
ee/spec/frontend/threat_monitoring/utils_spec.js
View file @
38873a13
import
{
getContentWrapperHeight
}
from
'
ee/threat_monitoring/utils
'
;
import
{
getContentWrapperHeight
,
removeUnnecessaryDashes
}
from
'
ee/threat_monitoring/utils
'
;
import
{
setHTMLFixture
}
from
'
helpers/fixtures
'
;
describe
(
'
Threat Monitoring Utils
'
,
()
=>
{
...
...
@@ -23,4 +23,16 @@ describe('Threat Monitoring Utils', () => {
expect
(
getContentWrapperHeight
(
'
.does-not-exist
'
)).
toBe
(
''
);
});
});
describe
(
'
removeUnnecessaryDashes
'
,
()
=>
{
it
.
each
`
input | output
${
'
---
\n
one
'
}
|
${
'
one
'
}
${
'
two
'
}
|
${
'
two
'
}
${
'
--
\n
three
'
}
|
${
'
--
\n
three
'
}
${
'
four---
\n
'
}
|
${
'
four
'
}
`
(
'
returns $output when used on $input
'
,
({
input
,
output
})
=>
{
expect
(
removeUnnecessaryDashes
(
input
)).
toBe
(
output
);
});
});
});
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