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
0
Merge Requests
0
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
Léo-Paul Géneau
gitlab-ce
Commits
fac4f50c
Commit
fac4f50c
authored
Aug 07, 2018
by
Tim Zallmann
Committed by
Sean McGivern
Aug 07, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Send resize parameters for avatars
parent
dd627072
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
136 additions
and
122 deletions
+136
-122
app/assets/javascripts/lazy_loader.js
app/assets/javascripts/lazy_loader.js
+15
-3
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
...s/vue_shared/components/user_avatar/user_avatar_image.vue
+3
-2
app/models/concerns/avatarable.rb
app/models/concerns/avatarable.rb
+4
-3
app/views/admin/projects/_projects.html.haml
app/views/admin/projects/_projects.html.haml
+1
-1
app/views/layouts/nav/sidebar/_project.html.haml
app/views/layouts/nav/sidebar/_project.html.haml
+1
-1
app/views/projects/_home_panel.html.haml
app/views/projects/_home_panel.html.haml
+1
-1
app/views/projects/edit.html.haml
app/views/projects/edit.html.haml
+1
-1
app/views/shared/projects/_project.html.haml
app/views/shared/projects/_project.html.haml
+1
-1
spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+1
-1
spec/javascripts/boards/issue_card_spec.js
spec/javascripts/boards/issue_card_spec.js
+72
-82
spec/javascripts/pipelines/pipeline_url_spec.js
spec/javascripts/pipelines/pipeline_url_spec.js
+16
-10
spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
...ipts/vue_shared/components/notes/placeholder_note_spec.js
+1
-1
spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
...e_shared/components/user_avatar/user_avatar_image_spec.js
+15
-15
spec/models/concerns/avatarable_spec.rb
spec/models/concerns/avatarable_spec.rb
+4
-0
No files found.
app/assets/javascripts/lazy_loader.js
View file @
fac4f50c
import
_
from
'
underscore
'
;
import
_
from
'
underscore
'
;
export
const
placeholderImage
=
'
data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
'
;
export
const
placeholderImage
=
'
data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
'
;
const
SCROLL_THRESHOLD
=
300
;
const
SCROLL_THRESHOLD
=
300
;
export
default
class
LazyLoader
{
export
default
class
LazyLoader
{
...
@@ -48,7 +49,7 @@ export default class LazyLoader {
...
@@ -48,7 +49,7 @@ export default class LazyLoader {
const
visHeight
=
scrollTop
+
window
.
innerHeight
+
SCROLL_THRESHOLD
;
const
visHeight
=
scrollTop
+
window
.
innerHeight
+
SCROLL_THRESHOLD
;
// Loading Images which are in the current viewport or close to them
// Loading Images which are in the current viewport or close to them
this
.
lazyImages
=
this
.
lazyImages
.
filter
(
(
selectedImage
)
=>
{
this
.
lazyImages
=
this
.
lazyImages
.
filter
(
selectedImage
=>
{
if
(
selectedImage
.
getAttribute
(
'
data-src
'
))
{
if
(
selectedImage
.
getAttribute
(
'
data-src
'
))
{
const
imgBoundRect
=
selectedImage
.
getBoundingClientRect
();
const
imgBoundRect
=
selectedImage
.
getBoundingClientRect
();
const
imgTop
=
scrollTop
+
imgBoundRect
.
top
;
const
imgTop
=
scrollTop
+
imgBoundRect
.
top
;
...
@@ -66,7 +67,18 @@ export default class LazyLoader {
...
@@ -66,7 +67,18 @@ export default class LazyLoader {
}
}
static
loadImage
(
img
)
{
static
loadImage
(
img
)
{
if
(
img
.
getAttribute
(
'
data-src
'
))
{
if
(
img
.
getAttribute
(
'
data-src
'
))
{
img
.
setAttribute
(
'
src
'
,
img
.
getAttribute
(
'
data-src
'
));
let
imgUrl
=
img
.
getAttribute
(
'
data-src
'
);
// Only adding width + height for avatars for now
if
(
imgUrl
.
indexOf
(
'
/avatar/
'
)
>
-
1
&&
imgUrl
.
indexOf
(
'
?
'
)
===
-
1
)
{
let
targetWidth
=
null
;
if
(
img
.
getAttribute
(
'
width
'
))
{
targetWidth
=
img
.
getAttribute
(
'
width
'
);
}
else
{
targetWidth
=
img
.
width
;
}
if
(
targetWidth
)
imgUrl
+=
`?width=
${
targetWidth
}
`
;
}
img
.
setAttribute
(
'
src
'
,
imgUrl
);
img
.
removeAttribute
(
'
data-src
'
);
img
.
removeAttribute
(
'
data-src
'
);
img
.
classList
.
remove
(
'
lazy
'
);
img
.
classList
.
remove
(
'
lazy
'
);
img
.
classList
.
add
(
'
js-lazy-loaded
'
);
img
.
classList
.
add
(
'
js-lazy-loaded
'
);
...
...
app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
View file @
fac4f50c
<
script
>
<
script
>
/* This is a re-usable vue component for rendering a user avatar that
/* This is a re-usable vue component for rendering a user avatar that
does not need to link to the user's profile. The image and an optional
does not need to link to the user's profile. The image and an optional
tooltip can be configured by props passed to this component.
tooltip can be configured by props passed to this component.
...
@@ -67,7 +66,9 @@ export default {
...
@@ -67,7 +66,9 @@ export default {
// we provide an empty string when we use it inside user avatar link.
// we provide an empty string when we use it inside user avatar link.
// In both cases we should render the defaultAvatarUrl
// In both cases we should render the defaultAvatarUrl
sanitizedSource
()
{
sanitizedSource
()
{
return
this
.
imgSrc
===
''
||
this
.
imgSrc
===
null
?
defaultAvatarUrl
:
this
.
imgSrc
;
let
baseSrc
=
this
.
imgSrc
===
''
||
this
.
imgSrc
===
null
?
defaultAvatarUrl
:
this
.
imgSrc
;
if
(
baseSrc
.
indexOf
(
'
?
'
)
===
-
1
)
baseSrc
+=
`?width=
${
this
.
size
}
`
;
return
baseSrc
;
},
},
resultantSrcAttribute
()
{
resultantSrcAttribute
()
{
return
this
.
lazy
?
placeholderImage
:
this
.
sanitizedSource
;
return
this
.
lazy
?
placeholderImage
:
this
.
sanitizedSource
;
...
...
app/models/concerns/avatarable.rb
View file @
fac4f50c
...
@@ -19,7 +19,7 @@ module Avatarable
...
@@ -19,7 +19,7 @@ module Avatarable
# We use avatar_path instead of overriding avatar_url because of carrierwave.
# We use avatar_path instead of overriding avatar_url because of carrierwave.
# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864
# See https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11001/diffs#note_28659864
avatar_path
(
only_path:
args
.
fetch
(
:only_path
,
true
))
||
super
avatar_path
(
only_path:
args
.
fetch
(
:only_path
,
true
)
,
size:
args
[
:size
]
)
||
super
end
end
def
retrieve_upload
(
identifier
,
paths
)
def
retrieve_upload
(
identifier
,
paths
)
...
@@ -40,12 +40,13 @@ module Avatarable
...
@@ -40,12 +40,13 @@ module Avatarable
end
end
end
end
def
avatar_path
(
only_path:
true
)
def
avatar_path
(
only_path:
true
,
size:
nil
)
return
unless
self
[
:avatar
].
present?
return
unless
self
[
:avatar
].
present?
asset_host
=
ActionController
::
Base
.
asset_host
asset_host
=
ActionController
::
Base
.
asset_host
use_asset_host
=
asset_host
.
present?
use_asset_host
=
asset_host
.
present?
use_authentication
=
respond_to?
(
:public?
)
&&
!
public
?
use_authentication
=
respond_to?
(
:public?
)
&&
!
public
?
query_params
=
size
&
.
nonzero?
?
"?width=
#{
size
}
"
:
""
# Avatars for private and internal groups and projects require authentication to be viewed,
# Avatars for private and internal groups and projects require authentication to be viewed,
# which means they can only be served by Rails, on the regular GitLab host.
# which means they can only be served by Rails, on the regular GitLab host.
...
@@ -64,7 +65,7 @@ module Avatarable
...
@@ -64,7 +65,7 @@ module Avatarable
url_base
<<
gitlab_config
.
relative_url_root
url_base
<<
gitlab_config
.
relative_url_root
end
end
url_base
+
avatar
.
local_url
url_base
+
avatar
.
local_url
+
query_params
end
end
# Path that is persisted in the tracking Upload model. Used to fetch the
# Path that is persisted in the tracking Upload model. Used to fetch the
...
...
app/views/admin/projects/_projects.html.haml
View file @
fac4f50c
...
@@ -20,7 +20,7 @@
...
@@ -20,7 +20,7 @@
=
link_to
(
admin_namespace_project_path
(
project
.
namespace
,
project
))
do
=
link_to
(
admin_namespace_project_path
(
project
.
namespace
,
project
))
do
.dash-project-avatar
.dash-project-avatar
.avatar-container.s40
.avatar-container.s40
=
project_icon
(
project
,
alt:
''
,
class:
'avatar project-avatar s40'
)
=
project_icon
(
project
,
alt:
''
,
class:
'avatar project-avatar s40'
,
width:
40
,
height:
40
)
%span
.project-full-name
%span
.project-full-name
%span
.namespace-name
%span
.namespace-name
-
if
project
.
namespace
-
if
project
.
namespace
...
...
app/views/layouts/nav/sidebar/_project.html.haml
View file @
fac4f50c
...
@@ -4,7 +4,7 @@
...
@@ -4,7 +4,7 @@
.context-header
.context-header
=
link_to
project_path
(
@project
),
title:
@project
.
name
do
=
link_to
project_path
(
@project
),
title:
@project
.
name
do
.avatar-container.s40.project-avatar
.avatar-container.s40.project-avatar
=
project_icon
(
@project
,
alt:
@project
.
name
,
class:
'avatar s40 avatar-tile'
)
=
project_icon
(
@project
,
alt:
@project
.
name
,
class:
'avatar s40 avatar-tile'
,
width:
40
,
height:
40
)
.sidebar-context-title
.sidebar-context-title
=
@project
.
name
=
@project
.
name
%ul
.sidebar-top-level-items
%ul
.sidebar-top-level-items
...
...
app/views/projects/_home_panel.html.haml
View file @
fac4f50c
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
.project-home-panel.text-center
{
class:
(
"empty-project"
if
empty_repo
)
}
.project-home-panel.text-center
{
class:
(
"empty-project"
if
empty_repo
)
}
.limit-container-width
{
class:
container_class
}
.limit-container-width
{
class:
container_class
}
.avatar-container.s70.project-avatar
.avatar-container.s70.project-avatar
=
project_icon
(
@project
,
alt:
@project
.
name
,
class:
'avatar s70 avatar-tile'
)
=
project_icon
(
@project
,
alt:
@project
.
name
,
class:
'avatar s70 avatar-tile'
,
width:
70
,
height:
70
)
%h1
.project-title.qa-project-name
%h1
.project-title.qa-project-name
=
@project
.
name
=
@project
.
name
%span
.visibility-icon.has-tooltip
{
data:
{
container:
'body'
},
title:
visibility_icon_description
(
@project
)
}
%span
.visibility-icon.has-tooltip
{
data:
{
container:
'body'
},
title:
visibility_icon_description
(
@project
)
}
...
...
app/views/projects/edit.html.haml
View file @
fac4f50c
...
@@ -51,7 +51,7 @@
...
@@ -51,7 +51,7 @@
.form-group
.form-group
-
if
@project
.
avatar?
-
if
@project
.
avatar?
.avatar-container.s160.append-bottom-15
.avatar-container.s160.append-bottom-15
=
project_icon
(
@project
.
full_path
,
alt:
''
,
class:
'avatar project-avatar s160'
)
=
project_icon
(
@project
.
full_path
,
alt:
''
,
class:
'avatar project-avatar s160'
,
width:
160
,
height:
160
)
-
if
@project
.
avatar_in_git
-
if
@project
.
avatar_in_git
%p
.light
%p
.light
=
_
(
"Project avatar in repository: %{link}"
).
html_safe
%
{
link:
@project
.
avatar_in_git
}
=
_
(
"Project avatar in repository: %{link}"
).
html_safe
%
{
link:
@project
.
avatar_in_git
}
...
...
app/views/shared/projects/_project.html.haml
View file @
fac4f50c
...
@@ -19,7 +19,7 @@
...
@@ -19,7 +19,7 @@
-
if
project
.
creator
&&
use_creator_avatar
-
if
project
.
creator
&&
use_creator_avatar
=
image_tag
avatar_icon_for_user
(
project
.
creator
,
40
),
class:
"avatar s40"
,
alt
:''
=
image_tag
avatar_icon_for_user
(
project
.
creator
,
40
),
class:
"avatar s40"
,
alt
:''
-
else
-
else
=
project_icon
(
project
,
alt:
''
,
class:
'avatar project-avatar s40'
)
=
project_icon
(
project
,
alt:
''
,
class:
'avatar project-avatar s40'
,
width:
40
,
height:
40
)
.project-details
.project-details
%h3
.prepend-top-0.append-bottom-0
%h3
.prepend-top-0.append-bottom-0
=
link_to
project_path
(
project
),
class:
'text-plain'
do
=
link_to
project_path
(
project
),
class:
'text-plain'
do
...
...
spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
View file @
fac4f50c
...
@@ -15,7 +15,7 @@ describe 'User uploads avatar to profile' do
...
@@ -15,7 +15,7 @@ describe 'User uploads avatar to profile' do
visit
user_path
(
user
)
visit
user_path
(
user
)
expect
(
page
).
to
have_selector
(
%Q(img[data-src$="/uploads/-/system/user/avatar/
#{
user
.
id
}
/dk.png"])
)
expect
(
page
).
to
have_selector
(
%Q(img[data-src$="/uploads/-/system/user/avatar/
#{
user
.
id
}
/dk.png
?width=90
"])
)
# Cheating here to verify something that isn't user-facing, but is important
# Cheating here to verify something that isn't user-facing, but is important
expect
(
user
.
reload
.
avatar
.
file
).
to
exist
expect
(
user
.
reload
.
avatar
.
file
).
to
exist
...
...
spec/javascripts/boards/issue_card_spec.js
View file @
fac4f50c
...
@@ -69,109 +69,100 @@ describe('Issue card component', () => {
...
@@ -69,109 +69,100 @@ describe('Issue card component', () => {
});
});
it
(
'
renders issue title
'
,
()
=>
{
it
(
'
renders issue title
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-title
'
).
textContent
).
toContain
(
issue
.
title
);
component
.
$el
.
querySelector
(
'
.board-card-title
'
).
textContent
,
).
toContain
(
issue
.
title
);
});
});
it
(
'
includes issue base in link
'
,
()
=>
{
it
(
'
includes issue base in link
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-title a
'
).
getAttribute
(
'
href
'
)).
toContain
(
component
.
$el
.
querySelector
(
'
.board-card-title a
'
).
getAttribute
(
'
href
'
)
,
'
/test
'
,
)
.
toContain
(
'
/test
'
)
;
);
});
});
it
(
'
includes issue title on link
'
,
()
=>
{
it
(
'
includes issue title on link
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-title a
'
).
getAttribute
(
'
title
'
)).
toBe
(
component
.
$el
.
querySelector
(
'
.board-card-title a
'
).
getAttribute
(
'
title
'
)
,
issue
.
title
,
)
.
toBe
(
issue
.
title
)
;
);
});
});
it
(
'
does not render confidential icon
'
,
()
=>
{
it
(
'
does not render confidential icon
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.fa-eye-flash
'
)).
toBeNull
();
component
.
$el
.
querySelector
(
'
.fa-eye-flash
'
),
).
toBeNull
();
});
});
it
(
'
renders confidential icon
'
,
(
done
)
=>
{
it
(
'
renders confidential icon
'
,
done
=>
{
component
.
issue
.
confidential
=
true
;
component
.
issue
.
confidential
=
true
;
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.confidential-icon
'
)).
not
.
toBeNull
();
component
.
$el
.
querySelector
(
'
.confidential-icon
'
),
).
not
.
toBeNull
();
done
();
done
();
});
});
});
});
it
(
'
renders issue ID with #
'
,
()
=>
{
it
(
'
renders issue ID with #
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-number
'
).
textContent
).
toContain
(
`#
${
issue
.
id
}
`
);
component
.
$el
.
querySelector
(
'
.board-card-number
'
).
textContent
,
).
toContain
(
`#
${
issue
.
id
}
`
);
});
});
describe
(
'
assignee
'
,
()
=>
{
describe
(
'
assignee
'
,
()
=>
{
it
(
'
does not render assignee
'
,
()
=>
{
it
(
'
does not render assignee
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar
'
)).
toBeNull
();
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar
'
),
).
toBeNull
();
});
});
describe
(
'
exists
'
,
()
=>
{
describe
(
'
exists
'
,
()
=>
{
beforeEach
(
(
done
)
=>
{
beforeEach
(
done
=>
{
component
.
issue
.
assignees
=
[
user
];
component
.
issue
.
assignees
=
[
user
];
Vue
.
nextTick
(()
=>
done
());
Vue
.
nextTick
(()
=>
done
());
});
});
it
(
'
renders assignee
'
,
()
=>
{
it
(
'
renders assignee
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar
'
)).
not
.
toBeNull
();
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar
'
),
).
not
.
toBeNull
();
});
});
it
(
'
sets title
'
,
()
=>
{
it
(
'
sets title
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
).
getAttribute
(
'
data-original-title
'
),
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
)
.
getAttribute
(
'
data-original-title
'
),
).
toContain
(
`Assigned to
${
user
.
name
}
`
);
).
toContain
(
`Assigned to
${
user
.
name
}
`
);
});
});
it
(
'
sets users path
'
,
()
=>
{
it
(
'
sets users path
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee a
'
).
getAttribute
(
'
href
'
)).
toBe
(
component
.
$el
.
querySelector
(
'
.board-card-assignee a
'
).
getAttribute
(
'
href
'
)
,
'
/test
'
,
)
.
toBe
(
'
/test
'
)
;
);
});
});
it
(
'
renders avatar
'
,
()
=>
{
it
(
'
renders avatar
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
)).
not
.
toBeNull
();
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
),
).
not
.
toBeNull
();
});
});
});
});
describe
(
'
assignee default avatar
'
,
()
=>
{
describe
(
'
assignee default avatar
'
,
()
=>
{
beforeEach
((
done
)
=>
{
beforeEach
(
done
=>
{
component
.
issue
.
assignees
=
[
new
ListAssignee
({
component
.
issue
.
assignees
=
[
id
:
1
,
new
ListAssignee
(
name
:
'
testing 123
'
,
{
username
:
'
test
'
,
id
:
1
,
},
'
default_avatar
'
)];
name
:
'
testing 123
'
,
username
:
'
test
'
,
},
'
default_avatar
'
,
),
];
Vue
.
nextTick
(
done
);
Vue
.
nextTick
(
done
);
});
});
it
(
'
displays defaults avatar if users avatar is null
'
,
()
=>
{
it
(
'
displays defaults avatar if users avatar is null
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
)).
not
.
toBeNull
();
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
),
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
).
getAttribute
(
'
src
'
)).
toBe
(
).
not
.
toBeNull
();
'
default_avatar?width=20
'
,
expect
(
);
component
.
$el
.
querySelector
(
'
.board-card-assignee img
'
).
getAttribute
(
'
src
'
),
).
toBe
(
'
default_avatar
'
);
});
});
});
});
});
});
describe
(
'
multiple assignees
'
,
()
=>
{
describe
(
'
multiple assignees
'
,
()
=>
{
beforeEach
(
(
done
)
=>
{
beforeEach
(
done
=>
{
component
.
issue
.
assignees
=
[
component
.
issue
.
assignees
=
[
user
,
user
,
new
ListAssignee
({
new
ListAssignee
({
...
@@ -191,7 +182,8 @@ describe('Issue card component', () => {
...
@@ -191,7 +182,8 @@ describe('Issue card component', () => {
name
:
'
user4
'
,
name
:
'
user4
'
,
username
:
'
user4
'
,
username
:
'
user4
'
,
avatar
:
'
test_image
'
,
avatar
:
'
test_image
'
,
})];
}),
];
Vue
.
nextTick
(()
=>
done
());
Vue
.
nextTick
(()
=>
done
());
});
});
...
@@ -201,26 +193,30 @@ describe('Issue card component', () => {
...
@@ -201,26 +193,30 @@ describe('Issue card component', () => {
});
});
describe
(
'
more than four assignees
'
,
()
=>
{
describe
(
'
more than four assignees
'
,
()
=>
{
beforeEach
((
done
)
=>
{
beforeEach
(
done
=>
{
component
.
issue
.
assignees
.
push
(
new
ListAssignee
({
component
.
issue
.
assignees
.
push
(
id
:
5
,
new
ListAssignee
({
name
:
'
user5
'
,
id
:
5
,
username
:
'
user5
'
,
name
:
'
user5
'
,
avatar
:
'
test_image
'
,
username
:
'
user5
'
,
}));
avatar
:
'
test_image
'
,
}),
);
Vue
.
nextTick
(()
=>
done
());
Vue
.
nextTick
(()
=>
done
());
});
});
it
(
'
renders more avatar counter
'
,
()
=>
{
it
(
'
renders more avatar counter
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar-counter
'
).
innerText
).
toEqual
(
'
+2
'
);
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar-counter
'
).
innerText
,
).
toEqual
(
'
+2
'
);
});
});
it
(
'
renders three assignees
'
,
()
=>
{
it
(
'
renders three assignees
'
,
()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
.board-card-assignee .avatar
'
).
length
).
toEqual
(
3
);
expect
(
component
.
$el
.
querySelectorAll
(
'
.board-card-assignee .avatar
'
).
length
).
toEqual
(
3
);
});
});
it
(
'
renders 99+ avatar counter
'
,
(
done
)
=>
{
it
(
'
renders 99+ avatar counter
'
,
done
=>
{
for
(
let
i
=
5
;
i
<
104
;
i
+=
1
)
{
for
(
let
i
=
5
;
i
<
104
;
i
+=
1
)
{
const
u
=
new
ListAssignee
({
const
u
=
new
ListAssignee
({
id
:
i
,
id
:
i
,
...
@@ -232,7 +228,9 @@ describe('Issue card component', () => {
...
@@ -232,7 +228,9 @@ describe('Issue card component', () => {
}
}
Vue
.
nextTick
(()
=>
{
Vue
.
nextTick
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar-counter
'
).
innerText
).
toEqual
(
'
99+
'
);
expect
(
component
.
$el
.
querySelector
(
'
.board-card-assignee .avatar-counter
'
).
innerText
,
).
toEqual
(
'
99+
'
);
done
();
done
();
});
});
});
});
...
@@ -240,59 +238,51 @@ describe('Issue card component', () => {
...
@@ -240,59 +238,51 @@ describe('Issue card component', () => {
});
});
describe
(
'
labels
'
,
()
=>
{
describe
(
'
labels
'
,
()
=>
{
beforeEach
(
(
done
)
=>
{
beforeEach
(
done
=>
{
component
.
issue
.
addLabel
(
label1
);
component
.
issue
.
addLabel
(
label1
);
Vue
.
nextTick
(()
=>
done
());
Vue
.
nextTick
(()
=>
done
());
});
});
it
(
'
renders list label
'
,
()
=>
{
it
(
'
renders list label
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
length
).
toBe
(
2
);
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
length
,
).
toBe
(
2
);
});
});
it
(
'
renders label
'
,
()
=>
{
it
(
'
renders label
'
,
()
=>
{
const
nodes
=
[];
const
nodes
=
[];
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
forEach
(
(
label
)
=>
{
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
forEach
(
label
=>
{
nodes
.
push
(
label
.
getAttribute
(
'
data-original-title
'
));
nodes
.
push
(
label
.
getAttribute
(
'
data-original-title
'
));
});
});
expect
(
expect
(
nodes
.
includes
(
label1
.
description
)).
toBe
(
true
);
nodes
.
includes
(
label1
.
description
),
).
toBe
(
true
);
});
});
it
(
'
sets label description as title
'
,
()
=>
{
it
(
'
sets label description as title
'
,
()
=>
{
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.badge
'
).
getAttribute
(
'
data-original-title
'
)).
toContain
(
component
.
$el
.
querySelector
(
'
.badge
'
).
getAttribute
(
'
data-original-title
'
)
,
label1
.
description
,
)
.
toContain
(
label1
.
description
)
;
);
});
});
it
(
'
sets background color of button
'
,
()
=>
{
it
(
'
sets background color of button
'
,
()
=>
{
const
nodes
=
[];
const
nodes
=
[];
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
forEach
(
(
label
)
=>
{
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
forEach
(
label
=>
{
nodes
.
push
(
label
.
style
.
backgroundColor
);
nodes
.
push
(
label
.
style
.
backgroundColor
);
});
});
expect
(
expect
(
nodes
.
includes
(
label1
.
color
)).
toBe
(
true
);
nodes
.
includes
(
label1
.
color
),
).
toBe
(
true
);
});
});
it
(
'
does not render label if label does not have an ID
'
,
(
done
)
=>
{
it
(
'
does not render label if label does not have an ID
'
,
done
=>
{
component
.
issue
.
addLabel
(
new
ListLabel
({
component
.
issue
.
addLabel
(
title
:
'
closed
'
,
new
ListLabel
({
}));
title
:
'
closed
'
,
}),
);
Vue
.
nextTick
()
Vue
.
nextTick
()
.
then
(()
=>
{
.
then
(()
=>
{
expect
(
expect
(
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
length
).
toBe
(
2
);
component
.
$el
.
querySelectorAll
(
'
.badge
'
).
length
,
expect
(
component
.
$el
.
textContent
).
not
.
toContain
(
'
closed
'
);
).
toBe
(
2
);
expect
(
component
.
$el
.
textContent
,
).
not
.
toContain
(
'
closed
'
);
done
();
done
();
})
})
...
...
spec/javascripts/pipelines/pipeline_url_spec.js
View file @
fac4f50c
...
@@ -35,7 +35,9 @@ describe('Pipeline Url Component', () => {
...
@@ -35,7 +35,9 @@ describe('Pipeline Url Component', () => {
},
},
}).
$mount
();
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-link
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
foo
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-link
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
foo
'
,
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-link span
'
).
textContent
).
toEqual
(
'
#1
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-link span
'
).
textContent
).
toEqual
(
'
#1
'
);
});
});
...
@@ -61,11 +63,11 @@ describe('Pipeline Url Component', () => {
...
@@ -61,11 +63,11 @@ describe('Pipeline Url Component', () => {
const
image
=
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user img
'
);
const
image
=
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user img
'
);
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user
'
).
getAttribute
(
'
href
'
)).
toEqual
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-user
'
).
getAttribute
(
'
href
'
)
,
mockData
.
pipeline
.
user
.
web_url
,
)
.
toEqual
(
mockData
.
pipeline
.
user
.
web_url
)
;
);
expect
(
image
.
getAttribute
(
'
data-original-title
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
name
);
expect
(
image
.
getAttribute
(
'
data-original-title
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
name
);
expect
(
image
.
getAttribute
(
'
src
'
)).
toEqual
(
mockData
.
pipeline
.
user
.
avatar_url
);
expect
(
image
.
getAttribute
(
'
src
'
)).
toEqual
(
`
${
mockData
.
pipeline
.
user
.
avatar_url
}
?width=20`
);
});
});
it
(
'
should render "API" when no user is provided
'
,
()
=>
{
it
(
'
should render "API" when no user is provided
'
,
()
=>
{
...
@@ -100,7 +102,9 @@ describe('Pipeline Url Component', () => {
...
@@ -100,7 +102,9 @@ describe('Pipeline Url Component', () => {
}).
$mount
();
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-latest
'
).
textContent
).
toContain
(
'
latest
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-latest
'
).
textContent
).
toContain
(
'
latest
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-yaml
'
).
textContent
).
toContain
(
'
yaml invalid
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-yaml
'
).
textContent
).
toContain
(
'
yaml invalid
'
,
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-stuck
'
).
textContent
).
toContain
(
'
stuck
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-stuck
'
).
textContent
).
toContain
(
'
stuck
'
);
});
});
...
@@ -121,9 +125,9 @@ describe('Pipeline Url Component', () => {
...
@@ -121,9 +125,9 @@ describe('Pipeline Url Component', () => {
},
},
}).
$mount
();
}).
$mount
();
expect
(
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-autodevops
'
).
textContent
.
trim
()).
toEqual
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-autodevops
'
).
textContent
.
trim
()
,
'
Auto DevOps
'
,
)
.
toEqual
(
'
Auto DevOps
'
)
;
);
});
});
it
(
'
should render error badge when pipeline has a failure reason set
'
,
()
=>
{
it
(
'
should render error badge when pipeline has a failure reason set
'
,
()
=>
{
...
@@ -142,6 +146,8 @@ describe('Pipeline Url Component', () => {
...
@@ -142,6 +146,8 @@ describe('Pipeline Url Component', () => {
}).
$mount
();
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-failure
'
).
textContent
).
toContain
(
'
error
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-failure
'
).
textContent
).
toContain
(
'
error
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-failure
'
).
getAttribute
(
'
data-original-title
'
)).
toContain
(
'
some reason
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipeline-url-failure
'
).
getAttribute
(
'
data-original-title
'
),
).
toContain
(
'
some reason
'
);
});
});
});
});
spec/javascripts/vue_shared/components/notes/placeholder_note_spec.js
View file @
fac4f50c
...
@@ -27,7 +27,7 @@ describe('issue placeholder system note component', () => {
...
@@ -27,7 +27,7 @@ describe('issue placeholder system note component', () => {
userDataMock
.
path
,
userDataMock
.
path
,
);
);
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link img
'
).
getAttribute
(
'
src
'
)).
toEqual
(
expect
(
vm
.
$el
.
querySelector
(
'
.user-avatar-link img
'
).
getAttribute
(
'
src
'
)).
toEqual
(
userDataMock
.
avatar_url
,
`
${
userDataMock
.
avatar_url
}
?width=40`
,
);
);
});
});
});
});
...
...
spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
View file @
fac4f50c
...
@@ -12,7 +12,7 @@ const DEFAULT_PROPS = {
...
@@ -12,7 +12,7 @@ const DEFAULT_PROPS = {
tooltipPlacement
:
'
bottom
'
,
tooltipPlacement
:
'
bottom
'
,
};
};
describe
(
'
User Avatar Image Component
'
,
function
()
{
describe
(
'
User Avatar Image Component
'
,
function
()
{
let
vm
;
let
vm
;
let
UserAvatarImage
;
let
UserAvatarImage
;
...
@@ -20,37 +20,37 @@ describe('User Avatar Image Component', function () {
...
@@ -20,37 +20,37 @@ describe('User Avatar Image Component', function () {
UserAvatarImage
=
Vue
.
extend
(
userAvatarImage
);
UserAvatarImage
=
Vue
.
extend
(
userAvatarImage
);
});
});
describe
(
'
Initialization
'
,
function
()
{
describe
(
'
Initialization
'
,
function
()
{
beforeEach
(
function
()
{
beforeEach
(
function
()
{
vm
=
mountComponent
(
UserAvatarImage
,
{
vm
=
mountComponent
(
UserAvatarImage
,
{
...
DEFAULT_PROPS
,
...
DEFAULT_PROPS
,
}).
$mount
();
}).
$mount
();
});
});
it
(
'
should return a defined Vue component
'
,
function
()
{
it
(
'
should return a defined Vue component
'
,
function
()
{
expect
(
vm
).
toBeDefined
();
expect
(
vm
).
toBeDefined
();
});
});
it
(
'
should have <img> as a child element
'
,
function
()
{
it
(
'
should have <img> as a child element
'
,
function
()
{
expect
(
vm
.
$el
.
tagName
).
toBe
(
'
IMG
'
);
expect
(
vm
.
$el
.
tagName
).
toBe
(
'
IMG
'
);
expect
(
vm
.
$el
.
getAttribute
(
'
src
'
)).
toBe
(
DEFAULT_PROPS
.
imgSrc
);
expect
(
vm
.
$el
.
getAttribute
(
'
src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
expect
(
vm
.
$el
.
getAttribute
(
'
data-src
'
)).
toBe
(
DEFAULT_PROPS
.
imgSrc
);
expect
(
vm
.
$el
.
getAttribute
(
'
data-src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
expect
(
vm
.
$el
.
getAttribute
(
'
alt
'
)).
toBe
(
DEFAULT_PROPS
.
imgAlt
);
expect
(
vm
.
$el
.
getAttribute
(
'
alt
'
)).
toBe
(
DEFAULT_PROPS
.
imgAlt
);
});
});
it
(
'
should properly compute tooltipContainer
'
,
function
()
{
it
(
'
should properly compute tooltipContainer
'
,
function
()
{
expect
(
vm
.
tooltipContainer
).
toBe
(
'
body
'
);
expect
(
vm
.
tooltipContainer
).
toBe
(
'
body
'
);
});
});
it
(
'
should properly render tooltipContainer
'
,
function
()
{
it
(
'
should properly render tooltipContainer
'
,
function
()
{
expect
(
vm
.
$el
.
getAttribute
(
'
data-container
'
)).
toBe
(
'
body
'
);
expect
(
vm
.
$el
.
getAttribute
(
'
data-container
'
)).
toBe
(
'
body
'
);
});
});
it
(
'
should properly compute avatarSizeClass
'
,
function
()
{
it
(
'
should properly compute avatarSizeClass
'
,
function
()
{
expect
(
vm
.
avatarSizeClass
).
toBe
(
'
s99
'
);
expect
(
vm
.
avatarSizeClass
).
toBe
(
'
s99
'
);
});
});
it
(
'
should properly render img css
'
,
function
()
{
it
(
'
should properly render img css
'
,
function
()
{
const
{
classList
}
=
vm
.
$el
;
const
{
classList
}
=
vm
.
$el
;
const
containsAvatar
=
classList
.
contains
(
'
avatar
'
);
const
containsAvatar
=
classList
.
contains
(
'
avatar
'
);
const
containsSizeClass
=
classList
.
contains
(
'
s99
'
);
const
containsSizeClass
=
classList
.
contains
(
'
s99
'
);
...
@@ -64,21 +64,21 @@ describe('User Avatar Image Component', function () {
...
@@ -64,21 +64,21 @@ describe('User Avatar Image Component', function () {
});
});
});
});
describe
(
'
Initialization when lazy
'
,
function
()
{
describe
(
'
Initialization when lazy
'
,
function
()
{
beforeEach
(
function
()
{
beforeEach
(
function
()
{
vm
=
mountComponent
(
UserAvatarImage
,
{
vm
=
mountComponent
(
UserAvatarImage
,
{
...
DEFAULT_PROPS
,
...
DEFAULT_PROPS
,
lazy
:
true
,
lazy
:
true
,
}).
$mount
();
}).
$mount
();
});
});
it
(
'
should add lazy attributes
'
,
function
()
{
it
(
'
should add lazy attributes
'
,
function
()
{
const
{
classList
}
=
vm
.
$el
;
const
{
classList
}
=
vm
.
$el
;
const
lazyClass
=
classList
.
contains
(
'
lazy
'
);
const
lazyClass
=
classList
.
contains
(
'
lazy
'
);
expect
(
lazyClass
).
toBe
(
true
);
expect
(
lazyClass
).
toBe
(
true
);
expect
(
vm
.
$el
.
getAttribute
(
'
src
'
)).
toBe
(
placeholderImage
);
expect
(
vm
.
$el
.
getAttribute
(
'
src
'
)).
toBe
(
placeholderImage
);
expect
(
vm
.
$el
.
getAttribute
(
'
data-src
'
)).
toBe
(
DEFAULT_PROPS
.
imgSrc
);
expect
(
vm
.
$el
.
getAttribute
(
'
data-src
'
)).
toBe
(
`
${
DEFAULT_PROPS
.
imgSrc
}
?width=99`
);
});
});
});
});
});
});
spec/models/concerns/avatarable_spec.rb
View file @
fac4f50c
...
@@ -43,6 +43,10 @@ describe Avatarable do
...
@@ -43,6 +43,10 @@ describe Avatarable do
expect
(
project
.
avatar_path
(
only_path:
only_path
)).
to
eq
(
avatar_path
)
expect
(
project
.
avatar_path
(
only_path:
only_path
)).
to
eq
(
avatar_path
)
end
end
it
'returns the expected avatar path with width parameter'
do
expect
(
project
.
avatar_path
(
only_path:
only_path
,
size:
128
)).
to
eq
(
avatar_path
+
"?width=128"
)
end
context
"when avatar is stored remotely"
do
context
"when avatar is stored remotely"
do
before
do
before
do
stub_uploads_object_storage
(
AvatarUploader
)
stub_uploads_object_storage
(
AvatarUploader
)
...
...
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