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
8ca5afdf
Commit
8ca5afdf
authored
Apr 07, 2017
by
Filipa Lacerda
Committed by
Jacob Schatz
Apr 07, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
FE: Resolve "Performance issues when processing large build traces with Ansi2html"
parent
9216f59a
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
238 additions
and
173 deletions
+238
-173
app/assets/javascripts/build.js
app/assets/javascripts/build.js
+148
-120
app/assets/stylesheets/pages/builds.scss
app/assets/stylesheets/pages/builds.scss
+32
-0
app/views/projects/builds/show.html.haml
app/views/projects/builds/show.html.haml
+5
-0
spec/javascripts/build_spec.js
spec/javascripts/build_spec.js
+53
-53
No files found.
app/assets/javascripts/build.js
View file @
8ca5afdf
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, no-param-reassign, quotes, yoda, no-else-return, consistent-return, comma-dangle, object-shorthand, prefer-template, one-var, one-var-declaration-per-line, no-unused-vars, max-len, vars-on-top */
/* eslint-disable func-names, wrap-iife, no-use-before-define,
consistent-return, prefer-rest-params */
/* global Breakpoints */
/* global Breakpoints */
var
bind
=
function
(
fn
,
me
)
{
return
function
()
{
return
fn
.
apply
(
me
,
arguments
);
};
};
const
bind
=
function
(
fn
,
me
)
{
return
function
()
{
return
fn
.
apply
(
me
,
arguments
);
};
};
var
AUTO_SCROLL_OFFSET
=
75
;
const
AUTO_SCROLL_OFFSET
=
75
;
var
DOWN_BUILD_TRACE
=
'
#down-build-trace
'
;
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
)
{
options
=
options
||
$
(
'
.js-build-options
'
).
data
();
this
.
options
=
options
||
$
(
'
.js-build-options
'
).
data
();
this
.
pageUrl
=
options
.
pageUrl
;
this
.
buildUrl
=
options
.
build
Url
;
this
.
pageUrl
=
this
.
options
.
page
Url
;
this
.
build
Status
=
options
.
buildStatus
;
this
.
build
Url
=
this
.
options
.
buildUrl
;
this
.
state
=
options
.
logState
;
this
.
buildStatus
=
this
.
options
.
buildStatus
;
this
.
buildStage
=
options
.
buildStag
e
;
this
.
state
=
this
.
options
.
logStat
e
;
this
.
updateDropdown
=
bind
(
this
.
updateDropdown
,
this
)
;
this
.
buildStage
=
this
.
options
.
buildStage
;
this
.
$document
=
$
(
document
);
this
.
$document
=
$
(
document
);
this
.
updateDropdown
=
bind
(
this
.
updateDropdown
,
this
);
this
.
$body
=
$
(
'
body
'
);
this
.
$body
=
$
(
'
body
'
);
this
.
$buildTrace
=
$
(
'
#build-trace
'
);
this
.
$buildTrace
=
$
(
'
#build-trace
'
);
this
.
$autoScrollContainer
=
$
(
'
.autoscroll-container
'
);
this
.
$autoScrollContainer
=
$
(
'
.autoscroll-container
'
);
...
@@ -29,112 +33,110 @@ window.Build = (function() {
...
@@ -29,112 +33,110 @@ window.Build = (function() {
this
.
$scrollTopBtn
=
$
(
'
#scroll-top
'
);
this
.
$scrollTopBtn
=
$
(
'
#scroll-top
'
);
this
.
$scrollBottomBtn
=
$
(
'
#scroll-bottom
'
);
this
.
$scrollBottomBtn
=
$
(
'
#scroll-bottom
'
);
this
.
$buildRefreshAnimation
=
$
(
'
.js-build-refresh
'
);
this
.
$buildRefreshAnimation
=
$
(
'
.js-build-refresh
'
);
this
.
$buildScroll
=
$
(
'
#js-build-scroll
'
);
this
.
$truncatedInfo
=
$
(
'
.js-truncated-info
'
);
clearTimeout
(
Build
.
timeout
);
clearTimeout
(
Build
.
timeout
);
// Init breakpoint checker
// Init breakpoint checker
this
.
bp
=
Breakpoints
.
get
();
this
.
bp
=
Breakpoints
.
get
();
this
.
initSidebar
();
this
.
initSidebar
();
this
.
$buildScroll
=
$
(
'
#js-build-scroll
'
);
this
.
populateJobs
(
this
.
buildStage
);
this
.
populateJobs
(
this
.
buildStage
);
this
.
updateStageDropdownText
(
this
.
buildStage
);
this
.
updateStageDropdownText
(
this
.
buildStage
);
this
.
sidebarOnResize
();
this
.
sidebarOnResize
();
this
.
$document
.
off
(
'
click
'
,
'
.js-sidebar-build-toggle
'
).
on
(
'
click
'
,
'
.js-sidebar-build-toggle
'
,
this
.
sidebarOnClick
.
bind
(
this
));
this
.
$document
this
.
$document
.
off
(
'
click
'
,
'
.stage-item
'
).
on
(
'
click
'
,
'
.stage-item
'
,
this
.
updateDropdown
);
.
off
(
'
click
'
,
'
.js-sidebar-build-toggle
'
)
.
on
(
'
click
'
,
'
.js-sidebar-build-toggle
'
,
this
.
sidebarOnClick
.
bind
(
this
));
this
.
$document
.
off
(
'
click
'
,
'
.stage-item
'
)
.
on
(
'
click
'
,
'
.stage-item
'
,
this
.
updateDropdown
);
this
.
$document
.
on
(
'
scroll
'
,
this
.
initScrollMonitor
.
bind
(
this
));
this
.
$document
.
on
(
'
scroll
'
,
this
.
initScrollMonitor
.
bind
(
this
));
$
(
window
).
off
(
'
resize.build
'
).
on
(
'
resize.build
'
,
this
.
sidebarOnResize
.
bind
(
this
));
$
(
'
a
'
,
this
.
$buildScroll
).
off
(
'
click.stepTrace
'
).
on
(
'
click.stepTrace
'
,
this
.
stepTrace
);
$
(
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
.
updateArtifactRemoveDate
();
if
(
$
(
'
#build-trace
'
).
length
)
{
this
.
initScrollButtonAffix
();
this
.
getInitialBuildTrace
();
this
.
initScrollButtonAffix
();
}
this
.
invokeBuildTrace
();
this
.
invokeBuildTrace
();
}
}
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
);
this
.
$document
.
off
(
'
click
'
,
'
.js-sidebar-build-toggle
'
)
.
on
(
'
click
'
,
'
.js-sidebar-build-toggle
'
,
this
.
toggleSidebar
);
};
};
Build
.
prototype
.
location
=
function
()
{
Build
.
prototype
.
invokeBuildTrace
=
function
()
{
return
window
.
location
.
href
.
split
(
"
#
"
)[
0
]
;
return
this
.
getBuildTrace
()
;
};
};
Build
.
prototype
.
invokeBuildTrace
=
function
()
{
Build
.
prototype
.
getBuildTrace
=
function
()
{
var
continueRefreshStatuses
=
[
'
running
'
,
'
pending
'
];
// Continue to update build trace when build is running or pending
if
(
continueRefreshStatuses
.
indexOf
(
this
.
buildStatus
)
!==
-
1
)
{
// Check for new build output if user still watching build page
// Only valid for runnig build when output changes during time
Build
.
timeout
=
setTimeout
((
function
(
_this
)
{
return
function
()
{
if
(
_this
.
location
()
===
_this
.
pageUrl
)
{
return
_this
.
getBuildTrace
();
}
};
})(
this
),
4000
);
}
};
Build
.
prototype
.
getInitialBuildTrace
=
function
()
{
var
removeRefreshStatuses
=
[
'
success
'
,
'
failed
'
,
'
canceled
'
,
'
skipped
'
];
return
$
.
ajax
({
return
$
.
ajax
({
url
:
this
.
pageUrl
+
"
/trace.json
"
,
url
:
`
${
this
.
pageUrl
}
/trace.json`
,
dataType
:
'
json
'
,
dataType
:
'
json
'
,
success
:
function
(
buildData
)
{
data
:
{
$
(
'
.js-build-output
'
).
html
(
buildData
.
html
);
state
:
this
.
state
,
gl
.
utils
.
setCiStatusFavicon
(
`
${
this
.
pageUrl
}
/status.json`
);
},
if
(
window
.
location
.
hash
===
DOWN_BUILD_TRACE
)
{
success
:
((
log
)
=>
{
$
(
"
html,body
"
).
scrollTop
(
this
.
$buildTrace
.
height
());
const
$buildContainer
=
$
(
'
.js-build-output
'
);
if
(
log
.
state
)
{
this
.
state
=
log
.
state
;
}
}
if
(
removeRefreshStatuses
.
indexOf
(
buildData
.
status
)
!==
-
1
)
{
if
(
log
.
append
)
{
$buildContainer
.
append
(
log
.
html
);
}
else
{
$buildContainer
.
html
(
log
.
html
);
if
(
log
.
truncated
)
{
$
(
'
.js-truncated-info-size
'
).
html
(
`
${
log
.
size
}
`
);
this
.
$truncatedInfo
.
removeClass
(
'
hidden
'
);
this
.
initAffixTruncatedInfo
();
}
else
{
this
.
$truncatedInfo
.
addClass
(
'
hidden
'
);
}
}
this
.
checkAutoscroll
();
if
(
!
log
.
complete
)
{
Build
.
timeout
=
setTimeout
(()
=>
{
this
.
invokeBuildTrace
();
},
4000
);
}
else
{
this
.
$buildRefreshAnimation
.
remove
();
this
.
$buildRefreshAnimation
.
remove
();
return
this
.
initScrollMonitor
();
}
}
}.
bind
(
this
)
});
};
Build
.
prototype
.
getBuildTrace
=
function
()
{
if
(
log
.
status
!==
this
.
buildStatus
)
{
return
$
.
ajax
({
let
pageUrl
=
this
.
pageUrl
;
url
:
this
.
pageUrl
+
"
/trace.json?state=
"
+
(
encodeURIComponent
(
this
.
state
)),
dataType
:
"
json
"
,
if
(
this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
success
:
(
function
(
_this
)
{
pageUrl
+=
DOWN_BUILD_TRACE
;
return
function
(
log
)
{
var
pageUrl
;
if
(
log
.
state
)
{
_this
.
state
=
log
.
state
;
}
_this
.
invokeBuildTrace
();
if
(
log
.
status
===
"
running
"
)
{
if
(
log
.
append
)
{
$
(
'
.js-build-output
'
).
append
(
log
.
html
);
}
else
{
$
(
'
.js-build-output
'
).
html
(
log
.
html
);
}
return
_this
.
checkAutoscroll
();
}
else
if
(
log
.
status
!==
_this
.
buildStatus
)
{
pageUrl
=
_this
.
pageUrl
;
if
(
_this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
pageUrl
+=
DOWN_BUILD_TRACE
;
}
return
gl
.
utils
.
visitUrl
(
pageUrl
);
}
}
};
})(
this
)
gl
.
utils
.
visitUrl
(
pageUrl
);
}
}),
error
:
()
=>
{
this
.
$buildRefreshAnimation
.
remove
();
return
this
.
initScrollMonitor
();
},
});
});
};
};
Build
.
prototype
.
checkAutoscroll
=
function
()
{
Build
.
prototype
.
checkAutoscroll
=
function
()
{
if
(
this
.
$autoScrollStatus
.
data
(
"
state
"
)
===
"
enabled
"
)
{
if
(
this
.
$autoScrollStatus
.
data
(
'
state
'
)
===
'
enabled
'
)
{
return
$
(
"
html,body
"
).
scrollTop
(
this
.
$buildTrace
.
height
());
return
$
(
'
html,body
'
).
scrollTop
(
this
.
$buildTrace
.
height
());
}
}
// Handle a situation where user started new build
// Handle a situation where user started new build
...
@@ -146,7 +148,7 @@ window.Build = (function() {
...
@@ -146,7 +148,7 @@ window.Build = (function() {
}
}
};
};
Build
.
prototype
.
initScrollButtonAffix
=
function
()
{
Build
.
prototype
.
initScrollButtonAffix
=
function
()
{
// Hide everything initially
// Hide everything initially
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
...
@@ -167,15 +169,17 @@ window.Build = (function() {
...
@@ -167,15 +169,17 @@ window.Build = (function() {
// - Show Top Arrow button
// - Show Top Arrow button
// - Show Bottom Arrow button
// - Show Bottom Arrow button
// - Disable Autoscroll and hide indicator (when build is running)
// - Disable Autoscroll and hide indicator (when build is running)
Build
.
prototype
.
initScrollMonitor
=
function
()
{
Build
.
prototype
.
initScrollMonitor
=
function
()
{
if
(
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
if
(
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// User is somewhere in middle of Build Log
// User is somewhere in middle of Build Log
this
.
$scrollTopBtn
.
show
();
this
.
$scrollTopBtn
.
show
();
if
(
this
.
buildStatus
===
'
success
'
||
this
.
buildStatus
===
'
failed
'
)
{
// Check if Build is completed
if
(
this
.
buildStatus
===
'
success
'
||
this
.
buildStatus
===
'
failed
'
)
{
// Check if Build is completed
this
.
$scrollBottomBtn
.
show
();
this
.
$scrollBottomBtn
.
show
();
}
else
if
(
this
.
$buildRefreshAnimation
.
is
(
'
:visible
'
)
&&
!
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
)))
{
}
else
if
(
this
.
$buildRefreshAnimation
.
is
(
'
:visible
'
)
&&
!
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
)))
{
this
.
$scrollBottomBtn
.
show
();
this
.
$scrollBottomBtn
.
show
();
}
else
{
}
else
{
this
.
$scrollBottomBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
...
@@ -186,10 +190,13 @@ window.Build = (function() {
...
@@ -186,10 +190,13 @@ window.Build = (function() {
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
else
{
}
else
{
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
}).
show
();
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
,
}).
show
();
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
}
}
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
!
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// User is at Top of Build Log
// User is at Top of Build Log
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollTopBtn
.
hide
();
...
@@ -197,17 +204,22 @@ window.Build = (function() {
...
@@ -197,17 +204,22 @@ window.Build = (function() {
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollContainer
.
hide
();
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
else
if
((
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
||
}
else
if
((
!
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
(
this
.
$buildRefreshAnimation
.
is
(
'
:visible
'
)
&&
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
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
// User is at Bottom of Build Log
this
.
$scrollTopBtn
.
show
();
this
.
$scrollTopBtn
.
show
();
this
.
$scrollBottomBtn
.
hide
();
this
.
$scrollBottomBtn
.
hide
();
// Show and Reposition Autoscroll Status Indicator
// Show and Reposition Autoscroll Status Indicator
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
}).
show
();
this
.
$autoScrollContainer
.
css
({
top
:
this
.
$body
.
outerHeight
()
-
AUTO_SCROLL_OFFSET
,
}).
show
();
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
this
.
$autoScrollStatusText
.
addClass
(
'
animate
'
);
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
}
else
if
(
gl
.
utils
.
isInViewport
(
this
.
$upBuildTrace
.
get
(
0
))
&&
gl
.
utils
.
isInViewport
(
this
.
$downBuildTrace
.
get
(
0
)))
{
// Build Log height is small
// Build Log height is small
this
.
$scrollTopBtn
.
hide
();
this
.
$scrollTopBtn
.
hide
();
...
@@ -218,65 +230,81 @@ window.Build = (function() {
...
@@ -218,65 +230,81 @@ window.Build = (function() {
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
this
.
$autoScrollStatusText
.
removeClass
(
'
animate
'
);
}
}
if
(
this
.
buildStatus
===
"
running
"
||
this
.
buildStatus
===
"
pending
"
)
{
if
(
this
.
buildStatus
===
'
running
'
||
this
.
buildStatus
===
'
pending
'
)
{
// Check if Refresh Animation is in Viewport and enable Autoscroll, disable otherwise.
// 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
'
);
this
.
$autoScrollStatus
.
data
(
'
state
'
,
gl
.
utils
.
isInViewport
(
this
.
$buildRefreshAnimation
.
get
(
0
))
?
'
enabled
'
:
'
disabled
'
,
);
}
}
};
};
Build
.
prototype
.
shouldHideSidebarForViewport
=
function
()
{
Build
.
prototype
.
shouldHideSidebarForViewport
=
function
()
{
var
bootstrapBreakpoint
;
const
bootstrapBreakpoint
=
this
.
bp
.
getBreakpointSize
();
bootstrapBreakpoint
=
this
.
bp
.
getBreakpointSize
();
return
bootstrapBreakpoint
===
'
xs
'
||
bootstrapBreakpoint
===
'
sm
'
;
return
bootstrapBreakpoint
===
'
xs
'
||
bootstrapBreakpoint
===
'
sm
'
;
};
};
Build
.
prototype
.
toggleSidebar
=
function
(
shouldHide
)
{
Build
.
prototype
.
toggleSidebar
=
function
(
shouldHide
)
{
var
shouldShow
=
typeof
shouldHide
===
'
boolean
'
?
!
shouldHide
:
undefined
;
const
shouldShow
=
typeof
shouldHide
===
'
boolean
'
?
!
shouldHide
:
undefined
;
this
.
$buildScroll
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
this
.
$buildScroll
.
toggleClass
(
'
sidebar-expanded
'
,
shouldShow
)
.
toggleClass
(
'
sidebar-collapsed
'
,
shouldHide
);
.
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
);
.
toggleClass
(
'
right-sidebar-collapsed
'
,
shouldHide
);
};
};
Build
.
prototype
.
sidebarOnResize
=
function
()
{
Build
.
prototype
.
sidebarOnResize
=
function
()
{
this
.
toggleSidebar
(
this
.
shouldHideSidebarForViewport
());
this
.
toggleSidebar
(
this
.
shouldHideSidebarForViewport
());
};
};
Build
.
prototype
.
sidebarOnClick
=
function
()
{
Build
.
prototype
.
sidebarOnClick
=
function
()
{
if
(
this
.
shouldHideSidebarForViewport
())
this
.
toggleSidebar
();
if
(
this
.
shouldHideSidebarForViewport
())
this
.
toggleSidebar
();
};
};
Build
.
prototype
.
updateArtifactRemoveDate
=
function
()
{
Build
.
prototype
.
updateArtifactRemoveDate
=
function
()
{
var
$date
,
date
;
const
$date
=
$
(
'
.js-artifacts-remove
'
);
$date
=
$
(
'
.js-artifacts-remove
'
);
if
(
$date
.
length
)
{
if
(
$date
.
length
)
{
date
=
$date
.
text
();
const
date
=
$date
.
text
();
return
$date
.
text
(
gl
.
utils
.
timeFor
(
new
Date
(
date
.
replace
(
/
([
0-9
]
+
)
-
([
0-9
]
+
)
-
([
0-9
]
+
)
/g
,
'
$1/$2/$3
'
)),
'
'
));
return
$date
.
text
(
gl
.
utils
.
timeFor
(
new
Date
(
date
.
replace
(
/
([
0-9
]
+
)
-
([
0-9
]
+
)
-
([
0-9
]
+
)
/g
,
'
$1/$2/$3
'
)),
'
'
),
);
}
}
};
};
Build
.
prototype
.
populateJobs
=
function
(
stage
)
{
Build
.
prototype
.
populateJobs
=
function
(
stage
)
{
$
(
'
.build-job
'
).
hide
();
$
(
'
.build-job
'
).
hide
();
$
(
'
.build-job[data-stage="
'
+
stage
+
'
"]
'
).
show
();
$
(
`.build-job[data-stage="
${
stage
}
"]`
).
show
();
};
};
Build
.
prototype
.
updateStageDropdownText
=
function
(
stage
)
{
Build
.
prototype
.
updateStageDropdownText
=
function
(
stage
)
{
$
(
'
.stage-selection
'
).
text
(
stage
);
$
(
'
.stage-selection
'
).
text
(
stage
);
};
};
Build
.
prototype
.
updateDropdown
=
function
(
e
)
{
Build
.
prototype
.
updateDropdown
=
function
(
e
)
{
e
.
preventDefault
();
e
.
preventDefault
();
var
stage
=
e
.
currentTarget
.
text
;
const
stage
=
e
.
currentTarget
.
text
;
this
.
updateStageDropdownText
(
stage
);
this
.
updateStageDropdownText
(
stage
);
this
.
populateJobs
(
stage
);
this
.
populateJobs
(
stage
);
};
};
Build
.
prototype
.
stepTrace
=
function
(
e
)
{
Build
.
prototype
.
stepTrace
=
function
(
e
)
{
var
$currentTarget
;
e
.
preventDefault
();
e
.
preventDefault
();
$currentTarget
=
$
(
e
.
currentTarget
);
const
$currentTarget
=
$
(
e
.
currentTarget
);
$
.
scrollTo
(
$currentTarget
.
attr
(
'
href
'
),
{
$
.
scrollTo
(
$currentTarget
.
attr
(
'
href
'
),
{
offset
:
0
offset
:
0
,
});
};
Build
.
prototype
.
initAffixTruncatedInfo
=
function
()
{
const
offsetTop
=
this
.
$buildTrace
.
offset
().
top
;
this
.
$truncatedInfo
.
affix
({
offset
:
{
top
:
offsetTop
,
},
});
});
};
};
...
...
app/assets/stylesheets/pages/builds.scss
View file @
8ca5afdf
...
@@ -57,6 +57,37 @@
...
@@ -57,6 +57,37 @@
margin-right
:
5px
;
margin-right
:
5px
;
}
}
}
}
.truncated-info
{
text-align
:
center
;
border-bottom
:
1px
solid
;
background-color
:
$black-transparent
;
height
:
45px
;
&
.affix
{
top
:
0
;
}
// with sidebar
&
.affix.sidebar-expanded
{
right
:
312px
;
left
:
22px
;
}
// without sidebar
&
.affix.sidebar-collapsed
{
right
:
20px
;
left
:
20px
;
}
&
.affix-top
{
position
:
absolute
;
top
:
0
;
margin
:
0
auto
;
right
:
5px
;
left
:
5px
;
}
}
}
}
.scroll-controls
{
.scroll-controls
{
...
@@ -186,6 +217,7 @@
...
@@ -186,6 +217,7 @@
white-space
:
pre
;
white-space
:
pre
;
overflow-x
:
auto
;
overflow-x
:
auto
;
font-size
:
12px
;
font-size
:
12px
;
position
:
relative
;
.fa-refresh
{
.fa-refresh
{
font-size
:
24px
;
font-size
:
24px
;
...
...
app/views/projects/builds/show.html.haml
View file @
8ca5afdf
...
@@ -71,6 +71,11 @@
...
@@ -71,6 +71,11 @@
=
custom_icon
(
'scroll_down_hover_active'
)
=
custom_icon
(
'scroll_down_hover_active'
)
#up-build-trace
#up-build-trace
%pre
.build-trace
#build-trace
%pre
.build-trace
#build-trace
.js-truncated-info.truncated-info.hidden
%span<
Showing
last
%span
.js-truncated-info-size
><
KiB
of
log
%code
.bash.js-build-output
%code
.bash.js-build-output
.build-loader-animation.js-build-refresh
.build-loader-animation.js-build-refresh
...
...
spec/javascripts/build_spec.js
View file @
8ca5afdf
...
@@ -64,58 +64,33 @@ describe('Build', () => {
...
@@ -64,58 +64,33 @@ describe('Build', () => {
});
});
});
});
describe
(
'
initial build trace
'
,
()
=>
{
beforeEach
(()
=>
{
new
Build
();
});
it
(
'
displays the initial build trace
'
,
()
=>
{
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
1
);
const
[{
url
,
dataType
,
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
expect
(
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json`
,
);
expect
(
dataType
).
toBe
(
'
json
'
);
expect
(
success
).
toEqual
(
jasmine
.
any
(
Function
));
spyOn
(
gl
.
utils
,
'
setCiStatusFavicon
'
).
and
.
callFake
(()
=>
{});
success
.
call
(
context
,
{
html
:
'
<span>Example</span>
'
,
status
:
'
running
'
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Example/
);
});
it
(
'
removes the spinner
'
,
()
=>
{
const
[{
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
spyOn
(
gl
.
utils
,
'
setCiStatusFavicon
'
).
and
.
callFake
(()
=>
{});
success
.
call
(
context
,
{
trace_html
:
'
<span>Example</span>
'
,
status
:
'
success
'
});
expect
(
$
(
'
.js-build-refresh
'
).
length
).
toBe
(
0
);
});
});
describe
(
'
running build
'
,
()
=>
{
describe
(
'
running build
'
,
()
=>
{
beforeEach
(
function
()
{
beforeEach
(
function
()
{
$
(
'
.js-build-options
'
).
data
(
'
buildStatus
'
,
'
running
'
);
this
.
build
=
new
Build
();
this
.
build
=
new
Build
();
spyOn
(
this
.
build
,
'
location
'
).
and
.
returnValue
(
BUILD_URL
);
});
});
it
(
'
updates the build trace on an interval
'
,
function
()
{
it
(
'
updates the build trace on an interval
'
,
function
()
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
2
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
1
);
let
[{
url
,
dataType
,
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
1
);
expect
(
url
).
toBe
(
// We have to do it this way to prevent Webpack to fail to compile
`
${
BUILD_URL
}
/trace.json?state=`
,
// when destructuring assignments and reusing
);
// the same variables names inside the same scope
expect
(
dataType
).
toBe
(
'
json
'
);
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
];
expect
(
success
).
toEqual
(
jasmine
.
any
(
Function
));
expect
(
args
.
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json`
);
success
.
call
(
context
,
{
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
'
,
append
:
true
,
append
:
true
,
complete
:
false
,
});
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
...
@@ -123,17 +98,20 @@ describe('Build', () => {
...
@@ -123,17 +98,20 @@ describe('Build', () => {
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
3
);
expect
(
$
.
ajax
.
calls
.
count
()).
toBe
(
2
);
[{
url
,
dataType
,
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
2
);
expect
(
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json?state=newstate`
);
args
=
$
.
ajax
.
calls
.
argsFor
(
1
)[
0
];
expect
(
dataType
).
toBe
(
'
json
'
);
expect
(
args
.
url
).
toBe
(
`
${
BUILD_URL
}
/trace.json`
);
expect
(
success
).
toEqual
(
jasmine
.
any
(
Function
));
expect
(
args
.
dataType
).
toBe
(
'
json
'
);
expect
(
args
.
data
.
state
).
toBe
(
'
newstate
'
);
expect
(
args
.
success
).
toEqual
(
jasmine
.
any
(
Function
));
success
.
call
(
context
,
{
args
.
success
.
call
(
$
,
{
html
:
'
<span>More</span>
'
,
html
:
'
<span>More</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
state
:
'
finalstate
'
,
state
:
'
finalstate
'
,
append
:
true
,
append
:
true
,
complete
:
true
,
});
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/UpdateMore/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/UpdateMore/
);
...
@@ -141,19 +119,22 @@ describe('Build', () => {
...
@@ -141,19 +119,22 @@ describe('Build', () => {
});
});
it
(
'
replaces the entire build trace
'
,
()
=>
{
it
(
'
replaces the entire build trace
'
,
()
=>
{
spyOn
(
gl
.
utils
,
'
visitUrl
'
);
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
let
[{
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
1
)
;
let
args
=
$
.
ajax
.
calls
.
argsFor
(
0
)[
0
]
;
success
.
call
(
context
,
{
args
.
success
.
call
(
$
,
{
html
:
'
<span>Update</span>
'
,
html
:
'
<span>Update</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
append
:
true
,
append
:
false
,
complete
:
false
,
});
});
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Update/
);
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
[{
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
2
)
;
args
=
$
.
ajax
.
calls
.
argsFor
(
1
)[
0
]
;
success
.
call
(
context
,
{
args
.
success
.
call
(
$
,
{
html
:
'
<span>Different</span>
'
,
html
:
'
<span>Different</span>
'
,
status
:
'
running
'
,
status
:
'
running
'
,
append
:
false
,
append
:
false
,
...
@@ -163,15 +144,34 @@ describe('Build', () => {
...
@@ -163,15 +144,34 @@ describe('Build', () => {
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Different/
);
expect
(
$
(
'
#build-trace .js-build-output
'
).
text
()).
toMatch
(
/Different/
);
});
});
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
,
truncated
:
true
,
size
:
'
50
'
,
});
expect
(
$
(
'
#build-trace .js-truncated-info
'
).
text
().
trim
(),
).
toContain
(
'
Showing last 50 KiB of log
'
);
expect
(
$
(
'
#build-trace .js-truncated-info-size
'
).
text
()).
toMatch
(
'
50
'
);
});
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
'
);
jasmine
.
clock
().
tick
(
4001
);
jasmine
.
clock
().
tick
(
4001
);
const
[{
success
,
context
}]
=
$
.
ajax
.
calls
.
argsFor
(
1
);
const
[{
success
}]
=
$
.
ajax
.
calls
.
argsFor
(
0
);
success
.
call
(
context
,
{
success
.
call
(
$
,
{
html
:
'
<span>Final</span>
'
,
html
:
'
<span>Final</span>
'
,
status
:
'
passed
'
,
status
:
'
passed
'
,
append
:
true
,
append
:
true
,
complete
:
true
,
});
});
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BUILD_URL
);
expect
(
gl
.
utils
.
visitUrl
).
toHaveBeenCalledWith
(
BUILD_URL
);
...
...
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