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
cd023d42
Commit
cd023d42
authored
May 30, 2017
by
Filipa Lacerda
Committed by
Phil Hughes
May 30, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update looks job log
parent
763a3acd
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
480 additions
and
483 deletions
+480
-483
app/assets/javascripts/build.js
app/assets/javascripts/build.js
+150
-183
app/assets/stylesheets/pages/builds.scss
app/assets/stylesheets/pages/builds.scss
+104
-119
app/views/projects/builds/_sidebar.html.haml
app/views/projects/builds/_sidebar.html.haml
+0
-7
app/views/projects/builds/show.html.haml
app/views/projects/builds/show.html.haml
+36
-24
app/views/shared/icons/_scroll_down.svg
app/views/shared/icons/_scroll_down.svg
+4
-2
app/views/shared/icons/_scroll_down_hover_active.svg
app/views/shared/icons/_scroll_down_hover_active.svg
+0
-3
app/views/shared/icons/_scroll_up.svg
app/views/shared/icons/_scroll_up.svg
+1
-3
app/views/shared/icons/_scroll_up_hover_active.svg
app/views/shared/icons/_scroll_up_hover_active.svg
+0
-3
spec/features/projects/builds_spec.rb
spec/features/projects/builds_spec.rb
+9
-9
spec/javascripts/build_spec.js
spec/javascripts/build_spec.js
+176
-130
No files found.
app/assets/javascripts/build.js
View file @
cd023d42
...
...
@@ -2,15 +2,11 @@
consistent-return, prefer-rest-params */
/* global Breakpoints */
import
_
from
'
underscore
'
;
import
{
bytesToKiB
}
from
'
./lib/utils/number_utils
'
;
const
bind
=
function
(
fn
,
me
)
{
return
function
()
{
return
fn
.
apply
(
me
,
arguments
);
};
};
const
AUTO_SCROLL_OFFSET
=
75
;
const
DOWN_BUILD_TRACE
=
'
#down-build-trace
'
;
window
.
Build
=
(
function
()
{
Build
.
timeout
=
null
;
Build
.
state
=
null
;
function
Build
(
options
)
{
...
...
@@ -23,21 +19,22 @@ window.Build = (function () {
this
.
buildStage
=
this
.
options
.
buildStage
;
this
.
$document
=
$
(
document
);
this
.
logBytes
=
0
;
this
.
scrollOffsetPadding
=
30
;
this
.
updateDropdown
=
bind
(
this
.
updateDropdown
,
this
);
this
.
updateDropdown
=
this
.
updateDropdown
.
bind
(
this
);
this
.
getBuildTrace
=
this
.
getBuildTrace
.
bind
(
this
);
this
.
scrollToBottom
=
this
.
scrollToBottom
.
bind
(
this
);
this
.
$body
=
$
(
'
body
'
);
this
.
$buildTrace
=
$
(
'
#build-trace
'
);
this
.
$autoScrollContainer
=
$
(
'
.autoscroll-container
'
);
this
.
$autoScrollStatus
=
$
(
'
#autoscroll-status
'
);
this
.
$autoScrollStatusText
=
this
.
$autoScrollStatus
.
find
(
'
.status-text
'
);
this
.
$upBuildTrace
=
$
(
'
#up-build-trace
'
);
this
.
$downBuildTrace
=
$
(
DOWN_BUILD_TRACE
);
this
.
$scrollTopBtn
=
$
(
'
#scroll-top
'
);
this
.
$scrollBottomBtn
=
$
(
'
#scroll-bottom
'
);
this
.
$buildRefreshAnimation
=
$
(
'
.js-build-refresh
'
);
this
.
$buildScroll
=
$
(
'
#js-build-scroll
'
);
this
.
$truncatedInfo
=
$
(
'
.js-truncated-info
'
);
this
.
$buildTraceOutput
=
$
(
'
.js-build-output
'
);
this
.
$scrollContainer
=
$
(
'
.js-scroll-container
'
);
// Scroll controllers
this
.
$scrollTopBtn
=
$
(
'
.js-scroll-up
'
);
this
.
$scrollBottomBtn
=
$
(
'
.js-scroll-down
'
);
clearTimeout
(
Build
.
timeout
);
// Init breakpoint checker
...
...
@@ -56,54 +53,149 @@ window.Build = (function () {
.
off
(
'
click
'
,
'
.stage-item
'
)
.
on
(
'
click
'
,
'
.stage-item
'
,
this
.
updateDropdown
);
this
.
$document
.
on
(
'
scroll
'
,
this
.
initScrollMonitor
.
bind
(
this
));
// add event listeners to the scroll buttons
this
.
$scrollTopBtn
.
off
(
'
click
'
)
.
on
(
'
click
'
,
this
.
scrollToTop
.
bind
(
this
));
this
.
$scrollBottomBtn
.
off
(
'
click
'
)
.
on
(
'
click
'
,
this
.
scrollToBottom
.
bind
(
this
));
$
(
window
)
.
off
(
'
resize.build
'
)
.
on
(
'
resize.build
'
,
this
.
sidebarOnResize
.
bind
(
this
));
$
(
'
a
'
,
this
.
$buildScroll
)
.
off
(
'
click.stepTrace
'
)
.
on
(
'
click.stepTrace
'
,
this
.
stepTrace
);
this
.
updateArtifactRemoveDate
();
this
.
initScrollButtonAffix
();
this
.
invokeBuildTrace
();
// eslint-disable-next-line
this
.
getBuildTrace
()
.
then
(()
=>
this
.
makeTraceScrollable
())
.
then
(()
=>
this
.
scrollToBottom
());
this
.
verifyTopPosition
();
}
Build
.
prototype
.
makeTraceScrollable
=
function
()
{
this
.
$scrollContainer
.
niceScroll
({
cursorcolor
:
'
#fff
'
,
cursoropacitymin
:
1
,
cursorwidth
:
'
3px
'
,
railpadding
:
{
top
:
5
,
bottom
:
5
,
right
:
5
},
});
this
.
$scrollContainer
.
on
(
'
scroll
'
,
_
.
throttle
(
this
.
toggleScroll
.
bind
(
this
),
100
));
this
.
toggleScroll
();
};
Build
.
prototype
.
canScroll
=
function
()
{
return
(
this
.
$scrollContainer
.
prop
(
'
scrollHeight
'
)
-
this
.
scrollOffsetPadding
)
>
this
.
$scrollContainer
.
height
();
};
/**
* | | Up | Down |
* |--------------------------|----------|----------|
* | on scroll bottom | active | disabled |
* | on scroll top | disabled | active |
* | no scroll | disabled | disabled |
* | on.('scroll') is on top | disabled | active |
* | on('scroll) is on bottom | active | disabled |
*
*/
Build
.
prototype
.
toggleScroll
=
function
()
{
const
bottomScroll
=
this
.
$scrollContainer
.
scrollTop
()
+
this
.
scrollOffsetPadding
+
this
.
$scrollContainer
.
height
();
if
(
this
.
canScroll
())
{
if
(
this
.
$scrollContainer
.
scrollTop
()
===
0
)
{
this
.
toggleDisableButton
(
this
.
$scrollTopBtn
,
true
);
this
.
toggleDisableButton
(
this
.
$scrollBottomBtn
,
false
);
}
else
if
(
bottomScroll
===
this
.
$scrollContainer
.
prop
(
'
scrollHeight
'
))
{
this
.
toggleDisableButton
(
this
.
$scrollTopBtn
,
false
);
this
.
toggleDisableButton
(
this
.
$scrollBottomBtn
,
true
);
}
else
{
this
.
toggleDisableButton
(
this
.
$scrollTopBtn
,
false
);
this
.
toggleDisableButton
(
this
.
$scrollBottomBtn
,
false
);
}
}
};
Build
.
prototype
.
scrollToTop
=
function
()
{
this
.
$scrollContainer
.
getNiceScroll
(
0
).
doScrollTop
(
0
);
this
.
toggleScroll
();
};
Build
.
prototype
.
scrollToBottom
=
function
()
{
this
.
$scrollContainer
.
getNiceScroll
(
0
).
doScrollTo
(
this
.
$scrollContainer
.
prop
(
'
scrollHeight
'
));
this
.
toggleScroll
();
};
Build
.
prototype
.
toggleDisableButton
=
function
(
$button
,
disable
)
{
if
(
disable
&&
$button
.
prop
(
'
disabled
'
))
return
;
$button
.
prop
(
'
disabled
'
,
disable
);
};
Build
.
prototype
.
toggleScrollAnimation
=
function
(
toggle
)
{
this
.
$scrollBottomBtn
.
toggleClass
(
'
animate
'
,
toggle
);
};
/**
* Build trace top position depends on the space ocupied by the elments rendered before
*/
Build
.
prototype
.
verifyTopPosition
=
function
()
{
const
$buildPage
=
$
(
'
.build-page
'
);
const
$header
=
$
(
'
.build-header
'
,
$buildPage
);
const
$runnersStuck
=
$
(
'
.js-build-stuck
'
,
$buildPage
);
const
$startsEnvironment
=
$
(
'
.js-environment-container
'
,
$buildPage
);
const
$erased
=
$
(
'
.js-build-erased
'
,
$buildPage
);
let
topPostion
=
168
;
if
(
$header
)
{
topPostion
+=
$header
.
outerHeight
();
}
if
(
$runnersStuck
)
{
topPostion
+=
$runnersStuck
.
outerHeight
();
}
if
(
$startsEnvironment
)
{
topPostion
+=
$startsEnvironment
.
outerHeight
();
}
if
(
$erased
)
{
topPostion
+=
$erased
.
outerHeight
()
+
10
;
}
this
.
$buildTrace
.
css
({
top
:
topPostion
,
});
};
Build
.
prototype
.
initSidebar
=
function
()
{
this
.
$sidebar
=
$
(
'
.js-build-sidebar
'
);
this
.
$sidebar
.
niceScroll
();
this
.
$document
.
off
(
'
click
'
,
'
.js-sidebar-build-toggle
'
)
.
on
(
'
click
'
,
'
.js-sidebar-build-toggle
'
,
this
.
toggleSidebar
);
};
Build
.
prototype
.
invokeBuildTrace
=
function
()
{
return
this
.
getBuildTrace
();
};
Build
.
prototype
.
getBuildTrace
=
function
()
{
return
$
.
ajax
({
url
:
`
${
this
.
pageUrl
}
/trace.json`
,
dataType
:
'
json
'
,
data
:
{
state
:
this
.
state
,
},
success
:
((
log
)
=>
{
const
$buildContainer
=
$
(
'
.js-build-output
'
);
data
:
this
.
state
,
})
.
done
((
log
)
=>
{
gl
.
utils
.
setCiStatusFavicon
(
`
${
this
.
pageUrl
}
/status.json`
);
if
(
log
.
state
)
{
this
.
state
=
log
.
state
;
}
if
(
log
.
append
)
{
$buildContainer
.
append
(
log
.
html
);
this
.
$buildTraceOutput
.
append
(
log
.
html
);
this
.
logBytes
+=
log
.
size
;
}
else
{
$buildContainer
.
html
(
log
.
html
);
this
.
$buildTraceOutput
.
html
(
log
.
html
);
this
.
logBytes
=
log
.
size
;
}
...
...
@@ -114,141 +206,30 @@ window.Build = (function () {
const
size
=
bytesToKiB
(
this
.
logBytes
);
$
(
'
.js-truncated-info-size
'
).
html
(
`
${
size
}
`
);
this
.
$truncatedInfo
.
removeClass
(
'
hidden
'
);
this
.
initAffixTruncatedInfo
();
}
else
{
this
.
$truncatedInfo
.
addClass
(
'
hidden
'
);
}
this
.
checkAutoscroll
();
if
(
!
log
.
complete
)
{
this
.
toggleScrollAnimation
(
true
);
Build
.
timeout
=
setTimeout
(()
=>
{
this
.
invokeBuildTrace
();
//eslint-disable-next-line
this
.
getBuildTrace
()
.
then
(()
=>
this
.
scrollToBottom
());
},
4000
);
}
else
{
this
.
$buildRefreshAnimation
.
remove
();
this
.
toggleScrollAnimation
(
false
);
}
if
(
log
.
status
!==
this
.
buildStatus
)
{
let
pageUrl
=
this
.
pageUrl
;
if
(
this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
pageUrl
+=
DOWN_BUILD_TRACE
;
}
gl
.
utils
.
visitUrl
(
pageUrl
);
gl
.
utils
.
visitUrl
(
this
.
pageUrl
);
}
})
,
error
:
()
=>
{
})
.
fail
(
()
=>
{
this
.
$buildRefreshAnimation
.
remove
();
return
this
.
initScrollMonitor
();
},
});
};
Build
.
prototype
.
checkAutoscroll
=
function
()
{
if
(
this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
return
$
(
'
html,body
'
).
scrollTop
(
this
.
$buildTrace
.
height
());
}
// Handle a situation where user started new build
// but never scrolled a page
if
(
!
this
.
$scrollTopBtn
.
is
(
'
:visible
'
)
&&
!
this
.
$scrollBottomBtn
.
is
(
'
:visible
'
)
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
this
.
$scrollBottomBtn
.
show
();
}
};
Build
.
prototype
.
initScrollButtonAffix
=
function
()
{
// Hide everything initially
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
this
.
$autoScrollContainer
.
hide
();
};
// Page scroll listener to detect if user has scrolling page
// and handle following cases
// 1) User is at Top of Build Log;
// - Hide Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
// 2) User is at Bottom of Build Log;
// - Show Top Arrow button
// - Hide Bottom Arrow button
// - Enable Autoscroll and show indicator (when build is running)
// 3) User is somewhere in middle of Build Log;
// - Show Top Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
Build
.
prototype
.
initScrollMonitor
=
function
()
{
if
(
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// User is somewhere in middle of Build Log
this
.
$scrollTopBtn
.
show
();
if
(
this
.
buildStatus
===
'
success
'
||
this
.
buildStatus
===
'
failed
'
)
{
// Check if Build is completed
this
.
$scrollBottomBtn
.
show
();
}
else
if
(
this
.
$buildRefreshAnimation
.
is
(
'
:visible
'
)
&&
!
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
)))
{
this
.
$scrollBottomBtn
.
show
();
}
else
{
this
.
$scrollBottomBtn
.
hide
();
}
// Hide Autoscroll Status Indicator
if
(
this
.
$scrollBottomBtn
.
is
(
'
:visible
'
))
{
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
else
{
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
,
}).
show
();
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
}
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// User is at Top of Build Log
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollBottomBtn
.
show
();
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
else
if
((
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
||
(
this
.
$buildRefreshAnimation
.
is
(
'
:visible
'
)
&&
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
))))
{
// User is at Bottom of Build Log
this
.
$scrollTopBtn
.
show
();
this
.
$scrollBottomBtn
.
hide
();
// Show and Reposition Autoscroll Status Indicator
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
,
}).
show
();
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// Build Log height is small
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
// Hide Autoscroll Status Indicator
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
if
(
this
.
buildStatus
===
'
running
'
||
this
.
buildStatus
===
'
pending
'
)
{
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise.
this
.
$autoScrollStatus
.
data
(
'
state
'
,
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
))
?
'
enabled
'
:
'
disabled
'
,
);
}
});
};
Build
.
prototype
.
shouldHideSidebarForViewport
=
function
()
{
...
...
@@ -257,18 +238,23 @@ window.Build = (function () {
};
Build
.
prototype
.
toggleSidebar
=
function
(
shouldHide
)
{
const
shouldShow
=
typeof
shouldHide
===
'
boolean
'
?
!
shouldHide
:
undefined
;
const
shouldShow
=
!
shouldHide
;
this
.
$buildScroll
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
this
.
$buildTrace
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
.
toggleClass
(
'
sidebar-collapsed
'
,
shouldHide
);
this
.
$truncatedInfo
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
.
toggleClass
(
'
sidebar-collapsed
'
,
shouldHide
);
this
.
$sidebar
.
toggleClass
(
'
right-sidebar-expanded
'
,
shouldShow
)
this
.
$sidebar
.
toggleClass
(
'
right-sidebar-expanded
'
,
shouldShow
)
.
toggleClass
(
'
right-sidebar-collapsed
'
,
shouldHide
);
};
Build
.
prototype
.
sidebarOnResize
=
function
()
{
this
.
toggleSidebar
(
this
.
shouldHideSidebarForViewport
());
this
.
verifyTopPosition
();
if
(
this
.
$scrollContainer
.
getNiceScroll
(
0
))
{
this
.
toggleScroll
();
}
};
Build
.
prototype
.
sidebarOnClick
=
function
()
{
...
...
@@ -301,24 +287,5 @@ window.Build = (function () {
this
.
populateJobs
(
stage
);
};
Build
.
prototype
.
stepTrace
=
function
(
e
)
{
e
.
preventDefault
();
const
$currentTarget
=
$
(
e
.
currentTarget
);
$
.
scrollTo
(
$currentTarget
.
attr
(
'
href
'
),
{
offset
:
0
,
});
};
Build
.
prototype
.
initAffixTruncatedInfo
=
function
()
{
const
offsetTop
=
this
.
$buildTrace
.
offset
().
top
;
this
.
$truncatedInfo
.
affix
({
offset
:
{
top
:
offsetTop
,
},
});
};
return
Build
;
})();
app/assets/stylesheets/pages/builds.scss
View file @
cd023d42
...
...
@@ -29,129 +29,140 @@
}
}
.build-page
{
pre
.trace
{
background
:
$builds-trace-bg
;
color
:
$white-light
;
font-family
:
$monospace_font
;
white-space
:
pre-wrap
;
overflow
:
auto
;
overflow-y
:
hidden
;
font-size
:
12px
;
.fa-spinner
{
font-size
:
24px
;
margin-left
:
20px
;
}
}
.environment-information
{
background-color
:
$gray-light
;
border
:
1px
solid
$border-color
;
padding
:
12px
$gl-padding
;
border-radius
:
$border-radius-default
;
@keyframes
blinking-scroll-button
{
0
%
{
opacity
:
0
.2
;
}
25
%
{
opacity
:
0
.5
;
}
50
%
{
opacity
:
0
.7
;
}
100
%
{
opacity
:
1
;
}
}
svg
{
position
:
relative
;
top
:
1px
;
margin-right
:
5px
;
}
.build-page
{
.sticky
{
position
:
absolute
;
left
:
0
;
right
:
0
;
}
.truncated-info
{
text-align
:
center
;
border-bottom
:
1px
solid
;
background-color
:
$black
;
height
:
45px
;
padding
:
15px
;
.build-trace-container
{
position
:
absolute
;
top
:
225px
;
left
:
15px
;
bottom
:
10px
;
background
:
$black
;
color
:
$gray-darkest
;
font-family
:
$monospace_font
;
font-size
:
12px
;
&
.
affix
{
top
:
0
;
&
.
sidebar-expanded
{
right
:
305px
;
}
// with sidebar
&
.affix.sidebar-expanded
{
right
:
312px
;
left
:
22px
;
&
.sidebar-collapsed
{
right
:
16px
;
}
// without sidebar
&
.affix.sidebar-collapsed
{
right
:
20px
;
left
:
20px
;
code
{
background
:
$black
;
color
:
$gray-darkest
;
}
&
.affix-top
{
position
:
absolute
;
.top-bar
{
top
:
0
;
margin
:
0
auto
;
right
:
5p
x
;
left
:
5px
;
}
height
:
35px
;
display
:
fle
x
;
justify-content
:
flex-end
;
border-bottom
:
1px
outset
$white-light
;
.truncated-info-size
{
margin
:
0
5px
;
}
.truncated-info
{
margin
:
0
auto
;
align-self
:
center
;
.raw-link
{
color
:
inherit
;
margin-left
:
5px
;
text-decoration
:
underline
;
.truncated-info-size
{
margin
:
0
5px
;
}
.raw-link
{
color
:
inherit
;
margin-left
:
5px
;
text-decoration
:
underline
;
}
}
}
}
}
.scroll-controls
{
height
:
100%
;
.controllers
{
display
:
flex
;
align-self
:
center
;
font-size
:
15px
;
.scroll-step
{
width
:
31px
;
margin
:
0
0
0
auto
;
}
svg
{
height
:
15px
;
display
:
block
;
fill
:
$white-light
;
}
.scroll-link
,
.autoscroll-container
{
right
:
25
px
;
z-index
:
1
;
}
a
,
.btn-scroll
{
margin
:
0
8
px
;
color
:
$white-light
;
}
.scroll-link
{
position
:
fixed
;
display
:
block
;
margin-bottom
:
10px
;
.btn-scroll.animate
{
.first-triangle
{
animation
:
blinking-scroll-button
1s
ease
infinite
;
animation-delay
:
.3s
;
}
&
.scroll-top
.gitlab-icon-scroll-up-hover
,
&
.scroll-top
:hover
.gitlab-icon-scroll-up
,
&
.scroll-bottom
.gitlab-icon-scroll-down-hover
,
&
.scroll-bottom
:hover
.gitlab-icon-scroll-down
{
display
:
none
;
}
.second-triangle
{
animation
:
blinking-scroll-button
1s
ease
infinite
;
animation-delay
:
.2s
;
}
&
.scroll-top
:hover
.gitlab-icon-scroll-up-hover
,
&
.scroll-bottom
:hover
.gitlab-icon-scroll-down-hover
{
display
:
inline-block
;
}
.third-triangle
{
animation
:
blinking-scroll-button
1s
ease
infinite
;
}
&
.scroll-top
{
top
:
10px
;
}
&
:disabled
{
opacity
:
1
;
}
}
&
.scroll-bottom
{
bottom
:
-2px
;
.btn-scroll
:disabled
{
opacity
:
0
.35
;
cursor
:
not
-
allowed
;
}
}
}
.autoscroll-container
{
position
:
absolute
;
.bash
{
top
:
35px
;
left
:
10px
;
bottom
:
0
;
overflow-y
:
hidden
;
padding-bottom
:
20px
;
padding-right
:
20px
;
}
&
.sidebar-expanded
{
.environment-information
{
background-color
:
$gray-light
;
border
:
1px
solid
$border-color
;
padding
:
12px
$gl-padding
;
border-radius
:
$border-radius-default
;
.scroll-link
,
.autoscroll-container
{
right
:
(
$gutter_width
+
(
$gl-padding
*
2
));
svg
{
position
:
relative
;
top
:
1px
;
margin-right
:
5px
;
}
}
.build-loader-animation
{
position
:
relative
;
width
:
6px
;
height
:
6px
;
margin
:
auto
auto
12px
2px
;
border-radius
:
50%
;
animation
:
blinking-dots
1s
linear
infinite
;
}
}
.status-message
{
...
...
@@ -223,32 +234,6 @@
}
}
.build-trace
{
background
:
$black
;
color
:
$gray-darkest
;
white-space
:
pre
;
overflow-x
:
auto
;
font-size
:
12px
;
position
:
relative
;
.fa-spinner
{
font-size
:
24px
;
}
.bash
{
display
:
block
;
}
.build-loader-animation
{
position
:
relative
;
width
:
6px
;
height
:
6px
;
margin
:
auto
auto
12px
2px
;
border-radius
:
50%
;
animation
:
blinking-dots
1s
linear
infinite
;
}
}
.right-sidebar.build-sidebar
{
padding
:
$gl-padding
0
;
...
...
app/views/projects/builds/_sidebar.html.haml
View file @
cd023d42
...
...
@@ -68,15 +68,8 @@
-
elsif
@build
.
runner
\##{@build.runner.id}
.btn-group.btn-group-justified
{
role: :group
}
-
if
@build
.
has_trace?
=
link_to
'Raw'
,
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
class:
'btn btn-sm btn-default'
-
if
@build
.
active?
=
link_to
"Cancel"
,
cancel_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
class:
'btn btn-sm btn-default'
,
method: :post
-
if
can?
(
current_user
,
:update_build
,
@project
)
&&
@build
.
erasable?
=
link_to
erase_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
class:
"btn btn-sm btn-default"
,
method: :post
,
data:
{
confirm:
"Are you sure you want to erase this build?"
}
do
Erase
-
if
@build
.
trigger_request
.build-widget
...
...
app/views/projects/builds/show.html.haml
View file @
cd023d42
...
...
@@ -8,7 +8,7 @@
-
if
@build
.
stuck?
-
unless
@build
.
any_runners_online?
.bs-callout.bs-callout-warning
.bs-callout.bs-callout-warning
.js-build-stuck
%p
-
if
no_runners_for_project?
(
@build
.
project
)
This job is stuck, because the project doesn't have any runners online assigned to it.
...
...
@@ -26,7 +26,7 @@
Runners page
-
if
@build
.
starts_environment?
.prepend-top-default
.prepend-top-default
.js-environment-container
.environment-information
-
if
@build
.
outdated_deployment?
=
ci_icon_for_status
(
'success_with_warnings'
)
...
...
@@ -47,39 +47,51 @@
-
if
environment
.
try
(
:last_deployment
)
and will overwrite the
#{
deployment_link
(
environment
.
last_deployment
,
text:
'latest deployment'
)
}
.prepend-top-default
.prepend-top-default
.js-build-erased
-
if
@build
.
erased?
.erased.alert.alert-warning
-
if
@build
.
erased_by_user?
Job has been erased by
#{
link_to
(
@build
.
erased_by_name
,
user_path
(
@build
.
erased_by
))
}
#{
time_ago_with_tooltip
(
@build
.
erased_at
)
}
-
else
Job has been erased
#{
time_ago_with_tooltip
(
@build
.
erased_at
)
}
-
else
#js-build-scroll
.scroll-controls
.scroll-step
%a
.scroll-link.scroll-top
{
href:
'#up-build-trace'
,
id:
'scroll-top'
,
title:
'Scroll to top'
}
=
custom_icon
(
'scroll_up'
)
=
custom_icon
(
'scroll_up_hover_active'
)
%a
.scroll-link.scroll-bottom
{
href:
'#down-build-trace'
,
id:
'scroll-bottom'
,
title:
'Scroll to bottom'
}
=
custom_icon
(
'scroll_down'
)
=
custom_icon
(
'scroll_down_hover_active'
)
-
if
@build
.
active?
.autoscroll-container
%span
.status-message
#autoscroll-status
{
data:
{
state:
'disabled'
}
}
%span
.status-text
Autoscroll active
%i
.status-icon
=
custom_icon
(
'scroll_down_hover_active'
)
#up-build-trace
%pre
.build-trace
#build-trace
.prepend-top-default
.build-trace-container
#build-trace
.top-bar.sticky
.js-truncated-info.truncated-info.hidden
<
Showing
last
%span
.js-truncated-info-size.truncated-info-size
><
KiB
of
log
-
%a
.js-raw-link.raw-link
{
:href
=>
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
)
}><
Complete
Raw
%code
.bash.js-build-output
.build-loader-animation.js-build-refresh
%a
.js-raw-link.raw-link
{
href:
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
)
}><
Complete
Raw
.controllers
-
if
@build
.
has_trace?
=
link_to
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
title:
'Open raw trace'
,
data:
{
placement:
'top'
,
container:
'body'
},
class:
'js-raw-link-controller has-tooltip'
do
=
icon
(
'download'
)
-
if
can?
(
current_user
,
:update_build
,
@project
)
&&
@build
.
erasable?
=
link_to
erase_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
method: :post
,
data:
{
confirm:
'Are you sure you want to erase this build?'
,
placement:
'top'
,
container:
'body'
},
title:
'Erase Build'
,
class:
'has-tooltip js-erase-link'
do
=
icon
(
'trash'
)
#down-build-trace
%button
.js-scroll-up.btn-scroll.btn-transparent.btn-blank.has-tooltip
{
type:
'button'
,
disabled:
true
,
title:
'Scroll Up'
,
data:
{
placement:
'top'
,
container:
'body'
}
}
=
custom_icon
(
'scroll_up'
)
%button
.js-scroll-down.btn-scroll.btn-transparent.btn-blank.has-tooltip
{
type:
'button'
,
disabled:
true
,
title:
'Scroll Down'
,
data:
{
placement:
'top'
,
container:
'body'
}
}
=
custom_icon
(
'scroll_down'
)
.bash.sticky.js-scroll-container
%code
.js-build-output
.build-loader-animation.js-build-refresh
=
render
"sidebar"
...
...
app/views/shared/icons/_scroll_down.svg
View file @
cd023d42
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-down"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill=
"#ffffff"
d=
"M1.385 5.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15V5.535a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z"
fill-rule=
"evenodd"
/>
<svg
width=
"12"
height=
"16"
viewBox=
"0 0 12 16"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
class=
"first-triangle"
d=
"M1.048 14.155a.508.508 0 0 0-.32.105c-.091.07-.136.154-.136.25v.71c0 .095.045.178.135.249.09.07.197.105.321.105h10.043c.124 0 .23-.035.321-.105.09-.07.136-.154.136-.25v-.71c0-.095-.045-.178-.136-.249a.508.508 0 0 0-.32-.105"
/>
<path
class=
"second-triangle"
d=
"M.687 8.027c-.09-.087-.122-.16-.093-.22.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 12.91a.458.458 0 0 1-.136.089h-.37a.626.626 0 0 1-.136-.09"
/>
<path
class=
"third-triangle"
d=
"M.687 1.027C.597.94.565.867.594.807c.028-.06.104-.09.228-.09h10.5c.123 0 .2.03.228.09.029.06-.002.133-.093.22L6.393 5.91A.458.458 0 0 1 6.257 6h-.37a.626.626 0 0 1-.136-.09"
/>
</svg>
app/views/shared/icons/_scroll_down_hover_active.svg
deleted
100644 → 0
View file @
763a3acd
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-down-hover"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill=
"#ffffff"
d=
"M8.88 30.27v-4.351a.688.688 0 0 0-.69-.688.687.687 0 0 0-.69.688v4.334l-1.345-1.346a.69.69 0 0 0-.976.976l2.526 2.526a.685.685 0 0 0 .494.2.685.685 0 0 0 .493-.2l2.526-2.526a.69.69 0 1 0-.976-.976L8.88 30.27zM0 5.534A5.536 5.536 0 0 1 5.529 0h4.942A5.53 5.53 0 0 1 16 5.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 18.005V5.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0V6.544z"
fill-rule=
"evenodd"
/>
</svg>
app/views/shared/icons/_scroll_up.svg
View file @
cd023d42
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-up"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill=
"#ffffff"
d=
"M1.385 14.534v12.47a4.145 4.145 0 0 0 4.144 4.15h4.942a4.151 4.151 0 0 0 4.144-4.15v-12.47a4.145 4.145 0 0 0-4.144-4.15H5.53a4.151 4.151 0 0 0-4.144 4.15zM8.88 2.609V6.96a.688.688 0 0 1-.69.688.687.687 0 0 1-.69-.688V2.627L6.155 3.972a.69.69 0 0 1-.976-.976L7.705.47a.685.685 0 0 1 .494-.2.685.685 0 0 1 .493.2l2.526 2.526a.69.69 0 1 1-.976.976L8.88 2.609zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z"
fill-rule=
"evenodd"
/>
</svg>
<svg
width=
"12"
height=
"16"
viewBox=
"0 0 12 16"
xmlns=
"http://www.w3.org/2000/svg"
><path
d=
"M1.048 1.845a.508.508 0 0 1-.32-.105c-.091-.07-.136-.154-.136-.25V.78c0-.095.045-.178.135-.249a.508.508 0 0 1 .321-.105h10.043c.124 0 .23.035.321.105.09.07.136.154.136.25v.71c0 .095-.045.178-.136.249a.508.508 0 0 1-.32.105"
/><path
d=
"M.687 7.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 3.09A.458.458 0 0 0 6.257 3h-.37a.626.626 0 0 0-.136.09"
/><path
d=
"M.687 14.973c-.09.087-.122.16-.093.22.028.06.104.09.228.09h10.5c.123 0 .2-.03.228-.09.029-.06-.002-.133-.093-.22L6.393 10.09A.458.458 0 0 0 6.257 10h-.37a.626.626 0 0 0-.136.09"
/></svg>
app/views/shared/icons/_scroll_up_hover_active.svg
deleted
100644 → 0
View file @
763a3acd
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-up-hover"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/svg"
>
<path
fill=
"#ffffff"
d=
"M8.88 2.646l1.362 1.362a.69.69 0 0 0 .976-.976L8.692.507A.685.685 0 0 0 8.2.306a.685.685 0 0 0-.494.2L5.179 3.033a.69.69 0 1 0 .976.976L7.5 2.663v4.179c0 .38.306.688.69.688.381 0 .69-.306.69-.688V2.646zM0 14.534A5.536 5.536 0 0 1 5.529 9h4.942A5.53 5.53 0 0 1 16 14.534v12.47a5.536 5.536 0 0 1-5.529 5.534H5.53A5.53 5.53 0 0 1 0 27.005V14.534zm7 1.01a1 1 0 1 1 2 0v2.143a1 1 0 1 1-2 0v-2.143z"
fill-rule=
"evenodd"
/>
</svg>
spec/features/projects/builds_spec.rb
View file @
cd023d42
...
...
@@ -190,7 +190,7 @@ feature 'Builds', :feature do
end
it
do
expect
(
page
).
to
have_
link
'Raw'
expect
(
page
).
to
have_
css
(
'.js-raw-link'
)
end
end
...
...
@@ -369,14 +369,14 @@ feature 'Builds', :feature do
end
end
describe
'GET /:project/builds/:id/raw'
do
describe
'GET /:project/builds/:id/raw'
,
:js
do
context
'access source'
do
context
'build from project'
do
before
do
Capybara
.
current_session
.
driver
.
header
(
'X-Sendfile-Type'
,
'X-Sendfile'
)
Capybara
.
current_session
.
driver
.
header
s
=
{
'X-Sendfile-Type'
=>
'X-Sendfile'
}
build
.
run!
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
page
.
within
(
'.js-build-sidebar'
)
{
click_link
'Raw'
}
find
(
'.js-raw-link-controller'
).
click
()
end
it
'sends the right headers'
do
...
...
@@ -388,7 +388,7 @@ feature 'Builds', :feature do
context
'build from other project'
do
before
do
Capybara
.
current_session
.
driver
.
header
(
'X-Sendfile-Type'
,
'X-Sendfile'
)
Capybara
.
current_session
.
driver
.
header
s
=
{
'X-Sendfile-Type'
=>
'X-Sendfile'
}
build2
.
run!
visit
raw_namespace_project_build_path
(
project
.
namespace
,
project
,
build2
)
end
...
...
@@ -403,7 +403,7 @@ feature 'Builds', :feature do
let
(
:existing_file
)
{
Tempfile
.
new
(
'existing-trace-file'
).
path
}
before
do
Capybara
.
current_session
.
driver
.
header
(
'X-Sendfile-Type'
,
'X-Sendfile'
)
Capybara
.
current_session
.
driver
.
header
s
=
{
'X-Sendfile-Type'
=>
'X-Sendfile'
}
build
.
run!
...
...
@@ -413,13 +413,13 @@ feature 'Builds', :feature do
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
end
context
'when build has trace in file'
do
context
'when build has trace in file'
,
:js
do
let
(
:paths
)
do
[
existing_file
]
end
before
do
page
.
within
(
'.js-build-sidebar'
)
{
click_link
'Raw'
}
find
(
'.js-raw-link-controller'
).
click
()
end
it
'sends the right headers'
do
...
...
@@ -433,7 +433,7 @@ feature 'Builds', :feature do
let
(
:paths
)
{
[]
}
it
'sends the right headers'
do
expect
(
page
.
status_code
).
not_to
have_
link
(
'Raw
'
)
expect
(
page
.
status_code
).
not_to
have_
selector
(
'.js-raw-link-controller
'
)
end
end
end
...
...
spec/javascripts/build_spec.js
View file @
cd023d42
...
...
@@ -14,7 +14,6 @@ describe('Build', () => {
beforeEach
(()
=>
{
loadFixtures
(
'
builds/build-with-artifacts.html.raw
'
);
spyOn
(
$
,
'
ajax
'
);
});
describe
(
'
class constructor
'
,
()
=>
{
...
...
@@ -33,7 +32,6 @@ describe('Build', () => {
it
(
'
copies build options
'
,
function
()
{
expect
(
this
.
build
.
pageUrl
).
toBe
(
BUILD_URL
);
expect
(
this
.
build
.
buildUrl
).
toBe
(
`
${
BUILD_URL
}
.json`
);
expect
(
this
.
build
.
buildStatus
).
toBe
(
'
success
'
);
expect
(
this
.
build
.
buildStage
).
toBe
(
'
test
'
);
expect
(
this
.
build
.
state
).
toBe
(
''
);
...
...
@@ -65,27 +63,14 @@ describe('Build', () => {
});
describe
(
'
running build
'
,
()
=>
{
beforeEach
(
function
()
{
this
.
build
=
new
Build
();
});
it
(
'
updates the build trace on an interval
'
,
function
()
{
const
deferred1
=
$
.
Deferred
();
const
deferred2
=
$
.
Deferred
();
const
deferred3
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValues
(
deferred1
.
promise
(),
deferred2
.
promise
(),
deferred3
.
promise
());
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
1
);
// We have to do it this way to prevent Webpack to fail to compile
// when destructuring assignments and reusing
// the same variables names inside the same scope
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
expect
(
args
.
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json`
);
expect
(
args
.
dataType
).
toBe
(
'
json
'
);
expect
(
args
.
success
).
toEqual
(
jasmine
.
any
(
Function
));
args
.
success
.
call
(
$
,
{
deferred1
.
resolve
({
html
:
'
<span>Update<span>
'
,
status
:
'
running
'
,
state
:
'
newstate
'
,
...
...
@@ -93,20 +78,9 @@ describe('Build', () => {
complete
:
false
,
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
expect
(
this
.
build
.
state
).
toBe
(
'
newstate
'
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
3
);
args
=
$
.
ajax
.
calls
.
argsFor
(
2
)[
0
];
expect
(
args
.
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json`
);
expect
(
args
.
dataType
).
toBe
(
'
json
'
);
expect
(
args
.
data
.
state
).
toBe
(
'
newstate
'
);
expect
(
args
.
success
).
toEqual
(
jasmine
.
any
(
Function
));
deferred2
.
resolve
();
args
.
success
.
call
(
$
,
{
deferred3
.
resolve
(
{
html
:
'
<span>More</span>
'
,
status
:
'
running
'
,
state
:
'
finalstate
'
,
...
...
@@ -114,150 +88,222 @@ describe('Build', () => {
complete
:
true
,
});
this
.
build
=
new
Build
();
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
expect
(
this
.
build
.
state
).
toBe
(
'
newstate
'
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/UpdateMore/
);
expect
(
this
.
build
.
state
).
toBe
(
'
finalstate
'
);
});
it
(
'
replaces the entire build trace
'
,
()
=>
{
const
deferred1
=
$
.
Deferred
();
const
deferred2
=
$
.
Deferred
();
const
deferred3
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValues
(
deferred1
.
promise
(),
deferred2
.
promise
(),
deferred3
.
promise
());
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
args
.
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
deferred1
.
resolve
({
html
:
'
<span>Update<span>
'
,
status
:
'
running
'
,
append
:
false
,
complete
:
false
,
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
deferred2
.
resolve
(
);
jasmine
.
clock
().
tick
(
4001
);
args
=
$
.
ajax
.
calls
.
argsFor
(
2
)[
0
];
args
.
success
.
call
(
$
,
{
deferred3
.
resolve
({
html
:
'
<span>Different</span>
'
,
status
:
'
running
'
,
append
:
false
,
});
this
.
build
=
new
Build
();
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
not
.
toMatch
(
/Update/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Different/
);
});
it
(
'
reloads the page when the build is done
'
,
()
=>
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
deferred
=
$
.
Deferred
();
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
success
.
call
(
$
,
{
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Final</span>
'
,
status
:
'
passed
'
,
append
:
true
,
complete
:
true
,
});
this
.
build
=
new
Build
();
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BUILD_URL
);
});
});
describe
(
'
truncated information
'
,
()
=>
{
describe
(
'
when size is less than total
'
,
()
=>
{
it
(
'
shows information about truncated log
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
not
.
toContain
(
'
hidden
'
);
describe
(
'
truncated information
'
,
()
=>
{
describe
(
'
when size is less than total
'
,
()
=>
{
it
(
'
shows information about truncated log
'
,
()
=>
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
deferred
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
it
(
'
shows the size in KiB
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
const
size
=
50
;
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
size
)}
`
);
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
not
.
toContain
(
'
hidden
'
);
});
it
(
'
shows the size in KiB
'
,
()
=>
{
const
size
=
50
;
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
deferred
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
,
total
:
100
,
});
it
(
'
shows incremented size
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
args
.
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
50
)}
`
);
jasmine
.
clock
().
tick
(
4001
);
args
=
$
.
ajax
.
calls
.
argsFor
(
2
)[
0
];
args
.
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
true
,
size
:
10
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
60
)}
`
);
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
size
)}
`
);
});
it
(
'
shows incremented size
'
,
()
=>
{
const
deferred1
=
$
.
Deferred
();
const
deferred2
=
$
.
Deferred
();
const
deferred3
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValues
(
deferred1
.
promise
(),
deferred2
.
promise
(),
deferred3
.
promise
());
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
deferred1
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
it
(
'
renders the raw link
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-raw-link
'
).
textContent
.
trim
(),
).
toContain
(
'
Complete Raw
'
);
deferred2
.
resolve
();
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
50
)}
`
);
jasmine
.
clock
().
tick
(
4001
);
deferred3
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
true
,
size
:
10
,
total
:
100
,
});
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
60
)}
`
);
});
describe
(
'
when size is equal than total
'
,
()
=>
{
it
(
'
does not show the trunctated information
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
it
(
'
renders the raw link
'
,
()
=>
{
const
deferred
=
$
.
Deferred
();
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
100
,
total
:
100
,
});
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
toContain
(
'
hidden
'
);
expect
(
document
.
querySelector
(
'
.js-raw-link
'
).
textContent
.
trim
(),
).
toContain
(
'
Complete Raw
'
);
});
});
describe
(
'
when size is equal than total
'
,
()
=>
{
it
(
'
does not show the trunctated information
'
,
()
=>
{
const
deferred
=
$
.
Deferred
();
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
100
,
total
:
100
,
});
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
toContain
(
'
hidden
'
);
});
});
});
describe
(
'
output trace
'
,
()
=>
{
beforeEach
(()
=>
{
const
deferred
=
$
.
Deferred
();
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
append
:
false
,
size
:
50
,
total
:
100
,
});
this
.
build
=
new
Build
();
});
it
(
'
should render trace controls
'
,
()
=>
{
const
controllers
=
document
.
querySelector
(
'
.controllers
'
);
expect
(
controllers
.
querySelector
(
'
.js-raw-link-controller
'
)).
toBeDefined
();
expect
(
controllers
.
querySelector
(
'
.js-erase-link
'
)).
toBeDefined
();
expect
(
controllers
.
querySelector
(
'
.js-scroll-up
'
)).
toBeDefined
();
expect
(
controllers
.
querySelector
(
'
.js-scroll-down
'
)).
toBeDefined
();
});
it
(
'
should render received output
'
,
()
=>
{
expect
(
document
.
querySelector
(
'
.js-build-output
'
).
innerHTML
,
).
toEqual
(
'
<span>Update</span>
'
);
});
});
});
...
...
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