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
532bc104
Commit
532bc104
authored
Jan 25, 2020
by
Illya Klymov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Move tests to VTU
Update ide components spec to use @vue/test-utils
parent
97f38bf7
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
180 additions
and
179 deletions
+180
-179
spec/frontend/ide/components/branches/item_spec.js
spec/frontend/ide/components/branches/item_spec.js
+33
-33
spec/frontend/ide/components/jobs/detail/scroll_button_spec.js
...frontend/ide/components/jobs/detail/scroll_button_spec.js
+29
-38
spec/frontend/ide/components/preview/navigator_spec.js
spec/frontend/ide/components/preview/navigator_spec.js
+118
-108
No files found.
spec/frontend/ide/components/branches/item_spec.js
View file @
532bc104
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
mountCompontent
from
'
helpers/vue_mount_component_helper
'
;
import
router
from
'
~/ide/ide_router
'
;
import
router
from
'
~/ide/ide_router
'
;
import
Item
from
'
~/ide/components/branches/item.vue
'
;
import
Item
from
'
~/ide/components/branches/item.vue
'
;
import
{
getTimeago
}
from
'
~/lib/utils/datetime_utility
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
Timeago
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
{
projectData
}
from
'
../../mock_data
'
;
import
{
projectData
}
from
'
../../mock_data
'
;
const
TEST_BRANCH
=
{
const
TEST_BRANCH
=
{
...
@@ -12,45 +12,45 @@ const TEST_BRANCH = {
...
@@ -12,45 +12,45 @@ const TEST_BRANCH = {
const
TEST_PROJECT_ID
=
projectData
.
name_with_namespace
;
const
TEST_PROJECT_ID
=
projectData
.
name_with_namespace
;
describe
(
'
IDE branch item
'
,
()
=>
{
describe
(
'
IDE branch item
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
Item
);
let
wrapper
;
let
vm
;
beforeEach
(()
=>
{
function
createComponent
(
props
=
{})
{
vm
=
mountCompontent
(
Component
,
{
wrapper
=
shallowMount
(
Item
,
{
propsData
:
{
item
:
{
...
TEST_BRANCH
},
item
:
{
...
TEST_BRANCH
},
projectId
:
TEST_PROJECT_ID
,
projectId
:
TEST_PROJECT_ID
,
isActive
:
false
,
isActive
:
false
,
...
props
,
},
});
});
}
);
}
afterEach
(()
=>
{
afterEach
(()
=>
{
vm
.
$
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
if not active
'
,
()
=>
{
beforeEach
(()
=>
{
createComponent
();
});
it
(
'
renders branch name and timeago
'
,
()
=>
{
it
(
'
renders branch name and timeago
'
,
()
=>
{
const
timeText
=
getTimeago
().
format
(
TEST_BRANCH
.
committedDate
);
expect
(
wrapper
.
text
()).
toContain
(
TEST_BRANCH
.
name
);
expect
(
wrapper
.
find
(
Timeago
).
props
(
'
time
'
)).
toBe
(
TEST_BRANCH
.
committedDate
);
expect
(
vm
.
$el
.
textContent
).
toContain
(
TEST_BRANCH
.
name
);
expect
(
wrapper
.
find
(
Icon
).
exists
()).
toBe
(
false
);
expect
(
vm
.
$el
.
querySelector
(
'
time
'
)).
toHaveText
(
timeText
);
expect
(
vm
.
$el
.
querySelector
(
'
.ic-mobile-issue-close
'
)).
toBe
(
null
);
});
});
it
(
'
renders link to branch
'
,
()
=>
{
it
(
'
renders link to branch
'
,
()
=>
{
const
expectedHref
=
router
.
resolve
(
`/project/
${
TEST_PROJECT_ID
}
/edit/
${
TEST_BRANCH
.
name
}
`
)
const
expectedHref
=
router
.
resolve
(
`/project/
${
TEST_PROJECT_ID
}
/edit/
${
TEST_BRANCH
.
name
}
`
)
.
href
;
.
href
;
expect
(
vm
.
$el
.
textContent
).
toMatch
(
'
a
'
);
expect
(
wrapper
.
text
()).
toMatch
(
'
a
'
);
expect
(
vm
.
$el
).
toHaveAttr
(
'
href
'
,
expectedHref
);
expect
(
wrapper
.
attributes
(
'
href
'
)).
toBe
(
expectedHref
);
});
});
});
it
(
'
renders icon if is
Active
'
,
done
=>
{
it
(
'
renders icon if is
not active
'
,
()
=>
{
vm
.
isActive
=
true
;
createComponent
({
isActive
:
true
})
;
vm
.
$nextTick
()
expect
(
wrapper
.
find
(
Icon
).
exists
()).
toBe
(
true
);
.
then
(()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ic-mobile-issue-close
'
)).
not
.
toBe
(
null
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
});
});
spec/frontend/ide/components/jobs/detail/scroll_button_spec.js
View file @
532bc104
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
ScrollButton
from
'
~/ide/components/jobs/detail/scroll_button.vue
'
;
import
ScrollButton
from
'
~/ide/components/jobs/detail/scroll_button.vue
'
;
import
mountComponent
from
'
../../../../helpers/vue_mount_component_helper
'
;
describe
(
'
IDE job log scroll button
'
,
()
=>
{
describe
(
'
IDE job log scroll button
'
,
()
=>
{
const
Component
=
Vue
.
extend
(
ScrollButton
);
let
wrapper
;
let
vm
;
beforeEach
(()
=>
{
const
createComponent
=
props
=>
{
vm
=
mountComponent
(
Component
,
{
wrapper
=
shallowMount
(
ScrollButton
,
{
propsData
:
{
direction
:
'
up
'
,
direction
:
'
up
'
,
disabled
:
false
,
disabled
:
false
,
...
props
,
},
});
});
}
)
;
};
afterEach
(()
=>
{
afterEach
(()
=>
{
vm
.
$
destroy
();
wrapper
.
destroy
();
});
});
describe
(
'
iconName
'
,
()
=>
{
describe
.
each
`
[
'
up
'
,
'
down
'
].
forEach
(
direction
=>
{
direction | icon | title
it
(
`returns icon name for
${
direction
}
`
,
()
=>
{
${
'
up
'
}
|
${
'
scroll_up
'
}
|
${
'
Scroll to top
'
}
vm
.
direction
=
direction
;
${
'
down
'
}
|
${
'
scroll_down
'
}
|
${
'
Scroll to bottom
'
}
`
(
'
for $direction direction
'
,
({
direction
,
icon
,
title
})
=>
{
beforeEach
(()
=>
createComponent
({
direction
}));
expect
(
vm
.
iconName
).
toBe
(
`scroll_
${
direction
}
`
);
it
(
'
returns proper icon name
'
,
()
=>
{
});
expect
(
wrapper
.
find
(
Icon
).
props
(
'
name
'
)).
toBe
(
icon
);
});
});
});
describe
(
'
tooltipTitle
'
,
()
=>
{
it
(
'
returns proper title
'
,
()
=>
{
it
(
'
returns title for up
'
,
()
=>
{
expect
(
wrapper
.
attributes
(
'
data-original-title
'
)).
toBe
(
title
);
expect
(
vm
.
tooltipTitle
).
toBe
(
'
Scroll to top
'
);
});
it
(
'
returns title for down
'
,
()
=>
{
vm
.
direction
=
'
down
'
;
expect
(
vm
.
tooltipTitle
).
toBe
(
'
Scroll to bottom
'
);
});
});
});
});
it
(
'
emits click event on click
'
,
()
=>
{
it
(
'
emits click event on click
'
,
()
=>
{
jest
.
spyOn
(
vm
,
'
$emit
'
).
mockImplementation
(()
=>
{});
createComponent
();
vm
.
$el
.
querySelector
(
'
.btn-scroll
'
).
click
();
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
click
'
);
wrapper
.
find
(
'
button
'
).
trigger
(
'
click
'
);
expect
(
wrapper
.
emitted
().
click
).
toBeDefined
();
});
});
it
(
'
disables button when disabled is true
'
,
done
=>
{
it
(
'
disables button when disabled is true
'
,
()
=>
{
vm
.
disabled
=
true
;
createComponent
({
disabled
:
true
})
;
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
'
button
'
).
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.btn-scroll
'
).
hasAttribute
(
'
disabled
'
)).
toBe
(
true
);
done
();
});
});
});
});
});
spec/frontend/ide/components/preview/navigator_spec.js
View file @
532bc104
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
mountComponent
from
'
helpers/vue_mount_component_helper
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
ClientsideNavigator
from
'
~/ide/components/preview/navigator.vue
'
;
import
ClientsideNavigator
from
'
~/ide/components/preview/navigator.vue
'
;
import
{
listen
}
from
'
codesandbox-api
'
;
jest
.
mock
(
'
codesandbox-api
'
,
()
=>
({
listen
:
jest
.
fn
().
mockReturnValue
(
jest
.
fn
()),
}));
describe
(
'
IDE clientside preview navigator
'
,
()
=>
{
describe
(
'
IDE clientside preview navigator
'
,
()
=>
{
let
vm
;
let
wrapper
;
let
Component
;
let
manager
;
let
manager
;
let
listenHandler
;
beforeAll
(()
=>
{
const
findBackButton
=
()
=>
wrapper
.
findAll
(
'
button
'
).
at
(
0
);
Component
=
Vue
.
extend
(
ClientsideNavigator
);
const
findForwardButton
=
()
=>
wrapper
.
findAll
(
'
button
'
).
at
(
1
);
}
);
const
findRefreshButton
=
()
=>
wrapper
.
findAll
(
'
button
'
).
at
(
2
);
beforeEach
(()
=>
{
beforeEach
(()
=>
{
listen
.
mockClear
();
manager
=
{
bundlerURL
:
TEST_HOST
,
iframe
:
{
src
:
''
}
};
manager
=
{
bundlerURL
:
TEST_HOST
,
iframe
:
{
src
:
''
}
};
vm
=
mountComponent
(
Component
,
{
manager
});
wrapper
=
shallowMount
(
ClientsideNavigator
,
{
propsData
:
{
manager
}
});
[[
listenHandler
]]
=
listen
.
mock
.
calls
;
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
vm
.
$
destroy
();
wrapper
.
destroy
();
});
});
it
(
'
renders readonly URL bar
'
,
()
=>
{
it
(
'
renders readonly URL bar
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
input[readonly]
'
).
value
).
toBe
(
'
/
'
);
listenHandler
({
type
:
'
urlchange
'
,
url
:
manager
.
bundlerURL
});
return
wrapper
.
vm
.
$nextTick
(()
=>
{
expect
(
wrapper
.
find
(
'
input[readonly]
'
).
element
.
value
).
toBe
(
'
/
'
);
});
});
it
(
'
disables back button when navigationStack is empty
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.ide-navigator-btn
'
)).
toHaveAttr
(
'
disabled
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.ide-navigator-btn
'
).
classList
).
toContain
(
'
disabled-content
'
);
});
});
it
(
'
disables forward button when forwardNavigationStack is empty
'
,
()
=>
{
it
(
'
renders loading icon by default
'
,
()
=>
{
vm
.
forwardNavigationStack
=
[];
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
true
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
.ide-navigator-btn
'
)[
1
]).
toHaveAttr
(
'
disabled
'
);
expect
(
vm
.
$el
.
querySelectorAll
(
'
.ide-navigator-btn
'
)[
1
].
classList
).
toContain
(
'
disabled-content
'
,
);
});
});
it
(
'
calls back method when clicking back button
'
,
done
=>
{
it
(
'
removes loading icon when done event is fired
'
,
()
=>
{
vm
.
navigationStack
.
push
(
'
/test
'
);
listenHandler
({
type
:
'
done
'
});
vm
.
navigationStack
.
push
(
'
/test2
'
);
return
wrapper
.
vm
.
$nextTick
(()
=>
{
jest
.
spyOn
(
vm
,
'
back
'
).
mockReturnValue
();
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
false
);
vm
.
$nextTick
(()
=>
{
vm
.
$el
.
querySelector
(
'
.ide-navigator-btn
'
).
click
();
expect
(
vm
.
back
).
toHaveBeenCalled
();
done
();
});
});
});
});
it
(
'
calls forward method when clicking forward button
'
,
done
=>
{
it
(
'
does not count visiting same url multiple times
'
,
()
=>
{
vm
.
forwardNavigationStack
.
push
(
'
/test
'
);
listenHandler
({
type
:
'
done
'
});
jest
.
spyOn
(
vm
,
'
forward
'
).
mockReturnValue
();
listenHandler
({
type
:
'
done
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
listenHandler
({
type
:
'
done
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
vm
.
$nextTick
(()
=>
{
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
vm
.
$el
.
querySelectorAll
(
'
.ide-navigator-btn
'
)[
1
].
click
();
expect
(
findBackButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
expect
(
vm
.
forward
).
toHaveBeenCalled
();
done
();
});
});
});
});
describe
(
'
onUrlChange
'
,
()
=>
{
it
(
'
unsubscribes from listen on destroy
'
,
()
=>
{
it
(
'
updates the path
'
,
()
=>
{
const
unsubscribeFn
=
listen
();
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
expect
(
vm
.
path
).
toBe
(
'
/url
'
);
wrapper
.
destroy
();
expect
(
unsubscribeFn
).
toHaveBeenCalled
();
});
});
it
(
'
sets currentBrowsingIndex 0 if not already set
'
,
()
=>
{
describe
(
'
back button
'
,
()
=>
{
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
beforeEach
(()
=>
{
listenHandler
({
type
:
'
done
'
});
expect
(
vm
.
currentBrowsingIndex
).
toBe
(
0
);
listenHandler
({
type
:
'
urlchange
'
,
url
:
TEST_HOST
});
return
wrapper
.
vm
.
$nextTick
();
});
});
it
(
'
increases currentBrowsingIndex if path doesnt match
'
,
()
=>
{
it
(
'
is disabled by default
'
,
()
=>
{
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
expect
(
findBackButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url2`
});
expect
(
vm
.
currentBrowsingIndex
).
toBe
(
1
);
});
});
it
(
'
does not increase currentBrowsingIndex if path matches
'
,
()
=>
{
it
(
'
is enabled when there is previous entry
'
,
()
=>
{
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
findBackButton
().
trigger
(
'
click
'
);
expect
(
findBackButton
().
attributes
(
'
disabled
'
)).
toBeFalsy
();
});
});
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
it
(
'
is disabled when there is no previous entry
'
,
()
=>
{
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
return
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
findBackButton
().
trigger
(
'
click
'
);
expect
(
vm
.
currentBrowsingIndex
).
toBe
(
0
);
return
wrapper
.
vm
.
$nextTick
();
})
.
then
(()
=>
{
expect
(
findBackButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
});
});
});
it
(
'
pushes path into navigation stack
'
,
()
=>
{
it
(
'
updates manager iframe src
'
,
()
=>
{
vm
.
onUrlChange
({
url
:
`
${
TEST_HOST
}
/url`
});
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url2`
});
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
findBackButton
().
trigger
(
'
click
'
);
expect
(
vm
.
navigationStack
).
toEqual
([
'
/url
'
]);
expect
(
manager
.
iframe
.
src
).
toBe
(
`
${
TEST_HOST
}
/url1`
);
});
});
});
});
});
describe
(
'
back
'
,
()
=>
{
describe
(
'
forward button
'
,
()
=>
{
beforeEach
(()
=>
{
beforeEach
(()
=>
{
vm
.
path
=
'
/test2
'
;
listenHandler
({
type
:
'
done
'
});
vm
.
currentBrowsingIndex
=
1
;
listenHandler
({
type
:
'
urlchange
'
,
url
:
TEST_HOST
});
vm
.
navigationStack
.
push
(
'
/test
'
);
return
wrapper
.
vm
.
$nextTick
();
vm
.
navigationStack
.
push
(
'
/test2
'
);
jest
.
spyOn
(
vm
,
'
visitPath
'
).
mockReturnValue
();
vm
.
back
();
});
});
it
(
'
visits the last entry in navigationStack
'
,
()
=>
{
it
(
'
is disabled by default
'
,
()
=>
{
expect
(
vm
.
visitPath
).
toHaveBeenCalledWith
(
'
/test
'
);
expect
(
findForwardButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
});
});
it
(
'
adds last entry to forwardNavigationStack
'
,
()
=>
{
it
(
'
is enabled when there is next entry
'
,
()
=>
{
expect
(
vm
.
forwardNavigationStack
).
toEqual
([
'
/test2
'
]);
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
return
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
findBackButton
().
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
();
})
.
then
(()
=>
{
expect
(
findForwardButton
().
attributes
(
'
disabled
'
)).
toBeFalsy
();
});
});
it
(
'
clears navigation stack if currentBrowsingIndex is 1
'
,
()
=>
{
expect
(
vm
.
navigationStack
).
toEqual
([]);
});
});
it
(
'
sets currentBrowsingIndex to null is currentBrowsingIndex is 1
'
,
()
=>
{
it
(
'
is disabled when there is no next entry
'
,
()
=>
{
expect
(
vm
.
currentBrowsingIndex
).
toBe
(
null
);
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
return
wrapper
.
vm
.
$nextTick
()
.
then
(()
=>
{
findBackButton
().
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
();
})
.
then
(()
=>
{
findForwardButton
().
trigger
(
'
click
'
);
return
wrapper
.
vm
.
$nextTick
();
})
.
then
(()
=>
{
expect
(
findForwardButton
().
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
});
});
});
});
describe
(
'
forward
'
,
()
=>
{
it
(
'
updates manager iframe src
'
,
()
=>
{
it
(
'
calls visitPath with first entry in forwardNavigationStack
'
,
()
=>
{
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url1`
});
jest
.
spyOn
(
vm
,
'
visitPath
'
).
mockReturnValue
();
listenHandler
({
type
:
'
urlchange
'
,
url
:
`
${
TEST_HOST
}
/url2`
});
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
vm
.
forwardNavigationStack
.
push
(
'
/test
'
);
findBackButton
().
trigger
(
'
click
'
);
vm
.
forwardNavigationStack
.
push
(
'
/test2
'
);
vm
.
forward
();
expect
(
manager
.
iframe
.
src
).
toBe
(
`
${
TEST_HOST
}
/url1`
);
expect
(
vm
.
visitPath
).
toHaveBeenCalledWith
(
'
/test
'
);
});
});
});
});
describe
(
'
refresh
'
,
()
=>
{
it
(
'
calls refresh with current path
'
,
()
=>
{
jest
.
spyOn
(
vm
,
'
visitPath
'
).
mockReturnValue
();
vm
.
path
=
'
/test
'
;
vm
.
refresh
();
expect
(
vm
.
visitPath
).
toHaveBeenCalledWith
(
'
/test
'
);
});
});
describe
(
'
refresh button
'
,
()
=>
{
const
url
=
`
${
TEST_HOST
}
/some_url`
;
beforeEach
(()
=>
{
listenHandler
({
type
:
'
done
'
});
listenHandler
({
type
:
'
urlchange
'
,
url
});
return
wrapper
.
vm
.
$nextTick
();
});
});
describe
(
'
visitP
ath
'
,
()
=>
{
it
(
'
calls refresh with current p
ath
'
,
()
=>
{
it
(
'
updates iframe src with passed in path
'
,
()
=>
{
manager
.
iframe
.
src
=
'
something-other
'
;
vm
.
visitPath
(
'
/testpath
'
);
findRefreshButton
().
trigger
(
'
click
'
);
expect
(
manager
.
iframe
.
src
).
toBe
(
`
${
TEST_HOST
}
/testpath`
);
expect
(
manager
.
iframe
.
src
).
toBe
(
url
);
});
});
});
});
});
});
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