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
f47e86fe
Commit
f47e86fe
authored
May 30, 2017
by
Phil Hughes
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '30117-update-looks-job-log' into 'master'
Update looks job log Closes #30117 See merge request !11663
parents
763a3acd
cd023d42
Changes
10
Show 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 @
f47e86fe
...
@@ -2,15 +2,11 @@
...
@@ -2,15 +2,11 @@
consistent-return, prefer-rest-params */
consistent-return, prefer-rest-params */
/* global Breakpoints */
/* global Breakpoints */
import
_
from
'
underscore
'
;
import
{
bytesToKiB
}
from
'
./lib/utils/number_utils
'
;
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
()
{
window
.
Build
=
(
function
()
{
Build
.
timeout
=
null
;
Build
.
timeout
=
null
;
Build
.
state
=
null
;
Build
.
state
=
null
;
function
Build
(
options
)
{
function
Build
(
options
)
{
...
@@ -23,21 +19,22 @@ window.Build = (function () {
...
@@ -23,21 +19,22 @@ window.Build = (function () {
this
.
buildStage
=
this
.
options
.
buildStage
;
this
.
buildStage
=
this
.
options
.
buildStage
;
this
.
$document
=
$
(
document
);
this
.
$document
=
$
(
document
);
this
.
logBytes
=
0
;
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
.
$body
=
$
(
'
body
'
);
this
.
$buildTrace
=
$
(
'
#build-trace
'
);
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
.
$buildRefreshAnimation
=
$
(
'
.js-build-refresh
'
);
this
.
$buildScroll
=
$
(
'
#js-build-scroll
'
);
this
.
$truncatedInfo
=
$
(
'
.js-truncated-info
'
);
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
);
clearTimeout
(
Build
.
timeout
);
// Init breakpoint checker
// Init breakpoint checker
...
@@ -56,54 +53,149 @@ window.Build = (function () {
...
@@ -56,54 +53,149 @@ window.Build = (function () {
.
off
(
'
click
'
,
'
.stage-item
'
)
.
off
(
'
click
'
,
'
.stage-item
'
)
.
on
(
'
click
'
,
'
.stage-item
'
,
this
.
updateDropdown
);
.
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
)
$
(
window
)
.
off
(
'
resize.build
'
)
.
off
(
'
resize.build
'
)
.
on
(
'
resize.build
'
,
this
.
sidebarOnResize
.
bind
(
this
));
.
on
(
'
resize.build
'
,
this
.
sidebarOnResize
.
bind
(
this
));
$
(
'
a
'
,
this
.
$buildScroll
)
.
off
(
'
click.stepTrace
'
)
.
on
(
'
click.stepTrace
'
,
this
.
stepTrace
);
this
.
updateArtifactRemoveDate
();
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
()
{
Build
.
prototype
.
initSidebar
=
function
()
{
this
.
$sidebar
=
$
(
'
.js-build-sidebar
'
);
this
.
$sidebar
=
$
(
'
.js-build-sidebar
'
);
this
.
$sidebar
.
niceScroll
();
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
()
{
Build
.
prototype
.
getBuildTrace
=
function
()
{
return
$
.
ajax
({
return
$
.
ajax
({
url
:
`
${
this
.
pageUrl
}
/trace.json`
,
url
:
`
${
this
.
pageUrl
}
/trace.json`
,
dataType
:
'
json
'
,
data
:
this
.
state
,
data
:
{
})
state
:
this
.
state
,
.
done
((
log
)
=>
{
},
success
:
((
log
)
=>
{
const
$buildContainer
=
$
(
'
.js-build-output
'
);
gl
.
utils
.
setCiStatusFavicon
(
`
${
this
.
pageUrl
}
/status.json`
);
gl
.
utils
.
setCiStatusFavicon
(
`
${
this
.
pageUrl
}
/status.json`
);
if
(
log
.
state
)
{
if
(
log
.
state
)
{
this
.
state
=
log
.
state
;
this
.
state
=
log
.
state
;
}
}
if
(
log
.
append
)
{
if
(
log
.
append
)
{
$buildContainer
.
append
(
log
.
html
);
this
.
$buildTraceOutput
.
append
(
log
.
html
);
this
.
logBytes
+=
log
.
size
;
this
.
logBytes
+=
log
.
size
;
}
else
{
}
else
{
$buildContainer
.
html
(
log
.
html
);
this
.
$buildTraceOutput
.
html
(
log
.
html
);
this
.
logBytes
=
log
.
size
;
this
.
logBytes
=
log
.
size
;
}
}
...
@@ -114,161 +206,55 @@ window.Build = (function () {
...
@@ -114,161 +206,55 @@ window.Build = (function () {
const
size
=
bytesToKiB
(
this
.
logBytes
);
const
size
=
bytesToKiB
(
this
.
logBytes
);
$
(
'
.js-truncated-info-size
'
).
html
(
`
${
size
}
`
);
$
(
'
.js-truncated-info-size
'
).
html
(
`
${
size
}
`
);
this
.
$truncatedInfo
.
removeClass
(
'
hidden
'
);
this
.
$truncatedInfo
.
removeClass
(
'
hidden
'
);
this
.
initAffixTruncatedInfo
();
}
else
{
}
else
{
this
.
$truncatedInfo
.
addClass
(
'
hidden
'
);
this
.
$truncatedInfo
.
addClass
(
'
hidden
'
);
}
}
this
.
checkAutoscroll
();
if
(
!
log
.
complete
)
{
if
(
!
log
.
complete
)
{
this
.
toggleScrollAnimation
(
true
);
Build
.
timeout
=
setTimeout
(()
=>
{
Build
.
timeout
=
setTimeout
(()
=>
{
this
.
invokeBuildTrace
();
//eslint-disable-next-line
this
.
getBuildTrace
()
.
then
(()
=>
this
.
scrollToBottom
());
},
4000
);
},
4000
);
}
else
{
}
else
{
this
.
$buildRefreshAnimation
.
remove
();
this
.
$buildRefreshAnimation
.
remove
();
this
.
toggleScrollAnimation
(
false
);
}
}
if
(
log
.
status
!==
this
.
buildStatus
)
{
if
(
log
.
status
!==
this
.
buildStatus
)
{
let
pageUrl
=
this
.
pageUrl
;
gl
.
utils
.
visitUrl
(
this
.
pageUrl
);
if
(
this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
pageUrl
+=
DOWN_BUILD_TRACE
;
}
gl
.
utils
.
visitUrl
(
pageUrl
);
}
}
})
,
})
error
:
()
=>
{
.
fail
(
()
=>
{
this
.
$buildRefreshAnimation
.
remove
();
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
()
{
Build
.
prototype
.
shouldHideSidebarForViewport
=
function
()
{
const
bootstrapBreakpoint
=
this
.
bp
.
getBreakpointSize
();
const
bootstrapBreakpoint
=
this
.
bp
.
getBreakpointSize
();
return
bootstrapBreakpoint
===
'
xs
'
||
bootstrapBreakpoint
===
'
sm
'
;
return
bootstrapBreakpoint
===
'
xs
'
||
bootstrapBreakpoint
===
'
sm
'
;
};
};
Build
.
prototype
.
toggleSidebar
=
function
(
shouldHide
)
{
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
);
.
toggleClass
(
'
sidebar-collapsed
'
,
shouldHide
);
this
.
$truncatedInfo
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
this
.
$sidebar
.
toggleClass
(
'
sidebar-collapsed
'
,
shouldHide
);
.
toggleClass
(
'
right-sidebar-expanded
'
,
shouldShow
)
this
.
$sidebar
.
toggleClass
(
'
right-sidebar-expanded
'
,
shouldShow
)
.
toggleClass
(
'
right-sidebar-collapsed
'
,
shouldHide
);
.
toggleClass
(
'
right-sidebar-collapsed
'
,
shouldHide
);
};
};
Build
.
prototype
.
sidebarOnResize
=
function
()
{
Build
.
prototype
.
sidebarOnResize
=
function
()
{
this
.
toggleSidebar
(
this
.
shouldHideSidebarForViewport
());
this
.
toggleSidebar
(
this
.
shouldHideSidebarForViewport
());
this
.
verifyTopPosition
();
if
(
this
.
$scrollContainer
.
getNiceScroll
(
0
))
{
this
.
toggleScroll
();
}
};
};
Build
.
prototype
.
sidebarOnClick
=
function
()
{
Build
.
prototype
.
sidebarOnClick
=
function
()
{
...
@@ -301,24 +287,5 @@ window.Build = (function () {
...
@@ -301,24 +287,5 @@ window.Build = (function () {
this
.
populateJobs
(
stage
);
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
;
return
Build
;
})();
})();
app/assets/stylesheets/pages/builds.scss
View file @
f47e86fe
...
@@ -29,65 +29,53 @@
...
@@ -29,65 +29,53 @@
}
}
}
}
.build-page
{
@keyframes
blinking-scroll-button
{
pre
.trace
{
0
%
{
opacity
:
0
.2
;
}
background
:
$builds-trace-bg
;
25
%
{
opacity
:
0
.5
;
}
color
:
$white-light
;
50
%
{
opacity
:
0
.7
;
}
font-family
:
$monospace_font
;
100
%
{
opacity
:
1
;
}
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
;
svg
{
.build-page
{
position
:
relative
;
.sticky
{
top
:
1px
;
position
:
absolute
;
margin-right
:
5px
;
left
:
0
;
}
right
:
0
;
}
}
.truncated-info
{
.build-trace-container
{
text-align
:
center
;
position
:
absolute
;
border-bottom
:
1px
solid
;
top
:
225px
;
background-color
:
$black
;
left
:
15px
;
height
:
45px
;
bottom
:
10px
;
padding
:
15px
;
background
:
$black
;
color
:
$gray-darkest
;
font-family
:
$monospace_font
;
font-size
:
12px
;
&
.
affix
{
&
.
sidebar-expanded
{
top
:
0
;
right
:
305px
;
}
}
// with sidebar
&
.sidebar-collapsed
{
&
.affix.sidebar-expanded
{
right
:
16px
;
right
:
312px
;
left
:
22px
;
}
}
// without sidebar
code
{
&
.affix.sidebar-collapsed
{
background
:
$black
;
right
:
20px
;
color
:
$gray-darkest
;
left
:
20px
;
}
}
&
.affix-top
{
.top-bar
{
position
:
absolute
;
top
:
0
;
top
:
0
;
height
:
35px
;
display
:
flex
;
justify-content
:
flex-end
;
border-bottom
:
1px
outset
$white-light
;
.truncated-info
{
margin
:
0
auto
;
margin
:
0
auto
;
right
:
5px
;
align-self
:
center
;
left
:
5px
;
}
.truncated-info-size
{
.truncated-info-size
{
margin
:
0
5px
;
margin
:
0
5px
;
...
@@ -99,59 +87,82 @@
...
@@ -99,59 +87,82 @@
text-decoration
:
underline
;
text-decoration
:
underline
;
}
}
}
}
}
}
.scroll-controls
{
.controllers
{
height
:
100%
;
display
:
flex
;
align-self
:
center
;
font-size
:
15px
;
.scroll-step
{
svg
{
width
:
31px
;
height
:
15px
;
margin
:
0
0
0
auto
;
display
:
block
;
fill
:
$white-light
;
}
}
.scroll-link
,
a
,
.autoscroll-container
{
.btn-scroll
{
right
:
25
px
;
margin
:
0
8
px
;
z-index
:
1
;
color
:
$white-light
;
}
}
.scroll-link
{
.btn-scroll.animate
{
position
:
fixed
;
.first-triangle
{
display
:
block
;
animation
:
blinking-scroll-button
1s
ease
infinite
;
margin-bottom
:
10px
;
animation-delay
:
.3s
;
}
&
.scroll-top
.gitlab-icon-scroll-up-hover
,
.second-triangle
{
&
.scroll-top
:hover
.gitlab-icon-scroll-up
,
animation
:
blinking-scroll-button
1s
ease
infinite
;
&
.scroll-bottom
.gitlab-icon-scroll-down-hover
,
animation-delay
:
.2s
;
&
.scroll-bottom
:hover
.gitlab-icon-scroll-down
{
display
:
none
;
}
}
&
.scroll-top
:hover
.gitlab-icon-scroll-up-hover
,
.third-triangle
{
&
.scroll-bottom
:hover
.gitlab-icon-scroll-down-hover
{
animation
:
blinking-scroll-button
1s
ease
infinite
;
display
:
inline-block
;
}
}
&
.scroll-top
{
&
:disabled
{
top
:
10px
;
opacity
:
1
;
}
}
}
&
.scroll-bottom
{
.btn-scroll
:disabled
{
bottom
:
-2px
;
opacity
:
0
.35
;
cursor
:
not
-
allowed
;
}
}
}
}
}
.autoscroll-container
{
.bash
{
position
:
absolute
;
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
,
svg
{
.autoscroll-container
{
position
:
relative
;
right
:
(
$gutter_width
+
(
$gl-padding
*
2
));
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
{
.status-message
{
...
@@ -223,32 +234,6 @@
...
@@ -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
{
.right-sidebar.build-sidebar
{
padding
:
$gl-padding
0
;
padding
:
$gl-padding
0
;
...
...
app/views/projects/builds/_sidebar.html.haml
View file @
f47e86fe
...
@@ -68,15 +68,8 @@
...
@@ -68,15 +68,8 @@
-
elsif
@build
.
runner
-
elsif
@build
.
runner
\##{@build.runner.id}
\##{@build.runner.id}
.btn-group.btn-group-justified
{
role: :group
}
.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?
-
if
@build
.
active?
=
link_to
"Cancel"
,
cancel_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
),
class:
'btn btn-sm btn-default'
,
method: :post
=
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
-
if
@build
.
trigger_request
.build-widget
.build-widget
...
...
app/views/projects/builds/show.html.haml
View file @
f47e86fe
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
-
if
@build
.
stuck?
-
if
@build
.
stuck?
-
unless
@build
.
any_runners_online?
-
unless
@build
.
any_runners_online?
.bs-callout.bs-callout-warning
.bs-callout.bs-callout-warning
.js-build-stuck
%p
%p
-
if
no_runners_for_project?
(
@build
.
project
)
-
if
no_runners_for_project?
(
@build
.
project
)
This job is stuck, because the project doesn't have any runners online assigned to it.
This job is stuck, because the project doesn't have any runners online assigned to it.
...
@@ -26,7 +26,7 @@
...
@@ -26,7 +26,7 @@
Runners page
Runners page
-
if
@build
.
starts_environment?
-
if
@build
.
starts_environment?
.prepend-top-default
.prepend-top-default
.js-environment-container
.environment-information
.environment-information
-
if
@build
.
outdated_deployment?
-
if
@build
.
outdated_deployment?
=
ci_icon_for_status
(
'success_with_warnings'
)
=
ci_icon_for_status
(
'success_with_warnings'
)
...
@@ -47,39 +47,51 @@
...
@@ -47,39 +47,51 @@
-
if
environment
.
try
(
:last_deployment
)
-
if
environment
.
try
(
:last_deployment
)
and will overwrite the
#{
deployment_link
(
environment
.
last_deployment
,
text:
'latest 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?
-
if
@build
.
erased?
.erased.alert.alert-warning
.erased.alert.alert-warning
-
if
@build
.
erased_by_user?
-
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
)
}
Job has been erased by
#{
link_to
(
@build
.
erased_by_name
,
user_path
(
@build
.
erased_by
))
}
#{
time_ago_with_tooltip
(
@build
.
erased_at
)
}
-
else
-
else
Job has been erased
#{
time_ago_with_tooltip
(
@build
.
erased_at
)
}
Job has been erased
#{
time_ago_with_tooltip
(
@build
.
erased_at
)
}
-
else
#js-build-scroll
.scroll-controls
.prepend-top-default
.scroll-step
.build-trace-container
#build-trace
%a
.scroll-link.scroll-top
{
href:
'#up-build-trace'
,
id:
'scroll-top'
,
title:
'Scroll to top'
}
.top-bar.sticky
=
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
.js-truncated-info.truncated-info.hidden
<
.js-truncated-info.truncated-info.hidden
<
Showing
last
Showing
last
%span
.js-truncated-info-size.truncated-info-size
><
%span
.js-truncated-info-size.truncated-info-size
><
KiB
of
log
-
KiB
of
log
-
%a
.js-raw-link.raw-link
{
:href
=>
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
)
}><
Complete
Raw
%a
.js-raw-link.raw-link
{
href:
raw_namespace_project_build_path
(
@project
.
namespace
,
@project
,
@build
)
}><
Complete
Raw
%code
.bash.js-build-output
.controllers
.build-loader-animation.js-build-refresh
-
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"
=
render
"sidebar"
...
...
app/views/shared/icons/_scroll_down.svg
View file @
f47e86fe
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-down"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/svg"
>
<svg
width=
"12"
height=
"16"
viewBox=
"0 0 12 16"
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"
/>
<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>
</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 @
f47e86fe
<svg
width=
"16"
height=
"33"
class=
"gitlab-icon-scroll-up"
viewBox=
"0 0 16 33"
xmlns=
"http://www.w3.org/2000/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>
<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>
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 @
f47e86fe
...
@@ -190,7 +190,7 @@ feature 'Builds', :feature do
...
@@ -190,7 +190,7 @@ feature 'Builds', :feature do
end
end
it
do
it
do
expect
(
page
).
to
have_
link
'Raw'
expect
(
page
).
to
have_
css
(
'.js-raw-link'
)
end
end
end
end
...
@@ -369,14 +369,14 @@ feature 'Builds', :feature do
...
@@ -369,14 +369,14 @@ feature 'Builds', :feature do
end
end
end
end
describe
'GET /:project/builds/:id/raw'
do
describe
'GET /:project/builds/:id/raw'
,
:js
do
context
'access source'
do
context
'access source'
do
context
'build from project'
do
context
'build from project'
do
before
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!
build
.
run!
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
page
.
within
(
'.js-build-sidebar'
)
{
click_link
'Raw'
}
find
(
'.js-raw-link-controller'
).
click
()
end
end
it
'sends the right headers'
do
it
'sends the right headers'
do
...
@@ -388,7 +388,7 @@ feature 'Builds', :feature do
...
@@ -388,7 +388,7 @@ feature 'Builds', :feature do
context
'build from other project'
do
context
'build from other project'
do
before
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!
build2
.
run!
visit
raw_namespace_project_build_path
(
project
.
namespace
,
project
,
build2
)
visit
raw_namespace_project_build_path
(
project
.
namespace
,
project
,
build2
)
end
end
...
@@ -403,7 +403,7 @@ feature 'Builds', :feature do
...
@@ -403,7 +403,7 @@ feature 'Builds', :feature do
let
(
:existing_file
)
{
Tempfile
.
new
(
'existing-trace-file'
).
path
}
let
(
:existing_file
)
{
Tempfile
.
new
(
'existing-trace-file'
).
path
}
before
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!
build
.
run!
...
@@ -413,13 +413,13 @@ feature 'Builds', :feature do
...
@@ -413,13 +413,13 @@ feature 'Builds', :feature do
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
visit
namespace_project_build_path
(
project
.
namespace
,
project
,
build
)
end
end
context
'when build has trace in file'
do
context
'when build has trace in file'
,
:js
do
let
(
:paths
)
do
let
(
:paths
)
do
[
existing_file
]
[
existing_file
]
end
end
before
do
before
do
page
.
within
(
'.js-build-sidebar'
)
{
click_link
'Raw'
}
find
(
'.js-raw-link-controller'
).
click
()
end
end
it
'sends the right headers'
do
it
'sends the right headers'
do
...
@@ -433,7 +433,7 @@ feature 'Builds', :feature do
...
@@ -433,7 +433,7 @@ feature 'Builds', :feature do
let
(
:paths
)
{
[]
}
let
(
:paths
)
{
[]
}
it
'sends the right headers'
do
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
end
end
end
...
...
spec/javascripts/build_spec.js
View file @
f47e86fe
...
@@ -14,7 +14,6 @@ describe('Build', () => {
...
@@ -14,7 +14,6 @@ describe('Build', () => {
beforeEach
(()
=>
{
beforeEach
(()
=>
{
loadFixtures
(
'
builds/build-with-artifacts.html.raw
'
);
loadFixtures
(
'
builds/build-with-artifacts.html.raw
'
);
spyOn
(
$
,
'
ajax
'
);
});
});
describe
(
'
class constructor
'
,
()
=>
{
describe
(
'
class constructor
'
,
()
=>
{
...
@@ -33,7 +32,6 @@ describe('Build', () => {
...
@@ -33,7 +32,6 @@ describe('Build', () => {
it
(
'
copies build options
'
,
function
()
{
it
(
'
copies build options
'
,
function
()
{
expect
(
this
.
build
.
pageUrl
).
toBe
(
BUILD_URL
);
expect
(
this
.
build
.
pageUrl
).
toBe
(
BUILD_URL
);
expect
(
this
.
build
.
buildUrl
).
toBe
(
`
${
BUILD_URL
}
.json`
);
expect
(
this
.
build
.
buildStatus
).
toBe
(
'
success
'
);
expect
(
this
.
build
.
buildStatus
).
toBe
(
'
success
'
);
expect
(
this
.
build
.
buildStage
).
toBe
(
'
test
'
);
expect
(
this
.
build
.
buildStage
).
toBe
(
'
test
'
);
expect
(
this
.
build
.
state
).
toBe
(
''
);
expect
(
this
.
build
.
state
).
toBe
(
''
);
...
@@ -65,27 +63,14 @@ describe('Build', () => {
...
@@ -65,27 +63,14 @@ describe('Build', () => {
});
});
describe
(
'
running build
'
,
()
=>
{
describe
(
'
running build
'
,
()
=>
{
beforeEach
(
function
()
{
this
.
build
=
new
Build
();
});
it
(
'
updates the build trace on an interval
'
,
function
()
{
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
'
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
deferred1
.
resolve
({
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
(
$
,
{
html
:
'
<span>Update<span>
'
,
html
:
'
<span>Update<span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
state
:
'
newstate
'
,
state
:
'
newstate
'
,
...
@@ -93,20 +78,9 @@ describe('Build', () => {
...
@@ -93,20 +78,9 @@ describe('Build', () => {
complete
:
false
,
complete
:
false
,
});
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
deferred2
.
resolve
();
expect
(
this
.
build
.
state
).
toBe
(
'
newstate
'
);
jasmine
.
clock
().
tick
(
4001
);
deferred3
.
resolve
({
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
));
args
.
success
.
call
(
$
,
{
html
:
'
<span>More</span>
'
,
html
:
'
<span>More</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
state
:
'
finalstate
'
,
state
:
'
finalstate
'
,
...
@@ -114,58 +88,77 @@ describe('Build', () => {
...
@@ -114,58 +88,77 @@ describe('Build', () => {
complete
:
true
,
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
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/UpdateMore/
);
expect
(
this
.
build
.
state
).
toBe
(
'
finalstate
'
);
expect
(
this
.
build
.
state
).
toBe
(
'
finalstate
'
);
});
});
it
(
'
replaces the entire build trace
'
,
()
=>
{
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
'
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
deferred1
.
resolve
({
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
html
:
'
<span>Update<span>
'
,
args
.
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
append
:
false
,
append
:
false
,
complete
:
false
,
complete
:
false
,
});
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
deferred2
.
resolve
(
);
jasmine
.
clock
().
tick
(
4001
);
deferred3
.
resolve
({
args
=
$
.
ajax
.
calls
.
argsFor
(
2
)[
0
];
args
.
success
.
call
(
$
,
{
html
:
'
<span>Different</span>
'
,
html
:
'
<span>Different</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
append
:
false
,
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
()).
not
.
toMatch
(
/Update/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Different/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Different/
);
});
});
it
(
'
reloads the page when the build is done
'
,
()
=>
{
it
(
'
reloads the page when the build is done
'
,
()
=>
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
deferred
=
$
.
Deferred
();
jasmine
.
clock
().
tick
(
4001
);
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
deferred
.
resolve
({
success
.
call
(
$
,
{
html
:
'
<span>Final</span>
'
,
html
:
'
<span>Final</span>
'
,
status
:
'
passed
'
,
status
:
'
passed
'
,
append
:
true
,
append
:
true
,
complete
:
true
,
complete
:
true
,
});
});
this
.
build
=
new
Build
();
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BUILD_URL
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BUILD_URL
);
});
});
});
describe
(
'
truncated information
'
,
()
=>
{
describe
(
'
truncated information
'
,
()
=>
{
describe
(
'
when size is less than total
'
,
()
=>
{
describe
(
'
when size is less than total
'
,
()
=>
{
it
(
'
shows information about truncated log
'
,
()
=>
{
it
(
'
shows information about truncated log
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
const
deferred
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
success
.
call
(
$
,
{
deferred
.
resolve
(
{
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
false
,
append
:
false
,
...
@@ -173,15 +166,18 @@ describe('Build', () => {
...
@@ -173,15 +166,18 @@ describe('Build', () => {
total
:
100
,
total
:
100
,
});
});
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
not
.
toContain
(
'
hidden
'
);
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
not
.
toContain
(
'
hidden
'
);
});
});
it
(
'
shows the size in KiB
'
,
()
=>
{
it
(
'
shows the size in KiB
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
const
size
=
50
;
const
size
=
50
;
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
const
deferred
=
$
.
Deferred
();
success
.
call
(
$
,
{
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
false
,
append
:
false
,
...
@@ -189,15 +185,23 @@ describe('Build', () => {
...
@@ -189,15 +185,23 @@ describe('Build', () => {
total
:
100
,
total
:
100
,
});
});
this
.
build
=
new
Build
();
expect
(
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
size
)}
`
);
).
toEqual
(
`
${
bytesToKiB
(
size
)}
`
);
});
});
it
(
'
shows incremented size
'
,
()
=>
{
it
(
'
shows incremented size
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
deferred1
=
$
.
Deferred
();
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
const
deferred2
=
$
.
Deferred
();
args
.
success
.
call
(
$
,
{
const
deferred3
=
$
.
Deferred
();
spyOn
(
$
,
'
ajax
'
).
and
.
returnValues
(
deferred1
.
promise
(),
deferred2
.
promise
(),
deferred3
.
promise
());
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
deferred1
.
resolve
({
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
false
,
append
:
false
,
...
@@ -205,13 +209,17 @@ describe('Build', () => {
...
@@ -205,13 +209,17 @@ describe('Build', () => {
total
:
100
,
total
:
100
,
});
});
deferred2
.
resolve
();
this
.
build
=
new
Build
();
expect
(
expect
(
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
document
.
querySelector
(
'
.js-truncated-info-size
'
).
textContent
.
trim
(),
).
toEqual
(
`
${
bytesToKiB
(
50
)}
`
);
).
toEqual
(
`
${
bytesToKiB
(
50
)}
`
);
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
args
=
$
.
ajax
.
calls
.
argsFor
(
2
)[
0
];
args
.
success
.
call
(
$
,
{
deferred3
.
resolve
(
{
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
true
,
append
:
true
,
...
@@ -225,10 +233,11 @@ describe('Build', () => {
...
@@ -225,10 +233,11 @@ describe('Build', () => {
});
});
it
(
'
renders the raw link
'
,
()
=>
{
it
(
'
renders the raw link
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
deferred
=
$
.
Deferred
(
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
success
.
call
(
$
,
{
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
false
,
append
:
false
,
...
@@ -236,6 +245,8 @@ describe('Build', () => {
...
@@ -236,6 +245,8 @@ describe('Build', () => {
total
:
100
,
total
:
100
,
});
});
this
.
build
=
new
Build
();
expect
(
expect
(
document
.
querySelector
(
'
.js-raw-link
'
).
textContent
.
trim
(),
document
.
querySelector
(
'
.js-raw-link
'
).
textContent
.
trim
(),
).
toContain
(
'
Complete Raw
'
);
).
toContain
(
'
Complete Raw
'
);
...
@@ -244,10 +255,11 @@ describe('Build', () => {
...
@@ -244,10 +255,11 @@ describe('Build', () => {
describe
(
'
when size is equal than total
'
,
()
=>
{
describe
(
'
when size is equal than total
'
,
()
=>
{
it
(
'
does not show the trunctated information
'
,
()
=>
{
it
(
'
does not show the trunctated information
'
,
()
=>
{
jasmine
.
clock
().
tick
(
4001
);
const
deferred
=
$
.
Deferred
(
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
success
.
call
(
$
,
{
spyOn
(
$
,
'
ajax
'
).
and
.
returnValue
(
deferred
.
promise
());
deferred
.
resolve
({
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
success
'
,
status
:
'
success
'
,
append
:
false
,
append
:
false
,
...
@@ -255,10 +267,44 @@ describe('Build', () => {
...
@@ -255,10 +267,44 @@ describe('Build', () => {
total
:
100
,
total
:
100
,
});
});
this
.
build
=
new
Build
();
expect
(
document
.
querySelector
(
'
.js-truncated-info
'
).
classList
).
toContain
(
'
hidden
'
);
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