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
Jérome Perrin
gitlab-ce
Commits
453d755a
Commit
453d755a
authored
Mar 24, 2017
by
Alfredo Sumaran
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch '27574-pipelines-empty-state' into 'master'
Pipelines empty state Closes #27574 See merge request !9978
parents
856edee3
a5f17beb
Changes
22
Hide whitespace changes
Inline
Side-by-side
Showing
22 changed files
with
772 additions
and
103 deletions
+772
-103
app/assets/javascripts/commit/pipelines/pipelines_table.js
app/assets/javascripts/commit/pipelines/pipelines_table.js
+22
-9
app/assets/javascripts/vue_pipelines_index/components/empty_state.js
...javascripts/vue_pipelines_index/components/empty_state.js
+33
-0
app/assets/javascripts/vue_pipelines_index/components/error_state.js
...javascripts/vue_pipelines_index/components/error_state.js
+19
-0
app/assets/javascripts/vue_pipelines_index/components/nav_controls.js
...avascripts/vue_pipelines_index/components/nav_controls.js
+52
-0
app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js
...scripts/vue_pipelines_index/components/navigation_tabs.js
+68
-0
app/assets/javascripts/vue_pipelines_index/index.js
app/assets/javascripts/vue_pipelines_index/index.js
+2
-6
app/assets/javascripts/vue_pipelines_index/pipelines.js
app/assets/javascripts/vue_pipelines_index/pipelines.js
+155
-36
app/assets/stylesheets/pages/pipelines.scss
app/assets/stylesheets/pages/pipelines.scss
+5
-0
app/views/projects/commit/_pipelines_list.haml
app/views/projects/commit/_pipelines_list.haml
+1
-0
app/views/projects/pipelines/index.html.haml
app/views/projects/pipelines/index.html.haml
+13
-47
app/views/shared/empty_states/icons/_pipelines_empty.svg
app/views/shared/empty_states/icons/_pipelines_empty.svg
+1
-0
app/views/shared/empty_states/icons/_pipelines_failed.svg
app/views/shared/empty_states/icons/_pipelines_failed.svg
+1
-0
changelogs/unreleased/27574-pipelines-empty-state.yml
changelogs/unreleased/27574-pipelines-empty-state.yml
+4
-0
spec/features/projects/pipelines/pipelines_spec.rb
spec/features/projects/pipelines/pipelines_spec.rb
+1
-1
spec/javascripts/commit/pipelines/pipelines_spec.js
spec/javascripts/commit/pipelines/pipelines_spec.js
+5
-2
spec/javascripts/fixtures/pipelines.html.haml
spec/javascripts/fixtures/pipelines.html.haml
+14
-0
spec/javascripts/fixtures/pipelines_table.html.haml
spec/javascripts/fixtures/pipelines_table.html.haml
+1
-2
spec/javascripts/vue_pipelines_index/empty_state_spec.js
spec/javascripts/vue_pipelines_index/empty_state_spec.js
+38
-0
spec/javascripts/vue_pipelines_index/error_state_spec.js
spec/javascripts/vue_pipelines_index/error_state_spec.js
+23
-0
spec/javascripts/vue_pipelines_index/mock_data.js
spec/javascripts/vue_pipelines_index/mock_data.js
+107
-0
spec/javascripts/vue_pipelines_index/nav_controls_spec.js
spec/javascripts/vue_pipelines_index/nav_controls_spec.js
+93
-0
spec/javascripts/vue_pipelines_index/pipelines_spec.js
spec/javascripts/vue_pipelines_index/pipelines_spec.js
+114
-0
No files found.
app/assets/javascripts/commit/pipelines/pipelines_table.js
View file @
453d755a
/* eslint-disable no-new*/
/* global Flash */
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
PipelinesTableComponent
from
'
../../vue_shared/components/pipelines_table
'
;
import
PipelinesTableComponent
from
'
../../vue_shared/components/pipelines_table
'
;
import
PipelinesService
from
'
../../vue_pipelines_index/services/pipelines_service
'
;
import
PipelinesService
from
'
../../vue_pipelines_index/services/pipelines_service
'
;
import
PipelineStore
from
'
../../vue_pipelines_index/stores/pipelines_store
'
;
import
PipelineStore
from
'
../../vue_pipelines_index/stores/pipelines_store
'
;
import
eventHub
from
'
../../vue_pipelines_index/event_hub
'
;
import
eventHub
from
'
../../vue_pipelines_index/event_hub
'
;
import
EmptyState
from
'
../../vue_pipelines_index/components/empty_state
'
;
import
ErrorState
from
'
../../vue_pipelines_index/components/error_state
'
;
import
'
../../lib/utils/common_utils
'
;
import
'
../../lib/utils/common_utils
'
;
import
'
../../vue_shared/vue_resource_interceptor
'
;
import
'
../../vue_shared/vue_resource_interceptor
'
;
...
@@ -22,6 +22,8 @@ import '../../vue_shared/vue_resource_interceptor';
...
@@ -22,6 +22,8 @@ import '../../vue_shared/vue_resource_interceptor';
export
default
Vue
.
component
(
'
pipelines-table
'
,
{
export
default
Vue
.
component
(
'
pipelines-table
'
,
{
components
:
{
components
:
{
'
pipelines-table-component
'
:
PipelinesTableComponent
,
'
pipelines-table-component
'
:
PipelinesTableComponent
,
'
error-state
'
:
ErrorState
,
'
empty-state
'
:
EmptyState
,
},
},
/**
/**
...
@@ -36,12 +38,24 @@ export default Vue.component('pipelines-table', {
...
@@ -36,12 +38,24 @@ export default Vue.component('pipelines-table', {
return
{
return
{
endpoint
:
pipelinesTableData
.
endpoint
,
endpoint
:
pipelinesTableData
.
endpoint
,
helpPagePath
:
pipelinesTableData
.
helpPagePath
,
store
,
store
,
state
:
store
.
state
,
state
:
store
.
state
,
isLoading
:
false
,
isLoading
:
false
,
hasError
:
false
,
};
};
},
},
computed
:
{
shouldRenderErrorState
()
{
return
this
.
hasError
&&
!
this
.
isLoading
;
},
shouldRenderEmptyState
()
{
return
!
this
.
state
.
pipelines
.
length
&&
!
this
.
isLoading
;
},
},
/**
/**
* When the component is about to be mounted, tell the service to fetch the data
* When the component is about to be mounted, tell the service to fetch the data
*
*
...
@@ -80,8 +94,8 @@ export default Vue.component('pipelines-table', {
...
@@ -80,8 +94,8 @@ export default Vue.component('pipelines-table', {
this
.
isLoading
=
false
;
this
.
isLoading
=
false
;
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
this
.
hasError
=
true
;
this
.
isLoading
=
false
;
this
.
isLoading
=
false
;
new
Flash
(
'
An error occurred while fetching the pipelines, please reload the page again.
'
);
});
});
},
},
},
},
...
@@ -92,12 +106,11 @@ export default Vue.component('pipelines-table', {
...
@@ -92,12 +106,11 @@ export default Vue.component('pipelines-table', {
<i class="fa fa-spinner fa-spin"></i>
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
<div class="blank-state blank-state-no-icon"
<empty-state
v-if="!isLoading && state.pipelines.length === 0">
v-if="shouldRenderEmptyState"
<h2 class="blank-state-title js-blank-state-title">
:help-page-path="helpPagePath" />
No pipelines to show
</h2>
<error-state v-if="shouldRenderErrorState" />
</div>
<div class="table-holder"
<div class="table-holder"
v-if="!isLoading && state.pipelines.length > 0">
v-if="!isLoading && state.pipelines.length > 0">
...
...
app/assets/javascripts/vue_pipelines_index/components/empty_state.js
0 → 100644
View file @
453d755a
import
pipelinesEmptyStateSVG
from
'
empty_states/icons/_pipelines_empty.svg
'
;
export
default
{
props
:
{
helpPagePath
:
{
type
:
String
,
required
:
true
,
},
},
template
:
`
<div class="row empty-state">
<div class="col-xs-12">
<div class="svg-content">
${
pipelinesEmptyStateSVG
}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>Build with confidence</h4>
<p>
Continous Integration can help catch bugs by running your tests automatically,
while Continuous Deployment can help you deliver code to your product environment.
</p>
<a :href="helpPagePath" class="btn btn-info">
Get started with Pipelines
</a>
</div>
</div>
</div>
`
,
};
app/assets/javascripts/vue_pipelines_index/components/error_state.js
0 → 100644
View file @
453d755a
import
pipelinesErrorStateSVG
from
'
empty_states/icons/_pipelines_failed.svg
'
;
export
default
{
template
:
`
<div class="row empty-state js-pipelines-error-state">
<div class="col-xs-12">
<div class="svg-content">
${
pipelinesErrorStateSVG
}
</div>
</div>
<div class="col-xs-12 text-center">
<div class="text-content">
<h4>The API failed to fetch the pipelines.</h4>
</div>
</div>
</div>
`
,
};
app/assets/javascripts/vue_pipelines_index/components/nav_controls.js
0 → 100644
View file @
453d755a
export
default
{
props
:
{
newPipelinePath
:
{
type
:
String
,
required
:
true
,
},
hasCiEnabled
:
{
type
:
Boolean
,
required
:
true
,
},
helpPagePath
:
{
type
:
String
,
required
:
true
,
},
ciLintPath
:
{
type
:
String
,
required
:
true
,
},
canCreatePipeline
:
{
type
:
Boolean
,
required
:
true
,
},
},
template
:
`
<div class="nav-controls">
<a
v-if="canCreatePipeline"
:href="newPipelinePath"
class="btn btn-create">
Run Pipeline
</a>
<a
v-if="!hasCiEnabled"
:href="helpPagePath"
class="btn btn-info">
Get started with Pipelines
</a>
<a
:href="ciLintPath"
class="btn btn-default">
CI Lint
</a>
</div>
`
,
};
app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js
0 → 100644
View file @
453d755a
export
default
{
props
:
{
scope
:
{
type
:
String
,
required
:
true
,
},
count
:
{
type
:
Object
,
required
:
true
,
},
paths
:
{
type
:
Object
,
required
:
true
,
},
},
template
:
`
<ul class="nav-links">
<li
class="js-pipelines-tab-all"
:class="{ 'active': scope === 'all'}">
<a :href="paths.allPath">
All
<span class="badge js-totalbuilds-count">
{{count.all}}
</span>
</a>
</li>
<li class="js-pipelines-tab-pending"
:class="{ 'active': scope === 'pending'}">
<a :href="paths.pendingPath">
Pending
<span class="badge">
{{count.pending}}
</span>
</a>
</li>
<li class="js-pipelines-tab-running"
:class="{ 'active': scope === 'running'}">
<a :href="paths.runningPath">
Running
<span class="badge">
{{count.running}}
</span>
</a>
</li>
<li class="js-pipelines-tab-finished"
:class="{ 'active': scope === 'finished'}">
<a :href="paths.finishedPath">
Finished
<span class="badge">
{{count.finished}}
</span>
</a>
</li>
<li class="js-pipelines-tab-branches"
:class="{ 'active': scope === 'branches'}">
<a :href="paths.branchesPath">Branches</a>
</li>
<li class="js-pipelines-tab-tags"
:class="{ 'active': scope === 'tags'}">
<a :href="paths.tagsPath">Tags</a>
</li>
</ul>
`
,
};
app/assets/javascripts/vue_pipelines_index/index.js
View file @
453d755a
...
@@ -4,23 +4,19 @@ import PipelinesComponent from './pipelines';
...
@@ -4,23 +4,19 @@ import PipelinesComponent from './pipelines';
import
'
../vue_shared/vue_resource_interceptor
'
;
import
'
../vue_shared/vue_resource_interceptor
'
;
$
(()
=>
new
Vue
({
$
(()
=>
new
Vue
({
el
:
document
.
querySelector
(
'
.vue-pipelines-index
'
),
el
:
document
.
querySelector
(
'
#pipelines-list-vue
'
),
data
()
{
data
()
{
const
project
=
document
.
querySelector
(
'
.pipelines
'
);
const
store
=
new
PipelinesStore
();
const
store
=
new
PipelinesStore
();
return
{
return
{
store
,
store
,
endpoint
:
project
.
dataset
.
url
,
};
};
},
},
components
:
{
components
:
{
'
vue-pipelines
'
:
PipelinesComponent
,
'
vue-pipelines
'
:
PipelinesComponent
,
},
},
template
:
`
template
:
`
<vue-pipelines
<vue-pipelines :store="store" />
:endpoint="endpoint"
:store="store" />
`
,
`
,
}));
}));
app/assets/javascripts/vue_pipelines_index/pipelines.js
View file @
453d755a
/* global Flash */
/* eslint-disable no-new */
import
'
~/flash
'
;
import
Vue
from
'
vue
'
;
import
Vue
from
'
vue
'
;
import
PipelinesService
from
'
./services/pipelines_service
'
;
import
PipelinesService
from
'
./services/pipelines_service
'
;
import
eventHub
from
'
./event_hub
'
;
import
eventHub
from
'
./event_hub
'
;
import
PipelinesTableComponent
from
'
../vue_shared/components/pipelines_table
'
;
import
PipelinesTableComponent
from
'
../vue_shared/components/pipelines_table
'
;
import
TablePaginationComponent
from
'
../vue_shared/components/table_pagination
'
;
import
TablePaginationComponent
from
'
../vue_shared/components/table_pagination
'
;
import
EmptyState
from
'
./components/empty_state
'
;
import
ErrorState
from
'
./components/error_state
'
;
import
NavigationTabs
from
'
./components/navigation_tabs
'
;
import
NavigationControls
from
'
./components/nav_controls
'
;
export
default
{
export
default
{
props
:
{
props
:
{
endpoint
:
{
type
:
String
,
required
:
true
,
},
store
:
{
store
:
{
type
:
Object
,
type
:
Object
,
required
:
true
,
required
:
true
,
...
@@ -23,17 +19,109 @@ export default {
...
@@ -23,17 +19,109 @@ export default {
components
:
{
components
:
{
'
gl-pagination
'
:
TablePaginationComponent
,
'
gl-pagination
'
:
TablePaginationComponent
,
'
pipelines-table-component
'
:
PipelinesTableComponent
,
'
pipelines-table-component
'
:
PipelinesTableComponent
,
'
empty-state
'
:
EmptyState
,
'
error-state
'
:
ErrorState
,
'
navigation-tabs
'
:
NavigationTabs
,
'
navigation-controls
'
:
NavigationControls
,
},
},
data
()
{
data
()
{
const
pipelinesData
=
document
.
querySelector
(
'
#pipelines-list-vue
'
).
dataset
;
return
{
return
{
endpoint
:
pipelinesData
.
endpoint
,
cssClass
:
pipelinesData
.
cssClass
,
helpPagePath
:
pipelinesData
.
helpPagePath
,
newPipelinePath
:
pipelinesData
.
newPipelinePath
,
canCreatePipeline
:
pipelinesData
.
canCreatePipeline
,
allPath
:
pipelinesData
.
allPath
,
pendingPath
:
pipelinesData
.
pendingPath
,
runningPath
:
pipelinesData
.
runningPath
,
finishedPath
:
pipelinesData
.
finishedPath
,
branchesPath
:
pipelinesData
.
branchesPath
,
tagsPath
:
pipelinesData
.
tagsPath
,
hasCi
:
pipelinesData
.
hasCi
,
ciLintPath
:
pipelinesData
.
ciLintPath
,
state
:
this
.
store
.
state
,
state
:
this
.
store
.
state
,
apiScope
:
'
all
'
,
apiScope
:
'
all
'
,
pagenum
:
1
,
pagenum
:
1
,
pageRequest
:
false
,
isLoading
:
false
,
hasError
:
false
,
};
};
},
},
computed
:
{
canCreatePipelineParsed
()
{
return
gl
.
utils
.
convertPermissionToBoolean
(
this
.
canCreatePipeline
);
},
scope
()
{
const
scope
=
gl
.
utils
.
getParameterByName
(
'
scope
'
);
return
scope
===
null
?
'
all
'
:
scope
;
},
shouldRenderErrorState
()
{
return
this
.
hasError
&&
!
this
.
isLoading
;
},
/**
* The empty state should only be rendered when the request is made to fetch all pipelines
* and none is returned.
*
* @return {Boolean}
*/
shouldRenderEmptyState
()
{
return
!
this
.
isLoading
&&
!
this
.
hasError
&&
!
this
.
state
.
pipelines
.
length
&&
(
this
.
scope
===
'
all
'
||
this
.
scope
===
null
);
},
/**
* When a specific scope does not have pipelines we render a message.
*
* @return {Boolean}
*/
shouldRenderNoPipelinesMessage
()
{
return
!
this
.
isLoading
&&
!
this
.
hasError
&&
!
this
.
state
.
pipelines
.
length
&&
this
.
scope
!==
'
all
'
&&
this
.
scope
!==
null
;
},
shouldRenderTable
()
{
return
!
this
.
hasError
&&
!
this
.
isLoading
&&
this
.
state
.
pipelines
.
length
;
},
/**
* Pagination should only be rendered when there is more than one page.
*
* @return {Boolean}
*/
shouldRenderPagination
()
{
return
!
this
.
isLoading
&&
this
.
state
.
pipelines
.
length
&&
this
.
state
.
pageInfo
.
total
>
this
.
state
.
pageInfo
.
perPage
;
},
hasCiEnabled
()
{
return
this
.
hasCi
!==
undefined
;
},
paths
()
{
return
{
allPath
:
this
.
allPath
,
pendingPath
:
this
.
pendingPath
,
finishedPath
:
this
.
finishedPath
,
runningPath
:
this
.
runningPath
,
branchesPath
:
this
.
branchesPath
,
tagsPath
:
this
.
tagsPath
,
};
},
},
created
()
{
created
()
{
this
.
service
=
new
PipelinesService
(
this
.
endpoint
);
this
.
service
=
new
PipelinesService
(
this
.
endpoint
);
...
@@ -69,7 +157,7 @@ export default {
...
@@ -69,7 +157,7 @@ export default {
const
pageNumber
=
gl
.
utils
.
getParameterByName
(
'
page
'
)
||
this
.
pagenum
;
const
pageNumber
=
gl
.
utils
.
getParameterByName
(
'
page
'
)
||
this
.
pagenum
;
const
scope
=
gl
.
utils
.
getParameterByName
(
'
scope
'
)
||
this
.
apiScope
;
const
scope
=
gl
.
utils
.
getParameterByName
(
'
scope
'
)
||
this
.
apiScope
;
this
.
pageRequest
=
true
;
this
.
isLoading
=
true
;
return
this
.
service
.
getPipelines
(
scope
,
pageNumber
)
return
this
.
service
.
getPipelines
(
scope
,
pageNumber
)
.
then
(
resp
=>
({
.
then
(
resp
=>
({
headers
:
resp
.
headers
,
headers
:
resp
.
headers
,
...
@@ -81,41 +169,72 @@ export default {
...
@@ -81,41 +169,72 @@ export default {
this
.
store
.
storePagination
(
response
.
headers
);
this
.
store
.
storePagination
(
response
.
headers
);
})
})
.
then
(()
=>
{
.
then
(()
=>
{
this
.
pageRequest
=
false
;
this
.
isLoading
=
false
;
})
})
.
catch
(()
=>
{
.
catch
(()
=>
{
this
.
pageRequest
=
fals
e
;
this
.
hasError
=
tru
e
;
new
Flash
(
'
An error occurred while fetching the pipelines, please reload the page again.
'
)
;
this
.
isLoading
=
false
;
});
});
},
},
},
},
template
:
`
<div>
<div class="pipelines realtime-loading" v-if="pageRequest">
<i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
</div>
<div class="blank-state blank-state-no-icon"
template
:
`
v-if="!pageRequest && state.pipelines.length === 0">
<div :class="cssClass">
<h2 class="blank-state-title js-blank-state-title">
No pipelines to show
<div
</h2>
class="top-area"
v-if="!isLoading && !shouldRenderEmptyState">
<navigation-tabs
:scope="scope"
:count="state.count"
:paths="paths" />
<navigation-controls
:new-pipeline-path="newPipelinePath"
:has-ci-enabled="hasCiEnabled"
:help-page-path="helpPagePath"
:ciLintPath="ciLintPath"
:can-create-pipeline="canCreatePipelineParsed " />
</div>
</div>
<div class="table-holder" v-if="!pageRequest && state.pipelines.length">
<div class="content-list pipelines">
<pipelines-table-component
:pipelines="state.pipelines"
<div
:service="service"/>
class="realtime-loading"
v-if="isLoading">
<i
class="fa fa-spinner fa-spin"
aria-hidden="true" />
</div>
<empty-state
v-if="shouldRenderEmptyState"
:help-page-path="helpPagePath" />
<error-state v-if="shouldRenderErrorState" />
<div
class="blank-state blank-state-no-icon"
v-if="shouldRenderNoPipelinesMessage">
<h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
</div>
<div
class="table-holder"
v-if="shouldRenderTable">
<pipelines-table-component
:pipelines="state.pipelines"
:service="service"/>
</div>
<gl-pagination
v-if="shouldRenderPagination"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"/>
</div>
</div>
<gl-pagination
v-if="!pageRequest && state.pipelines.length && state.pageInfo.total > state.pageInfo.perPage"
:pagenum="pagenum"
:change="change"
:count="state.count.all"
:pageInfo="state.pageInfo"
>
</gl-pagination>
</div>
</div>
`
,
`
,
};
};
app/assets/stylesheets/pages/pipelines.scss
View file @
453d755a
...
@@ -2,6 +2,7 @@
...
@@ -2,6 +2,7 @@
.realtime-loading
{
.realtime-loading
{
font-size
:
40px
;
font-size
:
40px
;
text-align
:
center
;
text-align
:
center
;
margin
:
0
auto
;
}
}
.stage
{
.stage
{
...
@@ -13,6 +14,10 @@
...
@@ -13,6 +14,10 @@
white-space
:
nowrap
;
white-space
:
nowrap
;
}
}
.empty-state
{
margin
:
5%
auto
0
;
}
.table-holder
{
.table-holder
{
width
:
100%
;
width
:
100%
;
...
...
app/views/projects/commit/_pipelines_list.haml
View file @
453d755a
-
disable_initialization
=
local_assigns
.
fetch
(
:disable_initialization
,
false
)
-
disable_initialization
=
local_assigns
.
fetch
(
:disable_initialization
,
false
)
#commit-pipeline-table-view
{
data:
{
disable_initialization:
disable_initialization
,
#commit-pipeline-table-view
{
data:
{
disable_initialization:
disable_initialization
,
endpoint:
endpoint
,
endpoint:
endpoint
,
"help-page-path"
=>
help_page_path
(
'ci/quick_start/README'
),
}
}
}
}
-
content_for
:page_specific_javascripts
do
-
content_for
:page_specific_javascripts
do
...
...
app/views/projects/pipelines/index.html.haml
View file @
453d755a
...
@@ -2,53 +2,19 @@
...
@@ -2,53 +2,19 @@
-
page_title
"Pipelines"
-
page_title
"Pipelines"
=
render
"projects/pipelines/head"
=
render
"projects/pipelines/head"
%div
{
class:
container_class
}
#pipelines-list-vue
{
data:
{
endpoint:
namespace_project_pipelines_path
(
@project
.
namespace
,
@project
,
format: :json
),
.top-area
"css-class"
=>
container_class
,
%ul
.nav-links
"help-page-path"
=>
help_page_path
(
'ci/quick_start/README'
),
%li
.js-pipelines-tab-all
{
class:
active_when
(
@scope
.
nil?
)
}
>
"new-pipeline-path"
=>
new_namespace_project_pipeline_path
(
@project
.
namespace
,
@project
),
=
link_to
project_pipelines_path
(
@project
)
do
"can-create-pipeline"
=>
can?
(
current_user
,
:create_pipeline
,
@project
).
to_s
,
All
"all-path"
=>
project_pipelines_path
(
@project
),
%span
.badge.js-totalbuilds-count
"pending-path"
=>
project_pipelines_path
(
@project
,
scope: :pending
),
=
number_with_delimiter
(
@pipelines_count
)
"running-path"
=>
project_pipelines_path
(
@project
,
scope: :running
),
"finished-path"
=>
project_pipelines_path
(
@project
,
scope: :finished
),
%li
.js-pipelines-tab-pending
{
class:
active_when
(
@scope
==
'pending'
)
}
>
"branches-path"
=>
project_pipelines_path
(
@project
,
scope: :branches
),
=
link_to
project_pipelines_path
(
@project
,
scope: :pending
)
do
"tags-path"
=>
project_pipelines_path
(
@project
,
scope: :tags
),
Pending
"has-ci"
=>
@repository
.
gitlab_ci_yml
,
%span
.badge
"ci-lint-path"
=>
ci_lint_path
}
}
=
number_with_delimiter
(
@pending_count
)
%li
.js-pipelines-tab-running
{
class:
active_when
(
@scope
==
'running'
)
}
>
=
link_to
project_pipelines_path
(
@project
,
scope: :running
)
do
Running
%span
.badge.js-running-count
=
number_with_delimiter
(
@running_count
)
%li
.js-pipelines-tab-finished
{
class:
active_when
(
@scope
==
'finished'
)
}
>
=
link_to
project_pipelines_path
(
@project
,
scope: :finished
)
do
Finished
%span
.badge
=
number_with_delimiter
(
@finished_count
)
%li
.js-pipelines-tab-branches
{
class:
active_when
(
@scope
==
'branches'
)
}
>
=
link_to
project_pipelines_path
(
@project
,
scope: :branches
)
do
Branches
%li
.js-pipelines-tab-tags
{
class:
active_when
(
@scope
==
'tags'
)
}
>
=
link_to
project_pipelines_path
(
@project
,
scope: :tags
)
do
Tags
.nav-controls
-
if
can?
current_user
,
:create_pipeline
,
@project
=
link_to
new_namespace_project_pipeline_path
(
@project
.
namespace
,
@project
),
class:
'btn btn-create'
do
Run pipeline
-
unless
@repository
.
gitlab_ci_yml
=
link_to
'Get started with Pipelines'
,
help_page_path
(
'ci/quick_start/README'
),
class:
'btn btn-info'
=
link_to
ci_lint_path
,
class:
'btn btn-default'
do
%span
CI Lint
.content-list.pipelines
{
data:
{
url:
namespace_project_pipelines_path
(
@project
.
namespace
,
@project
,
format: :json
)
}
}
.vue-pipelines-index
=
page_specific_javascript_bundle_tag
(
'common_vue'
)
=
page_specific_javascript_bundle_tag
(
'common_vue'
)
=
page_specific_javascript_bundle_tag
(
'vue_pipelines'
)
=
page_specific_javascript_bundle_tag
(
'vue_pipelines'
)
app/views/shared/empty_states/icons/_pipelines_empty.svg
0 → 100644
View file @
453d755a
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 250 150"
><g
fill=
"none"
fill-rule=
"evenodd"
transform=
"translate(0-3)"
><g
transform=
"translate(0 105)"
><g
fill=
"#e5e5e5"
><rect
width=
"78"
height=
"4"
x=
"34"
y=
"21"
opacity=
".5"
rx=
"2"
/><path
d=
"m152 23c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2"
/></g><g
transform=
"translate(0 4)"
><path
fill=
"#98d7b2"
fill-rule=
"nonzero"
d=
"m19 38c-10.493 0-19-8.507-19-19 0-10.493 8.507-19 19-19 10.493 0 19 8.507 19 19 0 10.493-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"
/><path
fill=
"#31af64"
d=
"m17.07 21.02l-2.829-2.829c-.786-.786-2.047-.781-2.828 0-.786.786-.781 2.047 0 2.828l4.243 4.243c.392.392.902.587 1.412.588.512.002 1.021-.193 1.41-.582l7.79-7.79c.777-.777.775-2.042-.006-2.823-.786-.786-2.045-.784-2.823-.006l-6.37 6.37"
/></g><g
fill=
"#e52c5a"
transform=
"translate(102)"
><path
fill-rule=
"nonzero"
d=
"m24 47.5c-12.979 0-23.5-10.521-23.5-23.5 0-12.979 10.521-23.5 23.5-23.5 12.979 0 23.5 10.521 23.5 23.5 0 12.979-10.521 23.5-23.5 23.5m0-5c10.217 0 18.5-8.283 18.5-18.5 0-10.217-8.283-18.5-18.5-18.5-10.217 0-18.5 8.283-18.5 18.5 0 10.217 8.283 18.5 18.5 18.5"
/><path
d=
"m28.24 24l2.833-2.833c1.167-1.167 1.167-3.067-.004-4.239-1.169-1.169-3.069-1.173-4.239-.004l-2.833 2.833-2.833-2.833c-1.167-1.167-3.067-1.167-4.239.004-1.169 1.169-1.173 3.069-.004 4.239l2.833 2.833-2.833 2.833c-1.167 1.167-1.167 3.067.004 4.239 1.169 1.169 3.069 1.173 4.239.004l2.833-2.833 2.833 2.833c1.167 1.167 3.067 1.167 4.239-.004 1.169-1.169 1.173-3.069.004-4.239l-2.833-2.833"
/></g><path
fill=
"#e5e5e5"
fill-rule=
"nonzero"
d=
"m236 37c-7.732 0-14-6.268-14-14 0-7.732 6.268-14 14-14 7.732 0 14 6.268 14 14 0 7.732-6.268 14-14 14m0-4c5.523 0 10-4.477 10-10 0-5.523-4.477-10-10-10-5.523 0-10 4.477-10 10 0 5.523 4.477 10 10 10"
/></g><g
transform=
"translate(69 3)"
><path
fill=
"#e5e5e5"
fill-rule=
"nonzero"
d=
"m4 11.99v60.02c0 4.413 3.583 7.99 8 7.99h89.991c4.419 0 8-3.579 8-7.99v-60.02c0-4.413-3.583-7.99-8-7.99h-89.991c-4.419 0-8 3.579-8 7.99m-4 0c0-6.622 5.378-11.99 12-11.99h89.991c6.629 0 12 5.367 12 11.99v60.02c0 6.622-5.378 11.99-12 11.99h-89.991c-6.629 0-12-5.367-12-11.99v-60.02m52.874 80.3l-13.253-15.292h34.76l-13.253 15.292c-2.237 2.582-6.01 2.585-8.253 0m3.02-2.62c.644.743 1.564.743 2.207 0l7.516-8.673h-17.24l7.516 8.673"
/><rect
width=
"18"
height=
"6"
x=
"15"
y=
"23"
fill=
"#fc8a51"
rx=
"3"
/><rect
width=
"18"
height=
"6"
x=
"39"
y=
"39"
fill=
"#e52c5a"
rx=
"3"
/><rect
width=
"18"
height=
"6"
x=
"33"
y=
"55"
fill=
"#e5e5e5"
rx=
"3"
/><rect
width=
"12"
height=
"6"
x=
"39"
y=
"23"
fill=
"#fde5d8"
rx=
"3"
/><rect
width=
"12"
height=
"6"
x=
"57"
y=
"55"
fill=
"#e52c5a"
rx=
"3"
/><rect
width=
"12"
height=
"6"
x=
"15"
y=
"55"
fill=
"#b5a7dd"
rx=
"3"
/><rect
width=
"18"
height=
"6"
x=
"81"
y=
"23"
fill=
"#fc8a51"
rx=
"3"
/><rect
width=
"18"
height=
"6"
x=
"15"
y=
"39"
fill=
"#fde5d8"
rx=
"3"
/><rect
width=
"6"
height=
"6"
x=
"57"
y=
"23"
fill=
"#e52c5a"
rx=
"3"
/><g
fill=
"#fde5d8"
><rect
width=
"6"
height=
"6"
x=
"69"
y=
"23"
rx=
"3"
/><rect
width=
"6"
height=
"6"
x=
"75"
y=
"39"
rx=
"3"
/></g><rect
width=
"6"
height=
"6"
x=
"63"
y=
"39"
fill=
"#e52c5a"
rx=
"3"
/></g><g
transform=
"matrix(.70711-.70711.70711.70711 84.34 52.5)"
><path
fill=
"#6b4fbb"
fill-rule=
"nonzero"
d=
"m28.02 67.48c-15.927-2.825-28.02-16.738-28.02-33.476 0-18.778 15.222-34 34-34 18.778 0 34 15.222 34 34 0 16.738-12.1 30.652-28.02 33.476.015.173.023.347.023.524v21.999c0 3.314-2.693 6-6 6-3.314 0-6-2.682-6-6v-21.999c0-.177.008-.351.023-.524m5.977-7.476c14.359 0 26-11.641 26-26 0-14.359-11.641-26-26-26-14.359 0-26 11.641-26 26 0 14.359 11.641 26 26 26"
/><path
fill=
"#fff"
fill-opacity=
".3"
stroke=
"#6b4fbb"
stroke-width=
"8"
d=
"m31 71c16.569 0 30-13.431 30-30 0-16.569-13.431-30-30-30"
transform=
"matrix(.86603.5-.5.86603 26.663-17.507)"
/></g></g></svg>
\ No newline at end of file
app/views/shared/empty_states/icons/_pipelines_failed.svg
0 → 100644
View file @
453d755a
<svg
xmlns=
"http://www.w3.org/2000/svg"
viewBox=
"0 0 446 249"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
><defs><path
id=
"0"
d=
"m260.03 114h23.972v-.013c19.972-.53 36-16.887 36-36.987 0-20.435-16.565-37-37-37-.993 0-1.977.039-2.95.116-4.95-14.605-18.773-25.12-35.05-25.12-5.464 0-10.652 1.185-15.32 3.311-6.649-9.841-17.909-16.311-30.68-16.311-20.435 0-37 16.565-37 37 0 .701.019 1.397.058 2.088-16.11 3.999-28.06 18.561-28.06 35.912 0 20.435 16.565 37 37 37 .324 0 .646-.004.968-.012"
/><ellipse
id=
"2"
cx=
"41"
cy=
"41"
rx=
"41"
ry=
"41"
/><mask
id=
"1"
width=
"186"
height=
"112"
x=
"0"
y=
"0"
fill=
"#fff"
><use
xlink:href=
"#0"
/></mask><mask
id=
"3"
width=
"82"
height=
"82"
x=
"0"
y=
"0"
fill=
"#fff"
><use
xlink:href=
"#2"
/></mask></defs><g
fill=
"none"
fill-rule=
"evenodd"
><g
transform=
"matrix(.86603.5-.5.86603 228.11 137.43)"
><path
stroke=
"#b5a7dd"
stroke-width=
"4"
d=
"m.445.161c15.89 10.636 34.998 16.839 55.55 16.839"
/><g
transform=
"translate(56 4)"
><path
fill=
"#fb722e"
d=
"m16 8c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2m0 10c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2"
/><path
fill=
"#fde5d8"
fill-rule=
"nonzero"
d=
"m4 22h6c3.315 0 6-2.685 6-5.997v-6.01c0-3.315-2.684-5.997-6-5.997h-6v18m-4-18.992c0-1.661 1.343-3.01 2.994-3.01h7.01c5.523 0 10 4.47 10 9.997v6.01c0 5.521-4.476 9.997-10 9.997h-7.01c-1.654 0-2.994-1.343-2.994-3.01v-19.984"
/></g></g><g
fill-rule=
"nonzero"
transform=
"translate(257)"
><path
fill=
"#e5e5e5"
d=
"m3.597 18.747c5.611-9.09 15.519-14.747 26.403-14.747 17.12 0 31 13.879 31 31 0 7.02-2.34 13.685-6.58 19.1l3.149 2.466c4.786-6.111 7.431-13.639 7.431-21.565 0-19.33-15.67-35-35-35-12.286 0-23.476 6.384-29.808 16.647l3.404 2.1"
/><g
transform=
"matrix(.96593.25882-.25882.96593 15.98 9.578)"
><path
fill=
"#b5a7dd"
d=
"m12.426 11.592l-2.142 1.768-3.664-2.116c-.186-.107-.43-.042-.543.154l-1.229 2.129c-.116.2-.052.438.138.547l3.658 2.112-.455 2.735c-.109.657-.165 1.327-.165 2.01 0 .678.055 1.348.165 2.01l.455 2.735-3.658 2.112c-.186.107-.251.351-.138.547l1.229 2.129c.116.2.353.264.543.154l3.664-2.116 2.142 1.768c1.036.855 2.205 1.533 3.462 2l2.6.972v4.225c0 .215.179.393.405.393h2.458c.231 0 .405-.174.405-.393v-4.225l2.6-.972c1.257-.47 2.426-1.147 3.462-2l2.142-1.768 3.664 2.116c.186.107.43.042.543-.154l1.229-2.129c.116-.2.052-.438-.138-.547l-3.658-2.112.455-2.735c.109-.657.165-1.327.165-2.01 0-.678-.055-1.348-.165-2.01l-.455-2.735 3.658-2.112c.186-.107.251-.351.138-.547l-1.229-2.129c-.116-.2-.353-.264-.543-.154l-3.664 2.116-2.142-1.768c-1.036-.855-2.205-1.533-3.462-2l-2.6-.972v-4.225c0-.215-.179-.393-.405-.393h-2.458c-.231 0-.405.174-.405.393v4.225l-2.6.972c-1.257.47-2.426 1.147-3.462 2m2.062-5.749v-1.45c0-2.426 1.963-4.393 4.405-4.393h2.458c2.433 0 4.405 1.967 4.405 4.393v1.45c1.689.631 3.243 1.538 4.608 2.665l1.259-.727c2.101-1.213 4.786-.497 6.01 1.618l1.229 2.129c1.216 2.107.499 4.798-1.602 6.01l-1.257.726c.144.866.219 1.755.219 2.662 0 .907-.075 1.796-.219 2.662l1.257.726c2.101 1.213 2.823 3.896 1.602 6.01l-1.229 2.129c-1.216 2.107-3.906 2.832-6.01 1.618l-1.259-.727c-1.365 1.127-2.92 2.034-4.608 2.665v1.45c0 2.426-1.963 4.393-4.405 4.393h-2.458c-2.433 0-4.405-1.967-4.405-4.393v-1.45c-1.689-.631-3.243-1.538-4.608-2.665l-1.259.727c-2.101 1.213-4.786.497-6.01-1.618l-1.229-2.129c-1.216-2.107-.499-4.798 1.602-6.01l1.257-.726c-.144-.866-.219-1.755-.219-2.662 0-.907.075-1.796.219-2.662l-1.257-.726c-2.101-1.213-2.823-3.896-1.602-6.01l1.229-2.129c1.216-2.107 3.906-2.832 6.01-1.618l1.259.727c1.365-1.127 2.92-2.034 4.608-2.665"
/><path
fill=
"#6b4fbb"
d=
"m20.12 23.366c1.347 0 2.439-1.092 2.439-2.439 0-1.347-1.092-2.439-2.439-2.439-1.347 0-2.439 1.092-2.439 2.439 0 1.347 1.092 2.439 2.439 2.439m0 4c-3.556 0-6.439-2.883-6.439-6.439 0-3.556 2.883-6.439 6.439-6.439 3.556 0 6.439 2.883 6.439 6.439 0 3.556-2.883 6.439-6.439 6.439"
/></g></g><use
fill=
"#fff"
stroke=
"#e5e5e5"
stroke-width=
"8"
mask=
"url(#1)"
stroke-linejoin=
"round"
xlink:href=
"#0"
/><g
transform=
"translate(175 58)"
><use
fill=
"#fff"
stroke=
"#e5e5e5"
stroke-width=
"8"
mask=
"url(#3)"
xlink:href=
"#2"
/><g
fill-rule=
"nonzero"
><path
fill=
"#e5e5e5"
d=
"m41 78c20.435 0 37-16.565 37-37 0-20.435-16.565-37-37-37-20.435 0-37 16.565-37 37 0 20.435 16.565 37 37 37m0 4c-22.644 0-41-18.356-41-41 0-22.644 18.356-41 41-41 22.644 0 41 18.356 41 41 0 22.644-18.356 41-41 41"
/><g
transform=
"matrix(.96593.25882-.25882.96593 23.581 9.415)"
><path
fill=
"#b5a7dd"
d=
"m14.821 13.655l-2.142 1.768-3.933-2.271c-.72-.416-1.634-.171-2.046.543l-1.507 2.61c-.409.708-.161 1.631.553 2.043l3.926 2.267-.455 2.735c-.145.869-.218 1.754-.218 2.65 0 .896.073 1.782.218 2.65l.455 2.735-3.926 2.267c-.72.416-.965 1.329-.553 2.043l1.507 2.61c.409.708 1.332.955 2.046.543l3.933-2.271 2.142 1.768c1.369 1.131 2.916 2.027 4.579 2.648l2.6.972v4.534c0 .831.669 1.5 1.493 1.5h3.01c.817 0 1.493-.676 1.493-1.5v-4.534l2.6-.972c1.663-.621 3.21-1.518 4.579-2.648l2.142-1.768 3.933 2.271c.72.416 1.634.171 2.046-.543l1.507-2.61c.409-.708.161-1.631-.553-2.043l-3.926-2.267.455-2.735c.145-.869.218-1.754.218-2.65 0-.896-.073-1.782-.218-2.65l-.455-2.735 3.926-2.267c.72-.416.965-1.329.553-2.043l-1.507-2.61c-.409-.708-1.332-.955-2.046-.543l-3.933 2.271-2.142-1.768c-1.369-1.131-2.916-2.027-4.579-2.648l-2.6-.972v-4.534c0-.831-.669-1.5-1.493-1.5h-3.01c-.817 0-1.493.676-1.493 1.5v4.534l-2.6.972c-1.663.621-3.21 1.518-4.579 2.648m3.179-6.395v-1.759c0-3.038 2.471-5.5 5.493-5.5h3.01c3.034 0 5.493 2.46 5.493 5.5v1.759c2.098.784 4.03 1.91 5.725 3.311l1.528-.882c2.631-1.519 5.999-.61 7.51 2.01l1.507 2.61c1.517 2.627.616 5.987-2.02 7.507l-1.525.881c.179 1.076.272 2.18.272 3.307 0 1.127-.093 2.231-.272 3.307l1.525.881c2.631 1.519 3.528 4.89 2.02 7.507l-1.507 2.61c-1.517 2.627-4.877 3.527-7.51 2.01l-1.528-.882c-1.696 1.401-3.627 2.527-5.725 3.311v1.759c0 3.038-2.471 5.5-5.493 5.5h-3.01c-3.034 0-5.493-2.46-5.493-5.5v-1.759c-2.098-.784-4.03-1.91-5.725-3.311l-1.528.882c-2.631 1.519-5.999.61-7.51-2.01l-1.507-2.61c-1.517-2.627-.616-5.987 2.02-7.507l1.525-.881c-.179-1.076-.272-2.18-.272-3.307 0-1.127.093-2.231.272-3.307l-1.525-.881c-2.631-1.519-3.528-4.89-2.02-7.507l1.507-2.61c1.517-2.627 4.877-3.527 7.51-2.01l1.528.882c1.696-1.401 3.627-2.527 5.725-3.311"
/><path
fill=
"#6b4fbb"
d=
"m25 30c2.209 0 4-1.791 4-4 0-2.209-1.791-4-4-4-2.209 0-4 1.791-4 4 0 2.209 1.791 4 4 4m0 4c-4.418 0-8-3.582-8-8 0-4.418 3.582-8 8-8 4.418 0 8 3.582 8 8 0 4.418-3.582 8-8 8"
/></g></g></g><g
transform=
"translate(140 161)"
><path
fill=
"#e5e5e5"
fill-rule=
"nonzero"
d=
"m4 8.541v30.01c0 2.202 1.793 3.995 4 3.995h20c2.209 0 4-1.789 4-3.995v-30.01c0-2.202-1.793-3.995-4-3.995h-20c-2.209 0-4 1.789-4 3.995m-4 0c0-4.416 3.583-7.995 8-7.995h20c4.416 0 8 3.584 8 7.995v30.01c0 4.416-3.583 7.995-8 7.995h-20c-4.416 0-8-3.584-8-7.995v-30.01"
/><g
fill=
"#fb722e"
><rect
width=
"4"
height=
"11"
x=
"10"
y=
"18.545"
rx=
"2"
/><rect
width=
"4"
height=
"11"
x=
"21"
y=
"18.545"
rx=
"2"
/></g></g><path
fill=
"#e5e5e5"
fill-rule=
"nonzero"
d=
"m445.16 245.34c-16.874-11.778-110.62-20.336-222.14-20.336-111.61 0-205.4 8.571-222.18 20.364-.904.635-1.121 1.883-.486 2.786.635.904 1.883 1.121 2.786.486 15.756-11.07 109.46-19.636 219.88-19.636 110.34 0 203.99 8.55 219.85 19.617.906.632 2.153.41 2.785-.495.632-.906.41-2.153-.495-2.785"
/></g></svg>
\ No newline at end of file
changelogs/unreleased/27574-pipelines-empty-state.yml
0 → 100644
View file @
453d755a
---
title
:
Adds empty and error state to pipelines
merge_request
:
author
:
spec/features/projects/pipelines/pipelines_spec.rb
View file @
453d755a
...
@@ -442,7 +442,7 @@ describe 'Pipelines', :feature, :js do
...
@@ -442,7 +442,7 @@ describe 'Pipelines', :feature, :js do
context
'when project is public'
do
context
'when project is public'
do
let
(
:project
)
{
create
(
:project
,
:public
)
}
let
(
:project
)
{
create
(
:project
,
:public
)
}
it
{
expect
(
page
).
to
have_content
'
No pipelines to show
'
}
it
{
expect
(
page
).
to
have_content
'
Build with confidence
'
}
it
{
expect
(
page
).
to
have_http_status
(
:success
)
}
it
{
expect
(
page
).
to
have_http_status
(
:success
)
}
end
end
...
...
spec/javascripts/commit/pipelines/pipelines_spec.js
View file @
453d755a
...
@@ -33,7 +33,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
...
@@ -33,7 +33,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
});
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-blank-state-title
'
).
textContent
).
toContain
(
'
No pipelines to show
'
);
expect
(
component
.
$el
.
querySelector
(
'
.empty-state
'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
done
();
},
1
);
},
1
);
});
});
...
@@ -63,6 +64,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
...
@@ -63,6 +64,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelectorAll
(
'
table > tbody > tr
'
).
length
).
toEqual
(
1
);
expect
(
component
.
$el
.
querySelectorAll
(
'
table > tbody > tr
'
).
length
).
toEqual
(
1
);
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
done
();
},
0
);
},
0
);
});
});
...
@@ -92,7 +94,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
...
@@ -92,7 +94,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
});
setTimeout
(()
=>
{
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-blank-state-title
'
).
textContent
).
toContain
(
'
No pipelines to show
'
);
expect
(
component
.
$el
.
querySelector
(
'
.js-pipelines-error-state
'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
done
();
},
0
);
},
0
);
});
});
...
...
spec/javascripts/fixtures/pipelines.html.haml
0 → 100644
View file @
453d755a
%div
#pipelines-list-vue
{
data:
{
endpoint:
'foo'
,
"css-class"
=>
'foo'
,
"help-page-path"
=>
'foo'
,
"new-pipeline-path"
=>
'foo'
,
"can-create-pipeline"
=>
'true'
,
"all-path"
=>
'foo'
,
"pending-path"
=>
'foo'
,
"running-path"
=>
'foo'
,
"finished-path"
=>
'foo'
,
"branches-path"
=>
'foo'
,
"tags-path"
=>
'foo'
,
"has-ci"
=>
'foo'
,
"ci-lint-path"
=>
'foo'
}
}
spec/javascripts/fixtures/pipelines_table.html.haml
View file @
453d755a
#commit-pipeline-table-view
{
data:
{
endpoint:
"endpoint"
}
}
#commit-pipeline-table-view
{
data:
{
endpoint:
"endpoint"
,
"help-page-path"
:
"foo"
}
}
.pipeline-svgs
{
data:
{
"commit_icon_svg"
:
"svg"
}
}
spec/javascripts/vue_pipelines_index/empty_state_spec.js
0 → 100644
View file @
453d755a
import
Vue
from
'
vue
'
;
import
emptyStateComp
from
'
~/vue_pipelines_index/components/empty_state
'
;
describe
(
'
Pipelines Empty State
'
,
()
=>
{
let
component
;
let
EmptyStateComponent
;
beforeEach
(()
=>
{
EmptyStateComponent
=
Vue
.
extend
(
emptyStateComp
);
component
=
new
EmptyStateComponent
({
propsData
:
{
helpPagePath
:
'
foo
'
,
},
}).
$mount
();
});
it
(
'
should render empty state SVG
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.svg-content svg
'
)).
toBeDefined
();
});
it
(
'
should render emtpy state information
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
h4
'
).
textContent
).
toContain
(
'
Build with confidence
'
);
expect
(
component
.
$el
.
querySelector
(
'
p
'
).
textContent
,
).
toContain
(
'
Continous Integration can help catch bugs by running your tests automatically
'
);
expect
(
component
.
$el
.
querySelector
(
'
p
'
).
textContent
,
).
toContain
(
'
Continuous Deployment can help you deliver code to your product environment
'
);
});
it
(
'
should render a link with provided help path
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.btn-info
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
foo
'
);
expect
(
component
.
$el
.
querySelector
(
'
.btn-info
'
).
textContent
).
toContain
(
'
Get started with Pipelines
'
);
});
});
spec/javascripts/vue_pipelines_index/error_state_spec.js
0 → 100644
View file @
453d755a
import
Vue
from
'
vue
'
;
import
errorStateComp
from
'
~/vue_pipelines_index/components/error_state
'
;
describe
(
'
Pipelines Error State
'
,
()
=>
{
let
component
;
let
ErrorStateComponent
;
beforeEach
(()
=>
{
ErrorStateComponent
=
Vue
.
extend
(
errorStateComp
);
component
=
new
ErrorStateComponent
().
$mount
();
});
it
(
'
should render error state SVG
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.svg-content svg
'
)).
toBeDefined
();
});
it
(
'
should render emtpy state information
'
,
()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
h4
'
).
textContent
,
).
toContain
(
'
The API failed to fetch the pipelines
'
);
});
});
spec/javascripts/vue_pipelines_index/mock_data.js
0 → 100644
View file @
453d755a
export
default
{
pipelines
:
[{
id
:
115
,
user
:
{
name
:
'
Root
'
,
username
:
'
root
'
,
id
:
1
,
state
:
'
active
'
,
avatar_url
:
'
http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80
\
u0026d=identicon
'
,
web_url
:
'
http://localhost:3000/root
'
,
},
path
:
'
/root/review-app/pipelines/115
'
,
details
:
{
status
:
{
icon
:
'
icon_status_failed
'
,
text
:
'
failed
'
,
label
:
'
failed
'
,
group
:
'
failed
'
,
has_details
:
true
,
details_path
:
'
/root/review-app/pipelines/115
'
,
},
duration
:
null
,
finished_at
:
'
2017-03-17T19:00:15.996Z
'
,
stages
:
[{
name
:
'
build
'
,
title
:
'
build: failed
'
,
status
:
{
icon
:
'
icon_status_failed
'
,
text
:
'
failed
'
,
label
:
'
failed
'
,
group
:
'
failed
'
,
has_details
:
true
,
details_path
:
'
/root/review-app/pipelines/115#build
'
,
},
path
:
'
/root/review-app/pipelines/115#build
'
,
dropdown_path
:
'
/root/review-app/pipelines/115/stage.json?stage=build
'
,
},
{
name
:
'
review
'
,
title
:
'
review: skipped
'
,
status
:
{
icon
:
'
icon_status_skipped
'
,
text
:
'
skipped
'
,
label
:
'
skipped
'
,
group
:
'
skipped
'
,
has_details
:
true
,
details_path
:
'
/root/review-app/pipelines/115#review
'
,
},
path
:
'
/root/review-app/pipelines/115#review
'
,
dropdown_path
:
'
/root/review-app/pipelines/115/stage.json?stage=review
'
,
}],
artifacts
:
[],
manual_actions
:
[{
name
:
'
stop_review
'
,
path
:
'
/root/review-app/builds/3766/play
'
,
}],
},
flags
:
{
latest
:
true
,
triggered
:
false
,
stuck
:
false
,
yaml_errors
:
false
,
retryable
:
true
,
cancelable
:
false
,
},
ref
:
{
name
:
'
thisisabranch
'
,
path
:
'
/root/review-app/tree/thisisabranch
'
,
tag
:
false
,
branch
:
true
,
},
commit
:
{
id
:
'
9e87f87625b26c42c59a2ee0398f81d20cdfe600
'
,
short_id
:
'
9e87f876
'
,
title
:
'
Update README.md
'
,
created_at
:
'
2017-03-15T22:58:28.000+00:00
'
,
parent_ids
:
[
'
3744f9226e699faec2662a8b267e5d3fd0bfff0e
'
],
message
:
'
Update README.md
'
,
author_name
:
'
Root
'
,
author_email
:
'
admin@example.com
'
,
authored_date
:
'
2017-03-15T22:58:28.000+00:00
'
,
committer_name
:
'
Root
'
,
committer_email
:
'
admin@example.com
'
,
committed_date
:
'
2017-03-15T22:58:28.000+00:00
'
,
author
:
{
name
:
'
Root
'
,
username
:
'
root
'
,
id
:
1
,
state
:
'
active
'
,
avatar_url
:
'
http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80
\
u0026d=identicon
'
,
web_url
:
'
http://localhost:3000/root
'
,
},
author_gravatar_url
:
'
http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80
\
u0026d=identicon
'
,
commit_url
:
'
http://localhost:3000/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600
'
,
commit_path
:
'
/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600
'
,
},
retry_path
:
'
/root/review-app/pipelines/115/retry
'
,
created_at
:
'
2017-03-15T22:58:33.436Z
'
,
updated_at
:
'
2017-03-17T19:00:15.997Z
'
,
}],
count
:
{
all
:
52
,
running
:
0
,
pending
:
0
,
finished
:
52
,
},
};
spec/javascripts/vue_pipelines_index/nav_controls_spec.js
0 → 100644
View file @
453d755a
import
Vue
from
'
vue
'
;
import
navControlsComp
from
'
~/vue_pipelines_index/components/nav_controls
'
;
describe
(
'
Pipelines Nav Controls
'
,
()
=>
{
let
NavControlsComponent
;
beforeEach
(()
=>
{
NavControlsComponent
=
Vue
.
extend
(
navControlsComp
);
});
it
(
'
should render link to create a new pipeline
'
,
()
=>
{
const
mockData
=
{
newPipelinePath
:
'
foo
'
,
hasCiEnabled
:
true
,
helpPagePath
:
'
foo
'
,
ciLintPath
:
'
foo
'
,
canCreatePipeline
:
true
,
};
const
component
=
new
NavControlsComponent
({
propsData
:
mockData
,
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.btn-create
'
).
textContent
).
toContain
(
'
Run Pipeline
'
);
expect
(
component
.
$el
.
querySelector
(
'
.btn-create
'
).
getAttribute
(
'
href
'
)).
toEqual
(
mockData
.
newPipelinePath
);
});
it
(
'
should not render link to create pipeline if no permission is provided
'
,
()
=>
{
const
mockData
=
{
newPipelinePath
:
'
foo
'
,
hasCiEnabled
:
true
,
helpPagePath
:
'
foo
'
,
ciLintPath
:
'
foo
'
,
canCreatePipeline
:
false
,
};
const
component
=
new
NavControlsComponent
({
propsData
:
mockData
,
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.btn-create
'
)).
toEqual
(
null
);
});
it
(
'
should render link for CI lint
'
,
()
=>
{
const
mockData
=
{
newPipelinePath
:
'
foo
'
,
hasCiEnabled
:
true
,
helpPagePath
:
'
foo
'
,
ciLintPath
:
'
foo
'
,
canCreatePipeline
:
true
,
};
const
component
=
new
NavControlsComponent
({
propsData
:
mockData
,
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.btn-default
'
).
textContent
).
toContain
(
'
CI Lint
'
);
expect
(
component
.
$el
.
querySelector
(
'
.btn-default
'
).
getAttribute
(
'
href
'
)).
toEqual
(
mockData
.
ciLintPath
);
});
it
(
'
should render link to help page when CI is not enabled
'
,
()
=>
{
const
mockData
=
{
newPipelinePath
:
'
foo
'
,
hasCiEnabled
:
false
,
helpPagePath
:
'
foo
'
,
ciLintPath
:
'
foo
'
,
canCreatePipeline
:
true
,
};
const
component
=
new
NavControlsComponent
({
propsData
:
mockData
,
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.btn-info
'
).
textContent
).
toContain
(
'
Get started with Pipelines
'
);
expect
(
component
.
$el
.
querySelector
(
'
.btn-info
'
).
getAttribute
(
'
href
'
)).
toEqual
(
mockData
.
helpPagePath
);
});
it
(
'
should not render link to help page when CI is enabled
'
,
()
=>
{
const
mockData
=
{
newPipelinePath
:
'
foo
'
,
hasCiEnabled
:
true
,
helpPagePath
:
'
foo
'
,
ciLintPath
:
'
foo
'
,
canCreatePipeline
:
true
,
};
const
component
=
new
NavControlsComponent
({
propsData
:
mockData
,
}).
$mount
();
expect
(
component
.
$el
.
querySelector
(
'
.btn-info
'
)).
toEqual
(
null
);
});
});
spec/javascripts/vue_pipelines_index/pipelines_spec.js
0 → 100644
View file @
453d755a
import
Vue
from
'
vue
'
;
import
pipelinesComp
from
'
~/vue_pipelines_index/pipelines
'
;
import
Store
from
'
~/vue_pipelines_index/stores/pipelines_store
'
;
import
pipelinesData
from
'
./mock_data
'
;
describe
(
'
Pipelines
'
,
()
=>
{
preloadFixtures
(
'
static/pipelines.html.raw
'
);
let
PipelinesComponent
;
beforeEach
(()
=>
{
loadFixtures
(
'
static/pipelines.html.raw
'
);
PipelinesComponent
=
Vue
.
extend
(
pipelinesComp
);
});
describe
(
'
successfull request
'
,
()
=>
{
describe
(
'
with pipelines
'
,
()
=>
{
const
pipelinesInterceptor
=
(
request
,
next
)
=>
{
next
(
request
.
respondWith
(
JSON
.
stringify
(
pipelinesData
),
{
status
:
200
,
}));
};
beforeEach
(()
=>
{
Vue
.
http
.
interceptors
.
push
(
pipelinesInterceptor
);
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptors
,
pipelinesInterceptor
,
);
});
it
(
'
should render table
'
,
(
done
)
=>
{
const
component
=
new
PipelinesComponent
({
propsData
:
{
store
:
new
Store
(),
},
}).
$mount
();
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.table-holder
'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
});
});
});
describe
(
'
without pipelines
'
,
()
=>
{
const
emptyInterceptor
=
(
request
,
next
)
=>
{
next
(
request
.
respondWith
(
JSON
.
stringify
([]),
{
status
:
200
,
}));
};
beforeEach
(()
=>
{
Vue
.
http
.
interceptors
.
push
(
emptyInterceptor
);
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptors
,
emptyInterceptor
,
);
});
it
(
'
should render empty state
'
,
(
done
)
=>
{
const
component
=
new
PipelinesComponent
({
propsData
:
{
store
:
new
Store
(),
},
}).
$mount
();
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.empty-state
'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
});
});
});
});
describe
(
'
unsuccessfull request
'
,
()
=>
{
const
errorInterceptor
=
(
request
,
next
)
=>
{
next
(
request
.
respondWith
(
JSON
.
stringify
([]),
{
status
:
500
,
}));
};
beforeEach
(()
=>
{
Vue
.
http
.
interceptors
.
push
(
errorInterceptor
);
});
afterEach
(()
=>
{
Vue
.
http
.
interceptors
=
_
.
without
(
Vue
.
http
.
interceptors
,
errorInterceptor
,
);
});
it
(
'
should render error state
'
,
(
done
)
=>
{
const
component
=
new
PipelinesComponent
({
propsData
:
{
store
:
new
Store
(),
},
}).
$mount
();
setTimeout
(()
=>
{
expect
(
component
.
$el
.
querySelector
(
'
.js-pipelines-error-state
'
)).
toBeDefined
();
expect
(
component
.
$el
.
querySelector
(
'
.realtime-loading
'
)).
toBe
(
null
);
done
();
});
});
});
});
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