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
Boxiang Sun
gitlab-ce
Commits
90e0709d
Commit
90e0709d
authored
Sep 27, 2018
by
Filipa Lacerda
Committed by
Phil Hughes
Sep 27, 2018
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Updates Vue job components to match new API
parent
b42e7740
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
159 additions
and
166 deletions
+159
-166
app/assets/javascripts/jobs/components/empty_state.vue
app/assets/javascripts/jobs/components/empty_state.vue
+2
-2
app/assets/javascripts/jobs/components/erased_block.vue
app/assets/javascripts/jobs/components/erased_block.vue
+23
-26
app/assets/javascripts/jobs/components/job_log.vue
app/assets/javascripts/jobs/components/job_log.vue
+2
-2
app/assets/javascripts/jobs/components/job_log_controllers.vue
...ssets/javascripts/jobs/components/job_log_controllers.vue
+43
-33
app/assets/javascripts/jobs/components/stuck_block.vue
app/assets/javascripts/jobs/components/stuck_block.vue
+3
-3
locale/gitlab.pot
locale/gitlab.pot
+0
-3
spec/javascripts/jobs/components/empty_state_spec.js
spec/javascripts/jobs/components/empty_state_spec.js
+1
-1
spec/javascripts/jobs/components/erased_block_spec.js
spec/javascripts/jobs/components/erased_block_spec.js
+4
-4
spec/javascripts/jobs/components/job_log_controllers_spec.js
spec/javascripts/jobs/components/job_log_controllers_spec.js
+78
-89
spec/javascripts/jobs/components/job_log_spec.js
spec/javascripts/jobs/components/job_log_spec.js
+3
-3
No files found.
app/assets/javascripts/jobs/components/empty_state.vue
View file @
90e0709d
...
...
@@ -25,7 +25,7 @@
validator
(
value
)
{
return
(
value
===
null
||
(
Object
.
prototype
.
hasOwnProperty
.
call
(
value
,
'
link
'
)
&&
(
Object
.
prototype
.
hasOwnProperty
.
call
(
value
,
'
path
'
)
&&
Object
.
prototype
.
hasOwnProperty
.
call
(
value
,
'
method
'
)
&&
Object
.
prototype
.
hasOwnProperty
.
call
(
value
,
'
title
'
))
);
...
...
@@ -63,7 +63,7 @@
class=
"text-center"
>
<a
:href=
"action.
link
"
:href=
"action.
path
"
:data-method=
"action.method"
class=
"js-job-empty-state-action btn btn-primary"
>
...
...
app/assets/javascripts/jobs/components/erased_block.vue
View file @
90e0709d
<
script
>
import
TimeagoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
import
_
from
'
underscore
'
;
import
TimeagoTooltip
from
'
~/vue_shared/components/time_ago_tooltip.vue
'
;
export
default
{
components
:
{
TimeagoTooltip
,
},
props
:
{
erasedByUser
:
{
type
:
Boolean
,
required
:
true
,
export
default
{
components
:
{
TimeagoTooltip
,
},
username
:
{
type
:
String
,
required
:
false
,
default
:
null
,
props
:
{
user
:
{
type
:
Object
,
required
:
false
,
default
:
()
=>
({}),
},
erasedAt
:
{
type
:
String
,
required
:
true
,
},
},
linkToUser
:
{
type
:
String
,
required
:
false
,
default
:
null
,
computed
:
{
isErasedByUser
()
{
return
!
_
.
isEmpty
(
this
.
user
);
}
,
},
erasedAt
:
{
type
:
String
,
required
:
true
,
},
},
};
};
</
script
>
<
template
>
<div
class=
"prepend-top-default js-build-erased"
>
<div
class=
"erased alert alert-warning"
>
<template
v-if=
"
e
rasedByUser"
>
<template
v-if=
"
isE
rasedByUser"
>
{{
s__
(
"
Job|Job has been erased by
"
)
}}
<a
:href=
"
linkToUser
"
>
{{
username
}}
<a
:href=
"
user.web_url
"
>
{{
user
.
user
name
}}
</a>
</
template
>
<
template
v-else
>
...
...
app/assets/javascripts/jobs/components/job_log.vue
View file @
90e0709d
...
...
@@ -6,7 +6,7 @@
type
:
String
,
required
:
true
,
},
is
ReceivingBuildTrac
e
:
{
is
Complet
e
:
{
type
:
Boolean
,
required
:
true
,
},
...
...
@@ -22,7 +22,7 @@
</code>
<div
v-if=
"is
ReceivingBuildTrac
e"
v-if=
"is
Complet
e"
class=
"js-log-animation build-loader-animation"
>
<div
class=
"dot"
></div>
...
...
app/assets/javascripts/jobs/components/job_log_controllers.vue
View file @
90e0709d
<
script
>
import
{
polyfillSticky
}
from
'
~/lib/utils/sticky
'
;
import
Icon
from
'
~/vue_shared/components/icon.vue
'
;
import
tooltip
from
'
~/vue_shared/directives/tooltip
'
;
import
{
numberToHumanSize
}
from
'
~/lib/utils/number_utils
'
;
import
{
s
__
,
s
printf
}
from
'
~/locale
'
;
import
{
sprintf
}
from
'
~/locale
'
;
export
default
{
components
:
{
...
...
@@ -12,44 +13,48 @@
tooltip
,
},
props
:
{
canEraseJob
:
{
type
:
Boolean
,
required
:
true
,
erasePath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
size
:
{
type
:
Number
,
required
:
true
,
},
raw
Trace
Path
:
{
rawPath
:
{
type
:
String
,
required
:
false
,
default
:
null
,
},
canScrollToTop
:
{
isScrollTopDisabled
:
{
type
:
Boolean
,
required
:
true
,
},
isScrollBottomDisabled
:
{
type
:
Boolean
,
required
:
true
,
},
isScrollingDown
:
{
type
:
Boolean
,
required
:
true
,
},
canScrollToBottom
:
{
isTraceSizeVisible
:
{
type
:
Boolean
,
required
:
true
,
},
},
computed
:
{
jobLogSize
()
{
return
sprintf
(
'
Showing last %{startSpanTag} %{size} %{endSpanTag} of log -
'
,
{
startSpanTag
:
'
<span class="s-truncated-info-size truncated-info-size">
'
,
endSpanTag
:
'
</span>
'
,
return
sprintf
(
'
Showing last %{size} of log -
'
,
{
size
:
numberToHumanSize
(
this
.
size
),
});
},
},
mounted
()
{
polyfillSticky
(
this
.
$el
);
},
methods
:
{
handleEraseJobClick
()
{
// eslint-disable-next-line no-alert
if
(
window
.
confirm
(
s__
(
'
Job|Are you sure you want to erase this job?
'
)))
{
this
.
$emit
(
'
eraseJob
'
);
}
},
handleScrollToTop
()
{
this
.
$emit
(
'
scrollJobLogTop
'
);
},
...
...
@@ -57,48 +62,52 @@
this
.
$emit
(
'
scrollJobLogBottom
'
);
},
},
};
</
script
>
<
template
>
<div
class=
"top-bar"
>
<!-- truncate information -->
<div
class=
"js-truncated-info truncated-info d-none d-sm-block float-left"
>
<p
v-html=
"jobLogSize"
></p>
<template
v-if=
"isTraceSizeVisible"
>
{{
jobLogSize
}}
<a
v-if=
"rawTracePath"
:href=
"rawTracePath"
class=
"js-raw-link raw-link"
>
{{
s__
(
"
Job|Complete Raw
"
)
}}
</a>
<a
v-if=
"rawPath"
:href=
"rawPath"
class=
"js-raw-link raw-link"
>
{{
s__
(
"
Job|Complete Raw
"
)
}}
</a>
</
template
>
</div>
<!-- eo truncate information -->
<div
class=
"controllers float-right"
>
<!-- links -->
<a
v-if=
"raw
Trace
Path"
v-if=
"rawPath"
v-tooltip
:title=
"s__('Job|Show complete raw')"
:href=
"raw
Trace
Path"
:href=
"rawPath"
class=
"js-raw-link-controller controllers-buttons"
data-container=
"body"
>
<icon
name=
"doc-text"
/>
</a>
<
button
v-if=
"
canEraseJob
"
<
a
v-if=
"
erasePath
"
v-tooltip
:title=
"s__('Job|Erase job log')"
type=
"button"
:href=
"erasePath"
data-confirm=
"__('Are you sure you want to erase this build?')"
class=
"js-erase-link controllers-buttons"
data-container=
"body"
@
click=
"handleEraseJobClick
"
data-method=
"post
"
>
<icon
name=
"remove"
/>
</
button
>
</
a
>
<!-- eo links -->
<!-- scroll buttons -->
...
...
@@ -109,7 +118,7 @@
data-container=
"body"
>
<button
:disabled=
"
!canScrollToTop
"
:disabled=
"
isScrollTopDisabled
"
type=
"button"
class=
"js-scroll-top btn-scroll btn-transparent btn-blank"
@
click=
"handleScrollToTop"
...
...
@@ -125,9 +134,10 @@
data-container=
"body"
>
<button
:disabled=
"
!canScrollToBottom
"
:disabled=
"
isScrollBottomDisabled
"
type=
"button"
class=
"js-scroll-bottom btn-scroll btn-transparent btn-blank"
:class=
"{ animate: isScrollingDown }"
@
click=
"handleScrollToBottom"
>
<icon
name=
"scroll_down"
/>
...
...
app/assets/javascripts/jobs/components/stuck_block.vue
View file @
90e0709d
...
...
@@ -24,14 +24,14 @@ export default {
<div
class=
"bs-callout bs-callout-warning"
>
<p
v-if=
"hasNoRunnersForProject"
class=
"js-stuck-no-runners"
class=
"js-stuck-no-runners
append-bottom-0
"
>
{{
s__
(
`Job|This job is stuck, because the project
doesn't have any runners online assigned to it.`
)
}}
</p>
<p
v-else-if=
"tags.length"
class=
"js-stuck-with-tags"
class=
"js-stuck-with-tags
append-bottom-0
"
>
{{
s__
(
`This job is stuck, because you don't have
any active runners online with any of these tags assigned to them:`
)
}}
...
...
@@ -45,7 +45,7 @@ export default {
</p>
<p
v-else
class=
"js-stuck-no-active-runner"
class=
"js-stuck-no-active-runner
append-bottom-0
"
>
{{
s__
(
`This job is stuck, because you don't
have any active runners that can run this job.`
)
}}
...
...
locale/gitlab.pot
View file @
90e0709d
...
...
@@ -3338,9 +3338,6 @@ msgstr ""
msgid "Jobs"
msgstr ""
msgid "Job|Are you sure you want to erase this job?"
msgstr ""
msgid "Job|Browse"
msgstr ""
...
...
spec/javascripts/jobs/components/empty_state_spec.js
View file @
90e0709d
...
...
@@ -66,7 +66,7 @@ describe('Empty State', () => {
...
props
,
content
,
action
:
{
link
:
'
runner
'
,
path
:
'
runner
'
,
title
:
'
Check runner
'
,
method
:
'
post
'
,
},
...
...
spec/javascripts/jobs/components/erased_block_spec.js
View file @
90e0709d
...
...
@@ -18,9 +18,10 @@ describe('Erased block', () => {
describe
(
'
with job erased by user
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
erasedByUser
:
true
,
username
:
'
root
'
,
linkToUser
:
'
gitlab.com/root
'
,
user
:
{
username
:
'
root
'
,
web_url
:
'
gitlab.com/root
'
,
},
erasedAt
,
});
});
...
...
@@ -40,7 +41,6 @@ describe('Erased block', () => {
describe
(
'
with erased job
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
erasedByUser
:
false
,
erasedAt
,
});
});
...
...
spec/javascripts/jobs/components/job_log_controllers_spec.js
View file @
90e0709d
...
...
@@ -10,50 +10,51 @@ describe('Job log controllers', () => {
vm
.
$destroy
();
});
describe
(
'
Truncate information
'
,
()
=>
{
const
props
=
{
rawPath
:
'
/raw
'
,
erasePath
:
'
/erase
'
,
size
:
511952
,
isScrollTopDisabled
:
false
,
isScrollBottomDisabled
:
false
,
isScrollingDown
:
true
,
isTraceSizeVisible
:
true
,
};
beforeEach
(
()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
true
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
describe
(
'
Truncate information
'
,
()
=>
{
describe
(
'
with isTraceSizeVisible
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
props
);
});
it
(
'
renders size information
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-truncated-info
'
).
textContent
).
toContain
(
'
499.95 KiB
'
);
});
});
it
(
'
renders size information
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-truncated-info
'
).
textContent
).
toContain
(
'
499.95 KiB
'
);
});
it
(
'
renders link to raw trace
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-raw-link
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
/raw
'
);
it
(
'
renders link to raw trace
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-raw-link
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
/raw
'
);
});
});
});
describe
(
'
links section
'
,
()
=>
{
describe
(
'
with raw trace path
'
,
()
=>
{
it
(
'
renders raw trace link
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
true
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
});
vm
=
mountComponent
(
Component
,
props
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-raw-link-controller
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
/raw
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-raw-link-controller
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
/raw
'
,
);
});
});
describe
(
'
without raw trace path
'
,
()
=>
{
it
(
'
does not render raw trace link
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
canEraseJob
:
true
,
erasePath
:
'
/erase
'
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
isScrollTopDisabled
:
true
,
isScrollBottomDisabled
:
true
,
isScrollingDown
:
false
,
isTraceSizeVisible
:
true
,
});
expect
(
vm
.
$el
.
querySelector
(
'
.js-raw-link-controller
'
)).
toBeNull
();
...
...
@@ -62,52 +63,23 @@ describe('Job log controllers', () => {
describe
(
'
when is erasable
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
true
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
});
vm
=
mountComponent
(
Component
,
props
);
});
it
(
'
renders erase job
button
'
,
()
=>
{
it
(
'
renders erase job
link
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-erase-link
'
)).
not
.
toBeNull
();
});
describe
(
'
on click
'
,
()
=>
{
describe
(
'
when user confirms action
'
,
()
=>
{
it
(
'
emits eraseJob event
'
,
()
=>
{
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
true
);
spyOn
(
vm
,
'
$emit
'
);
vm
.
$el
.
querySelector
(
'
.js-erase-link
'
).
click
();
expect
(
vm
.
$emit
).
toHaveBeenCalledWith
(
'
eraseJob
'
);
});
});
describe
(
'
when user does not confirm action
'
,
()
=>
{
it
(
'
does not emit eraseJob event
'
,
()
=>
{
spyOn
(
window
,
'
confirm
'
).
and
.
returnValue
(
false
);
spyOn
(
vm
,
'
$emit
'
);
vm
.
$el
.
querySelector
(
'
.js-erase-link
'
).
click
();
expect
(
vm
.
$emit
).
not
.
toHaveBeenCalledWith
(
'
eraseJob
'
);
});
});
});
});
describe
(
'
when it is not erasable
'
,
()
=>
{
it
(
'
does not render erase button
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
false
,
rawPath
:
'
/raw
'
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
isScrollTopDisabled
:
true
,
isScrollBottomDisabled
:
true
,
isScrollingDown
:
false
,
isTraceSizeVisible
:
true
,
});
expect
(
vm
.
$el
.
querySelector
(
'
.js-erase-link
'
)).
toBeNull
();
...
...
@@ -119,13 +91,7 @@ describe('Job log controllers', () => {
describe
(
'
scroll top button
'
,
()
=>
{
describe
(
'
when user can scroll top
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
true
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
});
vm
=
mountComponent
(
Component
,
props
);
});
it
(
'
renders enabled scroll top button
'
,
()
=>
{
...
...
@@ -143,16 +109,20 @@ describe('Job log controllers', () => {
describe
(
'
when user can not scroll top
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
raw
Trace
Path
:
'
/raw
'
,
canEraseJob
:
true
,
rawPath
:
'
/raw
'
,
erasePath
:
'
/erase
'
,
size
:
511952
,
canScrollToTop
:
false
,
canScrollToBottom
:
true
,
isScrollTopDisabled
:
true
,
isScrollBottomDisabled
:
false
,
isScrollingDown
:
false
,
isTraceSizeVisible
:
true
,
});
});
it
(
'
renders disabled scroll top button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-top
'
).
getAttribute
(
'
disabled
'
)).
toEqual
(
'
disabled
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-top
'
).
getAttribute
(
'
disabled
'
)).
toEqual
(
'
disabled
'
,
);
});
it
(
'
does not emit scrollJobLogTop event on click
'
,
()
=>
{
...
...
@@ -167,13 +137,7 @@ describe('Job log controllers', () => {
describe
(
'
scroll bottom button
'
,
()
=>
{
describe
(
'
when user can scroll bottom
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
rawTracePath
:
'
/raw
'
,
canEraseJob
:
true
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
true
,
});
vm
=
mountComponent
(
Component
,
props
);
});
it
(
'
renders enabled scroll bottom button
'
,
()
=>
{
...
...
@@ -191,17 +155,20 @@ describe('Job log controllers', () => {
describe
(
'
when user can not scroll bottom
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
mountComponent
(
Component
,
{
raw
Trace
Path
:
'
/raw
'
,
canEraseJob
:
true
,
rawPath
:
'
/raw
'
,
erasePath
:
'
/erase
'
,
size
:
511952
,
canScrollToTop
:
true
,
canScrollToBottom
:
false
,
isScrollTopDisabled
:
false
,
isScrollBottomDisabled
:
true
,
isScrollingDown
:
false
,
isTraceSizeVisible
:
true
,
});
});
it
(
'
renders disabled scroll bottom button
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-bottom
'
).
getAttribute
(
'
disabled
'
)).
toEqual
(
'
disabled
'
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-bottom
'
).
getAttribute
(
'
disabled
'
)).
toEqual
(
'
disabled
'
,
);
});
it
(
'
does not emit scrollJobLogBottom event on click
'
,
()
=>
{
...
...
@@ -211,7 +178,29 @@ describe('Job log controllers', () => {
expect
(
vm
.
$emit
).
not
.
toHaveBeenCalledWith
(
'
scrollJobLogBottom
'
);
});
});
describe
(
'
while isScrollingDown is true
'
,
()
=>
{
it
(
'
renders animate class for the scroll down button
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
props
);
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-bottom
'
).
className
).
toContain
(
'
animate
'
);
});
});
describe
(
'
while isScrollingDown is false
'
,
()
=>
{
it
(
'
does not render animate class for the scroll down button
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
rawPath
:
'
/raw
'
,
erasePath
:
'
/erase
'
,
size
:
511952
,
isScrollTopDisabled
:
true
,
isScrollBottomDisabled
:
false
,
isScrollingDown
:
false
,
isTraceSizeVisible
:
true
,
});
expect
(
vm
.
$el
.
querySelector
(
'
.js-scroll-bottom
'
).
className
).
not
.
toContain
(
'
animate
'
);
});
});
});
});
});
spec/javascripts/jobs/components/job_log_spec.js
View file @
90e0709d
...
...
@@ -15,7 +15,7 @@ describe('Job Log', () => {
it
(
'
renders provided trace
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
trace
,
is
ReceivingBuildTrac
e
:
true
,
is
Complet
e
:
true
,
});
expect
(
vm
.
$el
.
querySelector
(
'
code
'
).
textContent
).
toContain
(
'
Running with gitlab-runner 11.1.0 (081978aa)
'
);
...
...
@@ -25,7 +25,7 @@ describe('Job Log', () => {
it
(
'
renders animation
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
trace
,
is
ReceivingBuildTrac
e
:
true
,
is
Complet
e
:
true
,
});
expect
(
vm
.
$el
.
querySelector
(
'
.js-log-animation
'
)).
not
.
toBeNull
();
...
...
@@ -36,7 +36,7 @@ describe('Job Log', () => {
it
(
'
does not render animation
'
,
()
=>
{
vm
=
mountComponent
(
Component
,
{
trace
,
is
ReceivingBuildTrac
e
:
false
,
is
Complet
e
:
false
,
});
expect
(
vm
.
$el
.
querySelector
(
'
.js-log-animation
'
)).
toBeNull
();
...
...
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