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
88d94289
Commit
88d94289
authored
Oct 21, 2020
by
Jarek Ostrowski
Committed by
Natalia Tepluhina
Oct 21, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update change username to gl-modal
MR:
https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44325
parent
d802e948
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
127 additions
and
141 deletions
+127
-141
app/assets/javascripts/profile/account/components/update_username.vue
...avascripts/profile/account/components/update_username.vue
+30
-14
changelogs/unreleased/229674-update-change-username-modal.yml
...gelogs/unreleased/229674-update-change-username-modal.yml
+5
-0
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/features/profile_spec.rb
spec/features/profile_spec.rb
+2
-2
spec/features/profiles/account_spec.rb
spec/features/profiles/account_spec.rb
+2
-2
spec/frontend/profile/account/components/update_username_spec.js
...ontend/profile/account/components/update_username_spec.js
+85
-123
No files found.
app/assets/javascripts/profile/account/components/update_username.vue
View file @
88d94289
<
script
>
<
script
>
/* eslint-disable vue/no-v-html */
import
{
escape
}
from
'
lodash
'
;
import
{
escape
}
from
'
lodash
'
;
import
{
Gl
Button
}
from
'
@gitlab/ui
'
;
import
{
Gl
SafeHtmlDirective
as
SafeHtml
,
GlButton
,
GlModal
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
DeprecatedModal2
from
'
~/vue_shared/components/deprecated_modal_2.vue
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
{
s__
,
sprintf
}
from
'
~/locale
'
;
import
{
deprecatedCreateFlash
as
Flash
}
from
'
~/flash
'
;
import
{
deprecatedCreateFlash
as
Flash
}
from
'
~/flash
'
;
export
default
{
export
default
{
components
:
{
components
:
{
GlModal
:
DeprecatedModal2
,
GlModal
,
GlButton
,
GlButton
,
},
},
directives
:
{
GlModalDirective
,
SafeHtml
,
},
props
:
{
props
:
{
actionUrl
:
{
actionUrl
:
{
type
:
String
,
type
:
String
,
...
@@ -54,6 +56,21 @@ Please update your Git repository remotes as soon as possible.`),
...
@@ -54,6 +56,21 @@ Please update your Git repository remotes as soon as possible.`),
false
,
false
,
);
);
},
},
primaryProps
()
{
return
{
text
:
s__
(
'
Update username
'
),
attributes
:
[
{
variant
:
'
warning
'
},
{
category
:
'
primary
'
},
{
disabled
:
this
.
isRequestPending
},
],
};
},
cancelProps
()
{
return
{
text
:
s__
(
'
Cancel
'
),
};
},
},
},
methods
:
{
methods
:
{
onConfirm
()
{
onConfirm
()
{
...
@@ -103,22 +120,21 @@ Please update your Git repository remotes as soon as possible.`),
...
@@ -103,22 +120,21 @@ Please update your Git repository remotes as soon as possible.`),
<p
class=
"form-text text-muted"
>
{{
path
}}
</p>
<p
class=
"form-text text-muted"
>
{{
path
}}
</p>
</div>
</div>
<gl-button
<gl-button
:data-target=
"`#$
{$options.modalId}`
"
v-gl-modal-directive=
"$options.modalId
"
:disabled=
"isRequestPending || newUsername === username"
:disabled=
"isRequestPending || newUsername === username"
category=
"primary"
category=
"primary"
variant=
"warning"
variant=
"warning"
data-toggle="modal"
data-testid=
"username-change-confirmation-modal"
>
{{
$options
.
buttonText
}}
</gl-button
>
>
{{
$options
.
buttonText
}}
</gl-button>
<gl-modal
<gl-modal
:id=
"$options.modalId"
:
modal-
id=
"$options.modalId"
:
header-title-text
=
"s__('Profiles|Change username') + '?'"
:
title
=
"s__('Profiles|Change username') + '?'"
:
footer-primary-button-text=
"$options.buttonText
"
:
action-primary=
"primaryProps
"
footer-primary-button-variant=
"warning
"
:action-cancel=
"cancelProps
"
@
submit
=
"onConfirm"
@
primary
=
"onConfirm"
>
>
<span
v-html=
"modalText"
></span>
<span
v-
safe-
html=
"modalText"
></span>
</gl-modal>
</gl-modal>
</div>
</div>
</
template
>
</
template
>
changelogs/unreleased/229674-update-change-username-modal.yml
0 → 100644
View file @
88d94289
---
title
:
Update change username modal
merge_request
:
44325
author
:
type
:
changed
locale/gitlab.pot
View file @
88d94289
...
@@ -28386,6 +28386,9 @@ msgstr ""
...
@@ -28386,6 +28386,9 @@ msgstr ""
msgid "Update now"
msgid "Update now"
msgstr ""
msgstr ""
msgid "Update username"
msgstr ""
msgid "Update variable"
msgid "Update variable"
msgstr ""
msgstr ""
...
...
spec/features/profile_spec.rb
View file @
88d94289
...
@@ -101,10 +101,10 @@ RSpec.describe 'Profile account page', :js do
...
@@ -101,10 +101,10 @@ RSpec.describe 'Profile account page', :js do
it
'changes my username'
do
it
'changes my username'
do
fill_in
'username-change-input'
,
with:
'new-username'
fill_in
'username-change-input'
,
with:
'new-username'
page
.
find
(
'[data-t
arget="#
username-change-confirmation-modal"]'
).
click
page
.
find
(
'[data-t
estid="
username-change-confirmation-modal"]'
).
click
page
.
within
(
'.modal'
)
do
page
.
within
(
'.modal'
)
do
find
(
'.js-modal-
primary-action
'
).
click
find
(
'.js-modal-
action-primary
'
).
click
end
end
expect
(
page
).
to
have_content
(
'new-username'
)
expect
(
page
).
to
have_content
(
'new-username'
)
...
...
spec/features/profiles/account_spec.rb
View file @
88d94289
...
@@ -128,10 +128,10 @@ def update_username(new_username)
...
@@ -128,10 +128,10 @@ def update_username(new_username)
fill_in
'username-change-input'
,
with:
new_username
fill_in
'username-change-input'
,
with:
new_username
page
.
find
(
'[data-t
arget="#
username-change-confirmation-modal"]'
).
click
page
.
find
(
'[data-t
estid="
username-change-confirmation-modal"]'
).
click
page
.
within
(
'.modal'
)
do
page
.
within
(
'.modal'
)
do
find
(
'.js-modal-
primary-action
'
).
click
find
(
'.js-modal-
action-primary
'
).
click
end
end
wait_for_requests
wait_for_requests
...
...
spec/frontend/profile/account/components/update_username_spec.js
View file @
88d94289
import
Vue
from
'
vue
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
GlModal
}
from
'
@gitlab/ui
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
{
TEST_HOST
}
from
'
helpers/test_constants
'
;
import
mountComponent
from
'
helpers/vue_mount_component_helper
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
axios
from
'
~/lib/utils/axios_utils
'
;
import
u
pdateUsername
from
'
~/profile/account/components/update_username.vue
'
;
import
U
pdateUsername
from
'
~/profile/account/components/update_username.vue
'
;
describe
(
'
UpdateUsername component
'
,
()
=>
{
describe
(
'
UpdateUsername component
'
,
()
=>
{
const
rootUrl
=
TEST_HOST
;
const
rootUrl
=
TEST_HOST
;
const
actionUrl
=
`
${
TEST_HOST
}
/update/username`
;
const
actionUrl
=
`
${
TEST_HOST
}
/update/username`
;
const
username
=
'
hasnoname
'
;
const
defaultProps
=
{
const
newUsername
=
'
new_username
'
;
actionUrl
,
let
Component
;
rootUrl
,
let
vm
;
initialUsername
:
'
hasnoname
'
,
};
let
wrapper
;
let
axiosMock
;
let
axiosMock
;
const
createComponent
=
(
props
=
{})
=>
{
wrapper
=
shallowMount
(
UpdateUsername
,
{
propsData
:
{
...
defaultProps
,
...
props
,
},
stubs
:
{
GlModal
,
},
});
};
beforeEach
(()
=>
{
beforeEach
(()
=>
{
axiosMock
=
new
MockAdapter
(
axios
);
axiosMock
=
new
MockAdapter
(
axios
);
Component
=
Vue
.
extend
(
updateUsername
);
createComponent
();
vm
=
mountComponent
(
Component
,
{
actionUrl
,
rootUrl
,
initialUsername
:
username
,
});
});
});
afterEach
(()
=>
{
afterEach
(()
=>
{
vm
.
$
destroy
();
wrapper
.
destroy
();
axiosMock
.
restore
();
axiosMock
.
restore
();
});
});
const
findElements
=
()
=>
{
const
findElements
=
()
=>
{
const
modal
Selector
=
`#
${
vm
.
$options
.
modalId
}
`
;
const
modal
=
wrapper
.
find
(
GlModal
)
;
return
{
return
{
input
:
vm
.
$el
.
querySelector
(
`#
${
vm
.
$options
.
inputId
}
`
)
,
modal
,
openModalBtn
:
vm
.
$el
.
querySelector
(
`[data-target="
${
modalSelector
}
"]
`
),
input
:
wrapper
.
find
(
`#
${
wrapper
.
vm
.
$options
.
inputId
}
`
),
modal
:
vm
.
$el
.
querySelector
(
modalSelector
),
openModalBtn
:
wrapper
.
find
(
'
[data-testid="username-change-confirmation-modal"]
'
),
modalBody
:
vm
.
$el
.
querySelector
(
`
${
modalSelector
}
.modal-body`
),
modalBody
:
modal
.
find
(
'
.modal-body
'
),
modalHeader
:
vm
.
$el
.
querySelector
(
`
${
modalSelector
}
.modal-title`
),
modalHeader
:
modal
.
find
(
'
.modal-title
'
),
confirmModalBtn
:
vm
.
$el
.
querySelector
(
`
${
modalSelector
}
.btn-warning`
),
confirmModalBtn
:
wrapper
.
find
(
'
.btn-warning
'
),
};
};
};
};
it
(
'
has a disabled button if the username was not changed
'
,
done
=>
{
it
(
'
has a disabled button if the username was not changed
'
,
async
()
=>
{
const
{
input
,
openModalBtn
}
=
findElements
();
const
{
openModalBtn
}
=
findElements
();
input
.
dispatchEvent
(
new
Event
(
'
input
'
));
await
wrapper
.
vm
.
$nextTick
();
Vue
.
nextTick
()
.
then
(()
=>
{
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
true
);
expect
(
vm
.
username
).
toBe
(
username
);
expect
(
vm
.
newUsername
).
toBe
(
username
);
expect
(
openModalBtn
).
toBeDisabled
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
it
(
'
has an enabled button which if the username was changed
'
,
done
=>
{
it
(
'
has an enabled button which if the username was changed
'
,
async
()
=>
{
const
{
input
,
openModalBtn
}
=
findElements
();
const
{
input
,
openModalBtn
}
=
findElements
();
input
.
value
=
newUsername
;
input
.
dispatchEvent
(
new
Event
(
'
input
'
));
Vue
.
nextTick
()
.
then
(()
=>
{
expect
(
vm
.
username
).
toBe
(
username
);
expect
(
vm
.
newUsername
).
toBe
(
newUsername
);
expect
(
openModalBtn
).
not
.
toBeDisabled
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'
confirmation modal contains proper header and body
'
,
done
=>
{
input
.
element
.
value
=
'
newUsername
'
;
const
{
modalBody
,
modalHeader
}
=
findElements
(
);
input
.
trigger
(
'
input
'
);
vm
.
newUsername
=
newUsername
;
await
wrapper
.
vm
.
$nextTick
()
;
Vue
.
nextTick
()
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
false
);
.
then
(()
=>
{
expect
(
modalHeader
.
textContent
).
toContain
(
'
Change username?
'
);
expect
(
modalBody
.
textContent
).
toContain
(
`You are going to change the username
${
username
}
to
${
newUsername
}
`
,
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
it
(
'
confirmation modal should escape usernames properly
'
,
done
=>
{
describe
(
'
changing username
'
,
()
=>
{
const
{
modalBody
}
=
findElements
()
;
const
newUsername
=
'
new_username
'
;
vm
.
username
=
'
<i>Italic</i>
'
;
beforeEach
(
async
()
=>
{
vm
.
newUsername
=
vm
.
username
;
createComponent
();
wrapper
.
setData
({
newUsername
});
Vue
.
nextTick
()
await
wrapper
.
vm
.
$nextTick
();
.
then
(()
=>
{
});
expect
(
modalBody
.
innerHTML
).
toContain
(
'
<i>Italic</i>
'
);
expect
(
modalBody
.
innerHTML
).
not
.
toContain
(
vm
.
username
);
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'
executes API call on confirmation button click
'
,
done
=>
{
it
(
'
confirmation modal contains proper header and body
'
,
async
()
=>
{
const
{
confirmModalBtn
}
=
findElements
();
const
{
modal
}
=
findElements
();
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
[
200
,
{
message
:
'
Username changed
'
}]);
expect
(
modal
.
attributes
(
'
title
'
)).
toBe
(
'
Change username?
'
);
jest
.
spyOn
(
axios
,
'
put
'
);
expect
(
modal
.
text
()).
toContain
(
`You are going to change the username
${
defaultProps
.
initialUsername
}
to
${
newUsername
}
`
,
);
});
vm
.
newUsername
=
newUsername
;
it
(
'
executes API call on confirmation button click
'
,
async
()
=>
{
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
[
200
,
{
message
:
'
Username changed
'
}]);
jest
.
spyOn
(
axios
,
'
put
'
);
Vue
.
nextTick
()
await
wrapper
.
vm
.
onConfirm
();
.
then
(()
=>
{
await
wrapper
.
vm
.
$nextTick
();
confirmModalBtn
.
click
();
expect
(
axios
.
put
).
toHaveBeenCalledWith
(
actionUrl
,
{
user
:
{
username
:
newUsername
}
});
expect
(
axios
.
put
).
toHaveBeenCalledWith
(
actionUrl
,
{
user
:
{
username
:
newUsername
}
});
})
});
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'
sets the username after a successful update
'
,
done
=>
{
it
(
'
sets the username after a successful update
'
,
async
()
=>
{
const
{
input
,
openModalBtn
}
=
findElements
();
const
{
input
,
openModalBtn
}
=
findElements
();
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
{
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
{
expect
(
input
).
toBeDisabled
(
);
expect
(
input
.
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
expect
(
openModalBtn
).
toBeDisabled
(
);
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
true
);
return
[
200
,
{
message
:
'
Username changed
'
}];
return
[
200
,
{
message
:
'
Username changed
'
}];
});
await
wrapper
.
vm
.
onConfirm
();
await
wrapper
.
vm
.
$nextTick
();
expect
(
input
.
attributes
(
'
disabled
'
)).
toBe
(
undefined
);
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
true
);
});
});
vm
.
newUsername
=
newUsername
;
it
(
'
does not set the username after a erroneous update
'
,
async
()
=>
{
const
{
input
,
openModalBtn
}
=
findElements
();
vm
.
onConfirm
()
.
then
(()
=>
{
expect
(
vm
.
username
).
toBe
(
newUsername
);
expect
(
vm
.
newUsername
).
toBe
(
newUsername
);
expect
(
input
).
not
.
toBeDisabled
();
expect
(
input
.
value
).
toBe
(
newUsername
);
expect
(
openModalBtn
).
toBeDisabled
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
it
(
'
does not set the username after a erroneous update
'
,
done
=>
{
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
{
const
{
input
,
openModalBtn
}
=
findElements
();
expect
(
input
.
attributes
(
'
disabled
'
)).
toBe
(
'
disabled
'
);
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
true
);
axiosMock
.
onPut
(
actionUrl
).
replyOnce
(()
=>
{
return
[
400
,
{
message
:
'
Invalid username
'
}];
expect
(
input
).
toBeDisabled
();
});
expect
(
openModalBtn
).
toBeDisabled
();
return
[
400
,
{
message
:
'
Invalid username
'
}];
await
expect
(
wrapper
.
vm
.
onConfirm
()).
rejects
.
toThrow
();
expect
(
input
.
attributes
(
'
disabled
'
)).
toBe
(
undefined
);
expect
(
openModalBtn
.
props
(
'
disabled
'
)).
toBe
(
false
);
});
});
const
invalidUsername
=
'
anything.git
'
;
vm
.
newUsername
=
invalidUsername
;
vm
.
onConfirm
()
.
then
(()
=>
done
.
fail
(
'
Expected onConfirm to throw!
'
))
.
catch
(()
=>
{
expect
(
vm
.
username
).
toBe
(
username
);
expect
(
vm
.
newUsername
).
toBe
(
invalidUsername
);
expect
(
input
).
not
.
toBeDisabled
();
expect
(
input
.
value
).
toBe
(
invalidUsername
);
expect
(
openModalBtn
).
not
.
toBeDisabled
();
})
.
then
(
done
)
.
catch
(
done
.
fail
);
});
});
});
});
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