Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/pipelines')
-rw-r--r--spec/frontend/pipelines/components/dag/__snapshots__/dag_graph_spec.js.snap230
-rw-r--r--spec/frontend/pipelines/components/dag/dag_annotations_spec.js98
-rw-r--r--spec/frontend/pipelines/components/dag/dag_graph_spec.js209
-rw-r--r--spec/frontend/pipelines/components/dag/dag_spec.js168
-rw-r--r--spec/frontend/pipelines/components/dag/drawing_utils_spec.js57
-rw-r--r--spec/frontend/pipelines/components/dag/mock_data.js674
-rw-r--r--spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js80
-rw-r--r--spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js141
-rw-r--r--spec/frontend/pipelines/components/jobs/jobs_app_spec.js127
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/job_item_spec.js29
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph_spec.js122
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage_spec.js247
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js166
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js407
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/mock_data.js150
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js123
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js46
-rw-r--r--spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js63
-rw-r--r--spec/frontend/pipelines/components/pipeline_tabs_spec.js114
-rw-r--r--spec/frontend/pipelines/components/pipelines_filtered_search_spec.js199
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js107
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js133
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js58
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_job_details_spec.js254
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js279
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js78
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js139
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js58
-rw-r--r--spec/frontend/pipelines/components/pipelines_list/pipieline_stop_modal_spec.js27
-rw-r--r--spec/frontend/pipelines/empty_state_spec.js87
-rw-r--r--spec/frontend/pipelines/graph/action_component_spec.js116
-rw-r--r--spec/frontend/pipelines/graph/graph_component_spec.js182
-rw-r--r--spec/frontend/pipelines/graph/graph_component_wrapper_spec.js603
-rw-r--r--spec/frontend/pipelines/graph/graph_view_selector_spec.js217
-rw-r--r--spec/frontend/pipelines/graph/job_group_dropdown_spec.js84
-rw-r--r--spec/frontend/pipelines/graph/job_item_spec.js492
-rw-r--r--spec/frontend/pipelines/graph/job_name_component_spec.js30
-rw-r--r--spec/frontend/pipelines/graph/linked_pipeline_spec.js464
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_column_spec.js214
-rw-r--r--spec/frontend/pipelines/graph/linked_pipelines_mock_data.js27
-rw-r--r--spec/frontend/pipelines/graph/mock_data.js387
-rw-r--r--spec/frontend/pipelines/graph/stage_column_component_spec.js228
-rw-r--r--spec/frontend/pipelines/graph_shared/__snapshots__/links_inner_spec.js.snap30
-rw-r--r--spec/frontend/pipelines/graph_shared/links_inner_spec.js223
-rw-r--r--spec/frontend/pipelines/graph_shared/links_layer_spec.js85
-rw-r--r--spec/frontend/pipelines/linked_pipelines_mock.json3569
-rw-r--r--spec/frontend/pipelines/mock_data.js1379
-rw-r--r--spec/frontend/pipelines/nav_controls_spec.js80
-rw-r--r--spec/frontend/pipelines/notification/mock_data.js33
-rw-r--r--spec/frontend/pipelines/pipeline_details_header_spec.js452
-rw-r--r--spec/frontend/pipelines/pipeline_graph/mock_data.js283
-rw-r--r--spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js100
-rw-r--r--spec/frontend/pipelines/pipeline_graph/utils_spec.js197
-rw-r--r--spec/frontend/pipelines/pipeline_labels_spec.js164
-rw-r--r--spec/frontend/pipelines/pipeline_multi_actions_spec.js288
-rw-r--r--spec/frontend/pipelines/pipeline_operations_spec.js77
-rw-r--r--spec/frontend/pipelines/pipeline_tabs_spec.js63
-rw-r--r--spec/frontend/pipelines/pipeline_triggerer_spec.js76
-rw-r--r--spec/frontend/pipelines/pipeline_url_spec.js184
-rw-r--r--spec/frontend/pipelines/pipelines_artifacts_spec.js64
-rw-r--r--spec/frontend/pipelines/pipelines_manual_actions_spec.js216
-rw-r--r--spec/frontend/pipelines/pipelines_spec.js850
-rw-r--r--spec/frontend/pipelines/pipelines_store_spec.js80
-rw-r--r--spec/frontend/pipelines/pipelines_table_spec.js280
-rw-r--r--spec/frontend/pipelines/test_reports/empty_state_spec.js45
-rw-r--r--spec/frontend/pipelines/test_reports/mock_data.js31
-rw-r--r--spec/frontend/pipelines/test_reports/stores/actions_spec.js149
-rw-r--r--spec/frontend/pipelines/test_reports/stores/getters_spec.js171
-rw-r--r--spec/frontend/pipelines/test_reports/stores/mutations_spec.js114
-rw-r--r--spec/frontend/pipelines/test_reports/stores/utils_spec.js40
-rw-r--r--spec/frontend/pipelines/test_reports/test_case_details_spec.js149
-rw-r--r--spec/frontend/pipelines/test_reports/test_reports_spec.js125
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js169
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_spec.js106
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_table_spec.js100
-rw-r--r--spec/frontend/pipelines/time_ago_spec.js85
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js142
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_source_token_spec.js53
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_status_token_spec.js58
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js95
-rw-r--r--spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js99
-rw-r--r--spec/frontend/pipelines/unwrapping_utils_spec.js127
-rw-r--r--spec/frontend/pipelines/utils_spec.js191
83 files changed, 0 insertions, 18536 deletions
diff --git a/spec/frontend/pipelines/components/dag/__snapshots__/dag_graph_spec.js.snap b/spec/frontend/pipelines/components/dag/__snapshots__/dag_graph_spec.js.snap
deleted file mode 100644
index cb5f6ff5307..00000000000
--- a/spec/frontend/pipelines/components/dag/__snapshots__/dag_graph_spec.js.snap
+++ /dev/null
@@ -1,230 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`The DAG graph in the basic case renders the graph svg 1`] = `
-"<svg viewBox=\\"0,0,1000,540\\" width=\\"1000\\" height=\\"540\\">
- <g fill=\\"none\\" stroke-opacity=\\"0.8\\">
- <g id=\\"dag-link43\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad53\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"116\\" x2=\\"361.3333333333333\\">
- <stop offset=\\"0%\\" stop-color=\\"#e17223\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#83ab4a\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip63\\">
- <path d=\\"
- M100, 129
- V158
- H377.3333333333333
- V100
- H100
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M108,129L190,129L190,129L369.3333333333333,129\\" stroke=\\"url(#dag-grad53)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip63)\\"></path>
- </g>
- <g id=\\"dag-link44\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad54\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"377.3333333333333\\" x2=\\"622.6666666666666\\">
- <stop offset=\\"0%\\" stop-color=\\"#83ab4a\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#6f3500\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip64\\">
- <path d=\\"
- M361.3333333333333, 129.0000000000002
- V158.0000000000002
- H638.6666666666666
- V100
- H361.3333333333333
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M369.3333333333333,129L509.3333333333333,129L509.3333333333333,129.0000000000002L630.6666666666666,129.0000000000002\\" stroke=\\"url(#dag-grad54)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip64)\\"></path>
- </g>
- <g id=\\"dag-link45\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad55\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"116\\" x2=\\"622.6666666666666\\">
- <stop offset=\\"0%\\" stop-color=\\"#5772ff\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#6f3500\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip65\\">
- <path d=\\"
- M100, 187.0000000000002
- V241.00000000000003
- H638.6666666666666
- V158.0000000000002
- H100
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M108,212.00000000000003L306,212.00000000000003L306,187.0000000000002L630.6666666666666,187.0000000000002\\" stroke=\\"url(#dag-grad55)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip65)\\"></path>
- </g>
- <g id=\\"dag-link46\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad56\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"116\\" x2=\\"361.3333333333333\\">
- <stop offset=\\"0%\\" stop-color=\\"#b24800\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#006887\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip66\\">
- <path d=\\"
- M100, 269.9999999999998
- V324
- H377.3333333333333
- V240.99999999999977
- H100
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M108,295L338.93333333333334,295L338.93333333333334,269.9999999999998L369.3333333333333,269.9999999999998\\" stroke=\\"url(#dag-grad56)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip66)\\"></path>
- </g>
- <g id=\\"dag-link47\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad57\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"116\\" x2=\\"361.3333333333333\\">
- <stop offset=\\"0%\\" stop-color=\\"#25d2d2\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#487900\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip67\\">
- <path d=\\"
- M100, 352.99999999999994
- V407.00000000000006
- H377.3333333333333
- V323.99999999999994
- H100
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M108,378.00000000000006L144.66666666666669,378.00000000000006L144.66666666666669,352.99999999999994L369.3333333333333,352.99999999999994\\" stroke=\\"url(#dag-grad57)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip67)\\"></path>
- </g>
- <g id=\\"dag-link48\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad58\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"377.3333333333333\\" x2=\\"622.6666666666666\\">
- <stop offset=\\"0%\\" stop-color=\\"#006887\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#d84280\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip68\\">
- <path d=\\"
- M361.3333333333333, 270.0000000000001
- V299.0000000000001
- H638.6666666666666
- V240.99999999999977
- H361.3333333333333
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M369.3333333333333,269.9999999999998L464,269.9999999999998L464,270.0000000000001L630.6666666666666,270.0000000000001\\" stroke=\\"url(#dag-grad58)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip68)\\"></path>
- </g>
- <g id=\\"dag-link49\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad59\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"377.3333333333333\\" x2=\\"622.6666666666666\\">
- <stop offset=\\"0%\\" stop-color=\\"#487900\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#d84280\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip69\\">
- <path d=\\"
- M361.3333333333333, 328.0000000000001
- V381.99999999999994
- H638.6666666666666
- V299.0000000000001
- H361.3333333333333
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M369.3333333333333,352.99999999999994L522,352.99999999999994L522,328.0000000000001L630.6666666666666,328.0000000000001\\" stroke=\\"url(#dag-grad59)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip69)\\"></path>
- </g>
- <g id=\\"dag-link50\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad60\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"377.3333333333333\\" x2=\\"622.6666666666666\\">
- <stop offset=\\"0%\\" stop-color=\\"#487900\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#3547de\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip70\\">
- <path d=\\"
- M361.3333333333333, 411
- V440
- H638.6666666666666
- V381.99999999999994
- H361.3333333333333
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M369.3333333333333,410.99999999999994L580,410.99999999999994L580,411L630.6666666666666,411\\" stroke=\\"url(#dag-grad60)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip70)\\"></path>
- </g>
- <g id=\\"dag-link51\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad61\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"638.6666666666666\\" x2=\\"884\\">
- <stop offset=\\"0%\\" stop-color=\\"#d84280\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#006887\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip71\\">
- <path d=\\"
- M622.6666666666666, 270.1890725105691
- V299.1890725105691
- H900
- V241.0000000000001
- H622.6666666666666
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M630.6666666666666,270.0000000000001L861.6,270.0000000000001L861.6,270.1890725105691L892,270.1890725105691\\" stroke=\\"url(#dag-grad61)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip71)\\"></path>
- </g>
- <g id=\\"dag-link52\\" class=\\"dag-link gl-transition-property-stroke-opacity gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\">
- <linearGradient id=\\"dag-grad62\\" gradientUnits=\\"userSpaceOnUse\\" x1=\\"638.6666666666666\\" x2=\\"884\\">
- <stop offset=\\"0%\\" stop-color=\\"#3547de\\"></stop>
- <stop offset=\\"100%\\" stop-color=\\"#275600\\"></stop>
- </linearGradient>
- <clipPath id=\\"dag-clip72\\">
- <path d=\\"
- M622.6666666666666, 411
- V440
- H900
- V382
- H622.6666666666666
- Z
- \\"></path>
- </clipPath>
- <path d=\\"M630.6666666666666,411L679.9999999999999,411L679.9999999999999,411L892,411\\" stroke=\\"url(#dag-grad62)\\" style=\\"stroke-linejoin: round;\\" stroke-width=\\"56\\" clip-path=\\"url(#dag-clip72)\\"></path>
- </g>
- </g>
- <g>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node73\\" stroke=\\"#e17223\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"108\\" x2=\\"108\\" y1=\\"104\\" y2=\\"154.00000000000003\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node74\\" stroke=\\"#83ab4a\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"369\\" x2=\\"369\\" y1=\\"104\\" y2=\\"154\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node75\\" stroke=\\"#5772ff\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"108\\" x2=\\"108\\" y1=\\"187.00000000000003\\" y2=\\"237.00000000000003\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node76\\" stroke=\\"#b24800\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"108\\" x2=\\"108\\" y1=\\"270\\" y2=\\"320.00000000000006\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node77\\" stroke=\\"#25d2d2\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"108\\" x2=\\"108\\" y1=\\"353.00000000000006\\" y2=\\"403.0000000000001\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node78\\" stroke=\\"#6f3500\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"630\\" x2=\\"630\\" y1=\\"104.0000000000002\\" y2=\\"212.00000000000009\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node79\\" stroke=\\"#006887\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"369\\" x2=\\"369\\" y1=\\"244.99999999999977\\" y2=\\"294.99999999999994\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node80\\" stroke=\\"#487900\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"369\\" x2=\\"369\\" y1=\\"327.99999999999994\\" y2=\\"436\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node81\\" stroke=\\"#d84280\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"630\\" x2=\\"630\\" y1=\\"245.00000000000009\\" y2=\\"353\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node82\\" stroke=\\"#3547de\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"630\\" x2=\\"630\\" y1=\\"386\\" y2=\\"436\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node83\\" stroke=\\"#006887\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"892\\" x2=\\"892\\" y1=\\"245.18907251056908\\" y2=\\"295.1890725105691\\"></line>
- <line class=\\"dag-node gl-transition-property-stroke gl-cursor-pointer gl-transition-duration-slow gl-transition-timing-function-ease\\" id=\\"dag-node84\\" stroke=\\"#275600\\" stroke-width=\\"16\\" stroke-linecap=\\"round\\" x1=\\"892\\" x2=\\"892\\" y1=\\"386\\" y2=\\"436\\"></line>
- </g>
- <g class=\\"gl-font-sm\\">
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58.00000000000003px\\" width=\\"84\\" x=\\"8\\" y=\\"100\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58.00000000000003px; text-align: right;\\">build_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"369.3333333333333\\" y=\\"75\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: left;\\">test_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58px\\" width=\\"84\\" x=\\"8\\" y=\\"183.00000000000003\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58px; text-align: right;\\">test_b</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58.00000000000006px\\" width=\\"84\\" x=\\"8\\" y=\\"266\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58.00000000000006px; text-align: right;\\">post_test_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58.00000000000006px\\" width=\\"84\\" x=\\"8\\" y=\\"349.00000000000006\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58.00000000000006px; text-align: right;\\">post_test_b</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"630.6666666666666\\" y=\\"75.0000000000002\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: right;\\">post_test_c</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"369.3333333333333\\" y=\\"215.99999999999977\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: left;\\">staging_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"369.3333333333333\\" y=\\"298.99999999999994\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: left;\\">staging_b</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"630.6666666666666\\" y=\\"216.00000000000009\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: right;\\">canary_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"25px\\" width=\\"84\\" x=\\"630.6666666666666\\" y=\\"357\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 25px; text-align: right;\\">canary_c</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58px\\" width=\\"84\\" x=\\"908\\" y=\\"241.18907251056908\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58px; text-align: left;\\">production_a</div>
- </foreignObject>
- <foreignObject requiredFeatures=\\"http://www.w3.org/TR/SVG11/feature#Extensibility\\" height=\\"58px\\" width=\\"84\\" x=\\"908\\" y=\\"382\\" class=\\"gl-overflow-visible\\">
- <div class=\\"gl-display-flex gl-pointer-events-none gl-flex-direction-column gl-justify-content-center gl-overflow-wrap-break\\" style=\\"height: 58px; text-align: left;\\">production_d</div>
- </foreignObject>
- </g>
-</svg>"
-`;
diff --git a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js b/spec/frontend/pipelines/components/dag/dag_annotations_spec.js
deleted file mode 100644
index 124f02bcec7..00000000000
--- a/spec/frontend/pipelines/components/dag/dag_annotations_spec.js
+++ /dev/null
@@ -1,98 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import DagAnnotations from '~/pipelines/components/dag/dag_annotations.vue';
-import { singleNote, multiNote } from './mock_data';
-
-describe('The DAG annotations', () => {
- let wrapper;
-
- const getColorBlock = () => wrapper.find('[data-testid="dag-color-block"]');
- const getAllColorBlocks = () => wrapper.findAll('[data-testid="dag-color-block"]');
- const getTextBlock = () => wrapper.find('[data-testid="dag-note-text"]');
- const getAllTextBlocks = () => wrapper.findAll('[data-testid="dag-note-text"]');
- const getToggleButton = () => wrapper.findComponent(GlButton);
-
- const createComponent = (propsData = {}, method = shallowMount) => {
- wrapper = method(DagAnnotations, {
- propsData,
- data() {
- return {
- showList: true,
- };
- },
- });
- };
-
- describe('when there is one annotation', () => {
- const currentNote = singleNote['dag-link103'];
-
- beforeEach(() => {
- createComponent({ annotations: singleNote });
- });
-
- it('displays the color block', () => {
- expect(getColorBlock().exists()).toBe(true);
- });
-
- it('displays the text block', () => {
- expect(getTextBlock().exists()).toBe(true);
- expect(getTextBlock().text()).toBe(`${currentNote.source.name} → ${currentNote.target.name}`);
- });
-
- it('does not display the list toggle link', () => {
- expect(getToggleButton().exists()).toBe(false);
- });
- });
-
- describe('when there are multiple annoataions', () => {
- beforeEach(() => {
- createComponent({ annotations: multiNote });
- });
-
- it('displays a color block for each link', () => {
- expect(getAllColorBlocks().length).toBe(Object.keys(multiNote).length);
- });
-
- it('displays a text block for each link', () => {
- expect(getAllTextBlocks().length).toBe(Object.keys(multiNote).length);
-
- Object.values(multiNote).forEach((item, idx) => {
- expect(getAllTextBlocks().at(idx).text()).toBe(`${item.source.name} → ${item.target.name}`);
- });
- });
-
- it('displays the list toggle link', () => {
- expect(getToggleButton().exists()).toBe(true);
- expect(getToggleButton().text()).toBe('Hide list');
- });
- });
-
- describe('the list toggle', () => {
- beforeEach(() => {
- createComponent({ annotations: multiNote }, mount);
- });
-
- describe('clicking hide', () => {
- it('hides listed items and changes text to show', async () => {
- expect(getAllTextBlocks().length).toBe(Object.keys(multiNote).length);
- expect(getToggleButton().text()).toBe('Hide list');
- getToggleButton().trigger('click');
- await nextTick();
- expect(getAllTextBlocks().length).toBe(0);
- expect(getToggleButton().text()).toBe('Show list');
- });
- });
-
- describe('clicking show', () => {
- it('shows listed items and changes text to hide', async () => {
- getToggleButton().trigger('click');
- getToggleButton().trigger('click');
-
- await nextTick();
- expect(getAllTextBlocks().length).toBe(Object.keys(multiNote).length);
- expect(getToggleButton().text()).toBe('Hide list');
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/dag/dag_graph_spec.js b/spec/frontend/pipelines/components/dag/dag_graph_spec.js
deleted file mode 100644
index 6b46be3dd49..00000000000
--- a/spec/frontend/pipelines/components/dag/dag_graph_spec.js
+++ /dev/null
@@ -1,209 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { IS_HIGHLIGHTED, LINK_SELECTOR, NODE_SELECTOR } from '~/pipelines/components/dag/constants';
-import DagGraph from '~/pipelines/components/dag/dag_graph.vue';
-import { createSankey } from '~/pipelines/components/dag/drawing_utils';
-import { highlightIn, highlightOut } from '~/pipelines/components/dag/interactions';
-import { removeOrphanNodes } from '~/pipelines/components/parsing_utils';
-import { parsedData } from './mock_data';
-
-describe('The DAG graph', () => {
- let wrapper;
-
- const getGraph = () => wrapper.find('.dag-graph-container > svg');
- const getAllLinks = () => wrapper.findAll(`.${LINK_SELECTOR}`);
- const getAllNodes = () => wrapper.findAll(`.${NODE_SELECTOR}`);
- const getAllLabels = () => wrapper.findAll('foreignObject');
-
- const createComponent = (propsData = {}) => {
- if (wrapper?.destroy) {
- wrapper.destroy();
- }
-
- wrapper = shallowMount(DagGraph, {
- attachTo: document.body,
- propsData,
- data() {
- return {
- color: () => {},
- width: 0,
- height: 0,
- };
- },
- });
- };
-
- beforeEach(() => {
- createComponent({ graphData: parsedData });
- });
-
- describe('in the basic case', () => {
- beforeEach(() => {
- /*
- The graph uses random to offset links. To keep the snapshot consistent,
- we mock Math.random. Wheeeee!
- */
- const randomNumber = jest.spyOn(global.Math, 'random');
- randomNumber.mockImplementation(() => 0.2);
- createComponent({ graphData: parsedData });
- });
-
- it('renders the graph svg', () => {
- expect(getGraph().exists()).toBe(true);
- expect(getGraph().html()).toMatchSnapshot();
- });
- });
-
- describe('links', () => {
- it('renders the expected number of links', () => {
- expect(getAllLinks()).toHaveLength(parsedData.links.length);
- });
-
- it('renders the expected number of gradients', () => {
- expect(wrapper.findAll('linearGradient')).toHaveLength(parsedData.links.length);
- });
-
- it('renders the expected number of clip paths', () => {
- expect(wrapper.findAll('clipPath')).toHaveLength(parsedData.links.length);
- });
- });
-
- describe('nodes and labels', () => {
- const sankeyNodes = createSankey()(parsedData).nodes;
- const processedNodes = removeOrphanNodes(sankeyNodes);
-
- describe('nodes', () => {
- it('renders the expected number of nodes', () => {
- expect(getAllNodes()).toHaveLength(processedNodes.length);
- });
- });
-
- describe('labels', () => {
- it('renders the expected number of labels as foreignObjects', () => {
- expect(getAllLabels()).toHaveLength(processedNodes.length);
- });
-
- it('renders the title as text', () => {
- expect(getAllLabels().at(0).text()).toBe(parsedData.nodes[0].name);
- });
- });
- });
-
- describe('interactions', () => {
- const strokeOpacity = (opacity) => `stroke-opacity: ${opacity};`;
- const baseOpacity = () => wrapper.vm.$options.viewOptions.baseOpacity;
-
- describe('links', () => {
- const liveLink = () => getAllLinks().at(4);
- const otherLink = () => getAllLinks().at(1);
-
- describe('on hover', () => {
- it('sets the link opacity to baseOpacity and background links to 0.2', () => {
- liveLink().trigger('mouseover');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(highlightOut));
- });
-
- it('reverts the styles on mouseout', () => {
- liveLink().trigger('mouseover');
- liveLink().trigger('mouseout');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- });
- });
-
- describe('on click', () => {
- describe('toggles link liveness', () => {
- it('turns link on', () => {
- liveLink().trigger('click');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(highlightOut));
- });
-
- it('turns link off on second click', () => {
- liveLink().trigger('click');
- liveLink().trigger('click');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- });
- });
-
- it('the link remains live even after mouseout', () => {
- liveLink().trigger('click');
- liveLink().trigger('mouseout');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(highlightOut));
- });
-
- it('preserves state when multiple links are toggled on and off', () => {
- const anotherLiveLink = () => getAllLinks().at(2);
-
- liveLink().trigger('click');
- anotherLiveLink().trigger('click');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(anotherLiveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(highlightOut));
-
- anotherLiveLink().trigger('click');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(highlightIn));
- expect(anotherLiveLink().attributes('style')).toBe(strokeOpacity(highlightOut));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(highlightOut));
-
- liveLink().trigger('click');
- expect(liveLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- expect(anotherLiveLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- expect(otherLink().attributes('style')).toBe(strokeOpacity(baseOpacity()));
- });
- });
- });
-
- describe('nodes', () => {
- const liveNode = () => getAllNodes().at(10);
- const anotherLiveNode = () => getAllNodes().at(5);
- const nodesNotHighlighted = () => getAllNodes().filter((n) => !n.classes(IS_HIGHLIGHTED));
- const linksNotHighlighted = () => getAllLinks().filter((n) => !n.classes(IS_HIGHLIGHTED));
- const nodesHighlighted = () => getAllNodes().filter((n) => n.classes(IS_HIGHLIGHTED));
- const linksHighlighted = () => getAllLinks().filter((n) => n.classes(IS_HIGHLIGHTED));
-
- describe('on click', () => {
- it('highlights the clicked node and predecessors', () => {
- liveNode().trigger('click');
-
- expect(nodesNotHighlighted().length < getAllNodes().length).toBe(true);
- expect(linksNotHighlighted().length < getAllLinks().length).toBe(true);
-
- linksHighlighted().wrappers.forEach((link) => {
- expect(link.attributes('style')).toBe(strokeOpacity(highlightIn));
- });
-
- nodesHighlighted().wrappers.forEach((node) => {
- expect(node.attributes('stroke')).not.toBe('#f2f2f2');
- });
-
- linksNotHighlighted().wrappers.forEach((link) => {
- expect(link.attributes('style')).toBe(strokeOpacity(highlightOut));
- });
-
- nodesNotHighlighted().wrappers.forEach((node) => {
- expect(node.attributes('stroke')).toBe('#f2f2f2');
- });
- });
-
- it('toggles path off on second click', () => {
- liveNode().trigger('click');
- liveNode().trigger('click');
-
- expect(nodesNotHighlighted().length).toBe(getAllNodes().length);
- expect(linksNotHighlighted().length).toBe(getAllLinks().length);
- });
-
- it('preserves state when multiple nodes are toggled on and off', () => {
- anotherLiveNode().trigger('click');
- liveNode().trigger('click');
- anotherLiveNode().trigger('click');
- expect(nodesNotHighlighted().length < getAllNodes().length).toBe(true);
- expect(linksNotHighlighted().length < getAllLinks().length).toBe(true);
- });
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/dag/dag_spec.js b/spec/frontend/pipelines/components/dag/dag_spec.js
deleted file mode 100644
index 53719065611..00000000000
--- a/spec/frontend/pipelines/components/dag/dag_spec.js
+++ /dev/null
@@ -1,168 +0,0 @@
-import { GlAlert, GlEmptyState } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-import { ADD_NOTE, REMOVE_NOTE, REPLACE_NOTES } from '~/pipelines/components/dag/constants';
-import Dag from '~/pipelines/components/dag/dag.vue';
-import DagAnnotations from '~/pipelines/components/dag/dag_annotations.vue';
-import DagGraph from '~/pipelines/components/dag/dag_graph.vue';
-
-import { PARSE_FAILURE, UNSUPPORTED_DATA } from '~/pipelines/constants';
-import {
- mockParsedGraphQLNodes,
- tooSmallGraph,
- unparseableGraph,
- graphWithoutDependencies,
- singleNote,
- multiNote,
-} from './mock_data';
-
-describe('Pipeline DAG graph wrapper', () => {
- let wrapper;
- const getAlert = () => wrapper.findComponent(GlAlert);
- const getAllAlerts = () => wrapper.findAllComponents(GlAlert);
- const getGraph = () => wrapper.findComponent(DagGraph);
- const getNotes = () => wrapper.findComponent(DagAnnotations);
- const getErrorText = (type) => wrapper.vm.$options.errorTexts[type];
- const getEmptyState = () => wrapper.findComponent(GlEmptyState);
-
- const createComponent = ({
- graphData = mockParsedGraphQLNodes,
- provideOverride = {},
- method = shallowMount,
- } = {}) => {
- wrapper = method(Dag, {
- provide: {
- pipelineProjectPath: 'root/abc-dag',
- pipelineIid: '1',
- emptySvgPath: '/my-svg',
- dagDocPath: '/my-doc',
- ...provideOverride,
- },
- data() {
- return {
- graphData,
- showFailureAlert: false,
- };
- },
- });
- };
-
- describe('when a query argument is undefined', () => {
- beforeEach(() => {
- createComponent({
- provideOverride: { pipelineProjectPath: undefined },
- graphData: null,
- });
- });
-
- it('does not render the graph', () => {
- expect(getGraph().exists()).toBe(false);
- });
-
- it('does not render the empty state', () => {
- expect(getEmptyState().exists()).toBe(false);
- });
- });
-
- describe('when all query variables are defined', () => {
- describe('but the parse fails', () => {
- beforeEach(() => {
- createComponent({
- graphData: unparseableGraph,
- });
- });
-
- it('shows the PARSE_FAILURE alert and not the graph', () => {
- expect(getAlert().exists()).toBe(true);
- expect(getAlert().text()).toBe(getErrorText(PARSE_FAILURE));
- expect(getGraph().exists()).toBe(false);
- });
-
- it('does not render the empty state', () => {
- expect(getEmptyState().exists()).toBe(false);
- });
- });
-
- describe('parse succeeds', () => {
- beforeEach(() => {
- createComponent({ method: mount });
- });
-
- it('shows the graph', () => {
- expect(getGraph().exists()).toBe(true);
- });
-
- it('does not render the empty state', () => {
- expect(getEmptyState().exists()).toBe(false);
- });
- });
-
- describe('parse succeeds, but the resulting graph is too small', () => {
- beforeEach(() => {
- createComponent({
- graphData: tooSmallGraph,
- });
- });
-
- it('shows the UNSUPPORTED_DATA alert and not the graph', () => {
- expect(getAlert().exists()).toBe(true);
- expect(getAlert().text()).toBe(getErrorText(UNSUPPORTED_DATA));
- expect(getGraph().exists()).toBe(false);
- });
-
- it('does not show the empty dag graph state', () => {
- expect(getEmptyState().exists()).toBe(false);
- });
- });
-
- describe('the returned data is empty', () => {
- beforeEach(() => {
- createComponent({
- method: mount,
- graphData: graphWithoutDependencies,
- });
- });
-
- it('does not render an error alert or the graph', () => {
- expect(getAllAlerts().length).toBe(0);
- expect(getGraph().exists()).toBe(false);
- });
-
- it('shows the empty dag graph state', () => {
- expect(getEmptyState().exists()).toBe(true);
- });
- });
- });
-
- describe('annotations', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('toggles on link mouseover and mouseout', async () => {
- const currentNote = singleNote['dag-link103'];
-
- expect(getNotes().exists()).toBe(false);
-
- getGraph().vm.$emit('update-annotation', { type: ADD_NOTE, data: currentNote });
- await nextTick();
- expect(getNotes().exists()).toBe(true);
-
- getGraph().vm.$emit('update-annotation', { type: REMOVE_NOTE, data: currentNote });
- await nextTick();
- expect(getNotes().exists()).toBe(false);
- });
-
- it('toggles on node and link click', async () => {
- expect(getNotes().exists()).toBe(false);
-
- getGraph().vm.$emit('update-annotation', { type: REPLACE_NOTES, data: multiNote });
- await nextTick();
- expect(getNotes().exists()).toBe(true);
-
- getGraph().vm.$emit('update-annotation', { type: REPLACE_NOTES, data: {} });
- await nextTick();
- expect(getNotes().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/dag/drawing_utils_spec.js b/spec/frontend/pipelines/components/dag/drawing_utils_spec.js
deleted file mode 100644
index 095ded01298..00000000000
--- a/spec/frontend/pipelines/components/dag/drawing_utils_spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { createSankey } from '~/pipelines/components/dag/drawing_utils';
-import { parseData } from '~/pipelines/components/parsing_utils';
-import { mockParsedGraphQLNodes } from './mock_data';
-
-describe('DAG visualization drawing utilities', () => {
- const parsed = parseData(mockParsedGraphQLNodes);
-
- const layoutSettings = {
- width: 200,
- height: 200,
- nodeWidth: 10,
- nodePadding: 20,
- paddingForLabels: 100,
- };
-
- const sankeyLayout = createSankey(layoutSettings)(parsed);
-
- describe('createSankey', () => {
- it('returns a nodes data structure with expected d3-added properties', () => {
- const exampleNode = sankeyLayout.nodes[0];
- expect(exampleNode).toHaveProperty('sourceLinks');
- expect(exampleNode).toHaveProperty('targetLinks');
- expect(exampleNode).toHaveProperty('depth');
- expect(exampleNode).toHaveProperty('layer');
- expect(exampleNode).toHaveProperty('x0');
- expect(exampleNode).toHaveProperty('x1');
- expect(exampleNode).toHaveProperty('y0');
- expect(exampleNode).toHaveProperty('y1');
- });
-
- it('returns a links data structure with expected d3-added properties', () => {
- const exampleLink = sankeyLayout.links[0];
- expect(exampleLink).toHaveProperty('source');
- expect(exampleLink).toHaveProperty('target');
- expect(exampleLink).toHaveProperty('width');
- expect(exampleLink).toHaveProperty('y0');
- expect(exampleLink).toHaveProperty('y1');
- });
-
- describe('data structure integrity', () => {
- const newObject = { name: 'bad-actor' };
-
- beforeEach(() => {
- sankeyLayout.nodes.unshift(newObject);
- });
-
- it('sankey does not propagate changes back to the original', () => {
- expect(sankeyLayout.nodes[0]).toBe(newObject);
- expect(parsed.nodes[0]).not.toBe(newObject);
- });
-
- afterEach(() => {
- sankeyLayout.nodes.shift();
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/dag/mock_data.js b/spec/frontend/pipelines/components/dag/mock_data.js
deleted file mode 100644
index f27e7cf3d6b..00000000000
--- a/spec/frontend/pipelines/components/dag/mock_data.js
+++ /dev/null
@@ -1,674 +0,0 @@
-export const tooSmallGraph = [
- {
- category: 'test',
- name: 'jest',
- size: 2,
- jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }],
- },
- {
- category: 'test',
- name: 'rspec',
- size: 1,
- jobs: [{ name: 'rspec', needs: ['frontend fixtures'] }],
- },
- {
- category: 'fixtures',
- name: 'frontend fixtures',
- size: 1,
- jobs: [{ name: 'frontend fixtures' }],
- },
- {
- category: 'un-needed',
- name: 'un-needed',
- size: 1,
- jobs: [{ name: 'un-needed' }],
- },
-];
-
-export const graphWithoutDependencies = [
- {
- category: 'test',
- name: 'jest',
- size: 2,
- jobs: [{ name: 'jest 1/2' }, { name: 'jest 2/2' }],
- },
- {
- category: 'test',
- name: 'rspec',
- size: 1,
- jobs: [{ name: 'rspec' }],
- },
- {
- category: 'fixtures',
- name: 'frontend fixtures',
- size: 1,
- jobs: [{ name: 'frontend fixtures' }],
- },
- {
- category: 'un-needed',
- name: 'un-needed',
- size: 1,
- jobs: [{ name: 'un-needed' }],
- },
-];
-
-export const unparseableGraph = [
- {
- name: 'test',
- groups: [
- {
- name: 'jest',
- size: 2,
- jobs: [{ name: 'jest 1/2', needs: ['frontend fixtures'] }, { name: 'jest 2/2' }],
- },
- {
- name: 'rspec',
- size: 1,
- jobs: [{ name: 'rspec', needs: ['frontend fixtures'] }],
- },
- ],
- },
- {
- name: 'un-needed',
- groups: [
- {
- name: 'un-needed',
- size: 1,
- jobs: [{ name: 'un-needed' }],
- },
- ],
- },
-];
-
-/*
- This represents data that has been parsed by the wrapper
-*/
-export const parsedData = {
- nodes: [
- {
- name: 'build_a',
- size: 1,
- jobs: [
- {
- name: 'build_a',
- },
- ],
- category: 'build',
- },
- {
- name: 'build_b',
- size: 1,
- jobs: [
- {
- name: 'build_b',
- },
- ],
- category: 'build',
- },
- {
- name: 'test_a',
- size: 1,
- jobs: [
- {
- name: 'test_a',
- needs: ['build_a'],
- },
- ],
- category: 'test',
- },
- {
- name: 'test_b',
- size: 1,
- jobs: [
- {
- name: 'test_b',
- },
- ],
- category: 'test',
- },
- {
- name: 'test_c',
- size: 1,
- jobs: [
- {
- name: 'test_c',
- },
- ],
- category: 'test',
- },
- {
- name: 'test_d',
- size: 1,
- jobs: [
- {
- name: 'test_d',
- },
- ],
- category: 'test',
- },
- {
- name: 'post_test_a',
- size: 1,
- jobs: [
- {
- name: 'post_test_a',
- },
- ],
- category: 'post-test',
- },
- {
- name: 'post_test_b',
- size: 1,
- jobs: [
- {
- name: 'post_test_b',
- },
- ],
- category: 'post-test',
- },
- {
- name: 'post_test_c',
- size: 1,
- jobs: [
- {
- name: 'post_test_c',
- needs: ['test_a', 'test_b'],
- },
- ],
- category: 'post-test',
- },
- {
- name: 'staging_a',
- size: 1,
- jobs: [
- {
- name: 'staging_a',
- needs: ['post_test_a'],
- },
- ],
- category: 'staging',
- },
- {
- name: 'staging_b',
- size: 1,
- jobs: [
- {
- name: 'staging_b',
- needs: ['post_test_b'],
- },
- ],
- category: 'staging',
- },
- {
- name: 'staging_c',
- size: 1,
- jobs: [
- {
- name: 'staging_c',
- },
- ],
- category: 'staging',
- },
- {
- name: 'staging_d',
- size: 1,
- jobs: [
- {
- name: 'staging_d',
- },
- ],
- category: 'staging',
- },
- {
- name: 'staging_e',
- size: 1,
- jobs: [
- {
- name: 'staging_e',
- },
- ],
- category: 'staging',
- },
- {
- name: 'canary_a',
- size: 1,
- jobs: [
- {
- name: 'canary_a',
- needs: ['staging_a', 'staging_b'],
- },
- ],
- category: 'canary',
- },
- {
- name: 'canary_b',
- size: 1,
- jobs: [
- {
- name: 'canary_b',
- },
- ],
- category: 'canary',
- },
- {
- name: 'canary_c',
- size: 1,
- jobs: [
- {
- name: 'canary_c',
- needs: ['staging_b'],
- },
- ],
- category: 'canary',
- },
- {
- name: 'production_a',
- size: 1,
- jobs: [
- {
- name: 'production_a',
- needs: ['canary_a'],
- },
- ],
- category: 'production',
- },
- {
- name: 'production_b',
- size: 1,
- jobs: [
- {
- name: 'production_b',
- },
- ],
- category: 'production',
- },
- {
- name: 'production_c',
- size: 1,
- jobs: [
- {
- name: 'production_c',
- },
- ],
- category: 'production',
- },
- {
- name: 'production_d',
- size: 1,
- jobs: [
- {
- name: 'production_d',
- needs: ['canary_c'],
- },
- ],
- category: 'production',
- },
- ],
- links: [
- {
- source: 'build_a',
- target: 'test_a',
- value: 10,
- },
- {
- source: 'test_a',
- target: 'post_test_c',
- value: 10,
- },
- {
- source: 'test_b',
- target: 'post_test_c',
- value: 10,
- },
- {
- source: 'post_test_a',
- target: 'staging_a',
- value: 10,
- },
- {
- source: 'post_test_b',
- target: 'staging_b',
- value: 10,
- },
- {
- source: 'staging_a',
- target: 'canary_a',
- value: 10,
- },
- {
- source: 'staging_b',
- target: 'canary_a',
- value: 10,
- },
- {
- source: 'staging_b',
- target: 'canary_c',
- value: 10,
- },
- {
- source: 'canary_a',
- target: 'production_a',
- value: 10,
- },
- {
- source: 'canary_c',
- target: 'production_d',
- value: 10,
- },
- ],
-};
-
-export const singleNote = {
- 'dag-link103': {
- uid: 'dag-link103',
- source: {
- name: 'canary_a',
- color: '#b31756',
- },
- target: {
- name: 'production_a',
- color: '#b24800',
- },
- },
-};
-
-export const multiNote = {
- ...singleNote,
- 'dag-link104': {
- uid: 'dag-link104',
- source: {
- name: 'build_a',
- color: '#e17223',
- },
- target: {
- name: 'test_c',
- color: '#006887',
- },
- },
- 'dag-link105': {
- uid: 'dag-link105',
- source: {
- name: 'test_c',
- color: '#006887',
- },
- target: {
- name: 'post_test_c',
- color: '#3547de',
- },
- },
-};
-
-export const missingJob = 'missing_job';
-
-/*
- It is important that the base include parallel jobs
- as well as non-parallel jobs with spaces in the name to prevent
- us relying on spaces as an indicator.
-*/
-
-export const mockParsedGraphQLNodes = [
- {
- category: 'build',
- name: 'build_a',
- size: 1,
- jobs: [
- {
- name: 'build_a',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'build',
- name: 'build_b',
- size: 1,
- jobs: [
- {
- name: 'build_b',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'test',
- name: 'test_a',
- size: 1,
- jobs: [
- {
- name: 'test_a',
- needs: ['build_a'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'test',
- name: 'test_b',
- size: 1,
- jobs: [
- {
- name: 'test_b',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'test',
- name: 'test_c',
- size: 1,
- jobs: [
- {
- name: 'test_c',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'test',
- name: 'test_d',
- size: 1,
- jobs: [
- {
- name: 'test_d',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'post-test',
- name: 'post_test_a',
- size: 1,
- jobs: [
- {
- name: 'post_test_a',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'post-test',
- name: 'post_test_b',
- size: 1,
- jobs: [
- {
- name: 'post_test_b',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'post-test',
- name: 'post_test_c',
- size: 1,
- jobs: [
- {
- name: 'post_test_c',
- needs: ['test_b', 'test_a'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'staging',
- name: 'staging_a',
- size: 1,
- jobs: [
- {
- name: 'staging_a',
- needs: ['post_test_a'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'staging',
- name: 'staging_b',
- size: 1,
- jobs: [
- {
- name: 'staging_b',
- needs: ['post_test_b'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'staging',
- name: 'staging_c',
- size: 1,
- jobs: [
- {
- name: 'staging_c',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'staging',
- name: 'staging_d',
- size: 1,
- jobs: [
- {
- name: 'staging_d',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'staging',
- name: 'staging_e',
- size: 1,
- jobs: [
- {
- name: 'staging_e',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'canary',
- name: 'canary_a',
- size: 1,
- jobs: [
- {
- name: 'canary_a',
- needs: ['staging_b', 'staging_a'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'canary',
- name: 'canary_b',
- size: 1,
- jobs: [
- {
- name: 'canary_b',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'canary',
- name: 'canary_c',
- size: 1,
- jobs: [
- {
- name: 'canary_c',
- needs: ['staging_b'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'production',
- name: 'production_a',
- size: 1,
- jobs: [
- {
- name: 'production_a',
- needs: ['canary_a'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'production',
- name: 'production_b',
- size: 1,
- jobs: [
- {
- name: 'production_b',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'production',
- name: 'production_c',
- size: 1,
- jobs: [
- {
- name: 'production_c',
- needs: [],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'production',
- name: 'production_d',
- size: 1,
- jobs: [
- {
- name: 'production_d',
- needs: ['canary_c'],
- },
- ],
- __typename: 'CiGroup',
- },
- {
- category: 'production',
- name: 'production_e',
- size: 1,
- jobs: [
- {
- name: 'production_e',
- needs: [missingJob],
- },
- ],
- __typename: 'CiGroup',
- },
-];
diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js
deleted file mode 100644
index 6a2453704db..00000000000
--- a/spec/frontend/pipelines/components/jobs/failed_jobs_app_spec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/alert';
-import FailedJobsApp from '~/pipelines/components/jobs/failed_jobs_app.vue';
-import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue';
-import GetFailedJobsQuery from '~/pipelines/graphql/queries/get_failed_jobs.query.graphql';
-import { mockFailedJobsQueryResponse } from '../../mock_data';
-
-Vue.use(VueApollo);
-
-jest.mock('~/alert');
-
-describe('Failed Jobs App', () => {
- let wrapper;
- let resolverSpy;
-
- const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
- const findJobsTable = () => wrapper.findComponent(FailedJobsTable);
-
- const createMockApolloProvider = (resolver) => {
- const requestHandlers = [[GetFailedJobsQuery, resolver]];
-
- return createMockApollo(requestHandlers);
- };
-
- const createComponent = (resolver) => {
- wrapper = shallowMount(FailedJobsApp, {
- provide: {
- fullPath: 'root/ci-project',
- pipelineIid: 1,
- },
- apolloProvider: createMockApolloProvider(resolver),
- });
- };
-
- beforeEach(() => {
- resolverSpy = jest.fn().mockResolvedValue(mockFailedJobsQueryResponse);
- });
-
- describe('loading spinner', () => {
- it('displays loading spinner when fetching failed jobs', () => {
- createComponent(resolverSpy);
-
- expect(findLoadingSpinner().exists()).toBe(true);
- });
-
- it('hides loading spinner after the failed jobs have been fetched', async () => {
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- expect(findLoadingSpinner().exists()).toBe(false);
- });
- });
-
- it('displays the failed jobs table', async () => {
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- expect(findJobsTable().exists()).toBe(true);
- expect(createAlert).not.toHaveBeenCalled();
- });
-
- it('handles query fetch error correctly', async () => {
- resolverSpy = jest.fn().mockRejectedValue(new Error('GraphQL error'));
-
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalledWith({
- message: 'There was a problem fetching the failed jobs.',
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js b/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js
deleted file mode 100644
index 99a178120cc..00000000000
--- a/spec/frontend/pipelines/components/jobs/failed_jobs_table_spec.js
+++ /dev/null
@@ -1,141 +0,0 @@
-import { GlButton, GlLink, GlTableLite } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { createAlert } from '~/alert';
-import { redirectTo } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
-import FailedJobsTable from '~/pipelines/components/jobs/failed_jobs_table.vue';
-import RetryFailedJobMutation from '~/pipelines/graphql/mutations/retry_failed_job.mutation.graphql';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-import {
- successRetryMutationResponse,
- failedRetryMutationResponse,
- mockFailedJobsData,
- mockFailedJobsDataNoPermission,
-} from '../../mock_data';
-
-jest.mock('~/alert');
-jest.mock('~/lib/utils/url_utility');
-
-Vue.use(VueApollo);
-
-describe('Failed Jobs Table', () => {
- let wrapper;
-
- const successRetryMutationHandler = jest.fn().mockResolvedValue(successRetryMutationResponse);
- const failedRetryMutationHandler = jest.fn().mockResolvedValue(failedRetryMutationResponse);
-
- const findJobsTable = () => wrapper.findComponent(GlTableLite);
- const findRetryButton = () => wrapper.findComponent(GlButton);
- const findJobLink = () => wrapper.findComponent(GlLink);
- const findJobLog = () => wrapper.findByTestId('job-log');
- const findSummary = (index) => wrapper.findAllByTestId('job-trace-summary').at(index);
- const findFirstFailureMessage = () => wrapper.findAllByTestId('job-failure-message').at(0);
-
- const createMockApolloProvider = (resolver) => {
- const requestHandlers = [[RetryFailedJobMutation, resolver]];
- return createMockApollo(requestHandlers);
- };
-
- const createComponent = (resolver, failedJobsData = mockFailedJobsData) => {
- wrapper = mountExtended(FailedJobsTable, {
- propsData: {
- failedJobs: failedJobsData,
- },
- apolloProvider: createMockApolloProvider(resolver),
- });
- };
-
- it('displays the failed jobs table', () => {
- createComponent();
-
- expect(findJobsTable().exists()).toBe(true);
- });
-
- it('displays failed job summary', () => {
- createComponent();
-
- expect(findSummary(0).text()).toBe('Html Summary');
- });
-
- it('displays no job log when no trace', () => {
- createComponent();
-
- expect(findSummary(1).text()).toBe('No job log');
- });
-
- it('displays failure reason', () => {
- createComponent();
-
- expect(findFirstFailureMessage().text()).toBe('Job failed');
- });
-
- it('calls the retry failed job mutation and tracks the click', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- createComponent(successRetryMutationHandler);
-
- findRetryButton().trigger('click');
-
- expect(successRetryMutationHandler).toHaveBeenCalledWith({
- id: mockFailedJobsData[0].id,
- });
- expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry', {
- label: TRACKING_CATEGORIES.failed,
- });
-
- unmockTracking();
- });
-
- it('redirects to the new job after the mutation', async () => {
- const {
- data: {
- jobRetry: { job },
- },
- } = successRetryMutationResponse;
-
- createComponent(successRetryMutationHandler);
-
- findRetryButton().trigger('click');
-
- await waitForPromises();
-
- expect(redirectTo).toHaveBeenCalledWith(job.detailedStatus.detailsPath); // eslint-disable-line import/no-deprecated
- });
-
- it('shows error message if the retry failed job mutation fails', async () => {
- createComponent(failedRetryMutationHandler);
-
- findRetryButton().trigger('click');
-
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalledWith({
- message: 'There was a problem retrying the failed job.',
- });
- });
-
- it('hides the job log and retry button if a user does not have permission', () => {
- createComponent([[]], mockFailedJobsDataNoPermission);
-
- expect(findJobLog().exists()).toBe(false);
- expect(findRetryButton().exists()).toBe(false);
- });
-
- it('displays the job log and retry button if a user has permission', () => {
- createComponent();
-
- expect(findJobLog().exists()).toBe(true);
- expect(findRetryButton().exists()).toBe(true);
- });
-
- it('job name links to the correct job', () => {
- createComponent();
-
- expect(findJobLink().attributes('href')).toBe(mockFailedJobsData[0].detailedStatus.detailsPath);
- });
-});
diff --git a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js b/spec/frontend/pipelines/components/jobs/jobs_app_spec.js
deleted file mode 100644
index 39475788fe2..00000000000
--- a/spec/frontend/pipelines/components/jobs/jobs_app_spec.js
+++ /dev/null
@@ -1,127 +0,0 @@
-import { GlIntersectionObserver, GlSkeletonLoader, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/alert';
-import JobsApp from '~/pipelines/components/jobs/jobs_app.vue';
-import JobsTable from '~/jobs/components/table/jobs_table.vue';
-import getPipelineJobsQuery from '~/pipelines/graphql/queries/get_pipeline_jobs.query.graphql';
-import { mockPipelineJobsQueryResponse } from '../../mock_data';
-
-Vue.use(VueApollo);
-
-jest.mock('~/alert');
-
-describe('Jobs app', () => {
- let wrapper;
- let resolverSpy;
-
- const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
- const findLoadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
- const findJobsTable = () => wrapper.findComponent(JobsTable);
-
- const triggerInfiniteScroll = () =>
- wrapper.findComponent(GlIntersectionObserver).vm.$emit('appear');
-
- const createMockApolloProvider = (resolver) => {
- const requestHandlers = [[getPipelineJobsQuery, resolver]];
-
- return createMockApollo(requestHandlers);
- };
-
- const createComponent = (resolver) => {
- wrapper = shallowMount(JobsApp, {
- provide: {
- projectPath: 'root/ci-project',
- pipelineIid: 1,
- },
- apolloProvider: createMockApolloProvider(resolver),
- });
- };
-
- beforeEach(() => {
- resolverSpy = jest.fn().mockResolvedValue(mockPipelineJobsQueryResponse);
- });
-
- describe('loading spinner', () => {
- const setup = async () => {
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- triggerInfiniteScroll();
- };
-
- it('displays loading spinner when fetching more jobs', async () => {
- await setup();
-
- expect(findLoadingSpinner().exists()).toBe(true);
- expect(findSkeletonLoader().exists()).toBe(false);
- });
-
- it('hides loading spinner after jobs have been fetched', async () => {
- await setup();
- await waitForPromises();
-
- expect(findLoadingSpinner().exists()).toBe(false);
- expect(findSkeletonLoader().exists()).toBe(false);
- });
- });
-
- it('displays the skeleton loader', () => {
- createComponent(resolverSpy);
-
- expect(findSkeletonLoader().exists()).toBe(true);
- expect(findJobsTable().exists()).toBe(false);
- });
-
- it('displays the jobs table', async () => {
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- expect(findJobsTable().exists()).toBe(true);
- expect(findSkeletonLoader().exists()).toBe(false);
- expect(createAlert).not.toHaveBeenCalled();
- });
-
- it('handles job fetch error correctly', async () => {
- resolverSpy = jest.fn().mockRejectedValue(new Error('GraphQL error'));
-
- createComponent(resolverSpy);
-
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalledWith({
- message: 'An error occurred while fetching the pipelines jobs.',
- });
- });
-
- it('handles infinite scrolling by calling fetchMore', async () => {
- createComponent(resolverSpy);
- await waitForPromises();
-
- triggerInfiniteScroll();
- await waitForPromises();
-
- expect(resolverSpy).toHaveBeenCalledWith({
- after: 'eyJpZCI6Ijg0NyJ9',
- fullPath: 'root/ci-project',
- iid: 1,
- });
- });
-
- it('does not display skeleton loader again after fetchMore', async () => {
- createComponent(resolverSpy);
-
- expect(findSkeletonLoader().exists()).toBe(true);
- await waitForPromises();
-
- triggerInfiniteScroll();
- await waitForPromises();
-
- expect(findSkeletonLoader().exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/job_item_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/job_item_spec.js
deleted file mode 100644
index b89f27e5c05..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/job_item_spec.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import JobItem from '~/pipelines/components/pipeline_mini_graph/job_item.vue';
-
-describe('JobItem', () => {
- let wrapper;
-
- const defaultProps = {
- job: { id: '3' },
- };
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(JobItem, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- describe('when mounted', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the received HTML', () => {
- expect(wrapper.html()).toContain(defaultProps.job.id);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph_spec.js
deleted file mode 100644
index 6661bb079d2..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph_spec.js
+++ /dev/null
@@ -1,122 +0,0 @@
-import { mount } from '@vue/test-utils';
-import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
-import LegacyPipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
-import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
-import mockLinkedPipelines from './linked_pipelines_mock_data';
-
-const mockStages = pipelines[0].details.stages;
-
-describe('Legacy Pipeline Mini Graph', () => {
- let wrapper;
-
- const findLegacyPipelineMiniGraph = () => wrapper.findComponent(LegacyPipelineMiniGraph);
- const findPipelineStages = () => wrapper.findComponent(PipelineStages);
-
- const findLinkedPipelineUpstream = () =>
- wrapper.findComponent('[data-testid="pipeline-mini-graph-upstream"]');
- const findLinkedPipelineDownstream = () =>
- wrapper.findComponent('[data-testid="pipeline-mini-graph-downstream"]');
- const findDownstreamArrowIcon = () => wrapper.find('[data-testid="downstream-arrow-icon"]');
- const findUpstreamArrowIcon = () => wrapper.find('[data-testid="upstream-arrow-icon"]');
-
- const createComponent = (props = {}) => {
- wrapper = mount(LegacyPipelineMiniGraph, {
- propsData: {
- stages: mockStages,
- ...props,
- },
- });
- };
-
- describe('rendered state without upstream or downstream pipelines', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the pipeline stages', () => {
- expect(findPipelineStages().exists()).toBe(true);
- });
-
- it('should have the correct props', () => {
- expect(findLegacyPipelineMiniGraph().props()).toMatchObject({
- downstreamPipelines: [],
- isMergeTrain: false,
- pipelinePath: '',
- stages: expect.any(Array),
- updateDropdown: false,
- upstreamPipeline: undefined,
- });
- });
-
- it('should have no linked pipelines', () => {
- expect(findLinkedPipelineDownstream().exists()).toBe(false);
- expect(findLinkedPipelineUpstream().exists()).toBe(false);
- });
-
- it('should not render arrow icons', () => {
- expect(findUpstreamArrowIcon().exists()).toBe(false);
- expect(findDownstreamArrowIcon().exists()).toBe(false);
- });
- });
-
- describe('rendered state with upstream pipeline', () => {
- beforeEach(() => {
- createComponent({
- upstreamPipeline: mockLinkedPipelines.triggered_by,
- });
- });
-
- it('should have the correct props', () => {
- expect(findLegacyPipelineMiniGraph().props()).toMatchObject({
- downstreamPipelines: [],
- isMergeTrain: false,
- pipelinePath: '',
- stages: expect.any(Array),
- updateDropdown: false,
- upstreamPipeline: expect.any(Object),
- });
- });
-
- it('should render the upstream linked pipelines mini list only', () => {
- expect(findLinkedPipelineUpstream().exists()).toBe(true);
- expect(findLinkedPipelineDownstream().exists()).toBe(false);
- });
-
- it('should render an upstream arrow icon only', () => {
- expect(findDownstreamArrowIcon().exists()).toBe(false);
- expect(findUpstreamArrowIcon().exists()).toBe(true);
- expect(findUpstreamArrowIcon().props('name')).toBe('long-arrow');
- });
- });
-
- describe('rendered state with downstream pipelines', () => {
- beforeEach(() => {
- createComponent({
- downstreamPipelines: mockLinkedPipelines.triggered,
- pipelinePath: 'my/pipeline/path',
- });
- });
-
- it('should have the correct props', () => {
- expect(findLegacyPipelineMiniGraph().props()).toMatchObject({
- downstreamPipelines: expect.any(Array),
- isMergeTrain: false,
- pipelinePath: 'my/pipeline/path',
- stages: expect.any(Array),
- updateDropdown: false,
- upstreamPipeline: undefined,
- });
- });
-
- it('should render the downstream linked pipelines mini list only', () => {
- expect(findLinkedPipelineDownstream().exists()).toBe(true);
- expect(findLinkedPipelineUpstream().exists()).toBe(false);
- });
-
- it('should render a downstream arrow icon only', () => {
- expect(findUpstreamArrowIcon().exists()).toBe(false);
- expect(findDownstreamArrowIcon().exists()).toBe(true);
- expect(findDownstreamArrowIcon().props('name')).toBe('long-arrow');
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage_spec.js
deleted file mode 100644
index 3697eaeea1a..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage_spec.js
+++ /dev/null
@@ -1,247 +0,0 @@
-import { GlDropdown } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import LegacyPipelineStage from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage.vue';
-import eventHub from '~/pipelines/event_hub';
-import waitForPromises from 'helpers/wait_for_promises';
-import { stageReply } from '../../mock_data';
-
-const dropdownPath = 'path.json';
-
-describe('Pipelines stage component', () => {
- let wrapper;
- let mock;
- let glTooltipDirectiveMock;
-
- const createComponent = (props = {}) => {
- glTooltipDirectiveMock = jest.fn();
- wrapper = mount(LegacyPipelineStage, {
- attachTo: document.body,
- directives: {
- GlTooltip: glTooltipDirectiveMock,
- },
- propsData: {
- stage: {
- status: {
- group: 'success',
- icon: 'status_success',
- title: 'success',
- },
- dropdown_path: dropdownPath,
- },
- updateDropdown: false,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- jest.spyOn(eventHub, '$emit');
- });
-
- afterEach(() => {
- eventHub.$emit.mockRestore();
- mock.restore();
- // eslint-disable-next-line @gitlab/vtu-no-explicit-wrapper-destroy
- wrapper.destroy();
- });
-
- const findCiActionBtn = () => wrapper.find('.js-ci-action');
- const findCiIcon = () => wrapper.findComponent(CiIcon);
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findDropdownToggle = () => wrapper.find('button.dropdown-toggle');
- const findDropdownMenu = () =>
- wrapper.find('[data-testid="mini-pipeline-graph-dropdown-menu-list"]');
- const findDropdownMenuTitle = () =>
- wrapper.find('[data-testid="pipeline-stage-dropdown-menu-title"]');
- const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]');
- const findLoadingState = () => wrapper.find('[data-testid="pipeline-stage-loading-state"]');
-
- const openStageDropdown = async () => {
- await findDropdownToggle().trigger('click');
- await waitForPromises();
- await nextTick();
- };
-
- describe('loading state', () => {
- beforeEach(async () => {
- createComponent({ updateDropdown: true });
-
- mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
-
- await openStageDropdown();
- });
-
- it('displays loading state while jobs are being fetched', async () => {
- jest.runOnlyPendingTimers();
- await nextTick();
-
- expect(findLoadingState().exists()).toBe(true);
- expect(findLoadingState().text()).toBe(LegacyPipelineStage.i18n.loadingText);
- });
-
- it('does not display loading state after jobs have been fetched', async () => {
- await waitForPromises();
-
- expect(findLoadingState().exists()).toBe(false);
- });
- });
-
- describe('default appearance', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('sets up the tooltip to not have a show delay animation', () => {
- expect(glTooltipDirectiveMock.mock.calls[0][1].modifiers.ds0).toBe(true);
- });
-
- it('renders a dropdown with the status icon', () => {
- expect(findDropdown().exists()).toBe(true);
- expect(findDropdownToggle().exists()).toBe(true);
- expect(findCiIcon().exists()).toBe(true);
- });
-
- it('renders a borderless ci-icon', () => {
- expect(findCiIcon().exists()).toBe(true);
- expect(findCiIcon().props('isBorderless')).toBe(true);
- expect(findCiIcon().classes('borderless')).toBe(true);
- });
-
- it('renders a ci-icon with a custom border class', () => {
- expect(findCiIcon().exists()).toBe(true);
- expect(findCiIcon().classes('gl-border')).toBe(true);
- });
- });
-
- describe('when user opens dropdown and stage request is successful', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
- createComponent();
-
- await openStageDropdown();
- await jest.runAllTimers();
- await axios.waitForAll();
- });
-
- it('renders the received data and emits the correct events', () => {
- expect(findDropdownMenu().text()).toContain(stageReply.latest_statuses[0].name);
- expect(findDropdownMenuTitle().text()).toContain(stageReply.name);
- expect(eventHub.$emit).toHaveBeenCalledWith('clickedDropdown');
- expect(wrapper.emitted('miniGraphStageClick')).toEqual([[]]);
- });
-
- it('refreshes when updateDropdown is set to true', async () => {
- expect(mock.history.get).toHaveLength(1);
-
- wrapper.setProps({ updateDropdown: true });
- await axios.waitForAll();
-
- expect(mock.history.get).toHaveLength(2);
- });
- });
-
- describe('when user opens dropdown and stage request fails', () => {
- it('should close the dropdown', async () => {
- mock.onGet(dropdownPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
- createComponent();
-
- await openStageDropdown();
- await axios.waitForAll();
- await waitForPromises();
-
- expect(findDropdown().classes('show')).toBe(false);
- });
- });
-
- describe('update endpoint correctly', () => {
- beforeEach(async () => {
- const copyStage = { ...stageReply };
- copyStage.latest_statuses[0].name = 'this is the updated content';
- mock.onGet('bar.json').reply(HTTP_STATUS_OK, copyStage);
- createComponent({
- stage: {
- status: {
- group: 'running',
- icon: 'status_running',
- title: 'running',
- },
- dropdown_path: 'bar.json',
- },
- });
- await axios.waitForAll();
- });
-
- it('should update the stage to request the new endpoint provided', async () => {
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- expect(findDropdownMenu().text()).toContain('this is the updated content');
- });
- });
-
- describe('job update in dropdown', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
- mock.onPost(`${stageReply.latest_statuses[0].status.action.path}.json`).reply(HTTP_STATUS_OK);
-
- createComponent();
- await waitForPromises();
- await nextTick();
- });
-
- const clickCiAction = async () => {
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- await findCiActionBtn().trigger('click');
- };
-
- it('keeps dropdown open when job item action is clicked', async () => {
- await clickCiAction();
- await waitForPromises();
-
- expect(findDropdown().classes('show')).toBe(true);
- });
- });
-
- describe('With merge trains enabled', () => {
- it('shows a warning on the dropdown', async () => {
- mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
- createComponent({
- isMergeTrain: true,
- });
-
- await openStageDropdown();
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- const warning = findMergeTrainWarning();
-
- expect(warning.text()).toBe('Merge train pipeline jobs can not be retried');
- });
- });
-
- describe('With merge trains disabled', () => {
- beforeEach(async () => {
- mock.onGet(dropdownPath).reply(HTTP_STATUS_OK, stageReply);
- createComponent();
-
- await openStageDropdown();
- await axios.waitForAll();
- });
-
- it('does not show a warning on the dropdown', () => {
- const warning = findMergeTrainWarning();
-
- expect(warning.exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js
deleted file mode 100644
index a4ecb9041c9..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list_spec.js
+++ /dev/null
@@ -1,166 +0,0 @@
-import { mount } from '@vue/test-utils';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import LinkedPipelinesMiniList from '~/pipelines/components/pipeline_mini_graph/linked_pipelines_mini_list.vue';
-import mockData from './linked_pipelines_mock_data';
-
-describe('Linked pipeline mini list', () => {
- let wrapper;
-
- const findCiIcon = () => wrapper.findComponent(CiIcon);
- const findCiIcons = () => wrapper.findAllComponents(CiIcon);
- const findLinkedPipelineCounter = () => wrapper.find('[data-testid="linked-pipeline-counter"]');
- const findLinkedPipelineMiniItem = () =>
- wrapper.find('[data-testid="linked-pipeline-mini-item"]');
- const findLinkedPipelineMiniItems = () =>
- wrapper.findAll('[data-testid="linked-pipeline-mini-item"]');
- const findLinkedPipelineMiniList = () => wrapper.findComponent(LinkedPipelinesMiniList);
-
- const createComponent = (props = {}) => {
- wrapper = mount(LinkedPipelinesMiniList, {
- directives: {
- GlTooltip: createMockDirective('gl-tooltip'),
- },
- propsData: {
- ...props,
- },
- });
- };
-
- describe('when passed an upstream pipeline as prop', () => {
- beforeEach(() => {
- createComponent({
- triggeredBy: [mockData.triggered_by],
- });
- });
-
- it('should render one linked pipeline item', () => {
- expect(findLinkedPipelineMiniItem().exists()).toBe(true);
- });
-
- it('should render a linked pipeline with the correct href', () => {
- expect(findLinkedPipelineMiniItem().exists()).toBe(true);
-
- expect(findLinkedPipelineMiniItem().attributes('href')).toBe(
- '/gitlab-org/gitlab-foss/-/pipelines/129',
- );
- });
-
- it('should render one ci status icon', () => {
- expect(findCiIcon().exists()).toBe(true);
- });
-
- it('should render a borderless ci-icon', () => {
- expect(findCiIcon().exists()).toBe(true);
-
- expect(findCiIcon().props('isBorderless')).toBe(true);
- expect(findCiIcon().classes('borderless')).toBe(true);
- });
-
- it('should render a ci-icon with a custom border class', () => {
- expect(findCiIcon().exists()).toBe(true);
-
- expect(findCiIcon().classes('gl-border')).toBe(true);
- });
-
- it('should render the correct ci status icon', () => {
- expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
- });
-
- it('should have an activated tooltip', () => {
- expect(findLinkedPipelineMiniItem().exists()).toBe(true);
- const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
-
- expect(tooltip.value.title).toBe('GitLabCE - running');
- });
-
- it('should correctly set is-upstream', () => {
- expect(findLinkedPipelineMiniList().exists()).toBe(true);
-
- expect(findLinkedPipelineMiniList().classes('is-upstream')).toBe(true);
- });
-
- it('should correctly compute shouldRenderCounter', () => {
- expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(false);
- });
-
- it('should not render the pipeline counter', () => {
- expect(findLinkedPipelineCounter().exists()).toBe(false);
- });
- });
-
- describe('when passed downstream pipelines as props', () => {
- beforeEach(() => {
- createComponent({
- triggered: mockData.triggered,
- pipelinePath: 'my/pipeline/path',
- });
- });
-
- it('should render three linked pipeline items', () => {
- expect(findLinkedPipelineMiniItems().exists()).toBe(true);
- expect(findLinkedPipelineMiniItems().length).toBe(3);
- });
-
- it('should render three ci status icons', () => {
- expect(findCiIcons().exists()).toBe(true);
- expect(findCiIcons().length).toBe(3);
- });
-
- it('should render the correct ci status icon', () => {
- expect(findCiIcon().classes('ci-status-icon-running')).toBe(true);
- });
-
- it('should have an activated tooltip', () => {
- expect(findLinkedPipelineMiniItem().exists()).toBe(true);
- const tooltip = getBinding(findLinkedPipelineMiniItem().element, 'gl-tooltip');
-
- expect(tooltip.value.title).toBe('GitLabCE - running');
- });
-
- it('should correctly set is-downstream', () => {
- expect(findLinkedPipelineMiniList().exists()).toBe(true);
-
- expect(findLinkedPipelineMiniList().classes('is-downstream')).toBe(true);
- });
-
- it('should render a borderless ci-icon', () => {
- expect(findCiIcon().exists()).toBe(true);
-
- expect(findCiIcon().props('isBorderless')).toBe(true);
- expect(findCiIcon().classes('borderless')).toBe(true);
- });
-
- it('should render a ci-icon with a custom border class', () => {
- expect(findCiIcon().exists()).toBe(true);
-
- expect(findCiIcon().classes('gl-border')).toBe(true);
- });
-
- it('should render the pipeline counter', () => {
- expect(findLinkedPipelineCounter().exists()).toBe(true);
- });
-
- it('should correctly compute shouldRenderCounter', () => {
- expect(findLinkedPipelineMiniList().vm.shouldRenderCounter).toBe(true);
- });
-
- it('should correctly trim linkedPipelines', () => {
- expect(findLinkedPipelineMiniList().props('triggered').length).toBe(6);
- expect(findLinkedPipelineMiniList().vm.linkedPipelinesTrimmed.length).toBe(3);
- });
-
- it('should set the correct pipeline path', () => {
- expect(findLinkedPipelineCounter().exists()).toBe(true);
-
- expect(findLinkedPipelineCounter().attributes('href')).toBe('my/pipeline/path');
- });
-
- it('should render the correct counterTooltipText', () => {
- expect(findLinkedPipelineCounter().exists()).toBe(true);
- const tooltip = getBinding(findLinkedPipelineCounter().element, 'gl-tooltip');
-
- expect(tooltip.value.title).toBe(findLinkedPipelineMiniList().vm.counterTooltipText);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js b/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js
deleted file mode 100644
index 117c7f2ae52..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/linked_pipelines_mock_data.js
+++ /dev/null
@@ -1,407 +0,0 @@
-export default {
- triggered_by: {
- id: 129,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/129',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/129',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: '7-5-stable',
- path: '/gitlab-org/gitlab-foss/commits/7-5-stable',
- tag: false,
- branch: true,
- },
- commit: {
- id: '23433d4d8b20d7e45c103d0b6048faad38a130ab',
- short_id: '23433d4d',
- title: 'Version 7.5.0.rc1',
- created_at: '2014-11-17T15:44:14.000+01:00',
- parent_ids: ['30ac909f30f58d319b42ed1537664483894b18cd'],
- message: 'Version 7.5.0.rc1\n',
- author_name: 'Jacob Vosmaer',
- author_email: 'contact@jacobvosmaer.nl',
- authored_date: '2014-11-17T15:44:14.000+01:00',
- committer_name: 'Jacob Vosmaer',
- committer_email: 'contact@jacobvosmaer.nl',
- committed_date: '2014-11-17T15:44:14.000+01:00',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/e66d11c0eedf8c07b3b18fca46599807?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
- commit_path: '/gitlab-org/gitlab-foss/commit/23433d4d8b20d7e45c103d0b6048faad38a130ab',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/129/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/129/cancel',
- created_at: '2017-05-24T14:46:20.090Z',
- updated_at: '2017-05-24T14:46:29.906Z',
- },
- triggered: [
- {
- id: 132,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/132',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- short_id: 'b9d58c4c',
- title: 'getting user keys publically through http without any authentication, the github…',
- created_at: '2013-10-03T12:50:33.000+05:30',
- parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
- message:
- 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
- author_name: 'devaroop',
- author_email: 'devaroop123@yahoo.co.in',
- authored_date: '2013-10-02T20:39:29.000+05:30',
- committer_name: 'devaroop',
- committer_email: 'devaroop123@yahoo.co.in',
- committed_date: '2013-10-03T12:50:33.000+05:30',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
- created_at: '2017-05-24T14:46:24.644Z',
- updated_at: '2017-05-24T14:48:55.226Z',
- },
- {
- id: 133,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/133',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- short_id: 'b6bd4856',
- title: 'getting user keys publically through http without any authentication, the github…',
- created_at: '2013-10-02T20:39:29.000+05:30',
- parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
- message:
- 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
- author_name: 'devaroop',
- author_email: 'devaroop123@yahoo.co.in',
- authored_date: '2013-10-02T20:39:29.000+05:30',
- committer_name: 'devaroop',
- committer_email: 'devaroop123@yahoo.co.in',
- committed_date: '2013-10-02T20:39:29.000+05:30',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
- created_at: '2017-05-24T14:46:24.648Z',
- updated_at: '2017-05-24T14:48:59.673Z',
- },
- {
- id: 130,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/130',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- short_id: '6d7ced4a',
- title: 'Whitespace fixes to patch',
- created_at: '2013-10-08T13:53:22.000-05:00',
- parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
- message: 'Whitespace fixes to patch\n',
- author_name: 'Dale Hamel',
- author_email: 'dale.hamel@srvthe.net',
- authored_date: '2013-10-08T13:53:22.000-05:00',
- committer_name: 'Dale Hamel',
- committer_email: 'dale.hamel@invenia.ca',
- committed_date: '2013-10-08T13:53:22.000-05:00',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
- created_at: '2017-05-24T14:46:24.630Z',
- updated_at: '2017-05-24T14:49:45.091Z',
- },
- {
- id: 131,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/132',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/132',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- short_id: 'b9d58c4c',
- title: 'getting user keys publically through http without any authentication, the github…',
- created_at: '2013-10-03T12:50:33.000+05:30',
- parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
- message:
- 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n\nchangelog updated to include ssh key retrieval feature update\n',
- author_name: 'devaroop',
- author_email: 'devaroop123@yahoo.co.in',
- authored_date: '2013-10-02T20:39:29.000+05:30',
- committer_name: 'devaroop',
- committer_email: 'devaroop123@yahoo.co.in',
- committed_date: '2013-10-03T12:50:33.000+05:30',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- commit_path: '/gitlab-org/gitlab-foss/commit/b9d58c4cecd06be74c3cc32ccfb522b31544ab2e',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/132/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/132/cancel',
- created_at: '2017-05-24T14:46:24.644Z',
- updated_at: '2017-05-24T14:48:55.226Z',
- },
- {
- id: 134,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/133',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/133',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: 'b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- short_id: 'b6bd4856',
- title: 'getting user keys publically through http without any authentication, the github…',
- created_at: '2013-10-02T20:39:29.000+05:30',
- parent_ids: ['e219cf7246c6a0495e4507deaffeba11e79f13b8'],
- message:
- 'getting user keys publically through http without any authentication, the github way. E.g: http://github.com/devaroop.keys\n',
- author_name: 'devaroop',
- author_email: 'devaroop123@yahoo.co.in',
- authored_date: '2013-10-02T20:39:29.000+05:30',
- committer_name: 'devaroop',
- committer_email: 'devaroop123@yahoo.co.in',
- committed_date: '2013-10-02T20:39:29.000+05:30',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/35df4b155ec66a3127d53459941cf8a2?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- commit_path: '/gitlab-org/gitlab-foss/commit/b6bd4856a33df3d144be66c4ed1f1396009bb08b',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/133/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/133/cancel',
- created_at: '2017-05-24T14:46:24.648Z',
- updated_at: '2017-05-24T14:48:59.673Z',
- },
- {
- id: 135,
- active: true,
- path: '/gitlab-org/gitlab-foss/-/pipelines/130',
- project: {
- name: 'GitLabCE',
- },
- details: {
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- has_details: true,
- details_path: '/gitlab-org/gitlab-foss/-/pipelines/130',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- },
- flags: {
- latest: false,
- triggered: false,
- stuck: false,
- yaml_errors: false,
- retryable: true,
- cancelable: true,
- },
- ref: {
- name: 'crowd',
- path: '/gitlab-org/gitlab-foss/commits/crowd',
- tag: false,
- branch: true,
- },
- commit: {
- id: '6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- short_id: '6d7ced4a',
- title: 'Whitespace fixes to patch',
- created_at: '2013-10-08T13:53:22.000-05:00',
- parent_ids: ['1875141a963a4238bda29011d8f7105839485253'],
- message: 'Whitespace fixes to patch\n',
- author_name: 'Dale Hamel',
- author_email: 'dale.hamel@srvthe.net',
- authored_date: '2013-10-08T13:53:22.000-05:00',
- committer_name: 'Dale Hamel',
- committer_email: 'dale.hamel@invenia.ca',
- committed_date: '2013-10-08T13:53:22.000-05:00',
- author_gravatar_url:
- 'http://www.gravatar.com/avatar/cd08930e69fa5ad1a669206e7bafe476?s=80&d=identicon',
- commit_url:
- 'http://localhost:3000/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- commit_path: '/gitlab-org/gitlab-foss/commit/6d7ced4a2311eeff037c5575cca1868a6d3f586f',
- },
- retry_path: '/gitlab-org/gitlab-foss/-/pipelines/130/retry',
- cancel_path: '/gitlab-org/gitlab-foss/-/pipelines/130/cancel',
- created_at: '2017-05-24T14:46:24.630Z',
- updated_at: '2017-05-24T14:49:45.091Z',
- },
- ],
-};
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/mock_data.js b/spec/frontend/pipelines/components/pipeline_mini_graph/mock_data.js
deleted file mode 100644
index 1c13e9eb62b..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/mock_data.js
+++ /dev/null
@@ -1,150 +0,0 @@
-export const mockDownstreamPipelinesGraphql = ({ includeSourceJobRetried = true } = {}) => ({
- nodes: [
- {
- id: 'gid://gitlab/Ci::Pipeline/612',
- path: '/root/job-log-sections/-/pipelines/612',
- project: {
- id: 'gid://gitlab/Project/21',
- name: 'job-log-sections',
- __typename: 'Project',
- },
- detailedStatus: {
- id: 'success-612-612',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- sourceJob: {
- id: 'gid://gitlab/Ci::Bridge/532',
- retried: includeSourceJobRetried ? false : null,
- },
- __typename: 'Pipeline',
- },
- {
- id: 'gid://gitlab/Ci::Pipeline/611',
- path: '/root/job-log-sections/-/pipelines/611',
- project: {
- id: 'gid://gitlab/Project/21',
- name: 'job-log-sections',
- __typename: 'Project',
- },
- detailedStatus: {
- id: 'success-611-611',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- sourceJob: {
- id: 'gid://gitlab/Ci::Bridge/531',
- retried: includeSourceJobRetried ? true : null,
- },
- __typename: 'Pipeline',
- },
- {
- id: 'gid://gitlab/Ci::Pipeline/609',
- path: '/root/job-log-sections/-/pipelines/609',
- project: {
- id: 'gid://gitlab/Project/21',
- name: 'job-log-sections',
- __typename: 'Project',
- },
- detailedStatus: {
- id: 'success-609-609',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- sourceJob: {
- id: 'gid://gitlab/Ci::Bridge/530',
- retried: includeSourceJobRetried ? true : null,
- },
- __typename: 'Pipeline',
- },
- ],
- __typename: 'PipelineConnection',
-});
-
-const upstream = {
- id: 'gid://gitlab/Ci::Pipeline/610',
- path: '/root/trigger-downstream/-/pipelines/610',
- project: {
- id: 'gid://gitlab/Project/21',
- name: 'trigger-downstream',
- __typename: 'Project',
- },
- detailedStatus: {
- id: 'success-610-610',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- __typename: 'DetailedStatus',
- },
- __typename: 'Pipeline',
-};
-
-export const mockPipelineStagesQueryResponse = {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- pipeline: {
- id: 'gid://gitlab/Ci::Pipeline/320',
- stages: {
- nodes: [
- {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/409',
- name: 'build',
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'success-409-409',
- icon: 'status_success',
- group: 'success',
- },
- },
- ],
- },
- },
- },
- },
-};
-
-export const mockPipelineStatusResponse = {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- pipeline: {
- id: 'gid://gitlab/Ci::Pipeline/320',
- detailedStatus: {
- id: 'pending-320-320',
- detailsPath: '/root/ci-project/-/pipelines/320',
- icon: 'status_pending',
- group: 'pending',
- __typename: 'DetailedStatus',
- },
- __typename: 'Pipeline',
- },
- __typename: 'Project',
- },
- },
-};
-
-export const mockUpstreamDownstreamQueryResponse = {
- data: {
- project: {
- id: '1',
- pipeline: {
- id: 'pipeline-1',
- path: '/root/ci-project/-/pipelines/790',
- downstream: mockDownstreamPipelinesGraphql(),
- upstream,
- },
- __typename: 'Project',
- },
- },
-};
-
-export const linkedPipelinesFetchError = 'There was a problem fetching linked pipelines.';
-export const stagesFetchError = 'There was a problem fetching the pipeline stages.';
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
deleted file mode 100644
index b3e157f75f6..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_mini_graph_spec.js
+++ /dev/null
@@ -1,123 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { GlLoadingIcon } from '@gitlab/ui';
-
-import { createAlert } from '~/alert';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import createMockApollo from 'helpers/mock_apollo_helper';
-
-import getLinkedPipelinesQuery from '~/pipelines/graphql/queries/get_linked_pipelines.query.graphql';
-import getPipelineStagesQuery from '~/pipelines/graphql/queries/get_pipeline_stages.query.graphql';
-import LegacyPipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
-import PipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/pipeline_mini_graph.vue';
-import * as sharedGraphQlUtils from '~/graphql_shared/utils';
-
-import {
- linkedPipelinesFetchError,
- stagesFetchError,
- mockPipelineStagesQueryResponse,
- mockUpstreamDownstreamQueryResponse,
-} from './mock_data';
-
-Vue.use(VueApollo);
-jest.mock('~/alert');
-
-describe('PipelineMiniGraph', () => {
- let wrapper;
- let linkedPipelinesResponse;
- let pipelineStagesResponse;
-
- const fullPath = 'gitlab-org/gitlab';
- const iid = '315';
- const pipelineEtag = '/api/graphql:pipelines/id/315';
-
- const createComponent = ({
- pipelineStagesHandler = pipelineStagesResponse,
- linkedPipelinesHandler = linkedPipelinesResponse,
- } = {}) => {
- const handlers = [
- [getLinkedPipelinesQuery, linkedPipelinesHandler],
- [getPipelineStagesQuery, pipelineStagesHandler],
- ];
- const mockApollo = createMockApollo(handlers);
-
- wrapper = shallowMountExtended(PipelineMiniGraph, {
- propsData: {
- fullPath,
- iid,
- pipelineEtag,
- },
- apolloProvider: mockApollo,
- });
-
- return waitForPromises();
- };
-
- const findLegacyPipelineMiniGraph = () => wrapper.findComponent(LegacyPipelineMiniGraph);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- beforeEach(() => {
- linkedPipelinesResponse = jest.fn().mockResolvedValue(mockUpstreamDownstreamQueryResponse);
- pipelineStagesResponse = jest.fn().mockResolvedValue(mockPipelineStagesQueryResponse);
- });
-
- describe('when initial queries are loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows a loading icon and no mini graph', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findLegacyPipelineMiniGraph().exists()).toBe(false);
- });
- });
-
- describe('when queries have loaded', () => {
- it('does not show a loading icon', async () => {
- await createComponent();
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('renders the Pipeline Mini Graph', async () => {
- await createComponent();
-
- expect(findLegacyPipelineMiniGraph().exists()).toBe(true);
- });
-
- it('fires the queries', async () => {
- await createComponent();
-
- expect(linkedPipelinesResponse).toHaveBeenCalledWith({ iid, fullPath });
- expect(pipelineStagesResponse).toHaveBeenCalledWith({ iid, fullPath });
- });
- });
-
- describe('polling', () => {
- it('toggles query polling with visibility check', async () => {
- jest.spyOn(sharedGraphQlUtils, 'toggleQueryPollingByVisibility');
-
- createComponent();
-
- await waitForPromises();
-
- expect(sharedGraphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledTimes(2);
- });
- });
-
- describe('when pipeline queries are unsuccessful', () => {
- const failedHandler = jest.fn().mockRejectedValue(new Error('GraphQL error'));
- it.each`
- query | handlerName | errorMessage
- ${'pipeline stages'} | ${'pipelineStagesHandler'} | ${stagesFetchError}
- ${'linked pipelines'} | ${'linkedPipelinesHandler'} | ${linkedPipelinesFetchError}
- `('throws an error for the $query query', async ({ errorMessage, handlerName }) => {
- await createComponent({ [handlerName]: failedHandler });
-
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalledWith({ message: errorMessage });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
deleted file mode 100644
index 1989aad12b0..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stage_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import createMockApollo from 'helpers/mock_apollo_helper';
-
-import getPipelineStageQuery from '~/pipelines/graphql/queries/get_pipeline_stage.query.graphql';
-import PipelineStage from '~/pipelines/components/pipeline_mini_graph/pipeline_stage.vue';
-
-Vue.use(VueApollo);
-
-describe('PipelineStage', () => {
- let wrapper;
- let pipelineStageResponse;
-
- const defaultProps = {
- pipelineEtag: '/etag',
- stageId: '1',
- };
-
- const createComponent = ({ pipelineStageHandler = pipelineStageResponse } = {}) => {
- const handlers = [[getPipelineStageQuery, pipelineStageHandler]];
- const mockApollo = createMockApollo(handlers);
-
- wrapper = shallowMountExtended(PipelineStage, {
- propsData: {
- ...defaultProps,
- },
- apolloProvider: mockApollo,
- });
-
- return waitForPromises();
- };
-
- const findPipelineStage = () => wrapper.findComponent(PipelineStage);
-
- describe('when mounted', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders job item', () => {
- expect(findPipelineStage().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js b/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
deleted file mode 100644
index c212087b7e3..00000000000
--- a/spec/frontend/pipelines/components/pipeline_mini_graph/pipeline_stages_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { pipelines } from 'test_fixtures/pipelines/pipelines.json';
-import LegacyPipelineStage from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_stage.vue';
-import PipelineStages from '~/pipelines/components/pipeline_mini_graph/pipeline_stages.vue';
-
-const mockStages = pipelines[0].details.stages;
-
-describe('Pipeline Stages', () => {
- let wrapper;
-
- const findLegacyPipelineStages = () => wrapper.findAllComponents(LegacyPipelineStage);
- const findPipelineStagesAt = (i) => findLegacyPipelineStages().at(i);
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(PipelineStages, {
- propsData: {
- stages: mockStages,
- ...props,
- },
- });
- };
-
- it('renders stages', () => {
- createComponent();
-
- expect(findLegacyPipelineStages()).toHaveLength(mockStages.length);
- });
-
- it('does not fail when stages are empty', () => {
- createComponent({ stages: [] });
-
- expect(wrapper.exists()).toBe(true);
- expect(findLegacyPipelineStages()).toHaveLength(0);
- });
-
- it('update dropdown is false by default', () => {
- createComponent();
-
- expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(false);
- expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(false);
- });
-
- it('update dropdown is set to true', () => {
- createComponent({ updateDropdown: true });
-
- expect(findPipelineStagesAt(0).props('updateDropdown')).toBe(true);
- expect(findPipelineStagesAt(1).props('updateDropdown')).toBe(true);
- });
-
- it('is merge train is false by default', () => {
- createComponent();
-
- expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(false);
- expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(false);
- });
-
- it('is merge train is set to true', () => {
- createComponent({ isMergeTrain: true });
-
- expect(findPipelineStagesAt(0).props('isMergeTrain')).toBe(true);
- expect(findPipelineStagesAt(1).props('isMergeTrain')).toBe(true);
- });
-});
diff --git a/spec/frontend/pipelines/components/pipeline_tabs_spec.js b/spec/frontend/pipelines/components/pipeline_tabs_spec.js
deleted file mode 100644
index 0951e1ffb46..00000000000
--- a/spec/frontend/pipelines/components/pipeline_tabs_spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import { GlTab } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import PipelineTabs from '~/pipelines/components/pipeline_tabs.vue';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-
-describe('The Pipeline Tabs', () => {
- let wrapper;
- let trackingSpy;
-
- const $router = { push: jest.fn() };
-
- const findDagTab = () => wrapper.findByTestId('dag-tab');
- const findFailedJobsTab = () => wrapper.findByTestId('failed-jobs-tab');
- const findJobsTab = () => wrapper.findByTestId('jobs-tab');
- const findPipelineTab = () => wrapper.findByTestId('pipeline-tab');
- const findTestsTab = () => wrapper.findByTestId('tests-tab');
-
- const findFailedJobsBadge = () => wrapper.findByTestId('failed-builds-counter');
- const findJobsBadge = () => wrapper.findByTestId('builds-counter');
- const findTestsBadge = () => wrapper.findByTestId('tests-counter');
-
- const defaultProvide = {
- defaultTabValue: '',
- failedJobsCount: 1,
- totalJobCount: 10,
- testsCount: 123,
- };
-
- const createComponent = (provide = {}) => {
- wrapper = shallowMountExtended(PipelineTabs, {
- provide: {
- ...defaultProvide,
- ...provide,
- },
- stubs: {
- GlTab,
- RouterView: true,
- },
- mocks: {
- $router,
- },
- });
- };
-
- describe('Tabs', () => {
- it.each`
- tabName | tabComponent
- ${'Pipeline'} | ${findPipelineTab}
- ${'Dag'} | ${findDagTab}
- ${'Jobs'} | ${findJobsTab}
- ${'Failed Jobs'} | ${findFailedJobsTab}
- ${'Tests'} | ${findTestsTab}
- `('shows $tabName tab', ({ tabComponent }) => {
- createComponent();
-
- expect(tabComponent().exists()).toBe(true);
- });
-
- describe('with no failed jobs', () => {
- beforeEach(() => {
- createComponent({ failedJobsCount: 0 });
- });
-
- it('hides the failed jobs tab', () => {
- expect(findFailedJobsTab().exists()).toBe(false);
- });
- });
- });
-
- describe('Tabs badges', () => {
- it.each`
- tabName | badgeComponent | badgeText
- ${'Jobs'} | ${findJobsBadge} | ${String(defaultProvide.totalJobCount)}
- ${'Failed Jobs'} | ${findFailedJobsBadge} | ${String(defaultProvide.failedJobsCount)}
- ${'Tests'} | ${findTestsBadge} | ${String(defaultProvide.testsCount)}
- `('shows badge for $tabName with the correct text', ({ badgeComponent, badgeText }) => {
- createComponent();
-
- expect(badgeComponent().exists()).toBe(true);
- expect(badgeComponent().text()).toBe(badgeText);
- });
- });
-
- describe('Tab tracking', () => {
- beforeEach(() => {
- createComponent();
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks failed jobs tab click', () => {
- findFailedJobsTab().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', {
- label: TRACKING_CATEGORIES.failed,
- });
- });
-
- it('tracks tests tab click', () => {
- findTestsTab().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_tab', {
- label: TRACKING_CATEGORIES.tests,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js b/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
deleted file mode 100644
index 51a4487a3ef..00000000000
--- a/spec/frontend/pipelines/components/pipelines_filtered_search_spec.js
+++ /dev/null
@@ -1,199 +0,0 @@
-import { GlFilteredSearch } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import Api from '~/api';
-import axios from '~/lib/utils/axios_utils';
-import PipelinesFilteredSearch from '~/pipelines/components/pipelines_list/pipelines_filtered_search.vue';
-import {
- FILTERED_SEARCH_TERM,
- OPERATORS_IS,
-} from '~/vue_shared/components/filtered_search_bar/constants';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-import { users, mockSearch, branches, tags } from '../mock_data';
-
-describe('Pipelines filtered search', () => {
- let wrapper;
- let mock;
-
- const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
- const getSearchToken = (type) =>
- findFilteredSearch()
- .props('availableTokens')
- .find((token) => token.type === type);
- const findBranchToken = () => getSearchToken('ref');
- const findTagToken = () => getSearchToken('tag');
- const findUserToken = () => getSearchToken('username');
- const findStatusToken = () => getSearchToken('status');
- const findSourceToken = () => getSearchToken('source');
-
- const createComponent = (params = {}) => {
- wrapper = mount(PipelinesFilteredSearch, {
- propsData: {
- projectId: '21',
- defaultBranchName: 'main',
- params,
- },
- attachTo: document.body,
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
- jest.spyOn(Api, 'tags').mockResolvedValue({ data: tags });
-
- createComponent();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('displays UI elements', () => {
- expect(findFilteredSearch().exists()).toBe(true);
- });
-
- it('displays search tokens', () => {
- expect(findUserToken()).toMatchObject({
- type: 'username',
- icon: 'user',
- title: 'Trigger author',
- unique: true,
- projectId: '21',
- operators: OPERATORS_IS,
- });
-
- expect(findBranchToken()).toMatchObject({
- type: 'ref',
- icon: 'branch',
- title: 'Branch name',
- unique: true,
- projectId: '21',
- defaultBranchName: 'main',
- operators: OPERATORS_IS,
- });
-
- expect(findSourceToken()).toMatchObject({
- type: 'source',
- icon: 'trigger-source',
- title: 'Source',
- unique: true,
- operators: OPERATORS_IS,
- });
-
- expect(findStatusToken()).toMatchObject({
- type: 'status',
- icon: 'status',
- title: 'Status',
- unique: true,
- operators: OPERATORS_IS,
- });
-
- expect(findTagToken()).toMatchObject({
- type: 'tag',
- icon: 'tag',
- title: 'Tag name',
- unique: true,
- operators: OPERATORS_IS,
- });
- });
-
- it('emits filterPipelines on submit with correct filter', () => {
- findFilteredSearch().vm.$emit('submit', mockSearch);
-
- expect(wrapper.emitted('filterPipelines')).toHaveLength(1);
- expect(wrapper.emitted('filterPipelines')[0]).toEqual([mockSearch]);
- });
-
- it('disables tag name token when branch name token is active', async () => {
- findFilteredSearch().vm.$emit('input', [
- { type: 'ref', value: { data: 'branch-1', operator: '=' } },
- { type: FILTERED_SEARCH_TERM, value: { data: '' } },
- ]);
-
- await nextTick();
- expect(findBranchToken().disabled).toBe(false);
- expect(findTagToken().disabled).toBe(true);
- });
-
- it('disables branch name token when tag name token is active', async () => {
- findFilteredSearch().vm.$emit('input', [
- { type: 'tag', value: { data: 'tag-1', operator: '=' } },
- { type: FILTERED_SEARCH_TERM, value: { data: '' } },
- ]);
-
- await nextTick();
- expect(findBranchToken().disabled).toBe(true);
- expect(findTagToken().disabled).toBe(false);
- });
-
- it('resets tokens disabled state on clear', async () => {
- findFilteredSearch().vm.$emit('clearInput');
-
- await nextTick();
- expect(findBranchToken().disabled).toBe(false);
- expect(findTagToken().disabled).toBe(false);
- });
-
- it('resets tokens disabled state when clearing tokens by backspace', async () => {
- findFilteredSearch().vm.$emit('input', [{ type: FILTERED_SEARCH_TERM, value: { data: '' } }]);
-
- await nextTick();
- expect(findBranchToken().disabled).toBe(false);
- expect(findTagToken().disabled).toBe(false);
- });
-
- describe('Url query params', () => {
- const params = {
- username: 'deja.green',
- ref: 'main',
- };
-
- beforeEach(() => {
- createComponent(params);
- });
-
- it('sets default value if url query params', () => {
- const expectedValueProp = [
- {
- type: 'username',
- value: {
- data: params.username,
- operator: '=',
- },
- },
- {
- type: 'ref',
- value: {
- data: params.ref,
- operator: '=',
- },
- },
- { type: FILTERED_SEARCH_TERM, value: { data: '' } },
- ];
-
- expect(findFilteredSearch().props('value')).toMatchObject(expectedValueProp);
- expect(findFilteredSearch().props('value')).toHaveLength(expectedValueProp.length);
- });
- });
-
- describe('tracking', () => {
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks filtered search click', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- findFilteredSearch().vm.$emit('submit', mockSearch);
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filtered_search', {
- label: TRACKING_CATEGORIES.search,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js
deleted file mode 100644
index b560eea4882..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/empty_state/ci_templates_spec.js
+++ /dev/null
@@ -1,107 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import CiTemplates from '~/pipelines/components/pipelines_list/empty_state/ci_templates.vue';
-
-const pipelineEditorPath = '/-/ci/editor';
-const suggestedCiTemplates = [
- { name: 'Android', logo: '/assets/illustrations/logos/android.svg' },
- { name: 'Bash', logo: '/assets/illustrations/logos/bash.svg' },
- { name: 'C++', logo: '/assets/illustrations/logos/c_plus_plus.svg' },
-];
-
-describe('CI Templates', () => {
- let wrapper;
- let trackingSpy;
-
- const createWrapper = (propsData = {}) => {
- wrapper = shallowMountExtended(CiTemplates, {
- provide: {
- pipelineEditorPath,
- suggestedCiTemplates,
- },
- propsData,
- });
- };
-
- const findTemplateDescription = () => wrapper.findByTestId('template-description');
- const findTemplateLink = () => wrapper.findByTestId('template-link');
- const findTemplateNames = () => wrapper.findAllByTestId('template-name');
- const findTemplateName = () => wrapper.findByTestId('template-name');
- const findTemplateLogo = () => wrapper.findByTestId('template-logo');
-
- describe('renders template list', () => {
- beforeEach(() => {
- createWrapper();
- });
-
- it('renders all suggested templates', () => {
- expect(findTemplateNames().length).toBe(3);
- expect(wrapper.text()).toContain('Android', 'Bash', 'C++');
- });
-
- it('has the correct template name', () => {
- expect(findTemplateName().text()).toBe('Android');
- });
-
- it('links to the correct template', () => {
- expect(findTemplateLink().attributes('href')).toBe(
- pipelineEditorPath.concat('?template=Android'),
- );
- });
-
- it('has the link button enabled', () => {
- expect(findTemplateLink().props('disabled')).toBe(false);
- });
-
- it('has the description of the template', () => {
- expect(findTemplateDescription().text()).toBe(
- 'Continuous integration and deployment template to test and deploy your Android project.',
- );
- });
-
- it('has the right logo of the template', () => {
- expect(findTemplateLogo().attributes('src')).toBe('/assets/illustrations/logos/android.svg');
- });
- });
-
- describe('filtering the templates', () => {
- beforeEach(() => {
- createWrapper({ filterTemplates: ['Bash'] });
- });
-
- it('renders only the filtered templates', () => {
- expect(findTemplateNames()).toHaveLength(1);
- expect(findTemplateName().text()).toBe('Bash');
- });
- });
-
- describe('disabling the templates', () => {
- beforeEach(() => {
- createWrapper({ disabled: true });
- });
-
- it('has the link button disabled', () => {
- expect(findTemplateLink().props('disabled')).toBe(true);
- });
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- createWrapper();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('sends an event when template is clicked', () => {
- findTemplateLink().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'template_clicked', {
- label: 'Android',
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js
deleted file mode 100644
index 700be076e0c..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/empty_state/ios_templates_spec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import '~/commons';
-import { nextTick } from 'vue';
-import { GlPopover, GlButton } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
-import IosTemplates from '~/pipelines/components/pipelines_list/empty_state/ios_templates.vue';
-import CiTemplates from '~/pipelines/components/pipelines_list/empty_state/ci_templates.vue';
-
-const pipelineEditorPath = '/-/ci/editor';
-const registrationToken = 'SECRET_TOKEN';
-const iOSTemplateName = 'iOS-Fastlane';
-
-describe('iOS Templates', () => {
- let wrapper;
-
- const createWrapper = (providedPropsData = {}) => {
- return shallowMountExtended(IosTemplates, {
- provide: {
- pipelineEditorPath,
- iosRunnersAvailable: true,
- ...providedPropsData,
- },
- propsData: {
- registrationToken,
- },
- stubs: {
- GlButton,
- },
- });
- };
-
- const findIosTemplate = () => wrapper.findComponent(CiTemplates);
- const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
- const findRunnerInstructionsPopover = () => wrapper.findComponent(GlPopover);
- const findRunnerSetupTodoEmoji = () => wrapper.findByTestId('runner-setup-marked-todo');
- const findRunnerSetupCompletedEmoji = () => wrapper.findByTestId('runner-setup-marked-completed');
- const findSetupRunnerLink = () => wrapper.findByText('Set up a runner');
- const configurePipelineLink = () => wrapper.findByTestId('configure-pipeline-link');
-
- describe('when ios runners are not available', () => {
- beforeEach(() => {
- wrapper = createWrapper({ iosRunnersAvailable: false });
- });
-
- describe('the runner setup section', () => {
- it('marks the section as todo', () => {
- expect(findRunnerSetupTodoEmoji().isVisible()).toBe(true);
- expect(findRunnerSetupCompletedEmoji().isVisible()).toBe(false);
- });
-
- it('renders the setup runner link', () => {
- expect(findSetupRunnerLink().exists()).toBe(true);
- });
-
- it('renders the runner instructions modal with a popover once clicked', async () => {
- findSetupRunnerLink().element.parentElement.click();
-
- await nextTick();
-
- expect(findRunnerInstructionsModal().exists()).toBe(true);
- expect(findRunnerInstructionsModal().props('registrationToken')).toBe(registrationToken);
- expect(findRunnerInstructionsModal().props('defaultPlatformName')).toBe('osx');
-
- findRunnerInstructionsModal().vm.$emit('shown');
-
- await nextTick();
-
- expect(findRunnerInstructionsPopover().exists()).toBe(true);
- });
- });
-
- describe('the configure pipeline section', () => {
- it('has a disabled link button', () => {
- expect(configurePipelineLink().props('disabled')).toBe(true);
- });
- });
-
- describe('the ios-Fastlane template', () => {
- it('renders the template', () => {
- expect(findIosTemplate().props('filterTemplates')).toStrictEqual([iOSTemplateName]);
- });
-
- it('has a disabled link button', () => {
- expect(findIosTemplate().props('disabled')).toBe(true);
- });
- });
- });
-
- describe('when ios runners are available', () => {
- beforeEach(() => {
- wrapper = createWrapper();
- });
-
- describe('the runner setup section', () => {
- it('marks the section as completed', () => {
- expect(findRunnerSetupTodoEmoji().isVisible()).toBe(false);
- expect(findRunnerSetupCompletedEmoji().isVisible()).toBe(true);
- });
-
- it('does not render the setup runner link', () => {
- expect(findSetupRunnerLink().exists()).toBe(false);
- });
- });
-
- describe('the configure pipeline section', () => {
- it('has an enabled link button', () => {
- expect(configurePipelineLink().props('disabled')).toBe(false);
- });
-
- it('links to the pipeline editor with the right template', () => {
- expect(configurePipelineLink().attributes('href')).toBe(
- `${pipelineEditorPath}?template=${iOSTemplateName}`,
- );
- });
- });
-
- describe('the ios-Fastlane template', () => {
- it('renders the template', () => {
- expect(findIosTemplate().props('filterTemplates')).toStrictEqual([iOSTemplateName]);
- });
-
- it('has an enabled link button', () => {
- expect(findIosTemplate().props('disabled')).toBe(false);
- });
-
- it('links to the pipeline editor with the right template', () => {
- expect(configurePipelineLink().attributes('href')).toBe(
- `${pipelineEditorPath}?template=${iOSTemplateName}`,
- );
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js b/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js
deleted file mode 100644
index 4bf4257f462..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import '~/commons';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
-import CiTemplates from '~/pipelines/components/pipelines_list/empty_state/ci_templates.vue';
-
-const pipelineEditorPath = '/-/ci/editor';
-
-describe('Pipelines CI Templates', () => {
- let wrapper;
- let trackingSpy;
-
- const createWrapper = (propsData = {}, stubs = {}) => {
- return shallowMountExtended(PipelinesCiTemplates, {
- provide: {
- pipelineEditorPath,
- ...propsData,
- },
- stubs,
- });
- };
-
- const findTestTemplateLink = () => wrapper.findByTestId('test-template-link');
- const findCiTemplates = () => wrapper.findComponent(CiTemplates);
-
- describe('templates', () => {
- beforeEach(() => {
- wrapper = createWrapper();
- });
-
- it('renders test template and Ci templates', () => {
- expect(findTestTemplateLink().attributes('href')).toBe(
- pipelineEditorPath.concat('?template=Getting-Started'),
- );
- expect(findCiTemplates().exists()).toBe(true);
- });
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- wrapper = createWrapper();
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('sends an event when Getting-Started template is clicked', () => {
- findTestTemplateLink().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledTimes(1);
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'template_clicked', {
- label: 'Getting-Started',
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_job_details_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_job_details_spec.js
deleted file mode 100644
index 479ee854ecf..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_job_details_spec.js
+++ /dev/null
@@ -1,254 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { GlIcon, GlLink } from '@gitlab/ui';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/alert';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import FailedJobDetails from '~/pipelines/components/pipelines_list/failure_widget/failed_job_details.vue';
-import RetryMrFailedJobMutation from '~/pipelines/graphql/mutations/retry_mr_failed_job.mutation.graphql';
-import { BRIDGE_KIND } from '~/pipelines/components/graph/constants';
-import { job } from './mock';
-
-Vue.use(VueApollo);
-jest.mock('~/alert');
-
-const createFakeEvent = () => ({ stopPropagation: jest.fn() });
-
-describe('FailedJobDetails component', () => {
- let wrapper;
- let mockRetryResponse;
-
- const retrySuccessResponse = {
- data: {
- jobRetry: {
- errors: [],
- },
- },
- };
-
- const defaultProps = {
- job,
- };
-
- const createComponent = ({ props = {} } = {}) => {
- const handlers = [[RetryMrFailedJobMutation, mockRetryResponse]];
-
- wrapper = shallowMountExtended(FailedJobDetails, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- apolloProvider: createMockApollo(handlers),
- });
- };
-
- const findArrowIcon = () => wrapper.findComponent(GlIcon);
- const findJobId = () => wrapper.findComponent(GlLink);
- const findJobLog = () => wrapper.findByTestId('job-log');
- const findJobName = () => wrapper.findByText(defaultProps.job.name);
- const findRetryButton = () => wrapper.findByLabelText('Retry');
- const findRow = () => wrapper.findByTestId('widget-row');
- const findStageName = () => wrapper.findByText(defaultProps.job.stage.name);
-
- beforeEach(() => {
- mockRetryResponse = jest.fn();
- mockRetryResponse.mockResolvedValue(retrySuccessResponse);
- });
-
- describe('ui', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the job name', () => {
- expect(findJobName().exists()).toBe(true);
- });
-
- it('renders the stage name', () => {
- expect(findStageName().exists()).toBe(true);
- });
-
- it('renders the job id as a link', () => {
- const jobId = getIdFromGraphQLId(defaultProps.job.id);
-
- expect(findJobId().exists()).toBe(true);
- expect(findJobId().text()).toContain(String(jobId));
- });
-
- it('does not renders the job lob', () => {
- expect(findJobLog().exists()).toBe(false);
- });
- });
-
- describe('Retry action', () => {
- describe('when the job is not retryable', () => {
- beforeEach(() => {
- createComponent({ props: { job: { ...job, retryable: false } } });
- });
-
- it('disables the retry button', () => {
- expect(findRetryButton().props().disabled).toBe(true);
- });
- });
-
- describe('when the job is a bridge', () => {
- beforeEach(() => {
- createComponent({ props: { job: { ...job, kind: BRIDGE_KIND } } });
- });
-
- it('disables the retry button', () => {
- expect(findRetryButton().props().disabled).toBe(true);
- });
- });
-
- describe('when the job is retryable', () => {
- describe('and user has permission to update the build', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('enables the retry button', () => {
- expect(findRetryButton().props().disabled).toBe(false);
- });
-
- describe('when clicking on the retry button', () => {
- it('passes the loading state to the button', async () => {
- await findRetryButton().vm.$emit('click', createFakeEvent());
-
- expect(findRetryButton().props().loading).toBe(true);
- });
-
- describe('and it succeeds', () => {
- beforeEach(async () => {
- findRetryButton().vm.$emit('click', createFakeEvent());
- await waitForPromises();
- });
-
- it('is no longer loading', () => {
- expect(findRetryButton().props().loading).toBe(false);
- });
-
- it('calls the retry mutation', () => {
- expect(mockRetryResponse).toHaveBeenCalled();
- expect(mockRetryResponse).toHaveBeenCalledWith({
- id: job.id,
- });
- });
-
- it('emits the `retried-job` event', () => {
- expect(wrapper.emitted('job-retried')).toStrictEqual([[job.name]]);
- });
- });
-
- describe('and it fails', () => {
- const customErrorMsg = 'Custom error message from API';
-
- beforeEach(async () => {
- mockRetryResponse.mockResolvedValue({
- data: { jobRetry: { errors: [customErrorMsg] } },
- });
- findRetryButton().vm.$emit('click', createFakeEvent());
-
- await waitForPromises();
- });
-
- it('shows an error message', () => {
- expect(createAlert).toHaveBeenCalledWith({ message: customErrorMsg });
- });
-
- it('does not emits the `refetch-jobs` event', () => {
- expect(wrapper.emitted('refetch-jobs')).toBeUndefined();
- });
- });
- });
- });
-
- describe('and user does not have permission to update the build', () => {
- beforeEach(() => {
- createComponent({
- props: { job: { ...job, retryable: true, userPermissions: { updateBuild: false } } },
- });
- });
-
- it('disables the retry button', () => {
- expect(findRetryButton().props().disabled).toBe(true);
- });
- });
- });
- });
-
- describe('Job log', () => {
- describe('without permissions', () => {
- beforeEach(async () => {
- createComponent({ props: { job: { ...job, userPermissions: { readBuild: false } } } });
- await findRow().trigger('click');
- });
-
- it('does not renders the received html of the job log', () => {
- expect(findJobLog().html()).not.toContain(defaultProps.job.trace.htmlSummary);
- });
-
- it('shows a permission error message', () => {
- expect(findJobLog().text()).toBe("You do not have permission to read this job's log.");
- });
- });
-
- describe('with permissions', () => {
- beforeEach(() => {
- createComponent();
- });
-
- describe('when clicking on the row', () => {
- beforeEach(async () => {
- await findRow().trigger('click');
- });
-
- describe('while collapsed', () => {
- it('expands the job log', () => {
- expect(findJobLog().exists()).toBe(true);
- });
-
- it('renders the down arrow', () => {
- expect(findArrowIcon().props().name).toBe('chevron-down');
- });
-
- it('renders the received html of the job log', () => {
- expect(findJobLog().html()).toContain(defaultProps.job.trace.htmlSummary);
- });
- });
-
- describe('while expanded', () => {
- it('collapes the job log', async () => {
- expect(findJobLog().exists()).toBe(true);
-
- await findRow().trigger('click');
-
- expect(findJobLog().exists()).toBe(false);
- });
-
- it('renders the right arrow', async () => {
- expect(findArrowIcon().props().name).toBe('chevron-down');
-
- await findRow().trigger('click');
-
- expect(findArrowIcon().props().name).toBe('chevron-right');
- });
- });
- });
-
- describe('when clicking on a link element within the row', () => {
- it('does not expands/collapse the job log', async () => {
- expect(findJobLog().exists()).toBe(false);
- expect(findArrowIcon().props().name).toBe('chevron-right');
-
- await findJobId().vm.$emit('click');
-
- expect(findJobLog().exists()).toBe(false);
- expect(findArrowIcon().props().name).toBe('chevron-right');
- });
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js
deleted file mode 100644
index 967812cc627..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/failed_jobs_list_spec.js
+++ /dev/null
@@ -1,279 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-
-import { GlLoadingIcon, GlToast } from '@gitlab/ui';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/alert';
-import FailedJobsList from '~/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue';
-import FailedJobDetails from '~/pipelines/components/pipelines_list/failure_widget/failed_job_details.vue';
-import * as utils from '~/pipelines/components/pipelines_list/failure_widget/utils';
-import getPipelineFailedJobs from '~/pipelines/graphql/queries/get_pipeline_failed_jobs.query.graphql';
-import { failedJobsMock, failedJobsMock2, failedJobsMockEmpty, activeFailedJobsMock } from './mock';
-
-Vue.use(VueApollo);
-Vue.use(GlToast);
-
-jest.mock('~/alert');
-
-describe('FailedJobsList component', () => {
- let wrapper;
- let mockFailedJobsResponse;
- const showToast = jest.fn();
-
- const defaultProps = {
- failedJobsCount: 0,
- graphqlResourceEtag: 'api/graphql',
- isPipelineActive: false,
- pipelineIid: 1,
- projectPath: 'namespace/project/',
- };
-
- const defaultProvide = {
- graphqlPath: 'api/graphql',
- };
-
- const createComponent = ({ props = {}, provide } = {}) => {
- const handlers = [[getPipelineFailedJobs, mockFailedJobsResponse]];
- const mockApollo = createMockApollo(handlers);
-
- wrapper = shallowMountExtended(FailedJobsList, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- provide: {
- ...defaultProvide,
- ...provide,
- },
- apolloProvider: mockApollo,
- mocks: {
- $toast: {
- show: showToast,
- },
- },
- });
- };
-
- const findAllHeaders = () => wrapper.findAllByTestId('header');
- const findFailedJobRows = () => wrapper.findAllComponents(FailedJobDetails);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findNoFailedJobsText = () => wrapper.findByText('No failed jobs in this pipeline 🎉');
-
- beforeEach(() => {
- mockFailedJobsResponse = jest.fn();
- });
-
- describe('on mount', () => {
- beforeEach(() => {
- mockFailedJobsResponse.mockResolvedValue(failedJobsMock);
- createComponent();
- });
-
- it('fires the graphql query', () => {
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- expect(mockFailedJobsResponse).toHaveBeenCalledWith({
- fullPath: defaultProps.projectPath,
- pipelineIid: defaultProps.pipelineIid,
- });
- });
- });
-
- describe('when loading failed jobs', () => {
- beforeEach(() => {
- mockFailedJobsResponse.mockResolvedValue(failedJobsMock);
- createComponent();
- });
-
- it('shows a loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('when failed jobs have loaded', () => {
- beforeEach(async () => {
- mockFailedJobsResponse.mockResolvedValue(failedJobsMock);
- jest.spyOn(utils, 'sortJobsByStatus');
-
- createComponent();
-
- await waitForPromises();
- });
-
- it('does not renders a loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('renders table column', () => {
- expect(findAllHeaders()).toHaveLength(3);
- });
-
- it('shows the list of failed jobs', () => {
- expect(findFailedJobRows()).toHaveLength(
- failedJobsMock.data.project.pipeline.jobs.nodes.length,
- );
- });
-
- it('does not renders the empty state', () => {
- expect(findNoFailedJobsText().exists()).toBe(false);
- });
-
- it('calls sortJobsByStatus', () => {
- expect(utils.sortJobsByStatus).toHaveBeenCalledWith(
- failedJobsMock.data.project.pipeline.jobs.nodes,
- );
- });
- });
-
- describe('when there are no failed jobs', () => {
- beforeEach(async () => {
- mockFailedJobsResponse.mockResolvedValue(failedJobsMockEmpty);
- jest.spyOn(utils, 'sortJobsByStatus');
-
- createComponent();
-
- await waitForPromises();
- });
-
- it('renders the empty state', () => {
- expect(findNoFailedJobsText().exists()).toBe(true);
- });
- });
-
- describe('polling', () => {
- it.each`
- isGraphqlActive | text
- ${true} | ${'polls'}
- ${false} | ${'does not poll'}
- `(`$text when isGraphqlActive: $isGraphqlActive`, async ({ isGraphqlActive }) => {
- const defaultCount = 2;
- const newCount = 1;
-
- const expectedCount = isGraphqlActive ? newCount : defaultCount;
- const expectedCallCount = isGraphqlActive ? 2 : 1;
- const mockResponse = isGraphqlActive ? activeFailedJobsMock : failedJobsMock;
-
- // Second result is to simulate polling with a different response
- mockFailedJobsResponse.mockResolvedValueOnce(mockResponse);
- mockFailedJobsResponse.mockResolvedValueOnce(failedJobsMock2);
-
- createComponent();
- await waitForPromises();
-
- // Initially, we get the first response which is always the default
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- expect(findFailedJobRows()).toHaveLength(defaultCount);
-
- jest.advanceTimersByTime(10000);
- await waitForPromises();
-
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(expectedCallCount);
- expect(findFailedJobRows()).toHaveLength(expectedCount);
- });
- });
-
- describe('when a REST action occurs', () => {
- beforeEach(() => {
- // Second result is to simulate polling with a different response
- mockFailedJobsResponse.mockResolvedValueOnce(failedJobsMock);
- mockFailedJobsResponse.mockResolvedValueOnce(failedJobsMock2);
- });
-
- it.each([true, false])('triggers a refetch of the jobs count', async (isPipelineActive) => {
- const defaultCount = 2;
- const newCount = 1;
-
- createComponent({ props: { isPipelineActive } });
- await waitForPromises();
-
- // Initially, we get the first response which is always the default
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- expect(findFailedJobRows()).toHaveLength(defaultCount);
-
- wrapper.setProps({ isPipelineActive: !isPipelineActive });
- await waitForPromises();
-
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(2);
- expect(findFailedJobRows()).toHaveLength(newCount);
- });
- });
-
- describe('When the job count changes from REST', () => {
- beforeEach(() => {
- mockFailedJobsResponse.mockResolvedValue(failedJobsMockEmpty);
-
- createComponent();
- });
-
- describe('and the count is the same', () => {
- it('does not re-fetch the query', async () => {
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
-
- await wrapper.setProps({ failedJobsCount: 0 });
-
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('and the count is different', () => {
- it('re-fetches the query', async () => {
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(1);
-
- await wrapper.setProps({ failedJobsCount: 10 });
-
- expect(mockFailedJobsResponse).toHaveBeenCalledTimes(2);
- });
- });
- });
-
- describe('when an error occurs loading jobs', () => {
- const errorMessage = "We couldn't fetch jobs for you because you are not qualified";
-
- beforeEach(async () => {
- mockFailedJobsResponse.mockRejectedValue({ message: errorMessage });
-
- createComponent();
-
- await waitForPromises();
- });
- it('does not renders a loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('calls create Alert with the error message and danger variant', () => {
- expect(createAlert).toHaveBeenCalledWith({ message: errorMessage, variant: 'danger' });
- });
- });
-
- describe('when `refetch-jobs` job is fired from the widget', () => {
- beforeEach(async () => {
- mockFailedJobsResponse.mockResolvedValueOnce(failedJobsMock);
- mockFailedJobsResponse.mockResolvedValueOnce(failedJobsMock2);
-
- createComponent();
-
- await waitForPromises();
- });
-
- it('refetches all failed jobs', async () => {
- expect(findFailedJobRows()).not.toHaveLength(
- failedJobsMock2.data.project.pipeline.jobs.nodes.length,
- );
-
- await findFailedJobRows().at(0).vm.$emit('job-retried', 'job-name');
- await waitForPromises();
-
- expect(findFailedJobRows()).toHaveLength(
- failedJobsMock2.data.project.pipeline.jobs.nodes.length,
- );
- });
-
- it('shows a toast message', async () => {
- await findFailedJobRows().at(0).vm.$emit('job-retried', 'job-name');
- await waitForPromises();
-
- expect(showToast).toHaveBeenCalledWith('job-name job is being retried');
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js
deleted file mode 100644
index 318d787a984..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/mock.js
+++ /dev/null
@@ -1,78 +0,0 @@
-export const job = {
- id: 'gid://gitlab/Ci::Build/5241',
- allowFailure: false,
- detailedStatus: {
- id: 'status',
- detailsPath: '/jobs/5241',
- action: {
- id: 'action',
- path: '/retry',
- icon: 'retry',
- },
- group: 'running',
- icon: 'status_running_icon',
- },
- name: 'job-name',
- retried: false,
- retryable: true,
- kind: 'BUILD',
- stage: {
- id: '1',
- name: 'build',
- },
- trace: {
- htmlSummary: '<h1>Hello</h1>',
- },
- userPermissions: {
- readBuild: true,
- updateBuild: true,
- },
-};
-
-export const allowedToFailJob = {
- ...job,
- id: 'gid://gitlab/Ci::Build/5242',
- allowFailure: true,
-};
-
-export const createFailedJobsMockCount = ({ count = 4, active = false } = {}) => {
- return {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- pipeline: {
- id: 'gid://gitlab/Pipeline/20',
- active,
- jobs: {
- count,
- },
- },
- },
- },
- };
-};
-
-const createFailedJobsMock = (nodes, active = false) => {
- return {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- pipeline: {
- active,
- id: 'gid://gitlab/Pipeline/20',
- jobs: {
- count: nodes.length,
- nodes,
- },
- },
- },
- },
- };
-};
-
-export const failedJobsMock = createFailedJobsMock([allowedToFailJob, job]);
-export const failedJobsMockEmpty = createFailedJobsMock([]);
-
-export const activeFailedJobsMock = createFailedJobsMock([allowedToFailJob, job], true);
-
-export const failedJobsMock2 = createFailedJobsMock([job]);
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js
deleted file mode 100644
index 5bbb874edb0..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget_spec.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import { GlButton, GlCard, GlIcon, GlPopover } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import PipelineFailedJobsWidget from '~/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue';
-import FailedJobsList from '~/pipelines/components/pipelines_list/failure_widget/failed_jobs_list.vue';
-
-jest.mock('~/alert');
-
-describe('PipelineFailedJobsWidget component', () => {
- let wrapper;
-
- const defaultProps = {
- failedJobsCount: 4,
- isPipelineActive: false,
- pipelineIid: 1,
- pipelinePath: '/pipelines/1',
- projectPath: 'namespace/project/',
- };
-
- const defaultProvide = {
- fullPath: 'namespace/project/',
- };
-
- const createComponent = ({ props = {}, provide = {} } = {}) => {
- wrapper = shallowMountExtended(PipelineFailedJobsWidget, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- provide: {
- ...defaultProvide,
- ...provide,
- },
- stubs: { GlCard },
- });
- };
-
- const findFailedJobsCard = () => wrapper.findByTestId('failed-jobs-card');
- const findFailedJobsButton = () => wrapper.findComponent(GlButton);
- const findFailedJobsList = () => wrapper.findAllComponents(FailedJobsList);
- const findInfoIcon = () => wrapper.findComponent(GlIcon);
- const findInfoPopover = () => wrapper.findComponent(GlPopover);
-
- describe('when there are no failed jobs', () => {
- beforeEach(() => {
- createComponent({ props: { failedJobsCount: 0 } });
- });
-
- it('renders the show failed jobs button with a count of 0', () => {
- expect(findFailedJobsButton().exists()).toBe(true);
- expect(findFailedJobsButton().text()).toBe('Failed jobs (0)');
- });
- });
-
- describe('when there are failed jobs', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the show failed jobs button with correct count', () => {
- expect(findFailedJobsButton().exists()).toBe(true);
- expect(findFailedJobsButton().text()).toBe(`Failed jobs (${defaultProps.failedJobsCount})`);
- });
-
- it('renders the info icon', () => {
- expect(findInfoIcon().exists()).toBe(true);
- });
-
- it('renders the info popover', () => {
- expect(findInfoPopover().exists()).toBe(true);
- });
-
- it('does not render the failed jobs widget', () => {
- expect(findFailedJobsList().exists()).toBe(false);
- });
- });
-
- describe('when the job button is clicked', () => {
- beforeEach(async () => {
- createComponent();
- await findFailedJobsButton().vm.$emit('click');
- });
-
- it('renders the failed jobs widget', () => {
- expect(findFailedJobsList().exists()).toBe(true);
- });
-
- it('removes the CSS border classes', () => {
- expect(findFailedJobsCard().attributes('class')).not.toContain(
- 'gl-border-white gl-hover-border-gray-100',
- );
- });
- });
-
- describe('when the job details are not expanded', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('has the CSS border classes', () => {
- expect(findFailedJobsCard().attributes('class')).toContain(
- 'gl-border-white gl-hover-border-gray-100',
- );
- });
- });
-
- describe('when the job count changes', () => {
- beforeEach(() => {
- createComponent();
- });
-
- describe('from the prop', () => {
- it('updates the job count', async () => {
- const newJobCount = 12;
-
- expect(findFailedJobsButton().text()).toContain(String(defaultProps.failedJobsCount));
-
- await wrapper.setProps({ failedJobsCount: newJobCount });
-
- expect(findFailedJobsButton().text()).toContain(String(newJobCount));
- });
- });
-
- describe('from the event', () => {
- beforeEach(async () => {
- await findFailedJobsButton().vm.$emit('click');
- });
-
- it('updates the job count', async () => {
- const newJobCount = 12;
-
- expect(findFailedJobsButton().text()).toContain(String(defaultProps.failedJobsCount));
-
- await findFailedJobsList().at(0).vm.$emit('failed-jobs-count', newJobCount);
-
- expect(findFailedJobsButton().text()).toContain(String(newJobCount));
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js b/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js
deleted file mode 100644
index 44f16478151..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/failure_widget/utils_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import {
- isFailedJob,
- sortJobsByStatus,
-} from '~/pipelines/components/pipelines_list/failure_widget/utils';
-
-describe('isFailedJob', () => {
- describe('when the job argument is undefined', () => {
- it('returns false', () => {
- expect(isFailedJob()).toBe(false);
- });
- });
-
- describe('when the job is of status `failed`', () => {
- it('returns false', () => {
- expect(isFailedJob({ detailedStatus: { group: 'success' } })).toBe(false);
- });
- });
-
- describe('when the job status is `failed`', () => {
- it('returns true', () => {
- expect(isFailedJob({ detailedStatus: { group: 'failed' } })).toBe(true);
- });
- });
-});
-
-describe('sortJobsByStatus', () => {
- describe('when the arg is undefined', () => {
- it('returns an empty array', () => {
- expect(sortJobsByStatus()).toEqual([]);
- });
- });
-
- describe('when receiving an empty array', () => {
- it('returns an empty array', () => {
- expect(sortJobsByStatus([])).toEqual([]);
- });
- });
-
- describe('when reciving a list of jobs', () => {
- const jobArr = [
- { detailedStatus: { group: 'failed' } },
- { detailedStatus: { group: 'allowed_to_fail' } },
- { detailedStatus: { group: 'failed' } },
- { detailedStatus: { group: 'success' } },
- ];
-
- const expectedResult = [
- { detailedStatus: { group: 'failed' } },
- { detailedStatus: { group: 'failed' } },
- { detailedStatus: { group: 'allowed_to_fail' } },
- { detailedStatus: { group: 'success' } },
- ];
-
- it('sorts failed jobs first', () => {
- expect(sortJobsByStatus(jobArr)).toEqual(expectedResult);
- });
- });
-});
diff --git a/spec/frontend/pipelines/components/pipelines_list/pipieline_stop_modal_spec.js b/spec/frontend/pipelines/components/pipelines_list/pipieline_stop_modal_spec.js
deleted file mode 100644
index 249126390f1..00000000000
--- a/spec/frontend/pipelines/components/pipelines_list/pipieline_stop_modal_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlSprintf } from '@gitlab/ui';
-import PipelineStopModal from '~/pipelines/components/pipelines_list/pipeline_stop_modal.vue';
-import { mockPipelineHeader } from '../../mock_data';
-
-describe('PipelineStopModal', () => {
- let wrapper;
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineStopModal, {
- propsData: {
- pipeline: mockPipelineHeader,
- },
- stubs: {
- GlSprintf,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- it('should render "stop pipeline" warning', () => {
- expect(wrapper.text()).toMatch(`You’re about to stop pipeline #${mockPipelineHeader.id}.`);
- });
-});
diff --git a/spec/frontend/pipelines/empty_state_spec.js b/spec/frontend/pipelines/empty_state_spec.js
deleted file mode 100644
index 5465e4d77da..00000000000
--- a/spec/frontend/pipelines/empty_state_spec.js
+++ /dev/null
@@ -1,87 +0,0 @@
-import '~/commons';
-import { shallowMount } from '@vue/test-utils';
-import { GlEmptyState } from '@gitlab/ui';
-import { stubExperiments } from 'helpers/experimentation_helper';
-import EmptyState from '~/pipelines/components/pipelines_list/empty_state.vue';
-import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
-import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
-import IosTemplates from '~/pipelines/components/pipelines_list/empty_state/ios_templates.vue';
-
-describe('Pipelines Empty State', () => {
- let wrapper;
-
- const findIllustration = () => wrapper.find('img');
- const findButton = () => wrapper.find('a');
- const pipelinesCiTemplates = () => wrapper.findComponent(PipelinesCiTemplates);
- const iosTemplates = () => wrapper.findComponent(IosTemplates);
-
- const createWrapper = (props = {}) => {
- wrapper = shallowMount(EmptyState, {
- provide: {
- pipelineEditorPath: '',
- suggestedCiTemplates: [],
- anyRunnersAvailable: true,
- ciRunnerSettingsPath: '',
- },
- propsData: {
- emptyStateSvgPath: 'foo.svg',
- canSetCi: true,
- ...props,
- },
- stubs: {
- GlEmptyState,
- GitlabExperiment,
- },
- });
- };
-
- describe('when user can configure CI', () => {
- describe('when the ios_specific_templates experiment is active', () => {
- beforeEach(() => {
- stubExperiments({ ios_specific_templates: 'candidate' });
- createWrapper();
- });
-
- it('should render the iOS templates', () => {
- expect(iosTemplates().exists()).toBe(true);
- });
-
- it('should not render the CI/CD templates', () => {
- expect(pipelinesCiTemplates().exists()).toBe(false);
- });
- });
-
- describe('when the ios_specific_templates experiment is inactive', () => {
- beforeEach(() => {
- stubExperiments({ ios_specific_templates: 'control' });
- createWrapper();
- });
-
- it('should render the CI/CD templates', () => {
- expect(pipelinesCiTemplates().exists()).toBe(true);
- });
-
- it('should not render the iOS templates', () => {
- expect(iosTemplates().exists()).toBe(false);
- });
- });
- });
-
- describe('when user cannot configure CI', () => {
- beforeEach(() => {
- createWrapper({ canSetCi: false });
- });
-
- it('should render empty state SVG', () => {
- expect(findIllustration().attributes('src')).toBe('foo.svg');
- });
-
- it('should render empty state header', () => {
- expect(wrapper.text()).toBe('This project is not currently set up to run pipelines.');
- });
-
- it('should not render a link', () => {
- expect(findButton().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/action_component_spec.js b/spec/frontend/pipelines/graph/action_component_spec.js
deleted file mode 100644
index 890255f225e..00000000000
--- a/spec/frontend/pipelines/graph/action_component_spec.js
+++ /dev/null
@@ -1,116 +0,0 @@
-import { GlButton } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import ActionComponent from '~/pipelines/components/jobs_shared/action_component.vue';
-
-describe('pipeline graph action component', () => {
- let wrapper;
- let mock;
- const findButton = () => wrapper.findComponent(GlButton);
- const findTooltipWrapper = () => wrapper.find('[data-testid="ci-action-icon-tooltip-wrapper"]');
-
- const defaultProps = {
- tooltipText: 'bar',
- link: 'foo',
- actionIcon: 'cancel',
- };
-
- const createComponent = ({ props } = {}) => {
- wrapper = mount(ActionComponent, {
- propsData: { ...defaultProps, ...props },
- });
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- mock.onPost('foo.json').reply(HTTP_STATUS_OK);
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should render the provided title as a bootstrap tooltip', () => {
- expect(findTooltipWrapper().attributes('title')).toBe('bar');
- });
-
- it('should update bootstrap tooltip when title changes', async () => {
- wrapper.setProps({ tooltipText: 'changed' });
-
- await nextTick();
- expect(findTooltipWrapper().attributes('title')).toBe('changed');
- });
-
- it('should render an svg', () => {
- expect(wrapper.find('.ci-action-icon-wrapper').exists()).toBe(true);
- expect(wrapper.find('svg').exists()).toBe(true);
- });
- });
-
- describe('on click', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('emits `pipelineActionRequestComplete` after a successful request', async () => {
- findButton().trigger('click');
-
- await waitForPromises();
-
- expect(wrapper.emitted().pipelineActionRequestComplete).toHaveLength(1);
- });
-
- it('renders a loading icon while waiting for request', async () => {
- findButton().trigger('click');
-
- await nextTick();
- expect(wrapper.find('.js-action-icon-loading').exists()).toBe(true);
- });
- });
-
- describe('when has a confirmation modal', () => {
- beforeEach(() => {
- createComponent({ props: { withConfirmationModal: true, shouldTriggerClick: false } });
- });
-
- describe('and a first click is initiated', () => {
- beforeEach(async () => {
- findButton().trigger('click');
-
- await waitForPromises();
- });
-
- it('emits `showActionConfirmationModal` event', () => {
- expect(wrapper.emitted().showActionConfirmationModal).toHaveLength(1);
- });
-
- it('does not emit `pipelineActionRequestComplete` event', () => {
- expect(wrapper.emitted().pipelineActionRequestComplete).toBeUndefined();
- });
- });
-
- describe('and the `shouldTriggerClick` value becomes true', () => {
- beforeEach(async () => {
- await wrapper.setProps({ shouldTriggerClick: true });
- });
-
- it('does not emit `showActionConfirmationModal` event', () => {
- expect(wrapper.emitted().showActionConfirmationModal).toBeUndefined();
- });
-
- it('emits `actionButtonClicked` event', () => {
- expect(wrapper.emitted().actionButtonClicked).toHaveLength(1);
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/graph_component_spec.js b/spec/frontend/pipelines/graph/graph_component_spec.js
deleted file mode 100644
index e9bce037800..00000000000
--- a/spec/frontend/pipelines/graph/graph_component_spec.js
+++ /dev/null
@@ -1,182 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
-import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
-import JobItem from '~/pipelines/components/graph/job_item.vue';
-import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
-import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
-import { calculatePipelineLayersInfo } from '~/pipelines/components/graph/utils';
-import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-
-import { generateResponse, pipelineWithUpstreamDownstream } from './mock_data';
-
-describe('graph component', () => {
- let wrapper;
-
- const findDownstreamColumn = () => wrapper.findByTestId('downstream-pipelines');
- const findLinkedColumns = () => wrapper.findAllComponents(LinkedPipelinesColumn);
- const findLinksLayer = () => wrapper.findComponent(LinksLayer);
- const findStageColumns = () => wrapper.findAllComponents(StageColumnComponent);
- const findStageNameInJob = () => wrapper.findByTestId('stage-name-in-job');
-
- const defaultProps = {
- pipeline: generateResponse(mockPipelineResponse, 'root/fungi-xoxo'),
- showLinks: false,
- viewType: STAGE_VIEW,
- configPaths: {
- metricsPath: '',
- graphqlResourceEtag: 'this/is/a/path',
- },
- };
-
- const defaultData = {
- measurements: {
- width: 800,
- height: 800,
- },
- };
-
- const createComponent = ({
- data = {},
- mountFn = shallowMount,
- props = {},
- stubOverride = {},
- } = {}) => {
- wrapper = mountFn(PipelineGraph, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- data() {
- return {
- ...defaultData,
- ...data,
- };
- },
- stubs: {
- 'links-inner': true,
- 'linked-pipeline': true,
- 'job-item': true,
- 'job-group-dropdown': true,
- ...stubOverride,
- },
- });
- };
-
- describe('with data', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('renders the main columns in the graph', () => {
- expect(findStageColumns()).toHaveLength(defaultProps.pipeline.stages.length);
- });
-
- it('renders the links layer', () => {
- expect(findLinksLayer().exists()).toBe(true);
- });
-
- it('does not display stage name on the job in default (stage) mode', () => {
- expect(findStageNameInJob().exists()).toBe(false);
- });
-
- describe('when column requests a refresh', () => {
- beforeEach(() => {
- findStageColumns().at(0).vm.$emit('refreshPipelineGraph');
- });
-
- it('refreshPipelineGraph is emitted', () => {
- expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
- });
- });
-
- describe('when column request an update to the retry confirmation modal', () => {
- beforeEach(() => {
- findStageColumns().at(0).vm.$emit('setSkipRetryModal');
- });
-
- it('setSkipRetryModal is emitted', () => {
- expect(wrapper.emitted().setSkipRetryModal).toHaveLength(1);
- });
- });
-
- describe('when links are present', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mountExtended,
- stubOverride: { 'job-item': false },
- data: { hoveredJobName: 'test_a' },
- });
- findLinksLayer().vm.$emit('highlightedJobsChange', ['test_c', 'build_c']);
- });
-
- it('dims unrelated jobs', () => {
- const unrelatedJob = wrapper.findComponent(JobItem);
- expect(findLinksLayer().emitted().highlightedJobsChange).toHaveLength(1);
- expect(unrelatedJob.classes('gl-opacity-3')).toBe(true);
- });
- });
- });
-
- describe('when linked pipelines are not present', () => {
- beforeEach(() => {
- createComponent({ mountFn: mountExtended });
- });
-
- it('should not render a linked pipelines column', () => {
- expect(findLinkedColumns()).toHaveLength(0);
- });
- });
-
- describe('when linked pipelines are present', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mountExtended,
- props: { pipeline: pipelineWithUpstreamDownstream(mockPipelineResponse) },
- });
- });
-
- it('should render linked pipelines columns', () => {
- expect(findLinkedColumns()).toHaveLength(2);
- });
- });
-
- describe('in layers mode', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mountExtended,
- stubOverride: {
- 'job-item': false,
- 'job-group-dropdown': false,
- },
- props: {
- viewType: LAYER_VIEW,
- computedPipelineInfo: calculatePipelineLayersInfo(defaultProps.pipeline, 'layer', ''),
- },
- });
- });
-
- it('displays the stage name on the job', () => {
- expect(findStageNameInJob().exists()).toBe(true);
- });
- });
-
- describe('downstream pipelines', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mountExtended,
- props: {
- pipeline: pipelineWithUpstreamDownstream(mockPipelineResponse),
- },
- });
- });
-
- it('filters pipelines spawned from the same trigger job', () => {
- // The mock data has one downstream with `retried: true and one
- // with retried false. We filter the `retried: true` out so we
- // should only pass one downstream
- expect(findDownstreamColumn().props().linkedPipelines).toHaveLength(1);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js b/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
deleted file mode 100644
index 7b59d82ae6f..00000000000
--- a/spec/frontend/pipelines/graph/graph_component_wrapper_spec.js
+++ /dev/null
@@ -1,603 +0,0 @@
-import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon, GlToggle } from '@gitlab/ui';
-import MockAdapter from 'axios-mock-adapter';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import { useLocalStorageSpy } from 'helpers/local_storage_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { stubPerformanceWebAPI } from 'helpers/performance';
-import waitForPromises from 'helpers/wait_for_promises';
-import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
-import getUserCallouts from '~/graphql_shared/queries/get_user_callouts.query.graphql';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import {
- PIPELINES_DETAIL_LINK_DURATION,
- PIPELINES_DETAIL_LINKS_TOTAL,
- PIPELINES_DETAIL_LINKS_JOB_RATIO,
-} from '~/performance/constants';
-import * as perfUtils from '~/performance/utils';
-import {
- ACTION_FAILURE,
- LAYER_VIEW,
- STAGE_VIEW,
- VIEW_TYPE_KEY,
-} from '~/pipelines/components/graph/constants';
-import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
-import PipelineGraphWrapper from '~/pipelines/components/graph/graph_component_wrapper.vue';
-import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
-import * as Api from '~/pipelines/components/graph_shared/api';
-import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-import * as parsingUtils from '~/pipelines/components/parsing_utils';
-import getPipelineHeaderData from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
-import * as sentryUtils from '~/pipelines/utils';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-import { mockRunningPipelineHeaderData } from '../mock_data';
-import {
- mapCallouts,
- mockCalloutsResponse,
- mockPipelineResponseWithTooManyJobs,
-} from './mock_data';
-
-const defaultProvide = {
- graphqlResourceEtag: 'frog/amphibirama/etag/',
- metricsPath: '',
- pipelineProjectPath: 'frog/amphibirama',
- pipelineIid: '22',
-};
-
-describe('Pipeline graph wrapper', () => {
- Vue.use(VueApollo);
- useLocalStorageSpy();
-
- let wrapper;
- let requestHandlers;
- let pipelineDetailsHandler;
-
- const findAlert = () => wrapper.findByTestId('error-alert');
- const findJobCountWarning = () => wrapper.findByTestId('job-count-warning');
- const findDependenciesToggle = () => wrapper.findByTestId('show-links-toggle');
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findLinksLayer = () => wrapper.findComponent(LinksLayer);
- const findGraph = () => wrapper.findComponent(PipelineGraph);
- const findStageColumnTitle = () => wrapper.findByTestId('stage-column-title');
- const findViewSelector = () => wrapper.findComponent(GraphViewSelector);
- const findViewSelectorToggle = () => findViewSelector().findComponent(GlToggle);
- const findViewSelectorTrip = () => findViewSelector().findComponent(GlAlert);
- const getLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
-
- const createComponent = ({
- apolloProvider,
- data = {},
- provide = {},
- mountFn = shallowMountExtended,
- } = {}) => {
- wrapper = mountFn(PipelineGraphWrapper, {
- provide: {
- ...defaultProvide,
- ...provide,
- },
- apolloProvider,
- data() {
- return {
- ...data,
- };
- },
- });
- };
-
- const createComponentWithApollo = ({
- calloutsList = [],
- data = {},
- mountFn = shallowMountExtended,
- provide = {},
- } = {}) => {
- const callouts = mapCallouts(calloutsList);
-
- requestHandlers = {
- getUserCalloutsHandler: jest.fn().mockResolvedValue(mockCalloutsResponse(callouts)),
- getPipelineHeaderDataHandler: jest.fn().mockResolvedValue(mockRunningPipelineHeaderData),
- getPipelineDetailsHandler: pipelineDetailsHandler,
- };
-
- const handlers = [
- [getPipelineHeaderData, requestHandlers.getPipelineHeaderDataHandler],
- [getPipelineDetails, requestHandlers.getPipelineDetailsHandler],
- [getUserCallouts, requestHandlers.getUserCalloutsHandler],
- ];
-
- const apolloProvider = createMockApollo(handlers);
- createComponent({ apolloProvider, data, provide, mountFn });
- };
-
- beforeEach(() => {
- pipelineDetailsHandler = jest.fn();
- pipelineDetailsHandler.mockResolvedValue(mockPipelineResponse);
- });
-
- describe('when data is loading', () => {
- beforeEach(() => {
- createComponentWithApollo();
- });
-
- it('displays the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not display the alert', () => {
- expect(findAlert().exists()).toBe(false);
- });
-
- it('does not display the graph', () => {
- expect(findGraph().exists()).toBe(false);
- });
-
- it('skips querying headerPipeline', () => {
- expect(wrapper.vm.$apollo.queries.headerPipeline.skip).toBe(true);
- });
- });
-
- describe('when data has loaded', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('does not display the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('does not display the alert', () => {
- expect(findAlert().exists()).toBe(false);
- });
-
- it('displays the graph', () => {
- expect(findGraph().exists()).toBe(true);
- });
-
- it('passes the etag resource and metrics path to the graph', () => {
- expect(findGraph().props('configPaths')).toMatchObject({
- graphqlResourceEtag: defaultProvide.graphqlResourceEtag,
- metricsPath: defaultProvide.metricsPath,
- });
- });
- });
-
- describe('when a stage has 100 jobs or more', () => {
- beforeEach(async () => {
- pipelineDetailsHandler.mockResolvedValue(mockPipelineResponseWithTooManyJobs);
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('show a warning alert', () => {
- expect(findJobCountWarning().exists()).toBe(true);
- expect(findJobCountWarning().props().title).toBe(
- 'Only the first 100 jobs per stage are displayed',
- );
- });
- });
-
- describe('when there is an error', () => {
- beforeEach(async () => {
- pipelineDetailsHandler.mockRejectedValue(new Error('GraphQL error'));
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('does not display the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('displays the alert', () => {
- expect(findAlert().exists()).toBe(true);
- });
-
- it('does not display the graph', () => {
- expect(findGraph().exists()).toBe(false);
- });
- });
-
- describe('when there is no pipeline iid available', () => {
- beforeEach(async () => {
- createComponentWithApollo({
- provide: {
- pipelineIid: '',
- },
- });
- await waitForPromises();
- });
-
- it('does not display the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('displays the no iid alert', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().text()).toBe(
- 'The data in this pipeline is too old to be rendered as a graph. Please check the Jobs tab to access historical data.',
- );
- });
-
- it('does not display the graph', () => {
- expect(findGraph().exists()).toBe(false);
- });
- });
-
- describe('events', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await waitForPromises();
- });
- describe('when receiving `setSkipRetryModal` event', () => {
- it('passes down `skipRetryModal` value as true', async () => {
- expect(findGraph().props('skipRetryModal')).toBe(false);
-
- await findGraph().vm.$emit('setSkipRetryModal');
-
- expect(findGraph().props('skipRetryModal')).toBe(true);
- });
- });
- });
-
- describe('when there is an error with an action in the graph', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await waitForPromises();
- await findGraph().vm.$emit('error', { type: ACTION_FAILURE });
- });
-
- it('does not display the loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('displays the action error alert', () => {
- expect(findAlert().exists()).toBe(true);
- expect(findAlert().text()).toBe('An error occurred while performing this action.');
- });
-
- it('displays the graph', () => {
- expect(findGraph().exists()).toBe(true);
- });
- });
-
- describe('when refresh action is emitted', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await waitForPromises();
- findGraph().vm.$emit('refreshPipelineGraph');
- });
-
- it('calls refetch', () => {
- expect(requestHandlers.getPipelineHeaderDataHandler).toHaveBeenCalledWith({
- fullPath: 'frog/amphibirama',
- iid: '22',
- });
- expect(requestHandlers.getPipelineDetailsHandler).toHaveBeenCalledTimes(2);
- expect(requestHandlers.getUserCalloutsHandler).toHaveBeenCalledWith({});
- });
- });
-
- describe('when query times out', () => {
- const advanceApolloTimers = async () => {
- jest.runOnlyPendingTimers();
- await waitForPromises();
- };
-
- beforeEach(async () => {
- const errorData = {
- data: {
- project: {
- pipelines: null,
- },
- },
- errors: [{ message: 'timeout' }],
- };
-
- pipelineDetailsHandler
- .mockResolvedValueOnce(errorData)
- .mockResolvedValueOnce(mockPipelineResponse)
- .mockResolvedValueOnce(errorData);
-
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('shows correct errors and does not overwrite populated data when data is empty', async () => {
- /* fails at first, shows error, no data yet */
- expect(findAlert().exists()).toBe(true);
- expect(findGraph().exists()).toBe(false);
-
- /* succeeds, clears error, shows graph */
- await advanceApolloTimers();
- expect(findAlert().exists()).toBe(false);
- expect(findGraph().exists()).toBe(true);
-
- /* fails again, alert returns but data persists */
- await advanceApolloTimers();
- expect(findAlert().exists()).toBe(true);
- expect(findGraph().exists()).toBe(true);
- });
- });
-
- describe('view dropdown', () => {
- describe('default', () => {
- let layersFn;
- beforeEach(async () => {
- layersFn = jest.spyOn(parsingUtils, 'listByLayers');
- createComponentWithApollo({
- mountFn: mountExtended,
- });
-
- await waitForPromises();
- });
-
- it('appears when pipeline uses needs', () => {
- expect(findViewSelector().exists()).toBe(true);
- });
-
- it('switches between views', async () => {
- expect(findStageColumnTitle().text()).toBe('deploy');
-
- await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
-
- expect(findStageColumnTitle().text()).toBe('');
- });
-
- it('saves the view type to local storage', async () => {
- await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
- expect(localStorage.setItem.mock.calls).toEqual([[VIEW_TYPE_KEY, LAYER_VIEW]]);
- });
-
- it('calls listByLayers only once no matter how many times view is switched', async () => {
- expect(layersFn).not.toHaveBeenCalled();
- await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
- expect(layersFn).toHaveBeenCalledTimes(1);
- await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW);
- await findViewSelector().vm.$emit('updateViewType', LAYER_VIEW);
- await findViewSelector().vm.$emit('updateViewType', STAGE_VIEW);
- expect(layersFn).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when layers view is selected', () => {
- beforeEach(async () => {
- createComponentWithApollo({
- data: {
- currentViewType: LAYER_VIEW,
- },
- mountFn: mountExtended,
- });
-
- jest.runOnlyPendingTimers();
- await waitForPromises();
- });
-
- it('sets showLinks to true', async () => {
- /* This spec uses .props for performance reasons. */
- expect(findLinksLayer().exists()).toBe(true);
- expect(findLinksLayer().props('showLinks')).toBe(false);
- expect(findViewSelector().props('type')).toBe(LAYER_VIEW);
- await findDependenciesToggle().vm.$emit('change', true);
-
- jest.runOnlyPendingTimers();
- await waitForPromises();
- expect(wrapper.findComponent(LinksLayer).props('showLinks')).toBe(true);
- });
- });
-
- describe('when layers view is selected, and links are active', () => {
- beforeEach(async () => {
- createComponentWithApollo({
- data: {
- currentViewType: LAYER_VIEW,
- showLinks: true,
- },
- mountFn: mountExtended,
- });
-
- await waitForPromises();
- });
-
- it('shows the hover tip in the view selector', async () => {
- await findViewSelectorToggle().vm.$emit('change', true);
- expect(findViewSelectorTrip().exists()).toBe(true);
- });
- });
-
- describe('when hover tip would otherwise show, but it has been previously dismissed', () => {
- beforeEach(async () => {
- createComponentWithApollo({
- data: {
- currentViewType: LAYER_VIEW,
- showLinks: true,
- },
- mountFn: mountExtended,
- calloutsList: ['pipeline_needs_hover_tip'.toUpperCase()],
- });
-
- jest.runOnlyPendingTimers();
- await waitForPromises();
- });
-
- it('does not show the hover tip', async () => {
- await findViewSelectorToggle().vm.$emit('change', true);
- expect(findViewSelectorTrip().exists()).toBe(false);
- });
- });
-
- describe('when feature flag is on and local storage is set', () => {
- beforeEach(async () => {
- localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW);
-
- createComponentWithApollo({
- mountFn: mountExtended,
- });
-
- await waitForPromises();
- });
-
- afterEach(() => {
- localStorage.clear();
- });
-
- it('sets the asString prop on the LocalStorageSync component', () => {
- expect(getLocalStorageSync().props('asString')).toBe(true);
- });
-
- it('reads the view type from localStorage when available', () => {
- const viewSelectorNeedsSegment = wrapper
- .findComponent(GlButtonGroup)
- .findAllComponents(GlButton)
- .at(1);
- expect(viewSelectorNeedsSegment.classes()).toContain('selected');
- });
- });
-
- describe('when feature flag is on and local storage is set, but the graph does not use needs', () => {
- beforeEach(async () => {
- const nonNeedsResponse = { ...mockPipelineResponse };
- nonNeedsResponse.data.project.pipeline.usesNeeds = false;
-
- localStorage.setItem(VIEW_TYPE_KEY, LAYER_VIEW);
-
- pipelineDetailsHandler.mockResolvedValue(nonNeedsResponse);
- createComponentWithApollo({
- mountFn: mountExtended,
- });
-
- await waitForPromises();
- });
-
- afterEach(() => {
- localStorage.clear();
- });
-
- it('still passes stage type to graph', () => {
- expect(findGraph().props('viewType')).toBe(STAGE_VIEW);
- });
- });
-
- describe('when feature flag is on but pipeline does not use needs', () => {
- beforeEach(async () => {
- const nonNeedsResponse = { ...mockPipelineResponse };
- nonNeedsResponse.data.project.pipeline.usesNeeds = false;
-
- pipelineDetailsHandler.mockResolvedValue(nonNeedsResponse);
- createComponentWithApollo({
- mountFn: mountExtended,
- });
-
- jest.runOnlyPendingTimers();
- await waitForPromises();
- });
-
- it('does not appear when pipeline does not use needs', () => {
- expect(findViewSelector().exists()).toBe(false);
- });
- });
- });
-
- describe('performance metrics', () => {
- const metricsPath = '/root/project/-/ci/prometheus_metrics/histograms.json';
- let markAndMeasure;
- let reportToSentry;
- let reportPerformance;
- let mock;
-
- beforeEach(() => {
- jest.spyOn(window, 'requestAnimationFrame').mockImplementation((cb) => cb());
- markAndMeasure = jest.spyOn(perfUtils, 'performanceMarkAndMeasure');
- reportToSentry = jest.spyOn(sentryUtils, 'reportToSentry');
- reportPerformance = jest.spyOn(Api, 'reportPerformance');
- });
-
- describe('with no metrics path', () => {
- beforeEach(async () => {
- createComponentWithApollo();
- await waitForPromises();
- });
-
- it('is not called', () => {
- expect(markAndMeasure).not.toHaveBeenCalled();
- expect(reportToSentry).not.toHaveBeenCalled();
- expect(reportPerformance).not.toHaveBeenCalled();
- });
- });
-
- describe('with metrics path', () => {
- const duration = 500;
- const numLinks = 3;
- const totalGroups = 7;
- const metricsData = {
- histograms: [
- { name: PIPELINES_DETAIL_LINK_DURATION, value: duration / 1000 },
- { name: PIPELINES_DETAIL_LINKS_TOTAL, value: numLinks },
- {
- name: PIPELINES_DETAIL_LINKS_JOB_RATIO,
- value: numLinks / totalGroups,
- },
- ],
- };
-
- describe('when no duration is obtained', () => {
- beforeEach(async () => {
- stubPerformanceWebAPI();
-
- createComponentWithApollo({
- provide: {
- metricsPath,
- glFeatures: {
- pipelineGraphLayersView: true,
- },
- },
- data: {
- currentViewType: LAYER_VIEW,
- },
- });
-
- await waitForPromises();
- });
-
- it('attempts to collect metrics', () => {
- expect(markAndMeasure).toHaveBeenCalled();
- expect(reportPerformance).not.toHaveBeenCalled();
- expect(reportToSentry).not.toHaveBeenCalled();
- });
- });
-
- describe('with duration and no error', () => {
- beforeEach(async () => {
- mock = new MockAdapter(axios);
- mock.onPost(metricsPath).reply(HTTP_STATUS_OK, {});
-
- jest.spyOn(window.performance, 'getEntriesByName').mockImplementation(() => {
- return [{ duration }];
- });
-
- createComponentWithApollo({
- provide: {
- metricsPath,
- },
- data: {
- currentViewType: LAYER_VIEW,
- },
- });
- await waitForPromises();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('calls reportPerformance with expected arguments', () => {
- expect(markAndMeasure).toHaveBeenCalled();
- expect(reportPerformance).toHaveBeenCalled();
- expect(reportPerformance).toHaveBeenCalledWith(metricsPath, metricsData);
- expect(reportToSentry).not.toHaveBeenCalled();
- });
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/graph_view_selector_spec.js b/spec/frontend/pipelines/graph/graph_view_selector_spec.js
deleted file mode 100644
index 65ae9d19978..00000000000
--- a/spec/frontend/pipelines/graph/graph_view_selector_spec.js
+++ /dev/null
@@ -1,217 +0,0 @@
-import { GlAlert, GlButton, GlButtonGroup, GlLoadingIcon } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import { LAYER_VIEW, STAGE_VIEW } from '~/pipelines/components/graph/constants';
-import GraphViewSelector from '~/pipelines/components/graph/graph_view_selector.vue';
-
-describe('the graph view selector component', () => {
- let wrapper;
-
- const findDependenciesToggle = () => wrapper.find('[data-testid="show-links-toggle"]');
- const findViewTypeSelector = () => wrapper.findComponent(GlButtonGroup);
- const findStageViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(0);
- const findLayerViewButton = () => findViewTypeSelector().findAllComponents(GlButton).at(1);
- const findSwitcherLoader = () => wrapper.find('[data-testid="switcher-loading-state"]');
- const findToggleLoader = () => findDependenciesToggle().findComponent(GlLoadingIcon);
- const findHoverTip = () => wrapper.findComponent(GlAlert);
-
- const defaultProps = {
- showLinks: false,
- tipPreviouslyDismissed: false,
- type: STAGE_VIEW,
- };
-
- const defaultData = {
- hoverTipDismissed: false,
- isToggleLoading: false,
- isSwitcherLoading: false,
- showLinksActive: false,
- };
-
- const createComponent = ({ data = {}, mountFn = shallowMount, props = {} } = {}) => {
- wrapper = mountFn(GraphViewSelector, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- data() {
- return {
- ...defaultData,
- ...data,
- };
- },
- });
- };
-
- describe('when showing stage view', () => {
- beforeEach(() => {
- createComponent({ mountFn: mount });
- });
-
- it('shows the Stage view button as selected', () => {
- expect(findStageViewButton().classes('selected')).toBe(true);
- });
-
- it('shows the Job dependencies view button not selected', () => {
- expect(findLayerViewButton().exists()).toBe(true);
- expect(findLayerViewButton().classes('selected')).toBe(false);
- });
-
- it('does not show the Job dependencies (links) toggle', () => {
- expect(findDependenciesToggle().exists()).toBe(false);
- });
- });
-
- describe('when showing Job dependencies view', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mount,
- props: {
- type: LAYER_VIEW,
- },
- });
- });
-
- it('shows the Job dependencies view as selected', () => {
- expect(findLayerViewButton().classes('selected')).toBe(true);
- });
-
- it('shows the Stage button as not selected', () => {
- expect(findStageViewButton().exists()).toBe(true);
- expect(findStageViewButton().classes('selected')).toBe(false);
- });
-
- it('shows the Job dependencies (links) toggle', () => {
- expect(findDependenciesToggle().exists()).toBe(true);
- });
- });
-
- describe('events', () => {
- beforeEach(() => {
- createComponent({
- mountFn: mount,
- props: {
- type: LAYER_VIEW,
- },
- });
- });
-
- it('shows loading state and emits updateViewType when view type toggled', async () => {
- expect(wrapper.emitted().updateViewType).toBeUndefined();
- expect(findSwitcherLoader().exists()).toBe(false);
-
- await findStageViewButton().trigger('click');
- /*
- Loading happens before the event is emitted or timers are run.
- Then we run the timer because the event is emitted in setInterval
- which is what gives the loader a chace to show up.
- */
- expect(findSwitcherLoader().exists()).toBe(true);
- jest.runOnlyPendingTimers();
-
- expect(wrapper.emitted().updateViewType).toHaveLength(1);
- expect(wrapper.emitted().updateViewType).toEqual([[STAGE_VIEW]]);
- });
-
- it('shows loading state and emits updateShowLinks when show links toggle is clicked', async () => {
- expect(wrapper.emitted().updateShowLinksState).toBeUndefined();
- expect(findToggleLoader().exists()).toBe(false);
-
- await findDependenciesToggle().vm.$emit('change', true);
- /*
- Loading happens before the event is emitted or timers are run.
- Then we run the timer because the event is emitted in setInterval
- which is what gives the loader a chace to show up.
- */
- expect(findToggleLoader().exists()).toBe(true);
- jest.runOnlyPendingTimers();
-
- expect(wrapper.emitted().updateShowLinksState).toHaveLength(1);
- expect(wrapper.emitted().updateShowLinksState).toEqual([[true]]);
- });
-
- it('does not emit an event if the click occurs on the currently selected view button', async () => {
- expect(wrapper.emitted().updateShowLinksState).toBeUndefined();
-
- await findLayerViewButton().trigger('click');
-
- expect(wrapper.emitted().updateShowLinksState).toBeUndefined();
- });
- });
-
- describe('hover tip callout', () => {
- describe('when links are live and it has not been previously dismissed', () => {
- beforeEach(() => {
- createComponent({
- props: {
- showLinks: true,
- type: LAYER_VIEW,
- },
- data: {
- showLinksActive: true,
- },
- mountFn: mount,
- });
- });
-
- it('is displayed', () => {
- expect(findHoverTip().exists()).toBe(true);
- expect(findHoverTip().text()).toBe(wrapper.vm.$options.i18n.hoverTipText);
- });
-
- it('emits dismissHoverTip event when the tip is dismissed', async () => {
- expect(wrapper.emitted().dismissHoverTip).toBeUndefined();
- await findHoverTip().find('button').trigger('click');
- expect(wrapper.emitted().dismissHoverTip).toHaveLength(1);
- });
-
- it('is displayed at first then hidden on swith to STAGE_VIEW then displayed on switch to LAYER_VIEW', async () => {
- expect(findHoverTip().exists()).toBe(true);
- expect(findHoverTip().text()).toBe(wrapper.vm.$options.i18n.hoverTipText);
-
- await findStageViewButton().trigger('click');
- expect(findHoverTip().exists()).toBe(false);
-
- await findLayerViewButton().trigger('click');
- expect(findHoverTip().exists()).toBe(true);
- expect(findHoverTip().text()).toBe(wrapper.vm.$options.i18n.hoverTipText);
- });
- });
-
- describe('when links are live and it has been previously dismissed', () => {
- beforeEach(() => {
- createComponent({
- props: {
- showLinks: true,
- tipPreviouslyDismissed: true,
- type: LAYER_VIEW,
- },
- data: {
- showLinksActive: true,
- },
- });
- });
-
- it('is not displayed', () => {
- expect(findHoverTip().exists()).toBe(false);
- });
- });
-
- describe('when links are not live', () => {
- beforeEach(() => {
- createComponent({
- props: {
- showLinks: true,
- type: LAYER_VIEW,
- },
- data: {
- showLinksActive: false,
- },
- });
- });
-
- it('is not displayed', () => {
- expect(findHoverTip().exists()).toBe(false);
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/job_group_dropdown_spec.js b/spec/frontend/pipelines/graph/job_group_dropdown_spec.js
deleted file mode 100644
index 1419a7b9982..00000000000
--- a/spec/frontend/pipelines/graph/job_group_dropdown_spec.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import JobGroupDropdown from '~/pipelines/components/graph/job_group_dropdown.vue';
-
-describe('job group dropdown component', () => {
- const group = {
- jobs: [
- {
- id: 4256,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
- },
- {
- id: 4299,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4299',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4299/retry',
- method: 'post',
- },
- },
- },
- ],
- name: 'rspec:linux',
- size: 2,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4256',
- has_details: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
- };
-
- let wrapper;
- const findButton = () => wrapper.find('button');
-
- const createComponent = ({ mountFn = shallowMount }) => {
- wrapper = mountFn(JobGroupDropdown, { propsData: { group } });
- };
-
- beforeEach(() => {
- createComponent({ mountFn: mount });
- });
-
- it('renders button with group name and size', () => {
- expect(findButton().text()).toContain(group.name);
- expect(findButton().text()).toContain(group.size.toString());
- });
-
- it('renders dropdown with jobs', () => {
- expect(wrapper.findAll('.scrollable-menu>ul>li').length).toBe(group.jobs.length);
- });
-});
diff --git a/spec/frontend/pipelines/graph/job_item_spec.js b/spec/frontend/pipelines/graph/job_item_spec.js
deleted file mode 100644
index 8a8b0e9aa63..00000000000
--- a/spec/frontend/pipelines/graph/job_item_spec.js
+++ /dev/null
@@ -1,492 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
-import { GlBadge, GlModal, GlToast } from '@gitlab/ui';
-import JobItem from '~/pipelines/components/graph/job_item.vue';
-import axios from '~/lib/utils/axios_utils';
-import { useLocalStorageSpy } from 'helpers/local_storage_helper';
-import ActionComponent from '~/pipelines/components/jobs_shared/action_component.vue';
-
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import {
- delayedJob,
- mockJob,
- mockJobWithoutDetails,
- mockJobWithUnauthorizedAction,
- mockFailedJob,
- triggerJob,
- triggerJobWithRetryAction,
-} from './mock_data';
-
-describe('pipeline graph job item', () => {
- useLocalStorageSpy();
- Vue.use(GlToast);
-
- let wrapper;
- let mockAxios;
-
- const findJobWithoutLink = () => wrapper.findByTestId('job-without-link');
- const findJobWithLink = () => wrapper.findByTestId('job-with-link');
- const findActionVueComponent = () => wrapper.findComponent(ActionComponent);
- const findActionComponent = () => wrapper.findByTestId('ci-action-component');
- const findBadge = () => wrapper.findComponent(GlBadge);
- const findJobLink = () => wrapper.findByTestId('job-with-link');
- const findModal = () => wrapper.findComponent(GlModal);
-
- const clickOnModalPrimaryBtn = () => findModal().vm.$emit('primary');
- const clickOnModalCancelBtn = () => findModal().vm.$emit('hide');
- const clickOnModalCloseBtn = () => findModal().vm.$emit('close');
-
- const myCustomClass1 = 'my-class-1';
- const myCustomClass2 = 'my-class-2';
-
- const defaultProps = {
- job: mockJob,
- };
-
- const createWrapper = ({ props, data, mountFn = mountExtended, mocks = {} } = {}) => {
- wrapper = mountFn(JobItem, {
- data() {
- return {
- ...data,
- };
- },
- propsData: {
- ...defaultProps,
- ...props,
- },
- mocks: {
- ...mocks,
- },
- });
- };
-
- const triggerActiveClass = 'gl-shadow-x0-y0-b3-s1-blue-500';
-
- beforeEach(() => {
- mockAxios = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mockAxios.restore();
- });
-
- describe('name with link', () => {
- it('should render the job name and status with a link', async () => {
- createWrapper();
-
- await nextTick();
- const link = findJobLink();
-
- expect(link.attributes('href')).toBe(mockJob.status.detailsPath);
-
- expect(link.attributes('title')).toBe(`${mockJob.name} - ${mockJob.status.label}`);
-
- expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
-
- expect(wrapper.text()).toBe(mockJob.name);
- });
- });
-
- describe('name without link', () => {
- beforeEach(() => {
- createWrapper({
- props: {
- job: mockJobWithoutDetails,
- cssClassJobName: 'css-class-job-name',
- jobHovered: 'test',
- },
- });
- });
-
- it('should render status and name', () => {
- expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
- expect(findJobLink().exists()).toBe(false);
-
- expect(wrapper.text()).toBe(mockJobWithoutDetails.name);
- });
-
- it('should apply hover class and provided class name', () => {
- expect(findJobWithoutLink().classes()).toContain('css-class-job-name');
- });
- });
-
- describe('action icon', () => {
- it('should render the action icon', () => {
- createWrapper();
-
- const actionComponent = findActionComponent();
-
- expect(actionComponent.exists()).toBe(true);
- expect(actionComponent.props('actionIcon')).toBe('retry');
- expect(actionComponent.attributes('disabled')).toBeUndefined();
- });
-
- it('should render disabled action icon when user cannot run the action', () => {
- createWrapper({
- props: {
- job: mockJobWithUnauthorizedAction,
- },
- });
-
- const actionComponent = findActionComponent();
-
- expect(actionComponent.exists()).toBe(true);
- expect(actionComponent.props('actionIcon')).toBe('stop');
- expect(actionComponent.attributes('disabled')).toBeDefined();
- });
-
- it('action icon tooltip text when job has passed but can be ran again', () => {
- createWrapper({ props: { job: mockJob } });
-
- expect(findActionComponent().props('tooltipText')).toBe('Run again');
- });
-
- it('action icon tooltip text when job has failed and can be retried', () => {
- createWrapper({ props: { job: mockFailedJob } });
-
- expect(findActionComponent().props('tooltipText')).toBe('Retry');
- });
- });
-
- describe('job style', () => {
- beforeEach(() => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: 'css-class-job-name',
- },
- });
- });
-
- it('should render provided class name', () => {
- expect(findJobLink().classes()).toContain('css-class-job-name');
- });
-
- it('does not show a badge on the job item', () => {
- expect(findBadge().exists()).toBe(false);
- });
-
- it('does not apply the trigger job class', () => {
- expect(findJobWithLink().classes()).not.toContain('gl-rounded-lg');
- });
- });
-
- describe('status label', () => {
- it('should not render status label when it is not provided', () => {
- createWrapper({
- props: {
- job: {
- id: 4258,
- name: 'test',
- status: {
- icon: 'status_success',
- },
- },
- },
- });
-
- expect(findJobWithoutLink().attributes('title')).toBe('test');
- });
-
- it('should not render status label when it is provided', () => {
- createWrapper({
- props: {
- job: {
- id: 4259,
- name: 'test',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: 'success',
- },
- },
- },
- });
-
- expect(findJobWithoutLink().attributes('title')).toBe('test - success');
- });
- });
-
- describe('for delayed job', () => {
- it('displays remaining time in tooltip', () => {
- createWrapper({
- props: {
- job: delayedJob,
- },
- });
-
- expect(findJobWithLink().attributes('title')).toBe(
- `delayed job - delayed manual action (00:00:00)`,
- );
- });
- });
-
- describe('trigger job', () => {
- describe('card', () => {
- beforeEach(() => {
- createWrapper({
- props: {
- job: triggerJob,
- },
- });
- });
-
- it('shows a badge on the job item', () => {
- expect(findBadge().exists()).toBe(true);
- expect(findBadge().text()).toBe('Trigger job');
- });
-
- it('applies a rounded corner style instead of the usual pill shape', () => {
- expect(findJobWithoutLink().classes()).toContain('gl-rounded-lg');
- });
- });
-
- describe('when retrying', () => {
- const mockToastShow = jest.fn();
-
- beforeEach(async () => {
- createWrapper({
- mountFn: shallowMountExtended,
- props: {
- skipRetryModal: true,
- job: triggerJobWithRetryAction,
- },
- mocks: {
- $toast: {
- show: mockToastShow,
- },
- },
- });
-
- await findActionVueComponent().vm.$emit('pipelineActionRequestComplete');
- await nextTick();
- });
-
- it('shows a toast message that the downstream is being created', () => {
- expect(mockToastShow).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('highlighting', () => {
- it.each`
- job | jobName | expanded | link
- ${mockJob} | ${mockJob.name} | ${true} | ${true}
- ${mockJobWithoutDetails} | ${mockJobWithoutDetails.name} | ${true} | ${false}
- `(
- `trigger job should stay highlighted when downstream is expanded`,
- ({ job, jobName, expanded, link }) => {
- createWrapper({
- props: {
- job,
- pipelineExpanded: { jobName, expanded },
- },
- });
- const findJobEl = link ? findJobWithLink : findJobWithoutLink;
-
- expect(findJobEl().classes()).toContain(triggerActiveClass);
- },
- );
-
- it.each`
- job | jobName | expanded | link
- ${mockJob} | ${mockJob.name} | ${false} | ${true}
- ${mockJobWithoutDetails} | ${mockJobWithoutDetails.name} | ${false} | ${false}
- `(
- `trigger job should not be highlighted when downstream is not expanded`,
- ({ job, jobName, expanded, link }) => {
- createWrapper({
- props: {
- job,
- pipelineExpanded: { jobName, expanded },
- },
- });
- const findJobEl = link ? findJobWithLink : findJobWithoutLink;
-
- expect(findJobEl().classes()).not.toContain(triggerActiveClass);
- },
- );
- });
- });
-
- describe('job classes', () => {
- it('job class is shown', () => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: 'my-class',
- },
- });
-
- const jobLinkEl = findJobLink();
-
- expect(jobLinkEl.classes()).toContain('my-class');
-
- expect(jobLinkEl.classes()).not.toContain(triggerActiveClass);
- });
-
- it('job class is shown, along with hover', () => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: 'my-class',
- sourceJobHovered: mockJob.name,
- },
- });
-
- const jobLinkEl = findJobLink();
-
- expect(jobLinkEl.classes()).toContain('my-class');
- expect(jobLinkEl.classes()).toContain(triggerActiveClass);
- });
-
- it('multiple job classes are shown', () => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: [myCustomClass1, myCustomClass2],
- },
- });
-
- const jobLinkEl = findJobLink();
-
- expect(jobLinkEl.classes()).toContain(myCustomClass1);
- expect(jobLinkEl.classes()).toContain(myCustomClass2);
-
- expect(jobLinkEl.classes()).not.toContain(triggerActiveClass);
- });
-
- it('multiple job classes are shown conditionally', () => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: { [myCustomClass1]: true, [myCustomClass2]: true },
- },
- });
-
- const jobLinkEl = findJobLink();
-
- expect(jobLinkEl.classes()).toContain(myCustomClass1);
- expect(jobLinkEl.classes()).toContain(myCustomClass2);
-
- expect(jobLinkEl.classes()).not.toContain(triggerActiveClass);
- });
-
- it('multiple job classes are shown, along with a hover', () => {
- createWrapper({
- props: {
- job: mockJob,
- cssClassJobName: [myCustomClass1, myCustomClass2],
- sourceJobHovered: mockJob.name,
- },
- });
-
- const jobLinkEl = findJobLink();
-
- expect(jobLinkEl.classes()).toContain(myCustomClass1);
- expect(jobLinkEl.classes()).toContain(myCustomClass2);
- expect(jobLinkEl.classes()).toContain(triggerActiveClass);
- });
- });
-
- describe('confirmation modal', () => {
- describe('when clicking on the action component', () => {
- it.each`
- skipRetryModal | exists | visibilityText
- ${false} | ${true} | ${'shows'}
- ${true} | ${false} | ${'hides'}
- `(
- '$visibilityText the modal when `skipRetryModal` is $skipRetryModal',
- async ({ exists, skipRetryModal }) => {
- createWrapper({
- props: {
- skipRetryModal,
- job: triggerJobWithRetryAction,
- },
- });
- await findActionComponent().trigger('click');
-
- expect(findModal().exists()).toBe(exists);
- },
- );
- });
-
- describe('when showing the modal', () => {
- it.each`
- buttonName | shouldTriggerActionClick | actionBtn
- ${'primary'} | ${true} | ${clickOnModalPrimaryBtn}
- ${'cancel'} | ${false} | ${clickOnModalCancelBtn}
- ${'close'} | ${false} | ${clickOnModalCloseBtn}
- `(
- 'clicking on $buttonName will pass down shouldTriggerActionClick as $shouldTriggerActionClick to the action component',
- async ({ shouldTriggerActionClick, actionBtn }) => {
- createWrapper({
- props: {
- skipRetryModal: false,
- job: triggerJobWithRetryAction,
- },
- });
- await findActionComponent().trigger('click');
-
- await actionBtn();
-
- expect(findActionComponent().props().shouldTriggerClick).toBe(shouldTriggerActionClick);
- },
- );
- });
-
- describe('when not checking the "do not show this again" checkbox', () => {
- it.each`
- actionName | actionBtn
- ${'closing'} | ${clickOnModalCloseBtn}
- ${'cancelling'} | ${clickOnModalCancelBtn}
- ${'confirming'} | ${clickOnModalPrimaryBtn}
- `(
- 'does not emit any event and will not modify localstorage on $actionName',
- async ({ actionBtn }) => {
- createWrapper({
- props: {
- skipRetryModal: false,
- job: triggerJobWithRetryAction,
- },
- });
- await findActionComponent().trigger('click');
- await actionBtn();
-
- expect(wrapper.emitted().setSkipRetryModal).toBeUndefined();
- expect(localStorage.setItem).not.toHaveBeenCalled();
- },
- );
- });
-
- describe('when checking the "do not show this again" checkbox', () => {
- it.each`
- actionName | actionBtn
- ${'closing'} | ${clickOnModalCloseBtn}
- ${'cancelling'} | ${clickOnModalCancelBtn}
- ${'confirming'} | ${clickOnModalPrimaryBtn}
- `(
- 'emits "setSkipRetryModal" and set local storage key on $actionName the modal',
- async ({ actionBtn }) => {
- // We are passing the checkbox as a slot to the GlModal.
- // The way GlModal is mounted, we can neither click on the box
- // or emit an event directly. We therefore set the data property
- // as it would be if the box was checked.
- createWrapper({
- data: {
- currentSkipModalValue: true,
- },
- props: {
- skipRetryModal: false,
- job: triggerJobWithRetryAction,
- },
- });
- await findActionComponent().trigger('click');
- await actionBtn();
-
- expect(wrapper.emitted().setSkipRetryModal).toHaveLength(1);
- expect(localStorage.setItem).toHaveBeenCalledWith('skip_retry_modal', 'true');
- },
- );
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/job_name_component_spec.js b/spec/frontend/pipelines/graph/job_name_component_spec.js
deleted file mode 100644
index fca4c43d9fa..00000000000
--- a/spec/frontend/pipelines/graph/job_name_component_spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import { mount } from '@vue/test-utils';
-import jobNameComponent from '~/pipelines/components/jobs_shared/job_name_component.vue';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-
-describe('job name component', () => {
- let wrapper;
-
- const propsData = {
- name: 'foo',
- status: {
- icon: 'status_success',
- group: 'success',
- },
- };
-
- beforeEach(() => {
- wrapper = mount(jobNameComponent, {
- propsData,
- });
- });
-
- it('should render the provided name', () => {
- expect(wrapper.text()).toBe(propsData.name);
- });
-
- it('should render an icon with the provided status', () => {
- expect(wrapper.findComponent(CiIcon).exists()).toBe(true);
- expect(wrapper.find('.ci-status-icon-success').exists()).toBe(true);
- });
-});
diff --git a/spec/frontend/pipelines/graph/linked_pipeline_spec.js b/spec/frontend/pipelines/graph/linked_pipeline_spec.js
deleted file mode 100644
index 8dae2aac664..00000000000
--- a/spec/frontend/pipelines/graph/linked_pipeline_spec.js
+++ /dev/null
@@ -1,464 +0,0 @@
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { GlButton, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
-import { createWrapper } from '@vue/test-utils';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { BV_HIDE_TOOLTIP } from '~/lib/utils/constants';
-import { ACTION_FAILURE, UPSTREAM, DOWNSTREAM } from '~/pipelines/components/graph/constants';
-import LinkedPipelineComponent from '~/pipelines/components/graph/linked_pipeline.vue';
-import CancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql';
-import RetryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql';
-import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import mockPipeline from './linked_pipelines_mock_data';
-
-describe('Linked pipeline', () => {
- let wrapper;
- let requestHandlers;
-
- const downstreamProps = {
- pipeline: {
- ...mockPipeline,
- multiproject: false,
- },
- columnTitle: 'Downstream',
- type: DOWNSTREAM,
- expanded: false,
- isLoading: false,
- };
-
- const upstreamProps = {
- ...downstreamProps,
- columnTitle: 'Upstream',
- type: UPSTREAM,
- };
-
- const findButton = () => wrapper.findComponent(GlButton);
- const findCancelButton = () => wrapper.findByLabelText('Cancel downstream pipeline');
- const findCardTooltip = () => wrapper.findComponent(GlTooltip);
- const findDownstreamPipelineTitle = () => wrapper.findByTestId('downstream-title');
- const findExpandButton = () => wrapper.findByTestId('expand-pipeline-button');
- const findLinkedPipeline = () => wrapper.findComponent({ ref: 'linkedPipeline' });
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findPipelineLabel = () => wrapper.findByTestId('downstream-pipeline-label');
- const findPipelineLink = () => wrapper.findByTestId('pipelineLink');
- const findRetryButton = () => wrapper.findByLabelText('Retry downstream pipeline');
-
- const defaultHandlers = {
- cancelPipeline: jest.fn().mockResolvedValue({ data: { pipelineCancel: { errors: [] } } }),
- retryPipeline: jest.fn().mockResolvedValue({ data: { pipelineRetry: { errors: [] } } }),
- };
-
- const createMockApolloProvider = (handlers) => {
- Vue.use(VueApollo);
-
- requestHandlers = handlers;
- return createMockApollo([
- [CancelPipelineMutation, requestHandlers.cancelPipeline],
- [RetryPipelineMutation, requestHandlers.retryPipeline],
- ]);
- };
-
- const createComponent = ({ propsData, handlers = defaultHandlers }) => {
- const mockApollo = createMockApolloProvider(handlers);
-
- wrapper = mountExtended(LinkedPipelineComponent, {
- propsData,
- apolloProvider: mockApollo,
- });
- };
-
- describe('rendered output', () => {
- const props = {
- pipeline: mockPipeline,
- columnTitle: 'Downstream',
- type: DOWNSTREAM,
- expanded: false,
- isLoading: false,
- };
-
- beforeEach(() => {
- createComponent({ propsData: props });
- });
-
- it('should render the project name', () => {
- expect(wrapper.text()).toContain(props.pipeline.project.name);
- });
-
- it('should render an svg within the status container', () => {
- const pipelineStatusElement = wrapper.findComponent(CiIcon);
-
- expect(pipelineStatusElement.find('svg').exists()).toBe(true);
- });
-
- it('should render the pipeline status icon svg', () => {
- expect(wrapper.find('.ci-status-icon-success svg').exists()).toBe(true);
- });
-
- it('should have a ci-status child component', () => {
- expect(wrapper.findComponent(CiIcon).exists()).toBe(true);
- });
-
- it('should render the pipeline id', () => {
- expect(wrapper.text()).toContain(`#${props.pipeline.id}`);
- });
-
- it('adds the card tooltip text to the DOM', () => {
- expect(findCardTooltip().exists()).toBe(true);
-
- expect(findCardTooltip().text()).toContain(mockPipeline.project.name);
- expect(findCardTooltip().text()).toContain(mockPipeline.status.label);
- expect(findCardTooltip().text()).toContain(mockPipeline.sourceJob.name);
- expect(findCardTooltip().text()).toContain(mockPipeline.id.toString());
- });
-
- it('should display multi-project label when pipeline project id is not the same as triggered pipeline project id', () => {
- expect(findPipelineLabel().text()).toBe('Multi-project');
- });
- });
-
- describe('upstream pipelines', () => {
- beforeEach(() => {
- createComponent({ propsData: upstreamProps });
- });
-
- it('should display parent label when pipeline project id is the same as triggered_by pipeline project id', () => {
- expect(findPipelineLabel().exists()).toBe(true);
- });
-
- it('upstream pipeline should contain the correct link', () => {
- expect(findPipelineLink().attributes('href')).toBe(upstreamProps.pipeline.path);
- });
-
- it('applies the reverse-row css class to the card', () => {
- expect(findLinkedPipeline().classes()).toContain('gl-flex-direction-row-reverse');
- expect(findLinkedPipeline().classes()).not.toContain('gl-flex-direction-row');
- });
- });
-
- describe('downstream pipelines', () => {
- describe('styling', () => {
- beforeEach(() => {
- createComponent({ propsData: downstreamProps });
- });
-
- it('parent/child label container should exist', () => {
- expect(findPipelineLabel().exists()).toBe(true);
- });
-
- it('should display child label when pipeline project id is the same as triggered pipeline project id', () => {
- expect(findPipelineLabel().exists()).toBe(true);
- });
-
- it('should have the name of the trigger job on the card when it is a child pipeline', () => {
- expect(findDownstreamPipelineTitle().text()).toBe(mockPipeline.sourceJob.name);
- });
-
- it('downstream pipeline should contain the correct link', () => {
- expect(findPipelineLink().attributes('href')).toBe(downstreamProps.pipeline.path);
- });
-
- it('applies the flex-row css class to the card', () => {
- expect(findLinkedPipeline().classes()).toContain('gl-flex-direction-row');
- expect(findLinkedPipeline().classes()).not.toContain('gl-flex-direction-row-reverse');
- });
- });
-
- describe('action button', () => {
- describe('with permissions', () => {
- describe('on an upstream', () => {
- describe('when retryable', () => {
- beforeEach(() => {
- const retryablePipeline = {
- ...upstreamProps,
- pipeline: { ...mockPipeline, retryable: true },
- };
-
- createComponent({ propsData: retryablePipeline });
- });
-
- it('does not show the retry or cancel button', () => {
- expect(findCancelButton().exists()).toBe(false);
- expect(findRetryButton().exists()).toBe(false);
- });
- });
- });
-
- describe('on a downstream', () => {
- const retryablePipeline = {
- ...downstreamProps,
- pipeline: { ...mockPipeline, retryable: true },
- };
-
- describe('when retryable', () => {
- beforeEach(() => {
- createComponent({ propsData: retryablePipeline });
- });
-
- it('shows only the retry button', () => {
- expect(findCancelButton().exists()).toBe(false);
- expect(findRetryButton().exists()).toBe(true);
- });
-
- it.each`
- findElement | name
- ${findRetryButton} | ${'retry button'}
- ${findExpandButton} | ${'expand button'}
- `('hides the card tooltip when $name is hovered', async ({ findElement }) => {
- expect(findCardTooltip().exists()).toBe(true);
-
- await findElement().trigger('mouseover');
-
- expect(findCardTooltip().exists()).toBe(false);
- });
-
- describe('and the retry button is clicked', () => {
- describe('on success', () => {
- beforeEach(async () => {
- await findRetryButton().trigger('click');
- });
-
- it('calls the retry mutation', () => {
- expect(requestHandlers.retryPipeline).toHaveBeenCalledTimes(1);
- expect(requestHandlers.retryPipeline).toHaveBeenCalledWith({
- id: 'gid://gitlab/Ci::Pipeline/195',
- });
- });
-
- it('emits the refreshPipelineGraph event', async () => {
- await waitForPromises();
- expect(wrapper.emitted('refreshPipelineGraph')).toHaveLength(1);
- });
- });
-
- describe('on failure', () => {
- beforeEach(async () => {
- createComponent({
- propsData: retryablePipeline,
- handlers: {
- retryPipeline: jest.fn().mockRejectedValue({ errors: [] }),
- cancelPipeline: jest.fn().mockRejectedValue({ errors: [] }),
- },
- });
-
- await findRetryButton().trigger('click');
- });
-
- it('emits an error event', async () => {
- await waitForPromises();
- expect(wrapper.emitted('error')).toEqual([[{ type: ACTION_FAILURE }]]);
- });
- });
- });
- });
-
- describe('when cancelable', () => {
- const cancelablePipeline = {
- ...downstreamProps,
- pipeline: { ...mockPipeline, cancelable: true },
- };
-
- beforeEach(() => {
- createComponent({ propsData: cancelablePipeline });
- });
-
- it('shows only the cancel button', () => {
- expect(findCancelButton().exists()).toBe(true);
- expect(findRetryButton().exists()).toBe(false);
- });
-
- it.each`
- findElement | name
- ${findCancelButton} | ${'cancel button'}
- ${findExpandButton} | ${'expand button'}
- `('hides the card tooltip when $name is hovered', async ({ findElement }) => {
- expect(findCardTooltip().exists()).toBe(true);
-
- await findElement().trigger('mouseover');
-
- expect(findCardTooltip().exists()).toBe(false);
- });
-
- describe('and the cancel button is clicked', () => {
- describe('on success', () => {
- beforeEach(async () => {
- await findCancelButton().trigger('click');
- });
-
- it('calls the cancel mutation', () => {
- expect(requestHandlers.cancelPipeline).toHaveBeenCalledTimes(1);
- expect(requestHandlers.cancelPipeline).toHaveBeenCalledWith({
- id: 'gid://gitlab/Ci::Pipeline/195',
- });
- });
- it('emits the refreshPipelineGraph event', async () => {
- await waitForPromises();
- expect(wrapper.emitted('refreshPipelineGraph')).toHaveLength(1);
- });
- });
-
- describe('on failure', () => {
- beforeEach(async () => {
- createComponent({
- propsData: cancelablePipeline,
- handlers: {
- retryPipeline: jest.fn().mockRejectedValue({ errors: [] }),
- cancelPipeline: jest.fn().mockRejectedValue({ errors: [] }),
- },
- });
-
- await findCancelButton().trigger('click');
- });
-
- it('emits an error event', async () => {
- await waitForPromises();
- expect(wrapper.emitted('error')).toEqual([[{ type: ACTION_FAILURE }]]);
- });
- });
- });
- });
-
- describe('when both cancellable and retryable', () => {
- beforeEach(() => {
- const pipelineWithTwoActions = {
- ...downstreamProps,
- pipeline: { ...mockPipeline, cancelable: true, retryable: true },
- };
-
- createComponent({ propsData: pipelineWithTwoActions });
- });
-
- it('only shows the cancel button', () => {
- expect(findRetryButton().exists()).toBe(false);
- expect(findCancelButton().exists()).toBe(true);
- });
- });
- });
- });
-
- describe('without permissions', () => {
- beforeEach(() => {
- const pipelineWithTwoActions = {
- ...downstreamProps,
- pipeline: {
- ...mockPipeline,
- cancelable: true,
- retryable: true,
- userPermissions: { updatePipeline: false },
- },
- };
-
- createComponent({ propsData: pipelineWithTwoActions });
- });
-
- it('does not show any action button', () => {
- expect(findRetryButton().exists()).toBe(false);
- expect(findCancelButton().exists()).toBe(false);
- });
- });
- });
- });
-
- describe('expand button', () => {
- it.each`
- pipelineType | chevronPosition | buttonBorderClasses | expanded
- ${downstreamProps} | ${'chevron-lg-right'} | ${'gl-border-l-0!'} | ${false}
- ${downstreamProps} | ${'chevron-lg-left'} | ${'gl-border-l-0!'} | ${true}
- ${upstreamProps} | ${'chevron-lg-left'} | ${'gl-border-r-0!'} | ${false}
- ${upstreamProps} | ${'chevron-lg-right'} | ${'gl-border-r-0!'} | ${true}
- `(
- '$pipelineType.columnTitle pipeline button icon should be $chevronPosition with $buttonBorderClasses if expanded state is $expanded',
- ({ pipelineType, chevronPosition, buttonBorderClasses, expanded }) => {
- createComponent({ propsData: { ...pipelineType, expanded } });
- expect(findExpandButton().props('icon')).toBe(chevronPosition);
- expect(findExpandButton().classes()).toContain(buttonBorderClasses);
- },
- );
-
- describe('shadow border', () => {
- beforeEach(() => {
- createComponent({ propsData: downstreamProps });
- });
-
- it.each`
- activateEventName | deactivateEventName
- ${'mouseover'} | ${'mouseout'}
- ${'focus'} | ${'blur'}
- `(
- 'applies the class on $activateEventName and removes it on $deactivateEventName',
- async ({ activateEventName, deactivateEventName }) => {
- const shadowClass = 'gl-shadow-none!';
-
- expect(findExpandButton().classes()).toContain(shadowClass);
-
- await findExpandButton().vm.$emit(activateEventName);
- expect(findExpandButton().classes()).not.toContain(shadowClass);
-
- await findExpandButton().vm.$emit(deactivateEventName);
- expect(findExpandButton().classes()).toContain(shadowClass);
- },
- );
- });
- });
-
- describe('when isLoading is true', () => {
- const props = {
- pipeline: mockPipeline,
- columnTitle: 'Downstream',
- type: DOWNSTREAM,
- expanded: false,
- isLoading: true,
- };
-
- beforeEach(() => {
- createComponent({ propsData: props });
- });
-
- it('loading icon is visible', () => {
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('on click/hover', () => {
- const props = {
- pipeline: mockPipeline,
- columnTitle: 'Downstream',
- type: DOWNSTREAM,
- expanded: false,
- isLoading: false,
- };
-
- beforeEach(() => {
- createComponent({ propsData: props });
- });
-
- it('emits `pipelineClicked` event', () => {
- findButton().trigger('click');
-
- expect(wrapper.emitted('pipelineClicked')).toHaveLength(1);
- });
-
- it(`should emit ${BV_HIDE_TOOLTIP} to close the tooltip`, async () => {
- const root = createWrapper(wrapper.vm.$root);
- await findButton().vm.$emit('click');
-
- expect(root.emitted(BV_HIDE_TOOLTIP)).toHaveLength(1);
- });
-
- it('should emit downstreamHovered with job name on mouseover', () => {
- findLinkedPipeline().trigger('mouseover');
- expect(wrapper.emitted('downstreamHovered')).toStrictEqual([['test_c']]);
- });
-
- it('should emit downstreamHovered with empty string on mouseleave', () => {
- findLinkedPipeline().trigger('mouseleave');
- expect(wrapper.emitted('downstreamHovered')).toStrictEqual([['']]);
- });
-
- it('should emit pipelineExpanded with job name and expanded state on click', () => {
- findExpandButton().trigger('click');
- expect(wrapper.emitted('pipelineExpandToggle')).toStrictEqual([['test_c', true]]);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js b/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
deleted file mode 100644
index bcea140f2dd..00000000000
--- a/spec/frontend/pipelines/graph/linked_pipelines_column_spec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import { mount, shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import getPipelineDetails from 'shared_queries/pipelines/get_pipeline_details.query.graphql';
-import {
- DOWNSTREAM,
- UPSTREAM,
- LAYER_VIEW,
- STAGE_VIEW,
-} from '~/pipelines/components/graph/constants';
-import PipelineGraph from '~/pipelines/components/graph/graph_component.vue';
-import LinkedPipeline from '~/pipelines/components/graph/linked_pipeline.vue';
-import LinkedPipelinesColumn from '~/pipelines/components/graph/linked_pipelines_column.vue';
-import * as parsingUtils from '~/pipelines/components/parsing_utils';
-import { LOAD_FAILURE } from '~/pipelines/constants';
-
-import { pipelineWithUpstreamDownstream, wrappedPipelineReturn } from './mock_data';
-
-const processedPipeline = pipelineWithUpstreamDownstream(mockPipelineResponse);
-
-describe('Linked Pipelines Column', () => {
- const defaultProps = {
- columnTitle: 'Downstream',
- linkedPipelines: processedPipeline.downstream,
- showLinks: false,
- type: DOWNSTREAM,
- viewType: STAGE_VIEW,
- configPaths: {
- metricsPath: '',
- graphqlResourceEtag: 'this/is/a/path',
- },
- };
-
- let wrapper;
- const findLinkedColumnTitle = () => wrapper.find('[data-testid="linked-column-title"]');
- const findLinkedPipelineElements = () => wrapper.findAllComponents(LinkedPipeline);
- const findPipelineGraph = () => wrapper.findComponent(PipelineGraph);
- const findExpandButton = () => wrapper.find('[data-testid="expand-pipeline-button"]');
-
- Vue.use(VueApollo);
-
- const createComponent = ({ apolloProvider, mountFn = shallowMount, props = {} } = {}) => {
- wrapper = mountFn(LinkedPipelinesColumn, {
- apolloProvider,
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- const createComponentWithApollo = ({
- mountFn = shallowMount,
- getPipelineDetailsHandler = jest.fn().mockResolvedValue(wrappedPipelineReturn),
- props = {},
- } = {}) => {
- const requestHandlers = [[getPipelineDetails, getPipelineDetailsHandler]];
-
- const apolloProvider = createMockApollo(requestHandlers);
- createComponent({ apolloProvider, mountFn, props });
- };
-
- describe('it renders correctly', () => {
- beforeEach(() => {
- createComponentWithApollo();
- });
-
- it('renders the pipeline title', () => {
- expect(findLinkedColumnTitle().text()).toBe(defaultProps.columnTitle);
- });
-
- it('renders the correct number of linked pipelines', () => {
- expect(findLinkedPipelineElements()).toHaveLength(defaultProps.linkedPipelines.length);
- });
- });
-
- describe('click action', () => {
- const clickExpandButton = async () => {
- await findExpandButton().trigger('click');
- await waitForPromises();
- };
-
- describe('layer type rendering', () => {
- let layersFn;
-
- beforeEach(() => {
- layersFn = jest.spyOn(parsingUtils, 'listByLayers');
- createComponentWithApollo({ mountFn: mount });
- });
-
- it('calls listByLayers only once no matter how many times view is switched', async () => {
- expect(layersFn).not.toHaveBeenCalled();
- await clickExpandButton();
- await wrapper.setProps({ viewType: LAYER_VIEW });
- await nextTick();
- expect(layersFn).toHaveBeenCalledTimes(1);
- await wrapper.setProps({ viewType: STAGE_VIEW });
- await wrapper.setProps({ viewType: LAYER_VIEW });
- await wrapper.setProps({ viewType: STAGE_VIEW });
- expect(layersFn).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when graph does not use needs', () => {
- beforeEach(() => {
- const nonNeedsResponse = { ...wrappedPipelineReturn };
- nonNeedsResponse.data.project.pipeline.usesNeeds = false;
-
- createComponentWithApollo({
- props: {
- viewType: LAYER_VIEW,
- },
- getPipelineDetailsHandler: jest.fn().mockResolvedValue(nonNeedsResponse),
- mountFn: mount,
- });
- });
-
- it('shows the stage view, even when the main graph view type is layers', async () => {
- await clickExpandButton();
- expect(findPipelineGraph().props('viewType')).toBe(STAGE_VIEW);
- });
- });
-
- describe('downstream', () => {
- describe('when successful', () => {
- beforeEach(() => {
- createComponentWithApollo({ mountFn: mount });
- });
-
- it('toggles the pipeline visibility', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(true);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
-
- describe('on error', () => {
- beforeEach(() => {
- createComponentWithApollo({
- mountFn: mount,
- getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
- });
- });
-
- it('emits the error', async () => {
- await clickExpandButton();
- expect(wrapper.emitted().error).toEqual([[{ type: LOAD_FAILURE, skipSentry: true }]]);
- });
-
- it('does not show the pipeline', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
- });
-
- describe('upstream', () => {
- const upstreamProps = {
- columnTitle: 'Upstream',
- /*
- Because the IDs need to match to work, rather
- than make new mock data, we are representing
- the upstream pipeline with the downstream data.
- */
- linkedPipelines: processedPipeline.downstream,
- type: UPSTREAM,
- };
-
- describe('when successful', () => {
- beforeEach(() => {
- createComponentWithApollo({
- mountFn: mount,
- props: upstreamProps,
- });
- });
-
- it('toggles the pipeline visibility', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(true);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
-
- describe('on error', () => {
- beforeEach(() => {
- createComponentWithApollo({
- mountFn: mount,
- getPipelineDetailsHandler: jest.fn().mockRejectedValue(new Error('GraphQL error')),
- props: upstreamProps,
- });
- });
-
- it('emits the error', async () => {
- await clickExpandButton();
- expect(wrapper.emitted().error).toEqual([[{ type: LOAD_FAILURE, skipSentry: true }]]);
- });
-
- it('does not show the pipeline', async () => {
- expect(findPipelineGraph().exists()).toBe(false);
- await clickExpandButton();
- expect(findPipelineGraph().exists()).toBe(false);
- });
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph/linked_pipelines_mock_data.js b/spec/frontend/pipelines/graph/linked_pipelines_mock_data.js
deleted file mode 100644
index f7f5738e46d..00000000000
--- a/spec/frontend/pipelines/graph/linked_pipelines_mock_data.js
+++ /dev/null
@@ -1,27 +0,0 @@
-export default {
- __typename: 'Pipeline',
- id: 195,
- iid: '5',
- retryable: false,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- path: '/root/elemenohpee/-/pipelines/195',
- status: {
- __typename: 'DetailedStatus',
- group: 'success',
- label: 'passed',
- icon: 'status_success',
- },
- sourceJob: {
- __typename: 'CiJob',
- name: 'test_c',
- },
- project: {
- __typename: 'Project',
- name: 'elemenohpee',
- fullPath: 'root/elemenohpee',
- },
- multiproject: true,
-};
diff --git a/spec/frontend/pipelines/graph/mock_data.js b/spec/frontend/pipelines/graph/mock_data.js
deleted file mode 100644
index 8d06d6931ed..00000000000
--- a/spec/frontend/pipelines/graph/mock_data.js
+++ /dev/null
@@ -1,387 +0,0 @@
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import { unwrapPipelineData } from '~/pipelines/components/graph/utils';
-import {
- BUILD_KIND,
- BRIDGE_KIND,
- RETRY_ACTION_TITLE,
-} from '~/pipelines/components/graph/constants';
-
-// We mock this instead of using fixtures for performance reason.
-const mockPipelineResponseCopy = JSON.parse(JSON.stringify(mockPipelineResponse));
-const groups = new Array(100).fill({
- ...mockPipelineResponse.data.project.pipeline.stages.nodes[0].groups.nodes[0],
-});
-mockPipelineResponseCopy.data.project.pipeline.stages.nodes[0].groups.nodes = groups;
-export const mockPipelineResponseWithTooManyJobs = mockPipelineResponseCopy;
-
-export const downstream = {
- nodes: [
- {
- id: 175,
- iid: '31',
- path: '/root/elemenohpee/-/pipelines/175',
- retryable: true,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- status: {
- id: '70',
- group: 'success',
- label: 'passed',
- icon: 'status_success',
- __typename: 'DetailedStatus',
- },
- sourceJob: {
- name: 'test_c',
- id: '71',
- retried: false,
- __typename: 'CiJob',
- },
- project: {
- id: 'gid://gitlab/Project/25',
- name: 'elemenohpee',
- fullPath: 'root/elemenohpee',
- __typename: 'Project',
- },
- __typename: 'Pipeline',
- multiproject: true,
- },
- {
- id: 181,
- iid: '27',
- path: '/root/abcd-dag/-/pipelines/181',
- retryable: true,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- status: {
- id: '72',
- group: 'success',
- label: 'passed',
- icon: 'status_success',
- __typename: 'DetailedStatus',
- },
- sourceJob: {
- id: '73',
- name: 'test_d',
- retried: true,
- __typename: 'CiJob',
- },
- project: {
- id: 'gid://gitlab/Project/23',
- name: 'abcd-dag',
- fullPath: 'root/abcd-dag',
- __typename: 'Project',
- },
- __typename: 'Pipeline',
- multiproject: false,
- },
- ],
-};
-
-export const upstream = {
- id: 161,
- iid: '24',
- path: '/root/abcd-dag/-/pipelines/161',
- retryable: true,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- status: {
- id: '74',
- group: 'success',
- label: 'passed',
- icon: 'status_success',
- __typename: 'DetailedStatus',
- },
- sourceJob: null,
- project: {
- id: 'gid://gitlab/Project/23',
- name: 'abcd-dag',
- fullPath: 'root/abcd-dag',
- __typename: 'Project',
- },
- __typename: 'Pipeline',
- multiproject: true,
-};
-
-export const wrappedPipelineReturn = {
- data: {
- project: {
- __typename: 'Project',
- id: '75',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/175',
- iid: '38',
- complete: true,
- usesNeeds: true,
- userPermissions: {
- __typename: 'PipelinePermissions',
- updatePipeline: true,
- },
- downstream: {
- retryable: true,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- __typename: 'PipelineConnection',
- nodes: [],
- },
- upstream: {
- id: 'gid://gitlab/Ci::Pipeline/174',
- iid: '37',
- path: '/root/elemenohpee/-/pipelines/174',
- retryable: true,
- cancelable: false,
- userPermissions: {
- updatePipeline: true,
- },
- __typename: 'Pipeline',
- status: {
- __typename: 'DetailedStatus',
- id: '77',
- group: 'success',
- label: 'passed',
- icon: 'status_success',
- },
- sourceJob: {
- name: 'test_c',
- id: '78',
- retried: false,
- __typename: 'CiJob',
- },
- project: {
- id: 'gid://gitlab/Project/25',
- name: 'elemenohpee',
- fullPath: 'root/elemenohpee',
- __typename: 'Project',
- },
- },
- stages: {
- __typename: 'CiStageConnection',
- nodes: [
- {
- name: 'build',
- __typename: 'CiStage',
- id: '79',
- status: {
- action: null,
- id: '80',
- __typename: 'DetailedStatus',
- },
- groups: {
- __typename: 'CiGroupConnection',
- nodes: [
- {
- __typename: 'CiGroup',
- id: '81',
- status: {
- __typename: 'DetailedStatus',
- id: '82',
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- name: 'build_n',
- size: 1,
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- id: '83',
- kind: BUILD_KIND,
- name: 'build_n',
- scheduledAt: null,
- needs: {
- __typename: 'CiBuildNeedConnection',
- nodes: [],
- },
- previousStageJobsOrNeeds: {
- __typename: 'CiJobConnection',
- nodes: [],
- },
- status: {
- __typename: 'DetailedStatus',
- id: '84',
- icon: 'status_success',
- tooltip: 'passed',
- label: 'passed',
- hasDetails: true,
- detailsPath: '/root/elemenohpee/-/jobs/1662',
- group: 'success',
- action: {
- __typename: 'StatusAction',
- id: '85',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/elemenohpee/-/jobs/1662/retry',
- title: 'Retry',
- },
- },
- },
- ],
- },
- },
- ],
- },
- },
- ],
- },
- },
- },
- },
-};
-
-export const generateResponse = (raw, mockPath) => unwrapPipelineData(mockPath, raw.data);
-
-export const pipelineWithUpstreamDownstream = (base) => {
- const pip = { ...base };
- pip.data.project.pipeline.downstream = downstream;
- pip.data.project.pipeline.upstream = upstream;
-
- return generateResponse(pip, 'root/abcd-dag');
-};
-
-export const mapCallouts = (callouts) =>
- callouts.map((callout) => {
- return { featureName: callout, __typename: 'UserCallout' };
- });
-
-export const mockCalloutsResponse = (mappedCallouts) => ({
- data: {
- currentUser: {
- id: 45,
- __typename: 'User',
- callouts: {
- id: 5,
- __typename: 'UserCalloutConnection',
- nodes: mappedCallouts,
- },
- },
- },
-});
-
-export const delayedJob = {
- __typename: 'CiJob',
- kind: BUILD_KIND,
- name: 'delayed job',
- scheduledAt: '2015-07-03T10:01:00.000Z',
- needs: [],
- status: {
- __typename: 'DetailedStatus',
- icon: 'status_scheduled',
- tooltip: 'delayed manual action (%{remainingTime})',
- hasDetails: true,
- detailsPath: '/root/kinder-pipe/-/jobs/5339',
- group: 'scheduled',
- action: {
- __typename: 'StatusAction',
- icon: 'time-out',
- title: 'Unschedule',
- path: '/frontend-fixtures/builds-project/-/jobs/142/unschedule',
- buttonTitle: 'Unschedule job',
- },
- },
-};
-
-export const mockJob = {
- id: 4256,
- name: 'test',
- kind: BUILD_KIND,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- tooltip: 'passed',
- group: 'success',
- detailsPath: '/root/ci-mock/builds/4256',
- hasDetails: true,
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4256/retry',
- method: 'post',
- },
- },
-};
-
-export const mockJobWithoutDetails = {
- id: 4257,
- name: 'job_without_details',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- detailsPath: '/root/ci-mock/builds/4257',
- hasDetails: false,
- },
-};
-
-export const mockJobWithUnauthorizedAction = {
- id: 4258,
- name: 'stop-environment',
- status: {
- icon: 'status_manual',
- label: 'manual stop action (not allowed)',
- tooltip: 'manual action',
- group: 'manual',
- detailsPath: '/root/ci-mock/builds/4258',
- hasDetails: true,
- action: null,
- },
-};
-
-export const triggerJob = {
- id: 4259,
- name: 'trigger',
- kind: BRIDGE_KIND,
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- action: null,
- },
-};
-
-export const triggerJobWithRetryAction = {
- ...triggerJob,
- status: {
- ...triggerJob.status,
- action: {
- icon: 'retry',
- title: RETRY_ACTION_TITLE,
- path: '/root/ci-mock/builds/4259/retry',
- method: 'post',
- },
- },
-};
-
-export const mockFailedJob = {
- id: 3999,
- name: 'failed job',
- kind: BUILD_KIND,
- status: {
- id: 'failed-3999-3999',
- icon: 'status_failed',
- tooltip: 'failed - (stuck or timeout failure)',
- hasDetails: true,
- detailsPath: '/root/ci-project/-/jobs/3999',
- group: 'failed',
- label: 'failed',
- action: {
- id: 'Ci::BuildPresenter-failed-3999',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- path: '/root/ci-project/-/jobs/3999/retry',
- title: 'Retry',
- },
- },
-};
diff --git a/spec/frontend/pipelines/graph/stage_column_component_spec.js b/spec/frontend/pipelines/graph/stage_column_component_spec.js
deleted file mode 100644
index d4d7f1618c5..00000000000
--- a/spec/frontend/pipelines/graph/stage_column_component_spec.js
+++ /dev/null
@@ -1,228 +0,0 @@
-import { mount, shallowMount } from '@vue/test-utils';
-import JobItem from '~/pipelines/components/graph/job_item.vue';
-import StageColumnComponent from '~/pipelines/components/graph/stage_column_component.vue';
-import ActionComponent from '~/pipelines/components/jobs_shared/action_component.vue';
-
-const mockJob = {
- id: 4250,
- name: 'test',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- details_path: '/root/ci-mock/builds/4250',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/ci-mock/builds/4250/retry',
- method: 'post',
- },
- },
-};
-
-const mockGroups = Array(4)
- .fill(0)
- .map((item, idx) => {
- return { ...mockJob, jobs: [mockJob], id: idx, name: `fish-${idx}` };
- });
-
-const defaultProps = {
- name: 'Fish',
- groups: mockGroups,
- pipelineId: 159,
- userPermissions: {
- updatePipeline: true,
- },
-};
-
-describe('stage column component', () => {
- let wrapper;
-
- const findStageColumnTitle = () => wrapper.find('[data-testid="stage-column-title"]');
- const findStageColumnGroup = () => wrapper.find('[data-testid="stage-column-group"]');
- const findAllStageColumnGroups = () => wrapper.findAll('[data-testid="stage-column-group"]');
- const findJobItem = () => wrapper.findComponent(JobItem);
- const findActionComponent = () => wrapper.findComponent(ActionComponent);
-
- const createComponent = ({ method = shallowMount, props = {} } = {}) => {
- wrapper = method(StageColumnComponent, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- describe('when mounted', () => {
- beforeEach(() => {
- createComponent({ method: mount });
- });
-
- it('should render provided title', () => {
- expect(findStageColumnTitle().text()).toBe(defaultProps.name);
- });
-
- it('should render the provided groups', () => {
- expect(findAllStageColumnGroups().length).toBe(mockGroups.length);
- });
-
- it('should emit updateMeasurements event on mount', () => {
- expect(wrapper.emitted().updateMeasurements).toHaveLength(1);
- });
- });
-
- describe('when job notifies action is complete', () => {
- beforeEach(() => {
- createComponent({
- method: mount,
- props: {
- groups: [
- {
- title: 'Fish',
- size: 1,
- jobs: [mockJob],
- },
- ],
- },
- });
- findJobItem().vm.$emit('pipelineActionRequestComplete');
- });
-
- it('emits refreshPipelineGraph', () => {
- expect(wrapper.emitted().refreshPipelineGraph).toHaveLength(1);
- });
- });
-
- describe('job', () => {
- describe('text handling', () => {
- beforeEach(() => {
- createComponent({
- method: mount,
- props: {
- groups: [
- {
- ...mockJob,
- name: '<img src=x onerror=alert(document.domain)>',
- jobs: [
- {
- id: 4259,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: '<img src=x onerror=alert(document.domain)>',
- },
- },
- ],
- },
- ],
- name: 'test <img src=x onerror=alert(document.domain)>',
- },
- });
- });
-
- it('escapes name', () => {
- expect(findStageColumnTitle().html()).toContain(
- 'test &lt;img src=x onerror=alert(document.domain)&gt;',
- );
- });
-
- it('escapes id', () => {
- expect(findStageColumnGroup().attributes('id')).toBe(
- 'ci-badge-&lt;img src=x onerror=alert(document.domain)&gt;',
- );
- });
- });
-
- describe('interactions', () => {
- beforeEach(() => {
- createComponent({ method: mount });
- });
-
- it('emits jobHovered event on mouseenter and mouseleave', async () => {
- await findStageColumnGroup().trigger('mouseenter');
- expect(wrapper.emitted().jobHover).toEqual([[defaultProps.groups[0].name]]);
- await findStageColumnGroup().trigger('mouseleave');
- expect(wrapper.emitted().jobHover).toEqual([[defaultProps.groups[0].name], ['']]);
- });
- });
- });
-
- describe('with action', () => {
- const defaults = {
- groups: [
- {
- id: 4259,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: '<img src=x onerror=alert(document.domain)>',
- },
- jobs: [mockJob],
- },
- ],
- title: 'test',
- hasTriggeredBy: false,
- action: {
- icon: 'play',
- title: 'Play all',
- path: 'action',
- },
- };
-
- it('renders action button if permissions are permitted', () => {
- createComponent({
- method: mount,
- props: {
- ...defaults,
- },
- });
-
- expect(findActionComponent().exists()).toBe(true);
- });
-
- it('does not render action button if permissions are not permitted', () => {
- createComponent({
- method: mount,
- props: {
- ...defaults,
- userPermissions: {
- updatePipeline: false,
- },
- },
- });
-
- expect(findActionComponent().exists()).toBe(false);
- });
- });
-
- describe('without action', () => {
- beforeEach(() => {
- createComponent({
- method: mount,
- props: {
- groups: [
- {
- id: 4259,
- name: '<img src=x onerror=alert(document.domain)>',
- status: {
- icon: 'status_success',
- label: 'success',
- tooltip: '<img src=x onerror=alert(document.domain)>',
- },
- jobs: [mockJob],
- },
- ],
- title: 'test',
- hasTriggeredBy: false,
- },
- });
- });
-
- it('does not render action button', () => {
- expect(findActionComponent().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph_shared/__snapshots__/links_inner_spec.js.snap b/spec/frontend/pipelines/graph_shared/__snapshots__/links_inner_spec.js.snap
deleted file mode 100644
index 82206e907ff..00000000000
--- a/spec/frontend/pipelines/graph_shared/__snapshots__/links_inner_spec.js.snap
+++ /dev/null
@@ -1,30 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Links Inner component with a large number of needs matches snapshot and has expected path 1`] = `
-"<div class=\\"gl-display-flex gl-relative\\" totalgroups=\\"10\\"><svg id=\\"link-svg\\" viewBox=\\"0,0,1019,445\\" width=\\"1019px\\" height=\\"445px\\" class=\\"gl-absolute gl-pointer-events-none\\">
- <path d=\\"M202,118C52,118,52,138,102,138\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- <path d=\\"M202,118C62,118,62,148,112,148\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- <path d=\\"M222,138C72,138,72,158,122,158\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- <path d=\\"M212,128C82,128,82,168,132,168\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- <path d=\\"M232,148C92,148,92,178,142,178\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- </svg> </div>"
-`;
-
-exports[`Links Inner component with a parallel need matches snapshot and has expected path 1`] = `
-"<div class=\\"gl-display-flex gl-relative\\" totalgroups=\\"10\\"><svg id=\\"link-svg\\" viewBox=\\"0,0,1019,445\\" width=\\"1019px\\" height=\\"445px\\" class=\\"gl-absolute gl-pointer-events-none\\">
- <path d=\\"M192,108C32,108,32,118,82,118\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- </svg> </div>"
-`;
-
-exports[`Links Inner component with one need matches snapshot and has expected path 1`] = `
-"<div class=\\"gl-display-flex gl-relative\\" totalgroups=\\"10\\"><svg id=\\"link-svg\\" viewBox=\\"0,0,1019,445\\" width=\\"1019px\\" height=\\"445px\\" class=\\"gl-absolute gl-pointer-events-none\\">
- <path d=\\"M202,118C52,118,52,138,102,138\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- </svg> </div>"
-`;
-
-exports[`Links Inner component with same stage needs matches snapshot and has expected path 1`] = `
-"<div class=\\"gl-display-flex gl-relative\\" totalgroups=\\"10\\"><svg id=\\"link-svg\\" viewBox=\\"0,0,1019,445\\" width=\\"1019px\\" height=\\"445px\\" class=\\"gl-absolute gl-pointer-events-none\\">
- <path d=\\"M192,108C32,108,32,118,82,118\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- <path d=\\"M202,118C42,118,42,128,92,128\\" stroke-width=\\"2\\" class=\\"gl-fill-transparent gl-transition-duration-slow gl-transition-timing-function-ease gl-stroke-gray-200\\"></path>
- </svg> </div>"
-`;
diff --git a/spec/frontend/pipelines/graph_shared/links_inner_spec.js b/spec/frontend/pipelines/graph_shared/links_inner_spec.js
deleted file mode 100644
index b4ffd2658fe..00000000000
--- a/spec/frontend/pipelines/graph_shared/links_inner_spec.js
+++ /dev/null
@@ -1,223 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
-import { parseData } from '~/pipelines/components/parsing_utils';
-import { createJobsHash } from '~/pipelines/utils';
-import {
- jobRect,
- largePipelineData,
- parallelNeedData,
- pipelineData,
- pipelineDataWithNoNeeds,
- rootRect,
- sameStageNeeds,
-} from '../pipeline_graph/mock_data';
-
-describe('Links Inner component', () => {
- const containerId = 'pipeline-graph-container';
- const defaultProps = {
- containerId,
- containerMeasurements: { width: 1019, height: 445 },
- pipelineId: 1,
- pipelineData: [],
- totalGroups: 10,
- };
-
- let wrapper;
-
- const createComponent = (props) => {
- const currentPipelineData = props?.pipelineData || defaultProps.pipelineData;
- wrapper = shallowMount(LinksInner, {
- propsData: {
- ...defaultProps,
- ...props,
- linksData: parseData(currentPipelineData.flatMap(({ groups }) => groups)).links,
- },
- });
- };
-
- const findLinkSvg = () => wrapper.find('#link-svg');
- const findAllLinksPath = () => findLinkSvg().findAll('path');
-
- // We create fixture so that each job has an empty div that represent
- // the JobPill in the DOM. Each `JobPill` would have different coordinates,
- // so we increment their coordinates on each iteration to simulate different positions.
- const setHTMLFixtureLocal = ({ stages }) => {
- const jobs = createJobsHash(stages);
- const arrayOfJobs = Object.keys(jobs);
-
- const linksHtmlElements = arrayOfJobs.map((job) => {
- return `<div id=${job}-${defaultProps.pipelineId} />`;
- });
-
- setHTMLFixture(`<div id="${containerId}">${linksHtmlElements.join(' ')}</div>`);
-
- // We are mocking the clientRect data of each job and the container ID.
- jest
- .spyOn(document.getElementById(containerId), 'getBoundingClientRect')
- .mockImplementation(() => rootRect);
-
- arrayOfJobs.forEach((job, index) => {
- jest
- .spyOn(
- document.getElementById(`${job}-${defaultProps.pipelineId}`),
- 'getBoundingClientRect',
- )
- .mockImplementation(() => {
- const newValue = 10 * index;
- const { left, right, top, bottom, x, y } = jobRect;
- return {
- ...jobRect,
- left: left + newValue,
- right: right + newValue,
- top: top + newValue,
- bottom: bottom + newValue,
- x: x + newValue,
- y: y + newValue,
- };
- });
- });
- };
-
- afterEach(() => {
- resetHTMLFixture();
- });
-
- describe('basic SVG creation', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders an SVG of the right size', () => {
- expect(findLinkSvg().exists()).toBe(true);
- expect(findLinkSvg().attributes('width')).toBe(
- `${defaultProps.containerMeasurements.width}px`,
- );
- expect(findLinkSvg().attributes('height')).toBe(
- `${defaultProps.containerMeasurements.height}px`,
- );
- });
- });
-
- describe('no pipeline data', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the component', () => {
- expect(findLinkSvg().exists()).toBe(true);
- expect(findAllLinksPath()).toHaveLength(0);
- });
- });
-
- describe('pipeline data with no needs', () => {
- beforeEach(() => {
- createComponent({ pipelineData: pipelineDataWithNoNeeds.stages });
- });
-
- it('renders no links', () => {
- expect(findLinkSvg().exists()).toBe(true);
- expect(findAllLinksPath()).toHaveLength(0);
- });
- });
-
- describe('with one need', () => {
- beforeEach(() => {
- setHTMLFixtureLocal(pipelineData);
- createComponent({ pipelineData: pipelineData.stages });
- });
-
- it('renders one link', () => {
- expect(findAllLinksPath()).toHaveLength(1);
- });
-
- it('path does not contain NaN values', () => {
- expect(wrapper.html()).not.toContain('NaN');
- });
-
- it('matches snapshot and has expected path', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('with a parallel need', () => {
- beforeEach(() => {
- setHTMLFixtureLocal(parallelNeedData);
- createComponent({ pipelineData: parallelNeedData.stages });
- });
-
- it('renders only one link for all the same parallel jobs', () => {
- expect(findAllLinksPath()).toHaveLength(1);
- });
-
- it('path does not contain NaN values', () => {
- expect(wrapper.html()).not.toContain('NaN');
- });
-
- it('matches snapshot and has expected path', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('with same stage needs', () => {
- beforeEach(() => {
- setHTMLFixtureLocal(sameStageNeeds);
- createComponent({ pipelineData: sameStageNeeds.stages });
- });
-
- it('renders the correct number of links', () => {
- expect(findAllLinksPath()).toHaveLength(2);
- });
-
- it('path does not contain NaN values', () => {
- expect(wrapper.html()).not.toContain('NaN');
- });
-
- it('matches snapshot and has expected path', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('with a large number of needs', () => {
- beforeEach(() => {
- setHTMLFixtureLocal(largePipelineData);
- createComponent({ pipelineData: largePipelineData.stages });
- });
-
- it('renders the correct number of links', () => {
- expect(findAllLinksPath()).toHaveLength(5);
- });
-
- it('path does not contain NaN values', () => {
- expect(wrapper.html()).not.toContain('NaN');
- });
-
- it('matches snapshot and has expected path', () => {
- expect(wrapper.html()).toMatchSnapshot();
- });
- });
-
- describe('interactions', () => {
- beforeEach(() => {
- setHTMLFixtureLocal(largePipelineData);
- createComponent({ pipelineData: largePipelineData.stages });
- });
-
- it('highlight needs on hover', async () => {
- const firstLink = findAllLinksPath().at(0);
-
- const defaultColorClass = 'gl-stroke-gray-200';
- const hoverColorClass = 'gl-stroke-blue-400';
-
- expect(firstLink.classes(defaultColorClass)).toBe(true);
- expect(firstLink.classes(hoverColorClass)).toBe(false);
-
- // Because there is a watcher, we need to set the props after the component
- // has mounted.
- await wrapper.setProps({ highlightedJob: 'test_1' });
-
- expect(firstLink.classes(defaultColorClass)).toBe(false);
- expect(firstLink.classes(hoverColorClass)).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/graph_shared/links_layer_spec.js b/spec/frontend/pipelines/graph_shared/links_layer_spec.js
deleted file mode 100644
index 88ba84c395a..00000000000
--- a/spec/frontend/pipelines/graph_shared/links_layer_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
-import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-
-import { generateResponse } from '../graph/mock_data';
-
-describe('links layer component', () => {
- let wrapper;
-
- const findLinksInner = () => wrapper.findComponent(LinksInner);
-
- const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo');
- const containerId = `pipeline-links-container-${pipeline.id}`;
- const slotContent = "<div>Ceci n'est pas un graphique</div>";
-
- const defaultProps = {
- containerId,
- containerMeasurements: { width: 400, height: 400 },
- pipelineId: pipeline.id,
- pipelineData: pipeline.stages,
- showLinks: false,
- };
-
- const createComponent = ({ mountFn = shallowMount, props = {} } = {}) => {
- wrapper = mountFn(LinksLayer, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- slots: {
- default: slotContent,
- },
- stubs: {
- 'links-inner': true,
- },
- });
- };
-
- describe('with show links off', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the default slot', () => {
- expect(wrapper.html()).toContain(slotContent);
- });
-
- it('does not render inner links component', () => {
- expect(findLinksInner().exists()).toBe(false);
- });
- });
-
- describe('with show links on', () => {
- beforeEach(() => {
- createComponent({
- props: {
- showLinks: true,
- },
- });
- });
-
- it('renders the default slot', () => {
- expect(wrapper.html()).toContain(slotContent);
- });
-
- it('renders the inner links component', () => {
- expect(findLinksInner().exists()).toBe(true);
- });
- });
-
- describe('with width or height measurement at 0', () => {
- beforeEach(() => {
- createComponent({ props: { containerMeasurements: { width: 0, height: 100 } } });
- });
-
- it('renders the default slot', () => {
- expect(wrapper.html()).toContain(slotContent);
- });
-
- it('does not render the inner links component', () => {
- expect(findLinksInner().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/linked_pipelines_mock.json b/spec/frontend/pipelines/linked_pipelines_mock.json
deleted file mode 100644
index a68283032d2..00000000000
--- a/spec/frontend/pipelines/linked_pipelines_mock.json
+++ /dev/null
@@ -1,3569 +0,0 @@
-{
- "id": 23211253,
- "user": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": "<span class=\"user-status-emoji has-tooltip\" title=\"I like pizza\" data-html=\"true\" data-placement=\"top\"><gl-emoji title=\"slice of pizza\" data-name=\"pizza\" data-unicode-version=\"6.0\">🍕</gl-emoji></span>",
- "path": "/axil"
- },
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2018-06-05T11:31:30.452Z",
- "updated_at": "2018-10-31T16:35:31.305Z",
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "flags": {
- "latest": false,
- "stuck": false,
- "auto_devops": false,
- "merge_request": false,
- "yaml_errors": false,
- "retryable": false,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": 53,
- "finished_at": "2018-10-31T16:35:31.299Z",
- "stages": [
- {
- "name": "prebuild",
- "title": "prebuild: passed",
- "groups": [
- {
- "name": "review-docs-deploy",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 72469032,
- "name": "review-docs-deploy",
- "started": "2018-10-31T16:34:58.778Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/retry",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.495Z",
- "updated_at": "2018-10-31T16:35:31.251Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469032",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#prebuild",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=prebuild"
- },
- {
- "name": "test",
- "title": "test: passed",
- "groups": [
- {
- "name": "docs check links",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 72469033,
- "name": "docs check links",
- "started": "2018-06-05T11:31:33.240Z",
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "retry_path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.627Z",
- "updated_at": "2018-06-05T11:31:54.363Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469033",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469033/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#test",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=test"
- },
- {
- "name": "cleanup",
- "title": "cleanup: skipped",
- "groups": [
- {
- "name": "review-docs-cleanup",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- },
- "jobs": [
- {
- "id": 72469034,
- "name": "review-docs-cleanup",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "play_path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-06-05T11:31:30.760Z",
- "updated_at": "2018-06-05T11:31:56.037Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual stop action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/-/jobs/72469034",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "stop",
- "title": "Stop",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "method": "post",
- "button_title": "Stop this environment"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-org/gitlab-runner/pipelines/23211253#cleanup",
- "dropdown_path": "/gitlab-org/gitlab-runner/pipelines/23211253/stage.json?stage=cleanup"
- }
- ],
- "artifacts": [
-
- ],
- "manual_actions": [
- {
- "name": "review-docs-cleanup",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469034/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review-docs-deploy",
- "path": "/gitlab-org/gitlab-runner/-/jobs/72469032/play",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "ref": {
- "name": "docs/add-development-guide-to-readme",
- "path": "/gitlab-org/gitlab-runner/commits/docs/add-development-guide-to-readme",
- "tag": false,
- "branch": true,
- "merge_request": false
- },
- "commit": {
- "id": "8083eb0a920572214d0dccedd7981f05d535ad46",
- "short_id": "8083eb0a",
- "title": "Add link to development guide in readme",
- "created_at": "2018-06-05T11:30:48.000Z",
- "parent_ids": [
- "1d7cf79b5a1a2121b9474ac20d61c1b8f621289d"
- ],
- "message": "Add link to development guide in readme\n\nCloses https://gitlab.com/gitlab-org/gitlab-runner/issues/3122\n",
- "author_name": "Achilleas Pipinellis",
- "author_email": "axil@gitlab.com",
- "authored_date": "2018-06-05T11:30:48.000Z",
- "committer_name": "Achilleas Pipinellis",
- "committer_email": "axil@gitlab.com",
- "committed_date": "2018-06-05T11:30:48.000Z",
- "author": {
- "id": 3585,
- "name": "Achilleas Pipinellis",
- "username": "axil",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/3585/avatar.png",
- "web_url": "https://gitlab.com/axil",
- "status_tooltip_html": null,
- "path": "/axil"
- },
- "author_gravatar_url": "https://secure.gravatar.com/avatar/1d37af00eec153a8333a4ce18e9aea41?s=80&d=identicon",
- "commit_url": "https://gitlab.com/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46",
- "commit_path": "/gitlab-org/gitlab-runner/commit/8083eb0a920572214d0dccedd7981f05d535ad46"
- },
- "project": {
- "id": 1794617
- },
- "triggered_by": {
- "id": 12,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "duration": 118,
- "finished_at": "2018-10-31T16:41:40.615Z",
- "stages": [
- {
- "name": "build-images",
- "title": "build-images: skipped",
- "groups": [
- {
- "name": "image:bootstrap",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 11421321982853,
- "name": "image:bootstrap",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.704Z",
- "updated_at": "2018-10-31T16:35:24.118Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:builder-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 1149822131854,
- "name": "image:builder-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.728Z",
- "updated_at": "2018-10-31T16:35:24.070Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:nginx-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 11498285523424,
- "name": "image:nginx-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.753Z",
- "updated_at": "2018-10-31T16:35:24.033Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images"
- },
- {
- "name": "build",
- "title": "build: failed",
- "groups": [
- {
- "name": "compile_dev",
- "size": 1,
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 1149846949786,
- "name": "compile_dev",
- "started": "2018-10-31T16:39:41.598Z",
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:39:41.138Z",
- "updated_at": "2018-10-31T16:41:40.072Z",
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "recoverable": false
- }
- ]
- }
- ],
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build"
- },
- {
- "name": "deploy",
- "title": "deploy: skipped",
- "groups": [
- {
- "name": "review",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 11498282342357,
- "name": "review",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.805Z",
- "updated_at": "2018-10-31T16:41:40.569Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- },
- {
- "name": "review_stop",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 114982858,
- "name": "review_stop",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.840Z",
- "updated_at": "2018-10-31T16:41:40.480Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy"
- }
- ],
- "artifacts": [
-
- ],
- "manual_actions": [
- {
- "name": "image:bootstrap",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:builder-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:nginx-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review_stop",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play",
- "playable": false,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "project": {
- "id": 1794617,
- "name": "Test",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- },
- "triggered_by": {
- "id": 349932310342451,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "duration": 118,
- "finished_at": "2018-10-31T16:41:40.615Z",
- "stages": [
- {
- "name": "build-images",
- "title": "build-images: skipped",
- "groups": [
- {
- "name": "image:bootstrap",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 11421321982853,
- "name": "image:bootstrap",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.704Z",
- "updated_at": "2018-10-31T16:35:24.118Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:builder-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 1149822131854,
- "name": "image:builder-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.728Z",
- "updated_at": "2018-10-31T16:35:24.070Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:nginx-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 11498285523424,
- "name": "image:nginx-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.753Z",
- "updated_at": "2018-10-31T16:35:24.033Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images"
- },
- {
- "name": "build",
- "title": "build: failed",
- "groups": [
- {
- "name": "compile_dev",
- "size": 1,
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 1149846949786,
- "name": "compile_dev",
- "started": "2018-10-31T16:39:41.598Z",
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:39:41.138Z",
- "updated_at": "2018-10-31T16:41:40.072Z",
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "recoverable": false
- }
- ]
- }
- ],
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build"
- },
- {
- "name": "deploy",
- "title": "deploy: skipped",
- "groups": [
- {
- "name": "review",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 11498282342357,
- "name": "review",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.805Z",
- "updated_at": "2018-10-31T16:41:40.569Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- },
- {
- "name": "review_stop",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 114982858,
- "name": "review_stop",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.840Z",
- "updated_at": "2018-10-31T16:41:40.480Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy"
- }
- ],
- "artifacts": [
-
- ],
- "manual_actions": [
- {
- "name": "image:bootstrap",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:builder-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:nginx-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review_stop",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play",
- "playable": false,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- },
- "triggered": [
-
- ]
- },
- "triggered": [
- {
- "id": 34993051,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "duration": 118,
- "finished_at": "2018-10-31T16:41:40.615Z",
- "stages": [
- {
- "name": "build-images",
- "title": "build-images: skipped",
- "groups": [
- {
- "name": "image:bootstrap",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 114982853,
- "name": "image:bootstrap",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.704Z",
- "updated_at": "2018-10-31T16:35:24.118Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:builder-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 114982854,
- "name": "image:builder-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.728Z",
- "updated_at": "2018-10-31T16:35:24.070Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:nginx-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 114982855,
- "name": "image:nginx-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.753Z",
- "updated_at": "2018-10-31T16:35:24.033Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images"
- },
- {
- "name": "build",
- "title": "build: failed",
- "groups": [
- {
- "name": "compile_dev",
- "size": 1,
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 114984694,
- "name": "compile_dev",
- "started": "2018-10-31T16:39:41.598Z",
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:39:41.138Z",
- "updated_at": "2018-10-31T16:41:40.072Z",
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "recoverable": false
- }
- ]
- }
- ],
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build"
- },
- {
- "name": "deploy",
- "title": "deploy: skipped",
- "groups": [
- {
- "name": "review",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 114982857,
- "name": "review",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.805Z",
- "updated_at": "2018-10-31T16:41:40.569Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- },
- {
- "name": "review_stop",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 114982858,
- "name": "review_stop",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.840Z",
- "updated_at": "2018-10-31T16:41:40.480Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy"
- }
- ],
- "artifacts": [
-
- ],
- "manual_actions": [
- {
- "name": "image:bootstrap",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:builder-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:nginx-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review_stop",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play",
- "playable": false,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- },
- "triggered": [
- {
- }
- ]
- },
- {
- "id": 34993052,
- "user": {
- "id": 376774,
- "name": "Alessio Caiazza",
- "username": "nolith",
- "state": "active",
- "avatar_url": "https://assets.gitlab-static.net/uploads/-/system/user/avatar/376774/avatar.png",
- "web_url": "https://gitlab.com/nolith",
- "status_tooltip_html": null,
- "path": "/nolith"
- },
- "active": false,
- "coverage": null,
- "source": "pipeline",
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "details": {
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "duration": 118,
- "finished_at": "2018-10-31T16:41:40.615Z",
- "stages": [
- {
- "name": "build-images",
- "title": "build-images: skipped",
- "groups": [
- {
- "name": "image:bootstrap",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 114982853,
- "name": "image:bootstrap",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.704Z",
- "updated_at": "2018-10-31T16:35:24.118Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982853",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:builder-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 114982854,
- "name": "image:builder-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.728Z",
- "updated_at": "2018-10-31T16:35:24.070Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982854",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- },
- {
- "name": "image:nginx-onbuild",
- "size": 1,
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 1224982855,
- "name": "image:nginx-onbuild",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "play_path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.753Z",
- "updated_at": "2018-10-31T16:35:24.033Z",
- "status": {
- "icon": "status_manual",
- "text": "manual",
- "label": "manual play action",
- "group": "manual",
- "tooltip": "manual action",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982855",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_manual-829a0804612cef47d9efc1618dba38325483657c847dba0546c3b9f0295bb36c.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build-images",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build-images"
- },
- {
- "name": "build",
- "title": "build: failed",
- "groups": [
- {
- "name": "compile_dev",
- "size": 1,
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 1123984694,
- "name": "compile_dev",
- "started": "2018-10-31T16:39:41.598Z",
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "retry_path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:39:41.138Z",
- "updated_at": "2018-10-31T16:41:40.072Z",
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed - (script failure)",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114984694",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114984694/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "recoverable": false
- }
- ]
- }
- ],
- "status": {
- "icon": "status_failed",
- "text": "failed",
- "label": "failed",
- "group": "failed",
- "tooltip": "failed",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#build",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=build"
- },
- {
- "name": "deploy",
- "title": "deploy: skipped",
- "groups": [
- {
- "name": "review",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 1143232982857,
- "name": "review",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.805Z",
- "updated_at": "2018-10-31T16:41:40.569Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982857",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- },
- {
- "name": "review_stop",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 114921313182858,
- "name": "review_stop",
- "started": null,
- "archived": false,
- "build_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "playable": false,
- "scheduled": false,
- "created_at": "2018-10-31T16:35:23.840Z",
- "updated_at": "2018-10-31T16:41:40.480Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/-/jobs/114982858",
- "illustration": {
- "image": "https://assets.gitlab-static.net/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "illustration": null,
- "favicon": "https://gitlab.com/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "path": "/gitlab-com/gitlab-docs/pipelines/34993051#deploy",
- "dropdown_path": "/gitlab-com/gitlab-docs/pipelines/34993051/stage.json?stage=deploy"
- }
- ],
- "artifacts": [
-
- ],
- "manual_actions": [
- {
- "name": "image:bootstrap",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982853/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:builder-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982854/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "image:nginx-onbuild",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982855/play",
- "playable": true,
- "scheduled": false
- },
- {
- "name": "review_stop",
- "path": "/gitlab-com/gitlab-docs/-/jobs/114982858/play",
- "playable": false,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- },
- "triggered": [
- {
- "id": 26,
- "user": null,
- "active": false,
- "coverage": null,
- "source": "push",
- "created_at": "2019-01-06T17:48:37.599Z",
- "updated_at": "2019-01-06T17:48:38.371Z",
- "path": "/h5bp/html5-boilerplate/pipelines/26",
- "flags": {
- "latest": true,
- "stuck": false,
- "auto_devops": false,
- "merge_request": false,
- "yaml_errors": false,
- "retryable": true,
- "cancelable": false,
- "failure_reason": false
- },
- "details": {
- "status": {
- "icon": "status_warning",
- "text": "passed",
- "label": "passed with warnings",
- "group": "success-with-warnings",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "duration": null,
- "finished_at": "2019-01-06T17:48:38.370Z",
- "stages": [
- {
- "name": "build",
- "title": "build: passed",
- "groups": [
- {
- "name": "build:linux",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/526",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/526/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 526,
- "name": "build:linux",
- "started": "2019-01-06T08:48:20.236Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/526",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/526/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.806Z",
- "updated_at": "2019-01-06T17:48:37.806Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/526",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/526/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "build:osx",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/527",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/527/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 527,
- "name": "build:osx",
- "started": "2019-01-06T07:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/527",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/527/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.846Z",
- "updated_at": "2019-01-06T17:48:37.846Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/527",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/527/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26#build",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/h5bp/html5-boilerplate/pipelines/26#build",
- "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=build"
- },
- {
- "name": "test",
- "title": "test: passed with warnings",
- "groups": [
- {
- "name": "jenkins",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": null,
- "group": "success",
- "tooltip": null,
- "has_details": false,
- "details_path": null,
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "jobs": [
- {
- "id": 546,
- "name": "jenkins",
- "started": "2019-01-06T11:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/546",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.359Z",
- "updated_at": "2019-01-06T17:48:38.359Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": null,
- "group": "success",
- "tooltip": null,
- "has_details": false,
- "details_path": null,
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- }
- }
- ]
- },
- {
- "name": "rspec:linux",
- "size": 3,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": false,
- "details_path": null,
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "jobs": [
- {
- "id": 528,
- "name": "rspec:linux 0 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/528",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.885Z",
- "updated_at": "2019-01-06T17:48:37.885Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/528",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/528/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- },
- {
- "id": 529,
- "name": "rspec:linux 1 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/529",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/529/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.907Z",
- "updated_at": "2019-01-06T17:48:37.907Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/529",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/529/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- },
- {
- "id": 530,
- "name": "rspec:linux 2 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/530",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/530/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.927Z",
- "updated_at": "2019-01-06T17:48:37.927Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/530",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/530/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "rspec:osx",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/535",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/535/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 535,
- "name": "rspec:osx",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/535",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/535/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.018Z",
- "updated_at": "2019-01-06T17:48:38.018Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/535",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/535/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "rspec:windows",
- "size": 3,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": false,
- "details_path": null,
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "jobs": [
- {
- "id": 531,
- "name": "rspec:windows 0 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/531",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/531/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.944Z",
- "updated_at": "2019-01-06T17:48:37.944Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/531",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/531/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- },
- {
- "id": 532,
- "name": "rspec:windows 1 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/532",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/532/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.962Z",
- "updated_at": "2019-01-06T17:48:37.962Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/532",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/532/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- },
- {
- "id": 534,
- "name": "rspec:windows 2 3",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/534",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/534/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:37.999Z",
- "updated_at": "2019-01-06T17:48:37.999Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/534",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/534/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "spinach:linux",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/536",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/536/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 536,
- "name": "spinach:linux",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/536",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/536/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.050Z",
- "updated_at": "2019-01-06T17:48:38.050Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/536",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/536/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "spinach:osx",
- "size": 1,
- "status": {
- "icon": "status_warning",
- "text": "failed",
- "label": "failed (allowed to fail)",
- "group": "failed-with-warnings",
- "tooltip": "failed - (unknown failure) (allowed to fail)",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/537",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/537/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 537,
- "name": "spinach:osx",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/537",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/537/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.069Z",
- "updated_at": "2019-01-06T17:48:38.069Z",
- "status": {
- "icon": "status_warning",
- "text": "failed",
- "label": "failed (allowed to fail)",
- "group": "failed-with-warnings",
- "tooltip": "failed - (unknown failure) (allowed to fail)",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/537",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/537/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "callout_message": "There is an unknown failure, please try again",
- "recoverable": true
- }
- ]
- }
- ],
- "status": {
- "icon": "status_warning",
- "text": "passed",
- "label": "passed with warnings",
- "group": "success-with-warnings",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26#test",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/h5bp/html5-boilerplate/pipelines/26#test",
- "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=test"
- },
- {
- "name": "security",
- "title": "security: passed",
- "groups": [
- {
- "name": "container_scanning",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/541",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/541/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 541,
- "name": "container_scanning",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/541",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/541/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.186Z",
- "updated_at": "2019-01-06T17:48:38.186Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/541",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/541/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "dast",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/538",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/538/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 538,
- "name": "dast",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/538",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/538/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.087Z",
- "updated_at": "2019-01-06T17:48:38.087Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/538",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/538/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "dependency_scanning",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/540",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/540/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 540,
- "name": "dependency_scanning",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/540",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/540/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.153Z",
- "updated_at": "2019-01-06T17:48:38.153Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/540",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/540/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "sast",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/539",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/539/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 539,
- "name": "sast",
- "started": "2019-01-06T09:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/539",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/539/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.121Z",
- "updated_at": "2019-01-06T17:48:38.121Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/539",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/539/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26#security",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/h5bp/html5-boilerplate/pipelines/26#security",
- "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=security"
- },
- {
- "name": "deploy",
- "title": "deploy: passed",
- "groups": [
- {
- "name": "production",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/544",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 544,
- "name": "production",
- "started": null,
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/544",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.313Z",
- "updated_at": "2019-01-06T17:48:38.313Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/544",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- },
- {
- "name": "staging",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/542",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/542/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- },
- "jobs": [
- {
- "id": 542,
- "name": "staging",
- "started": "2019-01-06T11:48:20.237Z",
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/542",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/542/retry",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.219Z",
- "updated_at": "2019-01-06T17:48:38.219Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/542",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job does not have a trace."
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "retry",
- "title": "Retry",
- "path": "/h5bp/html5-boilerplate/-/jobs/542/retry",
- "method": "post",
- "button_title": "Retry this job"
- }
- }
- }
- ]
- },
- {
- "name": "stop staging",
- "size": 1,
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/543",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- },
- "jobs": [
- {
- "id": 543,
- "name": "stop staging",
- "started": null,
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/543",
- "playable": false,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.283Z",
- "updated_at": "2019-01-06T17:48:38.283Z",
- "status": {
- "icon": "status_skipped",
- "text": "skipped",
- "label": "skipped",
- "group": "skipped",
- "tooltip": "skipped",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/543",
- "illustration": {
- "image": "/assets/illustrations/skipped-job_empty-8b877955fbf175e42ae65b6cb95346e15282c6fc5b682756c329af3a0055225e.svg",
- "size": "svg-430",
- "title": "This job has been skipped"
- },
- "favicon": "/assets/ci_favicons/favicon_status_skipped-0b9c5e543588945e8c4ca57786bbf2d0c56631959c9f853300392d0315be829b.png"
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26#deploy",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/h5bp/html5-boilerplate/pipelines/26#deploy",
- "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=deploy"
- },
- {
- "name": "notify",
- "title": "notify: passed",
- "groups": [
- {
- "name": "slack",
- "size": 1,
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/545",
- "illustration": {
- "image": "/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/h5bp/html5-boilerplate/-/jobs/545/play",
- "method": "post",
- "button_title": "Run job"
- }
- },
- "jobs": [
- {
- "id": 545,
- "name": "slack",
- "started": null,
- "archived": false,
- "build_path": "/h5bp/html5-boilerplate/-/jobs/545",
- "retry_path": "/h5bp/html5-boilerplate/-/jobs/545/retry",
- "play_path": "/h5bp/html5-boilerplate/-/jobs/545/play",
- "playable": true,
- "scheduled": false,
- "created_at": "2019-01-06T17:48:38.341Z",
- "updated_at": "2019-01-06T17:48:38.341Z",
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "manual play action",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/-/jobs/545",
- "illustration": {
- "image": "/assets/illustrations/manual_action-2b4ca0d1bcfd92aebf33d484e36cbf7a102d007f76b5a0cfea636033a629d601.svg",
- "size": "svg-394",
- "title": "This job requires a manual action",
- "content": "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
- },
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png",
- "action": {
- "icon": "play",
- "title": "Play",
- "path": "/h5bp/html5-boilerplate/-/jobs/545/play",
- "method": "post",
- "button_title": "Run job"
- }
- }
- }
- ]
- }
- ],
- "status": {
- "icon": "status_success",
- "text": "passed",
- "label": "passed",
- "group": "success",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/h5bp/html5-boilerplate/pipelines/26#notify",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- },
- "path": "/h5bp/html5-boilerplate/pipelines/26#notify",
- "dropdown_path": "/h5bp/html5-boilerplate/pipelines/26/stage.json?stage=notify"
- }
- ],
- "artifacts": [
- {
- "name": "build:linux",
- "expired": null,
- "expire_at": null,
- "path": "/h5bp/html5-boilerplate/-/jobs/526/artifacts/download",
- "browse_path": "/h5bp/html5-boilerplate/-/jobs/526/artifacts/browse"
- },
- {
- "name": "build:osx",
- "expired": null,
- "expire_at": null,
- "path": "/h5bp/html5-boilerplate/-/jobs/527/artifacts/download",
- "browse_path": "/h5bp/html5-boilerplate/-/jobs/527/artifacts/browse"
- }
- ],
- "manual_actions": [
- {
- "name": "stop staging",
- "path": "/h5bp/html5-boilerplate/-/jobs/543/play",
- "playable": false,
- "scheduled": false
- },
- {
- "name": "production",
- "path": "/h5bp/html5-boilerplate/-/jobs/544/play",
- "playable": false,
- "scheduled": false
- },
- {
- "name": "slack",
- "path": "/h5bp/html5-boilerplate/-/jobs/545/play",
- "playable": true,
- "scheduled": false
- }
- ],
- "scheduled_actions": [
-
- ]
- },
- "ref": {
- "name": "master",
- "path": "/h5bp/html5-boilerplate/commits/master",
- "tag": false,
- "branch": true,
- "merge_request": false
- },
- "commit": {
- "id": "bad98c453eab56d20057f3929989251d45cd1a8b",
- "short_id": "bad98c45",
- "title": "remove instances of shrink-to-fit=no (#2103)",
- "created_at": "2018-12-17T20:52:18.000Z",
- "parent_ids": [
- "49130f6cfe9ff1f749015d735649a2bc6f66cf3a"
- ],
- "message": "remove instances of shrink-to-fit=no (#2103)\n\ncloses #2102\r\n\r\nPer my findings, the need for it as a default was rectified with the release of iOS 9.3, where the viewport no longer shrunk to accommodate overflow, as was introduced in iOS 9.",
- "author_name": "Scott O'Hara",
- "author_email": "scottaohara@users.noreply.github.com",
- "authored_date": "2018-12-17T20:52:18.000Z",
- "committer_name": "Rob Larsen",
- "committer_email": "rob@drunkenfist.com",
- "committed_date": "2018-12-17T20:52:18.000Z",
- "author": null,
- "author_gravatar_url": "https://www.gravatar.com/avatar/6d597df7cf998d16cbe00ccac063b31e?s=80&d=identicon",
- "commit_url": "http://localhost:3001/h5bp/html5-boilerplate/commit/bad98c453eab56d20057f3929989251d45cd1a8b",
- "commit_path": "/h5bp/html5-boilerplate/commit/bad98c453eab56d20057f3929989251d45cd1a8b"
- },
- "retry_path": "/h5bp/html5-boilerplate/pipelines/26/retry",
- "triggered_by": {
- "id": 4,
- "user": null,
- "active": false,
- "coverage": null,
- "source": "push",
- "path": "/gitlab-org/gitlab-test/pipelines/4",
- "details": {
- "status": {
- "icon": "status_warning",
- "text": "passed",
- "label": "passed with warnings",
- "group": "success-with-warnings",
- "tooltip": "passed",
- "has_details": true,
- "details_path": "/gitlab-org/gitlab-test/pipelines/4",
- "illustration": null,
- "favicon": "/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png"
- }
- },
- "project": {
- "id": 1,
- "name": "Gitlab Test",
- "full_path": "/gitlab-org/gitlab-test",
- "full_name": "Gitlab Org / Gitlab Test"
- }
- },
- "triggered": [
-
- ],
- "project": {
- "id": 1794617,
- "name": "GitLab Docs",
- "full_path": "/gitlab-com/gitlab-docs",
- "full_name": "GitLab.com / GitLab Docs"
- }
- }
- ]
- }
- ]
-}
diff --git a/spec/frontend/pipelines/mock_data.js b/spec/frontend/pipelines/mock_data.js
deleted file mode 100644
index 673db3b5178..00000000000
--- a/spec/frontend/pipelines/mock_data.js
+++ /dev/null
@@ -1,1379 +0,0 @@
-import pipelineHeaderSuccess from 'test_fixtures/graphql/pipelines/pipeline_header_success.json';
-import pipelineHeaderRunning from 'test_fixtures/graphql/pipelines/pipeline_header_running.json';
-import pipelineHeaderRunningWithDuration from 'test_fixtures/graphql/pipelines/pipeline_header_running_with_duration.json';
-import pipelineHeaderFailed from 'test_fixtures/graphql/pipelines/pipeline_header_failed.json';
-
-const PIPELINE_RUNNING = 'RUNNING';
-const PIPELINE_CANCELED = 'CANCELED';
-const PIPELINE_FAILED = 'FAILED';
-
-const threeWeeksAgo = new Date();
-threeWeeksAgo.setDate(threeWeeksAgo.getDate() - 21);
-
-export {
- pipelineHeaderSuccess,
- pipelineHeaderRunning,
- pipelineHeaderRunningWithDuration,
- pipelineHeaderFailed,
-};
-
-export const pipelineRetryMutationResponseSuccess = {
- data: { pipelineRetry: { errors: [] } },
-};
-
-export const pipelineRetryMutationResponseFailed = {
- data: { pipelineRetry: { errors: ['error'] } },
-};
-
-export const pipelineCancelMutationResponseSuccess = {
- data: { pipelineCancel: { errors: [] } },
-};
-
-export const pipelineCancelMutationResponseFailed = {
- data: { pipelineCancel: { errors: ['error'] } },
-};
-
-export const pipelineDeleteMutationResponseSuccess = {
- data: { pipelineDestroy: { errors: [] } },
-};
-
-export const pipelineDeleteMutationResponseFailed = {
- data: { pipelineDestroy: { errors: ['error'] } },
-};
-
-export const mockPipelineHeader = {
- detailedStatus: {},
- id: 123,
- userPermissions: {
- destroyPipeline: true,
- updatePipeline: true,
- },
- createdAt: threeWeeksAgo.toISOString(),
- user: {
- id: 'user-1',
- name: 'Foo',
- username: 'foobar',
- email: 'foo@bar.com',
- avatarUrl: 'link',
- },
-};
-
-export const mockFailedPipelineHeader = {
- ...mockPipelineHeader,
- status: PIPELINE_FAILED,
- retryable: true,
- cancelable: false,
- detailedStatus: {
- id: 'status-1',
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- detailsPath: 'path',
- },
-};
-
-export const mockFailedPipelineNoPermissions = {
- id: 123,
- userPermissions: {
- destroyPipeline: false,
- updatePipeline: false,
- },
- createdAt: threeWeeksAgo.toISOString(),
- user: {
- id: 'user-1',
- name: 'Foo',
- username: 'foobar',
- email: 'foo@bar.com',
- avatarUrl: 'link',
- },
- status: PIPELINE_RUNNING,
- retryable: true,
- cancelable: false,
- detailedStatus: {
- id: 'status-1',
- group: 'running',
- icon: 'status_running',
- label: 'running',
- text: 'running',
- detailsPath: 'path',
- },
-};
-
-export const mockRunningPipelineHeader = {
- ...mockPipelineHeader,
- status: PIPELINE_RUNNING,
- retryable: false,
- cancelable: true,
- detailedStatus: {
- id: 'status-1',
- group: 'running',
- icon: 'status_running',
- label: 'running',
- text: 'running',
- detailsPath: 'path',
- },
-};
-
-export const mockRunningPipelineNoPermissions = {
- id: 123,
- userPermissions: {
- destroyPipeline: false,
- updatePipeline: false,
- },
- createdAt: threeWeeksAgo.toISOString(),
- user: {
- id: 'user-1',
- name: 'Foo',
- username: 'foobar',
- email: 'foo@bar.com',
- avatarUrl: 'link',
- },
- status: PIPELINE_RUNNING,
- retryable: false,
- cancelable: true,
- detailedStatus: {
- id: 'status-1',
- group: 'running',
- icon: 'status_running',
- label: 'running',
- text: 'running',
- detailsPath: 'path',
- },
-};
-
-export const mockCancelledPipelineHeader = {
- ...mockPipelineHeader,
- status: PIPELINE_CANCELED,
- retryable: true,
- cancelable: false,
- detailedStatus: {
- id: 'status-1',
- group: 'cancelled',
- icon: 'status_cancelled',
- label: 'cancelled',
- text: 'cancelled',
- detailsPath: 'path',
- },
-};
-
-export const mockSuccessfulPipelineHeader = {
- ...mockPipelineHeader,
- status: 'SUCCESS',
- retryable: false,
- cancelable: false,
- detailedStatus: {
- id: 'status-1',
- group: 'success',
- icon: 'status_success',
- label: 'success',
- text: 'success',
- detailsPath: 'path',
- },
-};
-
-export const mockRunningPipelineHeaderData = {
- data: {
- project: {
- id: '1',
- pipeline: {
- ...mockRunningPipelineHeader,
- iid: '28',
- user: {
- id: 'user-1',
- name: 'Foo',
- username: 'foobar',
- webPath: '/foo',
- webUrl: '/foo',
- email: 'foo@bar.com',
- avatarUrl: 'link',
- status: null,
- __typename: 'UserCore',
- },
- __typename: 'Pipeline',
- },
- __typename: 'Project',
- },
- },
-};
-
-export const stageReply = {
- name: 'deploy',
- title: 'deploy: running',
- latest_statuses: [
- {
- id: 928,
- name: 'stop staging',
- started: false,
- build_path: '/twitter/flight/-/jobs/928',
- cancel_path: '/twitter/flight/-/jobs/928/cancel',
- playable: false,
- created_at: '2018-04-04T20:02:02.728Z',
- updated_at: '2018-04-04T20:02:02.766Z',
- status: {
- icon: 'status_pending',
- text: 'pending',
- label: 'pending',
- group: 'pending',
- tooltip: 'pending',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/928',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_pending-db32e1faf94b9f89530ac519790920d1f18ea8f6af6cd2e0a26cd6840cacf101.ico',
- action: {
- icon: 'cancel',
- title: 'Cancel',
- path: '/twitter/flight/-/jobs/928/cancel',
- method: 'post',
- },
- },
- },
- {
- id: 926,
- name: 'production',
- started: false,
- build_path: '/twitter/flight/-/jobs/926',
- retry_path: '/twitter/flight/-/jobs/926/retry',
- play_path: '/twitter/flight/-/jobs/926/play',
- playable: true,
- created_at: '2018-04-04T20:00:57.202Z',
- updated_at: '2018-04-04T20:11:13.110Z',
- status: {
- icon: 'status_canceled',
- text: 'canceled',
- label: 'manual play action',
- group: 'canceled',
- tooltip: 'canceled',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/926',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_canceled-5491840b9b6feafba0bc599cbd49ee9580321dc809683856cf1b0d51532b1af6.ico',
- action: {
- icon: 'play',
- title: 'Play',
- path: '/twitter/flight/-/jobs/926/play',
- method: 'post',
- },
- },
- },
- {
- id: 217,
- name: 'staging',
- started: '2018-03-07T08:41:46.234Z',
- build_path: '/twitter/flight/-/jobs/217',
- retry_path: '/twitter/flight/-/jobs/217/retry',
- playable: false,
- created_at: '2018-03-07T14:41:58.093Z',
- updated_at: '2018-03-07T14:41:58.093Z',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/twitter/flight/-/jobs/217',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/twitter/flight/-/jobs/217/retry',
- method: 'post',
- },
- },
- },
- ],
- status: {
- icon: 'status_running',
- text: 'running',
- label: 'running',
- group: 'running',
- tooltip: 'running',
- has_details: true,
- details_path: '/twitter/flight/pipelines/13#deploy',
- favicon:
- '/assets/ci_favicons/dev/favicon_status_running-c3ad2fc53ea6079c174e5b6c1351ff349e99ec3af5a5622fb77b0fe53ea279c1.ico',
- },
- path: '/twitter/flight/pipelines/13#deploy',
- dropdown_path: '/twitter/flight/pipelines/13/stage.json?stage=deploy',
-};
-
-export const users = [
- {
- id: 1,
- name: 'Administrator',
- username: 'root',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/root',
- },
- {
- id: 10,
- name: 'Angel Spinka',
- username: 'shalonda',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/709df1b65ad06764ee2b0edf1b49fc27?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/shalonda',
- },
- {
- id: 11,
- name: 'Art Davis',
- username: 'deja.green',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/bb56834c061522760e7a6dd7d431a306?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/deja.green',
- },
- {
- id: 32,
- name: 'Arnold Mante',
- username: 'reported_user_10',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/ab558033a82466d7905179e837d7723a?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/reported_user_10',
- },
- {
- id: 38,
- name: 'Cher Wintheiser',
- username: 'reported_user_16',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/2640356e8b5bc4314133090994ed162b?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/reported_user_16',
- },
- {
- id: 39,
- name: 'Bethel Wolf',
- username: 'reported_user_17',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/4b948694fadba4b01e4acfc06b065e8e?s=80\u0026d=identicon',
- web_url: 'http://192.168.1.22:3000/reported_user_17',
- },
-];
-
-export const branches = [
- {
- name: 'branch-1',
- commit: {
- id: '21fb056cc47dcf706670e6de635b1b326490ebdc',
- short_id: '21fb056c',
- created_at: '2020-05-07T10:58:28.000-04:00',
- parent_ids: null,
- title: 'Add new file',
- message: 'Add new file',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-05-07T10:58:28.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-05-07T10:58:28.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/21fb056cc47dcf706670e6de635b1b326490ebdc',
- },
- merged: false,
- protected: false,
- developers_can_push: false,
- developers_can_merge: false,
- can_push: true,
- default: false,
- web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-1',
- },
- {
- name: 'branch-10',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: null,
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- merged: false,
- protected: false,
- developers_can_push: false,
- developers_can_merge: false,
- can_push: true,
- default: false,
- web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-10',
- },
- {
- name: 'branch-11',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: null,
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- merged: false,
- protected: false,
- developers_can_push: false,
- developers_can_merge: false,
- can_push: true,
- default: false,
- web_url: 'http://192.168.1.22:3000/root/dag-pipeline/-/tree/branch-11',
- },
-];
-
-export const tags = [
- {
- name: 'tag-3',
- message: '',
- target: '66673b07efef254dab7d537f0433a40e61cf84fe',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: ['def28bf679235071140180495f25b657e2203587'],
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- release: null,
- protected: false,
- },
- {
- name: 'tag-2',
- message: '',
- target: '66673b07efef254dab7d537f0433a40e61cf84fe',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: ['def28bf679235071140180495f25b657e2203587'],
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- release: null,
- protected: false,
- },
- {
- name: 'tag-1',
- message: '',
- target: '66673b07efef254dab7d537f0433a40e61cf84fe',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: ['def28bf679235071140180495f25b657e2203587'],
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- release: null,
- protected: false,
- },
- {
- name: 'main-tag',
- message: '',
- target: '66673b07efef254dab7d537f0433a40e61cf84fe',
- commit: {
- id: '66673b07efef254dab7d537f0433a40e61cf84fe',
- short_id: '66673b07',
- created_at: '2020-03-16T11:04:46.000-04:00',
- parent_ids: ['def28bf679235071140180495f25b657e2203587'],
- title: 'Update .gitlab-ci.yml',
- message: 'Update .gitlab-ci.yml',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2020-03-16T11:04:46.000-04:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2020-03-16T11:04:46.000-04:00',
- web_url:
- 'http://192.168.1.22:3000/root/dag-pipeline/-/commit/66673b07efef254dab7d537f0433a40e61cf84fe',
- },
- release: null,
- protected: false,
- },
-];
-
-export const mockSearch = [
- { type: 'username', value: { data: 'root', operator: '=' } },
- { type: 'ref', value: { data: 'main', operator: '=' } },
- { type: 'status', value: { data: 'pending', operator: '=' } },
-];
-
-export const mockBranchesAfterMap = ['branch-1', 'branch-10', 'branch-11'];
-
-export const mockTagsAfterMap = ['tag-3', 'tag-2', 'tag-1', 'main-tag'];
-
-export const mockPipelineJobsQueryResponse = {
- data: {
- project: {
- id: 'gid://gitlab/Project/20',
- __typename: 'Project',
- pipeline: {
- id: 'gid://gitlab/Ci::Pipeline/224',
- __typename: 'Pipeline',
- jobs: {
- __typename: 'CiJobConnection',
- pageInfo: {
- endCursor: 'eyJpZCI6Ijg0NyJ9',
- hasNextPage: true,
- hasPreviousPage: false,
- startCursor: 'eyJpZCI6IjYyMCJ9',
- __typename: 'PageInfo',
- },
- nodes: [
- {
- artifacts: {
- nodes: [
- {
- downloadPath: '/root/ci-project/-/jobs/620/artifacts/download?file_type=trace',
- fileType: 'TRACE',
- __typename: 'CiJobArtifact',
- },
- ],
- __typename: 'CiJobArtifactConnection',
- },
- allowFailure: false,
- status: 'SUCCESS',
- scheduledAt: null,
- manualJob: false,
- triggered: null,
- createdByTag: false,
- detailedStatus: {
- id: 'success-620-620',
- detailsPath: '/root/ci-project/-/jobs/620',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed (retried)',
- action: null,
- __typename: 'DetailedStatus',
- },
- id: 'gid://gitlab/Ci::Build/620',
- refName: 'main',
- refPath: '/root/ci-project/-/commits/main',
- tags: [],
- shortSha: '5acce24b',
- commitPath: '/root/ci-project/-/commit/5acce24b3737d4f0d649ad0a26ae1903a2b35f5e',
- stage: { id: 'gid://gitlab/Ci::Stage/148', name: 'test', __typename: 'CiStage' },
- name: 'coverage_job',
- duration: 4,
- finishedAt: '2021-12-06T14:13:49Z',
- coverage: 82.71,
- retryable: false,
- playable: false,
- cancelable: false,
- active: false,
- stuck: false,
- userPermissions: {
- readBuild: true,
- readJobArtifacts: true,
- updateBuild: true,
- __typename: 'JobPermissions',
- },
- __typename: 'CiJob',
- },
- {
- artifacts: {
- nodes: [
- {
- downloadPath: '/root/ci-project/-/jobs/619/artifacts/download?file_type=trace',
- fileType: 'TRACE',
- __typename: 'CiJobArtifact',
- },
- ],
- __typename: 'CiJobArtifactConnection',
- },
- allowFailure: false,
- status: 'SUCCESS',
- scheduledAt: null,
- manualJob: false,
- triggered: null,
- createdByTag: false,
- detailedStatus: {
- id: 'success-619-619',
- detailsPath: '/root/ci-project/-/jobs/619',
- group: 'success',
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed (retried)',
- action: null,
- __typename: 'DetailedStatus',
- },
- id: 'gid://gitlab/Ci::Build/619',
- refName: 'main',
- refPath: '/root/ci-project/-/commits/main',
- tags: [],
- shortSha: '5acce24b',
- commitPath: '/root/ci-project/-/commit/5acce24b3737d4f0d649ad0a26ae1903a2b35f5e',
- stage: { id: 'gid://gitlab/Ci::Stage/148', name: 'test', __typename: 'CiStage' },
- name: 'test_job_two',
- duration: 4,
- finishedAt: '2021-12-06T14:13:44Z',
- coverage: null,
- retryable: false,
- playable: false,
- cancelable: false,
- active: false,
- stuck: false,
- userPermissions: {
- readBuild: true,
- readJobArtifacts: true,
- updateBuild: true,
- __typename: 'JobPermissions',
- },
- __typename: 'CiJob',
- },
- ],
- },
- },
- },
- },
-};
-
-export const mockPipeline = (projectPath) => {
- return {
- pipeline: {
- id: 1,
- user: {
- id: 1,
- name: 'Administrator',
- username: 'root',
- state: 'active',
- avatar_url: '',
- web_url: 'http://0.0.0.0:3000/root',
- show_status: false,
- path: '/root',
- },
- active: false,
- source: 'merge_request_event',
- created_at: '2021-10-19T21:17:38.698Z',
- updated_at: '2021-10-21T18:00:42.758Z',
- path: 'foo',
- flags: {},
- merge_request: {
- iid: 1,
- path: `/${projectPath}/1`,
- title: 'commit',
- source_branch: 'test-commit-name',
- source_branch_path: `/${projectPath}`,
- target_branch: 'main',
- target_branch_path: `/${projectPath}/-/commit/main`,
- },
- ref: {
- name: 'refs/merge-requests/1/head',
- path: `/${projectPath}/-/commits/refs/merge-requests/1/head`,
- tag: false,
- branch: false,
- merge_request: true,
- },
- commit: {
- id: 'fd6df5b3229e213c97d308844a6f3e7fd71e8f8c',
- short_id: 'fd6df5b3',
- created_at: '2021-10-19T21:17:12.000+00:00',
- parent_ids: ['7147906b84306e83cb3fec6582a25390b75713c6'],
- title: 'Commit',
- message: 'Commit',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2021-10-19T21:17:12.000+00:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2021-10-19T21:17:12.000+00:00',
- trailers: {},
- web_url: '',
- author: {
- id: 1,
- name: 'Administrator',
- username: 'root',
- state: 'active',
- avatar_url: '',
- web_url: '',
- show_status: false,
- path: '/root',
- },
- author_gravatar_url: '',
- commit_url: `/${projectPath}/fd6df5b3229e213c97d308844a6f3e7fd71e8f8c`,
- commit_path: `/${projectPath}/commit/fd6df5b3229e213c97d308844a6f3e7fd71e8f8c`,
- },
- project: {
- full_path: `/${projectPath}`,
- },
- triggered_by: null,
- triggered: [],
- },
- pipelineScheduleUrl: 'foo',
- pipelineKey: 'id',
- viewType: 'root',
- };
-};
-
-export const mockPipelineTag = () => {
- return {
- pipeline: {
- id: 311,
- iid: 37,
- user: {
- id: 1,
- username: 'root',
- name: 'Administrator',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://gdk.test:3000/root',
- show_status: false,
- path: '/root',
- },
- active: false,
- source: 'push',
- name: 'Build pipeline',
- created_at: '2022-02-02T15:39:04.012Z',
- updated_at: '2022-02-02T15:40:59.573Z',
- path: '/root/mr-widgets/-/pipelines/311',
- flags: {
- stuck: false,
- auto_devops: false,
- merge_request: false,
- yaml_errors: false,
- retryable: true,
- cancelable: false,
- failure_reason: false,
- detached_merge_request_pipeline: false,
- merge_request_pipeline: false,
- merge_train_pipeline: false,
- latest: true,
- },
- details: {
- status: {
- icon: 'status_warning',
- text: 'passed',
- label: 'passed with warnings',
- group: 'success-with-warnings',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/311',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- stages: [
- {
- name: 'accessibility',
- title: 'accessibility: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/311#accessibility',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/311#accessibility',
- dropdown_path: '/root/mr-widgets/-/pipelines/311/stage.json?stage=accessibility',
- },
- {
- name: 'validate',
- title: 'validate: passed with warnings',
- status: {
- icon: 'status_warning',
- text: 'passed',
- label: 'passed with warnings',
- group: 'success-with-warnings',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/311#validate',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/311#validate',
- dropdown_path: '/root/mr-widgets/-/pipelines/311/stage.json?stage=validate',
- },
- {
- name: 'test',
- title: 'test: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/311#test',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/311#test',
- dropdown_path: '/root/mr-widgets/-/pipelines/311/stage.json?stage=test',
- },
- {
- name: 'build',
- title: 'build: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/311#build',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/311#build',
- dropdown_path: '/root/mr-widgets/-/pipelines/311/stage.json?stage=build',
- },
- ],
- duration: 93,
- finished_at: '2022-02-02T15:40:59.384Z',
- event_type_name: 'Pipeline',
- manual_actions: [],
- scheduled_actions: [],
- },
- ref: {
- name: 'test',
- path: '/root/mr-widgets/-/commits/test',
- tag: true,
- branch: false,
- merge_request: false,
- },
- commit: {
- id: '9b92b4f730d1611bd9a086ca221ae206d5da1e59',
- short_id: '9b92b4f7',
- created_at: '2022-01-13T13:59:03.000+00:00',
- parent_ids: ['0ba763634114e207dc72c65c8e9459556b1204fb'],
- title: 'Update hello_world.js',
- message: 'Update hello_world.js',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2022-01-13T13:59:03.000+00:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2022-01-13T13:59:03.000+00:00',
- trailers: {},
- web_url:
- 'http://gdk.test:3000/root/mr-widgets/-/commit/9b92b4f730d1611bd9a086ca221ae206d5da1e59',
- author: {
- id: 1,
- username: 'root',
- name: 'Administrator',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://gdk.test:3000/root',
- show_status: false,
- path: '/root',
- },
- author_gravatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- commit_url:
- 'http://gdk.test:3000/root/mr-widgets/-/commit/9b92b4f730d1611bd9a086ca221ae206d5da1e59',
- commit_path: '/root/mr-widgets/-/commit/9b92b4f730d1611bd9a086ca221ae206d5da1e59',
- },
- retry_path: '/root/mr-widgets/-/pipelines/311/retry',
- delete_path: '/root/mr-widgets/-/pipelines/311',
- failed_builds: [
- {
- id: 1696,
- name: 'fmt',
- started: '2022-02-02T15:39:45.192Z',
- complete: true,
- archived: false,
- build_path: '/root/mr-widgets/-/jobs/1696',
- retry_path: '/root/mr-widgets/-/jobs/1696/retry',
- playable: false,
- scheduled: false,
- created_at: '2022-02-02T15:39:04.136Z',
- updated_at: '2022-02-02T15:39:57.969Z',
- status: {
- icon: 'status_warning',
- text: 'failed',
- label: 'failed (allowed to fail)',
- group: 'failed-with-warnings',
- tooltip: 'failed - (script failure) (allowed to fail)',
- has_details: true,
- details_path: '/root/mr-widgets/-/jobs/1696',
- illustration: {
- image:
- '/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
- size: 'svg-430',
- title: 'This job does not have a trace.',
- },
- favicon:
- '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/mr-widgets/-/jobs/1696/retry',
- method: 'post',
- button_title: 'Retry this job',
- },
- },
- recoverable: false,
- },
- ],
- project: {
- id: 23,
- name: 'mr-widgets',
- full_path: '/root/mr-widgets',
- full_name: 'Administrator / mr-widgets',
- },
- triggered_by: null,
- triggered: [],
- },
- pipelineScheduleUrl: 'foo',
- pipelineKey: 'id',
- viewType: 'root',
- };
-};
-
-export const mockPipelineBranch = () => {
- return {
- pipeline: {
- id: 268,
- iid: 34,
- user: {
- id: 1,
- username: 'root',
- name: 'Administrator',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://gdk.test:3000/root',
- show_status: false,
- path: '/root',
- },
- active: false,
- source: 'push',
- name: 'Build pipeline',
- created_at: '2022-01-14T17:40:27.866Z',
- updated_at: '2022-01-14T18:02:35.850Z',
- path: '/root/mr-widgets/-/pipelines/268',
- flags: {
- stuck: false,
- auto_devops: false,
- merge_request: false,
- yaml_errors: false,
- retryable: true,
- cancelable: false,
- failure_reason: false,
- detached_merge_request_pipeline: false,
- merge_request_pipeline: false,
- merge_train_pipeline: false,
- latest: true,
- },
- details: {
- status: {
- icon: 'status_warning',
- text: 'passed',
- label: 'passed with warnings',
- group: 'success-with-warnings',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/268',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- stages: [
- {
- name: 'validate',
- title: 'validate: passed with warnings',
- status: {
- icon: 'status_warning',
- text: 'passed',
- label: 'passed with warnings',
- group: 'success-with-warnings',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/268#validate',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/268#validate',
- dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=validate',
- },
- {
- name: 'test',
- title: 'test: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/268#test',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/268#test',
- dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=test',
- },
- {
- name: 'build',
- title: 'build: passed',
- status: {
- icon: 'status_success',
- text: 'passed',
- label: 'passed',
- group: 'success',
- tooltip: 'passed',
- has_details: true,
- details_path: '/root/mr-widgets/-/pipelines/268#build',
- illustration: null,
- favicon:
- '/assets/ci_favicons/favicon_status_success-8451333011eee8ce9f2ab25dc487fe24a8758c694827a582f17f42b0a90446a2.png',
- },
- path: '/root/mr-widgets/-/pipelines/268#build',
- dropdown_path: '/root/mr-widgets/-/pipelines/268/stage.json?stage=build',
- },
- ],
- duration: 75,
- finished_at: '2022-01-14T18:02:35.842Z',
- event_type_name: 'Pipeline',
- manual_actions: [],
- scheduled_actions: [],
- },
- ref: {
- name: 'update-ci',
- path: '/root/mr-widgets/-/commits/update-ci',
- tag: false,
- branch: true,
- merge_request: false,
- },
- commit: {
- id: '96aef9ecec5752c09371c1ade5fc77860aafc863',
- short_id: '96aef9ec',
- created_at: '2022-01-14T17:40:26.000+00:00',
- parent_ids: ['06860257572d4cf84b73806250b78169050aed83'],
- title: 'Update main.tf',
- message: 'Update main.tf',
- author_name: 'Administrator',
- author_email: 'admin@example.com',
- authored_date: '2022-01-14T17:40:26.000+00:00',
- committer_name: 'Administrator',
- committer_email: 'admin@example.com',
- committed_date: '2022-01-14T17:40:26.000+00:00',
- trailers: {},
- web_url:
- 'http://gdk.test:3000/root/mr-widgets/-/commit/96aef9ecec5752c09371c1ade5fc77860aafc863',
- author: {
- id: 1,
- username: 'root',
- name: 'Administrator',
- state: 'active',
- avatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- web_url: 'http://gdk.test:3000/root',
- show_status: false,
- path: '/root',
- },
- author_gravatar_url:
- 'https://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
- commit_url:
- 'http://gdk.test:3000/root/mr-widgets/-/commit/96aef9ecec5752c09371c1ade5fc77860aafc863',
- commit_path: '/root/mr-widgets/-/commit/96aef9ecec5752c09371c1ade5fc77860aafc863',
- },
- retry_path: '/root/mr-widgets/-/pipelines/268/retry',
- delete_path: '/root/mr-widgets/-/pipelines/268',
- failed_builds: [
- {
- id: 1260,
- name: 'fmt',
- started: '2022-01-14T17:40:36.435Z',
- complete: true,
- archived: false,
- build_path: '/root/mr-widgets/-/jobs/1260',
- retry_path: '/root/mr-widgets/-/jobs/1260/retry',
- playable: false,
- scheduled: false,
- created_at: '2022-01-14T17:40:27.879Z',
- updated_at: '2022-01-14T17:40:42.129Z',
- status: {
- icon: 'status_warning',
- text: 'failed',
- label: 'failed (allowed to fail)',
- group: 'failed-with-warnings',
- tooltip: 'failed - (script failure) (allowed to fail)',
- has_details: true,
- details_path: '/root/mr-widgets/-/jobs/1260',
- illustration: {
- image:
- '/assets/illustrations/skipped-job_empty-29a8a37d8a61d1b6f68cf3484f9024e53cd6eb95e28eae3554f8011a1146bf27.svg',
- size: 'svg-430',
- title: 'This job does not have a trace.',
- },
- favicon:
- '/assets/ci_favicons/favicon_status_failed-41304d7f7e3828808b0c26771f0309e55296819a9beea3ea9fbf6689d9857c12.png',
- action: {
- icon: 'retry',
- title: 'Retry',
- path: '/root/mr-widgets/-/jobs/1260/retry',
- method: 'post',
- button_title: 'Retry this job',
- },
- },
- recoverable: false,
- },
- ],
- project: {
- id: 23,
- name: 'mr-widgets',
- full_path: '/root/mr-widgets',
- full_name: 'Administrator / mr-widgets',
- },
- triggered_by: null,
- triggered: [],
- },
- pipelineScheduleUrl: 'foo',
- pipelineKey: 'id',
- viewType: 'root',
- };
-};
-
-export const mockFailedJobsQueryResponse = {
- data: {
- project: {
- __typename: 'Project',
- id: 'gid://gitlab/Project/20',
- pipeline: {
- __typename: 'Pipeline',
- id: 'gid://gitlab/Ci::Pipeline/300',
- jobs: {
- __typename: 'CiJobConnection',
- nodes: [
- {
- __typename: 'CiJob',
- status: 'FAILED',
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-1848-1848',
- detailsPath: '/root/ci-project/-/jobs/1848',
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- tooltip: 'failed - (script failure)',
- action: {
- __typename: 'StatusAction',
- id: 'Ci::Build-failed-1848',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- method: 'post',
- path: '/root/ci-project/-/jobs/1848/retry',
- title: 'Retry',
- },
- },
- id: 'gid://gitlab/Ci::Build/1848',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/358',
- name: 'build',
- },
- name: 'wait_job',
- retryable: true,
- userPermissions: {
- __typename: 'JobPermissions',
- readBuild: true,
- updateBuild: true,
- },
- trace: {
- htmlSummary: '<span>Html Summary</span>',
- },
- failureMessage: 'Failed',
- },
- {
- __typename: 'CiJob',
- status: 'FAILED',
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-1710-1710',
- detailsPath: '/root/ci-project/-/jobs/1710',
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- tooltip: 'failed - (script failure) (retried)',
- action: null,
- },
- id: 'gid://gitlab/Ci::Build/1710',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/358',
- name: 'build',
- },
- name: 'wait_job',
- retryable: false,
- userPermissions: {
- __typename: 'JobPermissions',
- readBuild: true,
- updateBuild: true,
- },
- trace: null,
- failureMessage: 'Failed',
- },
- ],
- },
- },
- },
- },
-};
-
-export const mockFailedJobsData = [
- {
- __typename: 'CiJob',
- status: 'FAILED',
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-1848-1848',
- detailsPath: '/root/ci-project/-/jobs/1848',
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- tooltip: 'failed - (script failure)',
- action: {
- __typename: 'StatusAction',
- id: 'Ci::Build-failed-1848',
- buttonTitle: 'Retry this job',
- icon: 'retry',
- method: 'post',
- path: '/root/ci-project/-/jobs/1848/retry',
- title: 'Retry',
- },
- },
- id: 'gid://gitlab/Ci::Build/1848',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/358',
- name: 'build',
- },
- name: 'wait_job',
- retryable: true,
- userPermissions: {
- __typename: 'JobPermissions',
- readBuild: true,
- updateBuild: true,
- },
- trace: {
- htmlSummary: '<span>Html Summary</span>',
- },
- failureMessage: 'Job failed',
- _showDetails: true,
- },
- {
- __typename: 'CiJob',
- status: 'FAILED',
- detailedStatus: {
- __typename: 'DetailedStatus',
- id: 'failed-1710-1710',
- detailsPath: '/root/ci-project/-/jobs/1710',
- group: 'failed',
- icon: 'status_failed',
- label: 'failed',
- text: 'failed',
- tooltip: 'failed - (script failure) (retried)',
- action: null,
- },
- id: 'gid://gitlab/Ci::Build/1710',
- stage: {
- __typename: 'CiStage',
- id: 'gid://gitlab/Ci::Stage/358',
- name: 'build',
- },
- name: 'wait_job',
- retryable: false,
- userPermissions: {
- __typename: 'JobPermissions',
- readBuild: true,
- updateBuild: true,
- },
- trace: null,
- failureMessage: 'Job failed',
- _showDetails: true,
- },
-];
-
-export const mockFailedJobsDataNoPermission = [
- {
- ...mockFailedJobsData[0],
- userPermissions: { __typename: 'JobPermissions', readBuild: false, updateBuild: false },
- },
-];
-
-export const successRetryMutationResponse = {
- data: {
- jobRetry: {
- job: {
- __typename: 'CiJob',
- id: '"gid://gitlab/Ci::Build/1985"',
- detailedStatus: {
- detailsPath: '/root/project/-/jobs/1985',
- id: 'pending-1985-1985',
- __typename: 'DetailedStatus',
- },
- },
- errors: [],
- __typename: 'JobRetryPayload',
- },
- },
-};
-
-export const failedRetryMutationResponse = {
- data: {
- jobRetry: {
- job: {},
- errors: ['New Error'],
- __typename: 'JobRetryPayload',
- },
- },
-};
diff --git a/spec/frontend/pipelines/nav_controls_spec.js b/spec/frontend/pipelines/nav_controls_spec.js
deleted file mode 100644
index 15de7dc51f1..00000000000
--- a/spec/frontend/pipelines/nav_controls_spec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import NavControls from '~/pipelines/components/pipelines_list/nav_controls.vue';
-
-describe('Pipelines Nav Controls', () => {
- let wrapper;
-
- const createComponent = (props) => {
- wrapper = shallowMountExtended(NavControls, {
- propsData: {
- ...props,
- },
- });
- };
-
- const findRunPipelineButton = () => wrapper.findByTestId('run-pipeline-button');
- const findCiLintButton = () => wrapper.findByTestId('ci-lint-button');
- const findClearCacheButton = () => wrapper.findByTestId('clear-cache-button');
-
- it('should render link to create a new pipeline', () => {
- const mockData = {
- newPipelinePath: 'foo',
- ciLintPath: 'foo',
- resetCachePath: 'foo',
- };
-
- createComponent(mockData);
-
- const runPipelineButton = findRunPipelineButton();
- expect(runPipelineButton.text()).toContain('Run pipeline');
- expect(runPipelineButton.attributes('href')).toBe(mockData.newPipelinePath);
- });
-
- it('should not render link to create pipeline if no path is provided', () => {
- const mockData = {
- helpPagePath: 'foo',
- ciLintPath: 'foo',
- resetCachePath: 'foo',
- };
-
- createComponent(mockData);
-
- expect(findRunPipelineButton().exists()).toBe(false);
- });
-
- it('should render link for CI lint', () => {
- const mockData = {
- newPipelinePath: 'foo',
- helpPagePath: 'foo',
- ciLintPath: 'foo',
- resetCachePath: 'foo',
- };
-
- createComponent(mockData);
- const ciLintButton = findCiLintButton();
-
- expect(ciLintButton.text()).toContain('CI lint');
- expect(ciLintButton.attributes('href')).toBe(mockData.ciLintPath);
- });
-
- describe('Reset Runners Cache', () => {
- beforeEach(() => {
- const mockData = {
- newPipelinePath: 'foo',
- ciLintPath: 'foo',
- resetCachePath: 'foo',
- };
- createComponent(mockData);
- });
-
- it('should render button for resetting runner caches', () => {
- expect(findClearCacheButton().text()).toContain('Clear runner caches');
- });
-
- it('should emit postAction event when reset runner cache button is clicked', () => {
- findClearCacheButton().vm.$emit('click');
-
- expect(wrapper.emitted('resetRunnersCache')).toEqual([['foo']]);
- });
- });
-});
diff --git a/spec/frontend/pipelines/notification/mock_data.js b/spec/frontend/pipelines/notification/mock_data.js
deleted file mode 100644
index e36f391a854..00000000000
--- a/spec/frontend/pipelines/notification/mock_data.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const randomWarning = {
- content: 'another random warning',
- id: 'gid://gitlab/Ci::PipelineMessage/272',
-};
-
-const rootTypeWarning = {
- content: 'root `types` will be removed in 15.0.',
- id: 'gid://gitlab/Ci::PipelineMessage/273',
-};
-
-const typeWarning = {
- content: '`type` will be removed in 15.0.',
- id: 'gid://gitlab/Ci::PipelineMessage/274',
-};
-
-function createWarningMock(warnings) {
- return {
- data: {
- project: {
- id: 'gid://gitlab/Project/28"',
- pipeline: {
- id: 'gid://gitlab/Ci::Pipeline/183',
- warningMessages: warnings,
- },
- },
- },
- };
-}
-
-export const mockWarningsWithoutDeprecation = createWarningMock([randomWarning]);
-export const mockWarningsRootType = createWarningMock([rootTypeWarning]);
-export const mockWarningsType = createWarningMock([typeWarning]);
-export const mockWarningsTypesAll = createWarningMock([rootTypeWarning, typeWarning]);
diff --git a/spec/frontend/pipelines/pipeline_details_header_spec.js b/spec/frontend/pipelines/pipeline_details_header_spec.js
deleted file mode 100644
index 5c75020afad..00000000000
--- a/spec/frontend/pipelines/pipeline_details_header_spec.js
+++ /dev/null
@@ -1,452 +0,0 @@
-import { GlAlert, GlBadge, GlLoadingIcon, GlModal, GlSprintf } from '@gitlab/ui';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import PipelineDetailsHeader from '~/pipelines/components/pipeline_details_header.vue';
-import { BUTTON_TOOLTIP_RETRY, BUTTON_TOOLTIP_CANCEL } from '~/pipelines/constants';
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
-import cancelPipelineMutation from '~/pipelines/graphql/mutations/cancel_pipeline.mutation.graphql';
-import deletePipelineMutation from '~/pipelines/graphql/mutations/delete_pipeline.mutation.graphql';
-import retryPipelineMutation from '~/pipelines/graphql/mutations/retry_pipeline.mutation.graphql';
-import getPipelineDetailsQuery from '~/pipelines/graphql/queries/get_pipeline_header_data.query.graphql';
-import {
- pipelineHeaderSuccess,
- pipelineHeaderRunning,
- pipelineHeaderRunningWithDuration,
- pipelineHeaderFailed,
- pipelineRetryMutationResponseSuccess,
- pipelineCancelMutationResponseSuccess,
- pipelineDeleteMutationResponseSuccess,
- pipelineRetryMutationResponseFailed,
- pipelineCancelMutationResponseFailed,
- pipelineDeleteMutationResponseFailed,
-} from './mock_data';
-
-Vue.use(VueApollo);
-
-describe('Pipeline details header', () => {
- let wrapper;
- let glModalDirective;
-
- const successHandler = jest.fn().mockResolvedValue(pipelineHeaderSuccess);
- const runningHandler = jest.fn().mockResolvedValue(pipelineHeaderRunning);
- const runningHandlerWithDuration = jest.fn().mockResolvedValue(pipelineHeaderRunningWithDuration);
- const failedHandler = jest.fn().mockResolvedValue(pipelineHeaderFailed);
-
- const retryMutationHandlerSuccess = jest
- .fn()
- .mockResolvedValue(pipelineRetryMutationResponseSuccess);
- const cancelMutationHandlerSuccess = jest
- .fn()
- .mockResolvedValue(pipelineCancelMutationResponseSuccess);
- const deleteMutationHandlerSuccess = jest
- .fn()
- .mockResolvedValue(pipelineDeleteMutationResponseSuccess);
- const retryMutationHandlerFailed = jest
- .fn()
- .mockResolvedValue(pipelineRetryMutationResponseFailed);
- const cancelMutationHandlerFailed = jest
- .fn()
- .mockResolvedValue(pipelineCancelMutationResponseFailed);
- const deleteMutationHandlerFailed = jest
- .fn()
- .mockResolvedValue(pipelineDeleteMutationResponseFailed);
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findStatus = () => wrapper.findComponent(CiBadgeLink);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findAllBadges = () => wrapper.findAllComponents(GlBadge);
- const findDeleteModal = () => wrapper.findComponent(GlModal);
- const findCreatedTimeAgo = () => wrapper.findByTestId('pipeline-created-time-ago');
- const findFinishedTimeAgo = () => wrapper.findByTestId('pipeline-finished-time-ago');
- const findPipelineName = () => wrapper.findByTestId('pipeline-name');
- const findCommitTitle = () => wrapper.findByTestId('pipeline-commit-title');
- const findTotalJobs = () => wrapper.findByTestId('total-jobs');
- const findComputeMinutes = () => wrapper.findByTestId('compute-minutes');
- const findCommitLink = () => wrapper.findByTestId('commit-link');
- const findPipelineRunningText = () => wrapper.findByTestId('pipeline-running-text').text();
- const findPipelineRefText = () => wrapper.findByTestId('pipeline-ref-text').text();
- const findRetryButton = () => wrapper.findByTestId('retry-pipeline');
- const findCancelButton = () => wrapper.findByTestId('cancel-pipeline');
- const findDeleteButton = () => wrapper.findByTestId('delete-pipeline');
- const findPipelineUserLink = () => wrapper.findByTestId('pipeline-user-link');
- const findPipelineDuration = () => wrapper.findByTestId('pipeline-duration-text');
-
- const defaultHandlers = [[getPipelineDetailsQuery, successHandler]];
-
- const defaultProvideOptions = {
- pipelineIid: 1,
- paths: {
- pipelinesPath: '/namespace/my-project/-/pipelines',
- fullProject: '/namespace/my-project',
- triggeredByPath: '',
- },
- };
-
- const defaultProps = {
- name: 'Ruby 3.0 master branch pipeline',
- totalJobs: '50',
- computeMinutes: '0.65',
- yamlErrors: 'errors',
- failureReason: 'pipeline failed',
- badges: {
- schedule: true,
- child: false,
- latest: true,
- mergeTrainPipeline: false,
- invalid: false,
- failed: false,
- autoDevops: false,
- detached: false,
- stuck: false,
- },
- refText:
- 'Related merge request <a class="mr-iid" href="/root/ci-project/-/merge_requests/1">!1</a> to merge <a class="ref-name" href="/root/ci-project/-/commits/test">test</a>',
- };
-
- const createMockApolloProvider = (handlers) => {
- return createMockApollo(handlers);
- };
-
- const createComponent = (handlers = defaultHandlers, props = defaultProps) => {
- glModalDirective = jest.fn();
-
- wrapper = shallowMountExtended(PipelineDetailsHeader, {
- provide: {
- ...defaultProvideOptions,
- },
- propsData: {
- ...props,
- },
- directives: {
- glModal: {
- bind(_, { value }) {
- glModalDirective(value);
- },
- },
- },
- stubs: { GlSprintf },
- apolloProvider: createMockApolloProvider(handlers),
- });
- };
-
- describe('loading state', () => {
- it('shows a loading state while graphQL is fetching initial data', () => {
- createComponent();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-
- describe('defaults', () => {
- beforeEach(async () => {
- createComponent();
-
- await waitForPromises();
- });
-
- it('does not display loading icon', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('displays pipeline status', () => {
- expect(findStatus().exists()).toBe(true);
- });
-
- it('displays pipeline name', () => {
- expect(findPipelineName().text()).toBe(defaultProps.name);
- });
-
- it('displays total jobs', () => {
- expect(findTotalJobs().text()).toBe('50 Jobs');
- });
-
- it('has link to commit', () => {
- const {
- data: {
- project: { pipeline },
- },
- } = pipelineHeaderSuccess;
-
- expect(findCommitLink().attributes('href')).toBe(pipeline.commit.webPath);
- });
-
- it('displays correct badges', () => {
- expect(findAllBadges()).toHaveLength(2);
- expect(wrapper.findByText('latest').exists()).toBe(true);
- expect(wrapper.findByText('Scheduled').exists()).toBe(true);
- });
-
- it('displays ref text', () => {
- expect(findPipelineRefText()).toBe('Related merge request !1 to merge test');
- });
-
- it('displays pipeline user link with required user popover attributes', () => {
- const {
- data: {
- project: {
- pipeline: { user },
- },
- },
- } = pipelineHeaderSuccess;
-
- const userId = getIdFromGraphQLId(user.id).toString();
-
- expect(findPipelineUserLink().classes()).toContain('js-user-link');
- expect(findPipelineUserLink().attributes('data-user-id')).toBe(userId);
- expect(findPipelineUserLink().attributes('data-username')).toBe(user.username);
- expect(findPipelineUserLink().attributes('href')).toBe(user.webUrl);
- });
- });
-
- describe('without pipeline name', () => {
- it('displays commit title', async () => {
- createComponent(defaultHandlers, { ...defaultProps, name: '' });
-
- await waitForPromises();
-
- const expectedTitle = pipelineHeaderSuccess.data.project.pipeline.commit.title;
-
- expect(findPipelineName().exists()).toBe(false);
- expect(findCommitTitle().text()).toBe(expectedTitle);
- });
- });
-
- describe('finished pipeline', () => {
- it('displays compute minutes when not zero', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(findComputeMinutes().text()).toBe('0.65');
- });
-
- it('does not display compute minutes when zero', async () => {
- createComponent(defaultHandlers, { ...defaultProps, computeMinutes: '0.0' });
-
- await waitForPromises();
-
- expect(findComputeMinutes().exists()).toBe(false);
- });
-
- it('does not display created time ago', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(findCreatedTimeAgo().exists()).toBe(false);
- });
-
- it('displays finished time ago', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(findFinishedTimeAgo().exists()).toBe(true);
- });
-
- it('displays pipeline duartion text', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(findPipelineDuration().text()).toBe(
- '120 minutes 10 seconds, queued for 3,600 seconds',
- );
- });
- });
-
- describe('running pipeline', () => {
- beforeEach(async () => {
- createComponent([[getPipelineDetailsQuery, runningHandler]]);
-
- await waitForPromises();
- });
-
- it('does not display compute minutes', () => {
- expect(findComputeMinutes().exists()).toBe(false);
- });
-
- it('does not display finished time ago', () => {
- expect(findFinishedTimeAgo().exists()).toBe(false);
- });
-
- it('does not display pipeline duration text', () => {
- expect(findPipelineDuration().exists()).toBe(false);
- });
-
- it('displays pipeline running text', () => {
- expect(findPipelineRunningText()).toBe('In progress, queued for 3,600 seconds');
- });
-
- it('displays created time ago', () => {
- expect(findCreatedTimeAgo().exists()).toBe(true);
- });
- });
-
- describe('running pipeline with duration', () => {
- beforeEach(async () => {
- createComponent([[getPipelineDetailsQuery, runningHandlerWithDuration]]);
-
- await waitForPromises();
- });
-
- it('does not display pipeline duration text', () => {
- expect(findPipelineDuration().exists()).toBe(false);
- });
- });
-
- describe('actions', () => {
- describe('retry action', () => {
- beforeEach(async () => {
- createComponent([
- [getPipelineDetailsQuery, failedHandler],
- [retryPipelineMutation, retryMutationHandlerSuccess],
- ]);
-
- await waitForPromises();
- });
-
- it('should call retryPipeline Mutation with pipeline id', () => {
- findRetryButton().vm.$emit('click');
-
- expect(retryMutationHandlerSuccess).toHaveBeenCalledWith({
- id: pipelineHeaderFailed.data.project.pipeline.id,
- });
- expect(findAlert().exists()).toBe(false);
- });
-
- it('should render retry action tooltip', () => {
- expect(findRetryButton().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
- });
- });
-
- describe('retry action failed', () => {
- beforeEach(async () => {
- createComponent([
- [getPipelineDetailsQuery, failedHandler],
- [retryPipelineMutation, retryMutationHandlerFailed],
- ]);
-
- await waitForPromises();
- });
-
- it('should display error message on failure', async () => {
- findRetryButton().vm.$emit('click');
-
- await waitForPromises();
-
- expect(findAlert().exists()).toBe(true);
- });
-
- it('retry button loading state should reset on error', async () => {
- findRetryButton().vm.$emit('click');
-
- await nextTick();
-
- expect(findRetryButton().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findRetryButton().props('loading')).toBe(false);
- });
- });
-
- describe('cancel action', () => {
- it('should call cancelPipeline Mutation with pipeline id', async () => {
- createComponent([
- [getPipelineDetailsQuery, runningHandler],
- [cancelPipelineMutation, cancelMutationHandlerSuccess],
- ]);
-
- await waitForPromises();
-
- findCancelButton().vm.$emit('click');
-
- expect(cancelMutationHandlerSuccess).toHaveBeenCalledWith({
- id: pipelineHeaderRunning.data.project.pipeline.id,
- });
- expect(findAlert().exists()).toBe(false);
- });
-
- it('should render cancel action tooltip', async () => {
- createComponent([
- [getPipelineDetailsQuery, runningHandler],
- [cancelPipelineMutation, cancelMutationHandlerSuccess],
- ]);
-
- await waitForPromises();
-
- expect(findCancelButton().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL);
- });
-
- it('should display error message on failure', async () => {
- createComponent([
- [getPipelineDetailsQuery, runningHandler],
- [cancelPipelineMutation, cancelMutationHandlerFailed],
- ]);
-
- await waitForPromises();
-
- findCancelButton().vm.$emit('click');
-
- await waitForPromises();
-
- expect(findAlert().exists()).toBe(true);
- });
- });
-
- describe('delete action', () => {
- it('displays delete modal when clicking on delete and does not call the delete action', async () => {
- createComponent([
- [getPipelineDetailsQuery, successHandler],
- [deletePipelineMutation, deleteMutationHandlerSuccess],
- ]);
-
- await waitForPromises();
-
- findDeleteButton().vm.$emit('click');
-
- const modalId = 'pipeline-delete-modal';
-
- expect(findDeleteModal().props('modalId')).toBe(modalId);
- expect(glModalDirective).toHaveBeenCalledWith(modalId);
- expect(deleteMutationHandlerSuccess).not.toHaveBeenCalled();
- expect(findAlert().exists()).toBe(false);
- });
-
- it('should call deletePipeline Mutation with pipeline id when modal is submitted', async () => {
- createComponent([
- [getPipelineDetailsQuery, successHandler],
- [deletePipelineMutation, deleteMutationHandlerSuccess],
- ]);
-
- await waitForPromises();
-
- findDeleteModal().vm.$emit('primary');
-
- expect(deleteMutationHandlerSuccess).toHaveBeenCalledWith({
- id: pipelineHeaderSuccess.data.project.pipeline.id,
- });
- });
-
- it('should display error message on failure', async () => {
- createComponent([
- [getPipelineDetailsQuery, successHandler],
- [deletePipelineMutation, deleteMutationHandlerFailed],
- ]);
-
- await waitForPromises();
-
- findDeleteModal().vm.$emit('primary');
-
- await waitForPromises();
-
- expect(findAlert().exists()).toBe(true);
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_graph/mock_data.js b/spec/frontend/pipelines/pipeline_graph/mock_data.js
deleted file mode 100644
index db77e0a0573..00000000000
--- a/spec/frontend/pipelines/pipeline_graph/mock_data.js
+++ /dev/null
@@ -1,283 +0,0 @@
-export const yamlString = `stages:
-- empty
-- build
-- test
-- deploy
-- final
-
-include:
-- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
-
-build_a:
- stage: build
- script: echo hello
-build_b:
- stage: build
- script: echo hello
-build_c:
- stage: build
- script: echo hello
-build_d:
- stage: Queen
- script: echo hello
-
-test_a:
- stage: test
- script: ls
- needs: [build_a, build_b, build_c]
-test_b:
- stage: test
- script: ls
- needs: [build_a, build_b, build_d]
-test_c:
- stage: test
- script: ls
- needs: [build_a, build_b, build_c]
-
-deploy_a:
- stage: deploy
- script: echo hello
-`;
-
-export const pipelineDataWithNoNeeds = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- ],
- },
- {
- name: 'test',
- groups: [
- {
- name: 'test_1',
- jobs: [{ script: 'yarn test', stage: 'test' }],
- },
- ],
- },
- ],
-};
-
-export const pipelineData = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- ],
- },
- {
- name: 'test',
- groups: [
- {
- name: 'test_1',
- jobs: [{ script: 'yarn test', stage: 'test' }],
- },
- {
- name: 'test_2',
- jobs: [{ script: 'yarn karma', stage: 'test' }],
- },
- ],
- },
- {
- name: 'deploy',
- groups: [
- {
- name: 'deploy_1',
- jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['test_1'] }],
- },
- ],
- },
- ],
-};
-
-export const invalidNeedsData = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- ],
- },
- {
- name: 'test',
- groups: [
- {
- name: 'test_1',
- jobs: [{ script: 'yarn test', stage: 'test' }],
- },
- {
- name: 'test_2',
- jobs: [{ script: 'yarn karma', stage: 'test' }],
- },
- ],
- },
- {
- name: 'deploy',
- groups: [
- {
- name: 'deploy_1',
- jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['invalid_job'] }],
- },
- ],
- },
- ],
-};
-
-export const parallelNeedData = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- parallel: 3,
- jobs: [
- { script: 'echo hello', stage: 'build', name: 'build_1 1/3' },
- { script: 'echo hello', stage: 'build', name: 'build_1 2/3' },
- { script: 'echo hello', stage: 'build', name: 'build_1 3/3' },
- ],
- },
- ],
- },
- {
- name: 'test',
- groups: [
- {
- name: 'test_1',
- jobs: [{ script: 'yarn test', stage: 'test', needs: ['build_1'] }],
- },
- ],
- },
- ],
-};
-
-export const sameStageNeeds = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build', name: 'build_1' }],
- },
- ],
- },
- {
- name: 'build',
- groups: [
- {
- name: 'build_2',
- jobs: [{ script: 'yarn test', stage: 'build', needs: ['build_1'] }],
- },
- ],
- },
- {
- name: 'build',
- groups: [
- {
- name: 'build_3',
- jobs: [{ script: 'yarn test', stage: 'build', needs: ['build_2'] }],
- },
- ],
- },
- ],
-};
-
-export const largePipelineData = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- {
- name: 'build_2',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- {
- name: 'build_3',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- ],
- },
- {
- name: 'test',
- groups: [
- {
- name: 'test_1',
- jobs: [{ script: 'yarn test', stage: 'test', needs: ['build_2'] }],
- },
- {
- name: 'test_2',
- jobs: [{ script: 'yarn karma', stage: 'test', needs: ['build_2'] }],
- },
- ],
- },
- {
- name: 'deploy',
- groups: [
- {
- name: 'deploy_1',
- jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['test_1'] }],
- },
- {
- name: 'deploy_2',
- jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['build_3'] }],
- },
- {
- name: 'deploy_3',
- jobs: [{ script: 'yarn magick', stage: 'deploy', needs: ['test_2'] }],
- },
- ],
- },
- ],
-};
-
-export const singleStageData = {
- stages: [
- {
- name: 'build',
- groups: [
- {
- name: 'build_1',
- jobs: [{ script: 'echo hello', stage: 'build' }],
- },
- ],
- },
- ],
-};
-
-export const rootRect = {
- bottom: 463,
- height: 271,
- left: 236,
- right: 1252,
- top: 192,
- width: 1016,
- x: 236,
- y: 192,
-};
-
-export const jobRect = {
- bottom: 312,
- height: 24,
- left: 308,
- right: 428,
- top: 288,
- width: 120,
- x: 308,
- y: 288,
-};
diff --git a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js b/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
deleted file mode 100644
index 123f2e011c3..00000000000
--- a/spec/frontend/pipelines/pipeline_graph/pipeline_graph_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { GlAlert } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { setHTMLFixture } from 'helpers/fixtures';
-import { CI_CONFIG_STATUS_VALID } from '~/ci/pipeline_editor/constants';
-import LinksInner from '~/pipelines/components/graph_shared/links_inner.vue';
-import LinksLayer from '~/pipelines/components/graph_shared/links_layer.vue';
-import JobPill from '~/pipelines/components/pipeline_graph/job_pill.vue';
-import PipelineGraph from '~/pipelines/components/pipeline_graph/pipeline_graph.vue';
-import StageName from '~/pipelines/components/pipeline_graph/stage_name.vue';
-import { pipelineData, singleStageData } from './mock_data';
-
-describe('pipeline graph component', () => {
- const defaultProps = { pipelineData };
- let wrapper;
-
- const containerId = 'pipeline-graph-container-0';
- setHTMLFixture(`<div id="${containerId}"></div>`);
-
- const createComponent = (props = defaultProps) => {
- return shallowMount(PipelineGraph, {
- propsData: {
- ...props,
- },
- stubs: { LinksLayer, LinksInner },
- data() {
- return {
- measurements: {
- width: 1000,
- height: 1000,
- },
- };
- },
- });
- };
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findAllJobPills = () => wrapper.findAllComponents(JobPill);
- const findAllStageNames = () => wrapper.findAllComponents(StageName);
- const findLinksLayer = () => wrapper.findComponent(LinksLayer);
- const findPipelineGraph = () => wrapper.find('[data-testid="graph-container"]');
-
- describe('with `VALID` status', () => {
- beforeEach(() => {
- wrapper = createComponent({
- pipelineData: {
- status: CI_CONFIG_STATUS_VALID,
- stages: [{ name: 'hello', groups: [] }],
- },
- });
- });
-
- it('renders the graph with no status error', () => {
- expect(findAlert().exists()).toBe(false);
- expect(findPipelineGraph().exists()).toBe(true);
- expect(findLinksLayer().exists()).toBe(true);
- });
- });
-
- describe('with only one stage', () => {
- beforeEach(() => {
- wrapper = createComponent({ pipelineData: singleStageData });
- });
-
- it('renders the right number of stage titles', () => {
- const expectedStagesLength = singleStageData.stages.length;
-
- expect(findAllStageNames()).toHaveLength(expectedStagesLength);
- });
-
- it('renders the right number of job pills', () => {
- // We count the number of jobs in the mock data
- const expectedJobsLength = singleStageData.stages.reduce((acc, val) => {
- return acc + val.groups.length;
- }, 0);
-
- expect(findAllJobPills()).toHaveLength(expectedJobsLength);
- });
- });
-
- describe('with multiple stages and jobs', () => {
- beforeEach(() => {
- wrapper = createComponent();
- });
-
- it('renders the right number of stage titles', () => {
- const expectedStagesLength = pipelineData.stages.length;
-
- expect(findAllStageNames()).toHaveLength(expectedStagesLength);
- });
-
- it('renders the right number of job pills', () => {
- // We count the number of jobs in the mock data
- const expectedJobsLength = pipelineData.stages.reduce((acc, val) => {
- return acc + val.groups.length;
- }, 0);
-
- expect(findAllJobPills()).toHaveLength(expectedJobsLength);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_graph/utils_spec.js b/spec/frontend/pipelines/pipeline_graph/utils_spec.js
deleted file mode 100644
index 96b18fcf96f..00000000000
--- a/spec/frontend/pipelines/pipeline_graph/utils_spec.js
+++ /dev/null
@@ -1,197 +0,0 @@
-import { createJobsHash, generateJobNeedsDict, getPipelineDefaultTab } from '~/pipelines/utils';
-import { validPipelineTabNames, pipelineTabName } from '~/pipelines/constants';
-
-describe('utils functions', () => {
- const jobName1 = 'build_1';
- const jobName2 = 'build_2';
- const jobName3 = 'test_1';
- const jobName4 = 'deploy_1';
- const job1 = { name: jobName1, script: 'echo hello', stage: 'build' };
- const job2 = { name: jobName2, script: 'echo build', stage: 'build' };
- const job3 = {
- name: jobName3,
- script: 'echo test',
- stage: 'test',
- needs: [jobName1, jobName2],
- };
- const job4 = {
- name: jobName4,
- script: 'echo deploy',
- stage: 'deploy',
- needs: [jobName3],
- };
- const userDefinedStage = 'myStage';
-
- const pipelineGraphData = {
- stages: [
- {
- name: userDefinedStage,
- groups: [],
- },
- {
- name: job4.stage,
- groups: [
- {
- name: jobName4,
- jobs: [{ ...job4 }],
- },
- ],
- },
- {
- name: job1.stage,
- groups: [
- {
- name: jobName1,
- jobs: [{ ...job1 }],
- },
- {
- name: jobName2,
- jobs: [{ ...job2 }],
- },
- ],
- },
- {
- name: job3.stage,
- groups: [
- {
- name: jobName3,
- jobs: [{ ...job3 }],
- },
- ],
- },
- ],
- };
-
- describe('createJobsHash', () => {
- it('returns an empty object if there are no jobs received as argument', () => {
- expect(createJobsHash([])).toEqual({});
- });
-
- it('returns a hash with the jobname as key and all its data as value', () => {
- const jobs = {
- [jobName1]: { jobs: [job1], name: jobName1, needs: [] },
- [jobName2]: { jobs: [job2], name: jobName2, needs: [] },
- [jobName3]: { jobs: [job3], name: jobName3, needs: job3.needs },
- [jobName4]: { jobs: [job4], name: jobName4, needs: job4.needs },
- };
-
- expect(createJobsHash(pipelineGraphData.stages)).toEqual(jobs);
- });
- });
-
- describe('generateJobNeedsDict', () => {
- it('generates an empty object if it receives no jobs', () => {
- expect(generateJobNeedsDict({})).toEqual({});
- });
-
- it('generates a dict with empty needs if there are no dependencies', () => {
- const smallGraph = {
- [jobName1]: job1,
- [jobName2]: job2,
- };
-
- expect(generateJobNeedsDict(smallGraph)).toEqual({
- [jobName1]: [],
- [jobName2]: [],
- });
- });
-
- it('generates a dict where key is the a job and its value is an array of all its needs', () => {
- const jobsWithNeeds = {
- [jobName1]: job1,
- [jobName2]: job2,
- [jobName3]: job3,
- [jobName4]: job4,
- };
-
- expect(generateJobNeedsDict(jobsWithNeeds)).toEqual({
- [jobName1]: [],
- [jobName2]: [],
- [jobName3]: [jobName1, jobName2],
- [jobName4]: [jobName3, jobName1, jobName2],
- });
- });
-
- it('removes needs which are not in the data', () => {
- const inexistantJobName = 'job5';
- const jobsWithNeeds = {
- [jobName1]: job1,
- [jobName2]: job2,
- [jobName3]: job3,
- [jobName4]: {
- name: jobName4,
- script: 'echo deploy',
- stage: 'deploy',
- needs: [inexistantJobName],
- },
- };
-
- expect(generateJobNeedsDict(jobsWithNeeds)).toEqual({
- [jobName1]: [],
- [jobName2]: [],
- [jobName3]: [jobName1, jobName2],
- [jobName4]: [],
- });
- });
-
- it('handles parallel jobs by adding the group name as a need', () => {
- const size = 3;
- const jobOptimize1 = 'optimize_1';
- const jobPrepareA = 'prepare_a';
- const jobPrepareA1 = `${jobPrepareA} 1/${size}`;
- const jobPrepareA2 = `${jobPrepareA} 2/${size}`;
- const jobPrepareA3 = `${jobPrepareA} 3/${size}`;
-
- const jobsParallel = {
- [jobOptimize1]: {
- jobs: [job1],
- name: [jobOptimize1],
- needs: [jobPrepareA1, jobPrepareA2, jobPrepareA3],
- },
- [jobPrepareA]: { jobs: [], name: jobPrepareA, needs: [], size },
- [jobPrepareA1]: { jobs: [], name: jobPrepareA, needs: [], size },
- [jobPrepareA2]: { jobs: [], name: jobPrepareA, needs: [], size },
- [jobPrepareA3]: { jobs: [], name: jobPrepareA, needs: [], size },
- };
-
- expect(generateJobNeedsDict(jobsParallel)).toEqual({
- [jobOptimize1]: [
- jobPrepareA1,
- // This is the important part, the `jobPrepareA` group name has been
- // added to our list of needs.
- jobPrepareA,
- jobPrepareA2,
- jobPrepareA3,
- ],
- [jobPrepareA]: [],
- [jobPrepareA1]: [],
- [jobPrepareA2]: [],
- [jobPrepareA3]: [],
- });
- });
- });
-
- describe('getPipelineDefaultTab', () => {
- const baseUrl = 'http://gitlab.com/user/multi-projects-small/-/pipelines/332/';
- it('returns pipeline tab name if there is only the base url', () => {
- expect(getPipelineDefaultTab(baseUrl)).toBe(pipelineTabName);
- });
-
- it('returns null if there was no valid last url part', () => {
- expect(getPipelineDefaultTab(`${baseUrl}something`)).toBe(null);
- });
-
- it('returns the correct tab name if present', () => {
- validPipelineTabNames.forEach((tabName) => {
- expect(getPipelineDefaultTab(`${baseUrl}${tabName}`)).toBe(tabName);
- });
- });
-
- it('returns the right value even with query params', () => {
- const [tabName] = validPipelineTabNames;
- expect(getPipelineDefaultTab(`${baseUrl}${tabName}?query="something"&query2="else"`)).toBe(
- tabName,
- );
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_labels_spec.js b/spec/frontend/pipelines/pipeline_labels_spec.js
deleted file mode 100644
index 6a37e36352b..00000000000
--- a/spec/frontend/pipelines/pipeline_labels_spec.js
+++ /dev/null
@@ -1,164 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { trimText } from 'helpers/text_helper';
-import PipelineLabelsComponent from '~/pipelines/components/pipelines_list/pipeline_labels.vue';
-import { mockPipeline } from './mock_data';
-
-const projectPath = 'test/test';
-
-describe('Pipeline label component', () => {
- let wrapper;
-
- const findScheduledTag = () => wrapper.findByTestId('pipeline-url-scheduled');
- const findLatestTag = () => wrapper.findByTestId('pipeline-url-latest');
- const findYamlTag = () => wrapper.findByTestId('pipeline-url-yaml');
- const findStuckTag = () => wrapper.findByTestId('pipeline-url-stuck');
- const findAutoDevopsTag = () => wrapper.findByTestId('pipeline-url-autodevops');
- const findAutoDevopsTagLink = () => wrapper.findByTestId('pipeline-url-autodevops-link');
- const findDetachedTag = () => wrapper.findByTestId('pipeline-url-detached');
- const findFailureTag = () => wrapper.findByTestId('pipeline-url-failure');
- const findForkTag = () => wrapper.findByTestId('pipeline-url-fork');
- const findTrainTag = () => wrapper.findByTestId('pipeline-url-train');
-
- const defaultProps = mockPipeline(projectPath);
-
- const createComponent = (props) => {
- wrapper = shallowMountExtended(PipelineLabelsComponent, {
- propsData: { ...defaultProps, ...props },
- provide: {
- targetProjectFullPath: projectPath,
- },
- });
- };
-
- it('should not render tags when flags are not set', () => {
- createComponent();
-
- expect(findStuckTag().exists()).toBe(false);
- expect(findLatestTag().exists()).toBe(false);
- expect(findYamlTag().exists()).toBe(false);
- expect(findAutoDevopsTag().exists()).toBe(false);
- expect(findFailureTag().exists()).toBe(false);
- expect(findScheduledTag().exists()).toBe(false);
- expect(findForkTag().exists()).toBe(false);
- expect(findTrainTag().exists()).toBe(false);
- });
-
- it('should render the stuck tag when flag is provided', () => {
- const stuckPipeline = defaultProps.pipeline;
- stuckPipeline.flags.stuck = true;
-
- createComponent({
- ...stuckPipeline.pipeline,
- });
-
- expect(findStuckTag().text()).toContain('stuck');
- });
-
- it('should render latest tag when flag is provided', () => {
- const latestPipeline = defaultProps.pipeline;
- latestPipeline.flags.latest = true;
-
- createComponent({
- ...latestPipeline,
- });
-
- expect(findLatestTag().text()).toContain('latest');
- });
-
- it('should render a yaml badge when it is invalid', () => {
- const yamlPipeline = defaultProps.pipeline;
- yamlPipeline.flags.yaml_errors = true;
-
- createComponent({
- ...yamlPipeline,
- });
-
- expect(findYamlTag().text()).toContain('yaml invalid');
- });
-
- it('should render an autodevops badge when flag is provided', () => {
- const autoDevopsPipeline = defaultProps.pipeline;
- autoDevopsPipeline.flags.auto_devops = true;
-
- createComponent({
- ...autoDevopsPipeline,
- });
-
- expect(trimText(findAutoDevopsTag().text())).toBe('Auto DevOps');
-
- expect(findAutoDevopsTagLink().attributes()).toMatchObject({
- href: '/help/topics/autodevops/index.md',
- target: '_blank',
- });
- });
-
- it('should render a detached badge when flag is provided', () => {
- const detachedMRPipeline = defaultProps.pipeline;
- detachedMRPipeline.flags.detached_merge_request_pipeline = true;
-
- createComponent({
- ...detachedMRPipeline,
- });
-
- expect(findDetachedTag().text()).toBe('merge request');
- });
-
- it('should render error badge when pipeline has a failure reason set', () => {
- const failedPipeline = defaultProps.pipeline;
- failedPipeline.flags.failure_reason = true;
- failedPipeline.failure_reason = 'some reason';
-
- createComponent({
- ...failedPipeline,
- });
-
- expect(findFailureTag().text()).toContain('error');
- expect(findFailureTag().attributes('title')).toContain('some reason');
- });
-
- it('should render scheduled badge when pipeline was triggered by a schedule', () => {
- const scheduledPipeline = defaultProps.pipeline;
- scheduledPipeline.source = 'schedule';
-
- createComponent({
- ...scheduledPipeline,
- });
-
- expect(findScheduledTag().exists()).toBe(true);
- expect(findScheduledTag().text()).toContain('Scheduled');
- });
-
- it('should render the fork badge when the pipeline was run in a fork', () => {
- const forkedPipeline = defaultProps.pipeline;
- forkedPipeline.project.full_path = '/test/forked';
-
- createComponent({
- ...forkedPipeline,
- });
-
- expect(findForkTag().exists()).toBe(true);
- expect(findForkTag().text()).toBe('fork');
- });
-
- it('should render the train badge when the pipeline is a merge train pipeline', () => {
- const mergeTrainPipeline = defaultProps.pipeline;
- mergeTrainPipeline.flags.merge_train_pipeline = true;
-
- createComponent({
- ...mergeTrainPipeline,
- });
-
- expect(findTrainTag().text()).toBe('merge train');
- });
-
- it('should not render the train badge when the pipeline is not a merge train pipeline', () => {
- const mergeTrainPipeline = defaultProps.pipeline;
- mergeTrainPipeline.flags.merge_train_pipeline = false;
-
- createComponent({
- ...mergeTrainPipeline,
- });
-
- expect(findTrainTag().exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_multi_actions_spec.js b/spec/frontend/pipelines/pipeline_multi_actions_spec.js
deleted file mode 100644
index 0fdc45a5931..00000000000
--- a/spec/frontend/pipelines/pipeline_multi_actions_spec.js
+++ /dev/null
@@ -1,288 +0,0 @@
-import { nextTick } from 'vue';
-import { GlAlert, GlDropdown, GlSprintf, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { stubComponent } from 'helpers/stub_component';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import PipelineMultiActions, {
- i18n,
-} from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-
-describe('Pipeline Multi Actions Dropdown', () => {
- let wrapper;
- let mockAxios;
- const focusInputMock = jest.fn();
-
- const artifacts = [
- {
- name: 'job my-artifact',
- path: '/download/path',
- },
- {
- name: 'job-2 my-artifact-2',
- path: '/download/path-two',
- },
- ];
- const newArtifacts = [
- {
- name: 'job-3 my-new-artifact',
- path: '/new/download/path',
- },
- {
- name: 'job-4 my-new-artifact-2',
- path: '/new/download/path-two',
- },
- {
- name: 'job-5 my-new-artifact-3',
- path: '/new/download/path-three',
- },
- ];
- const artifactItemTestId = 'artifact-item';
- const artifactsEndpointPlaceholder = ':pipeline_artifacts_id';
- const artifactsEndpoint = `endpoint/${artifactsEndpointPlaceholder}/artifacts.json`;
- const pipelineId = 108;
-
- const createComponent = () => {
- wrapper = extendedWrapper(
- shallowMount(PipelineMultiActions, {
- provide: {
- artifactsEndpoint,
- artifactsEndpointPlaceholder,
- },
- propsData: {
- pipelineId,
- },
- stubs: {
- GlSprintf,
- GlDropdown,
- GlSearchBoxByType: stubComponent(GlSearchBoxByType, {
- methods: { focusInput: focusInputMock },
- }),
- },
- }),
- );
- };
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findAllArtifactItems = () => wrapper.findAllByTestId(artifactItemTestId);
- const findFirstArtifactItem = () => wrapper.findByTestId(artifactItemTestId);
- const findAllArtifactItemsData = () =>
- wrapper.findAllByTestId(artifactItemTestId).wrappers.map((x) => ({
- path: x.attributes('href'),
- name: x.text(),
- }));
- const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType);
- const findEmptyMessage = () => wrapper.findByTestId('artifacts-empty-message');
- const findWarning = () => wrapper.findByTestId('artifacts-fetch-warning');
- const changePipelineId = (newId) => wrapper.setProps({ pipelineId: newId });
-
- beforeEach(() => {
- mockAxios = new MockAdapter(axios);
- });
-
- afterEach(() => {
- mockAxios.restore();
- });
-
- it('should render the dropdown', () => {
- createComponent();
-
- expect(findDropdown().exists()).toBe(true);
- });
-
- describe('Artifacts', () => {
- const endpoint = artifactsEndpoint.replace(artifactsEndpointPlaceholder, pipelineId);
-
- describe('while loading artifacts', () => {
- beforeEach(() => {
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_OK, { artifacts });
- });
-
- it('should render a loading spinner and no empty message', async () => {
- createComponent();
-
- findDropdown().vm.$emit('show');
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findEmptyMessage().exists()).toBe(false);
- });
- });
-
- describe('artifacts loaded successfully', () => {
- describe('artifacts exist', () => {
- beforeEach(async () => {
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_OK, { artifacts });
-
- createComponent();
-
- findDropdown().vm.$emit('show');
- await waitForPromises();
- });
-
- it('should fetch artifacts and show search box on dropdown click', () => {
- expect(mockAxios.history.get).toHaveLength(1);
- expect(findSearchBox().exists()).toBe(true);
- });
-
- it('should focus the search box when opened with artifacts', () => {
- findDropdown().vm.$emit('shown');
-
- expect(focusInputMock).toHaveBeenCalled();
- });
-
- it('should render all the provided artifacts when search query is empty', () => {
- findSearchBox().vm.$emit('input', '');
-
- expect(findAllArtifactItems()).toHaveLength(artifacts.length);
- expect(findEmptyMessage().exists()).toBe(false);
- });
-
- it('should render filtered artifacts when search query is not empty', async () => {
- findSearchBox().vm.$emit('input', 'job-2');
- await waitForPromises();
-
- expect(findAllArtifactItems()).toHaveLength(1);
- expect(findEmptyMessage().exists()).toBe(false);
- });
-
- it('should render the correct artifact name and path', () => {
- expect(findFirstArtifactItem().attributes('href')).toBe(artifacts[0].path);
- expect(findFirstArtifactItem().text()).toBe(artifacts[0].name);
- });
-
- describe('when opened again with new artifacts', () => {
- describe('with a successful refetch', () => {
- beforeEach(async () => {
- mockAxios.resetHistory();
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_OK, { artifacts: newArtifacts });
-
- findDropdown().vm.$emit('show');
- await nextTick();
- });
-
- it('should hide list and render a loading spinner on dropdown click', () => {
- expect(findAllArtifactItems()).toHaveLength(0);
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('should not render warning or empty message while loading', () => {
- expect(findEmptyMessage().exists()).toBe(false);
- expect(findWarning().exists()).toBe(false);
- });
-
- it('should render the correct new list', async () => {
- await waitForPromises();
-
- expect(findAllArtifactItemsData()).toEqual(newArtifacts);
- });
- });
-
- describe('with a failing refetch', () => {
- beforeEach(async () => {
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
-
- findDropdown().vm.$emit('show');
- await waitForPromises();
- });
-
- it('should render warning', () => {
- expect(findWarning().text()).toBe(i18n.artifactsFetchWarningMessage);
- });
-
- it('should render old list', () => {
- expect(findAllArtifactItemsData()).toEqual(artifacts);
- });
- });
- });
-
- describe('pipeline id has changed', () => {
- const newEndpoint = artifactsEndpoint.replace(
- artifactsEndpointPlaceholder,
- pipelineId + 1,
- );
-
- beforeEach(() => {
- changePipelineId(pipelineId + 1);
- });
-
- describe('followed by a failing request', () => {
- beforeEach(async () => {
- mockAxios.onGet(newEndpoint).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
-
- findDropdown().vm.$emit('show');
- await waitForPromises();
- });
-
- it('should render error message and no warning', () => {
- expect(findWarning().exists()).toBe(false);
- expect(findAlert().text()).toBe(i18n.artifactsFetchErrorMessage);
- });
-
- it('should clear list', () => {
- expect(findAllArtifactItems()).toHaveLength(0);
- });
- });
- });
- });
-
- describe('artifacts list is empty', () => {
- beforeEach(() => {
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_OK, { artifacts: [] });
- });
-
- it('should render empty message and no search box when no artifacts are found', async () => {
- createComponent();
-
- findDropdown().vm.$emit('show');
- await waitForPromises();
-
- expect(findEmptyMessage().exists()).toBe(true);
- expect(findSearchBox().exists()).toBe(false);
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
- });
-
- describe('with a failing request', () => {
- beforeEach(() => {
- mockAxios.onGet(endpoint).replyOnce(HTTP_STATUS_INTERNAL_SERVER_ERROR);
- });
-
- it('should render an error message', async () => {
- createComponent();
- findDropdown().vm.$emit('show');
- await waitForPromises();
-
- const error = findAlert();
- expect(error.exists()).toBe(true);
- expect(error.text()).toBe(i18n.artifactsFetchErrorMessage);
- });
- });
- });
-
- describe('tracking', () => {
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks artifacts dropdown click', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- createComponent();
-
- findDropdown().vm.$emit('show');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_artifacts_dropdown', {
- label: TRACKING_CATEGORIES.table,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_operations_spec.js b/spec/frontend/pipelines/pipeline_operations_spec.js
deleted file mode 100644
index b2191453824..00000000000
--- a/spec/frontend/pipelines/pipeline_operations_spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue';
-import PipelineMultiActions from '~/pipelines/components/pipelines_list/pipeline_multi_actions.vue';
-import PipelineOperations from '~/pipelines/components/pipelines_list/pipeline_operations.vue';
-import eventHub from '~/pipelines/event_hub';
-
-describe('Pipeline operations', () => {
- let wrapper;
-
- const defaultProps = {
- pipeline: {
- id: 329,
- iid: 234,
- details: {
- has_manual_actions: true,
- has_scheduled_actions: false,
- },
- flags: {
- retryable: true,
- cancelable: true,
- },
- cancel_path: '/root/ci-project/-/pipelines/329/cancel',
- retry_path: '/root/ci-project/-/pipelines/329/retry',
- },
- };
-
- const createComponent = (props = defaultProps) => {
- wrapper = shallowMountExtended(PipelineOperations, {
- propsData: {
- ...props,
- },
- });
- };
-
- const findManualActions = () => wrapper.findComponent(PipelinesManualActions);
- const findMultiActions = () => wrapper.findComponent(PipelineMultiActions);
- const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button');
- const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button');
-
- it('should display pipeline manual actions', () => {
- createComponent();
-
- expect(findManualActions().exists()).toBe(true);
- });
-
- it('should display pipeline multi actions', () => {
- createComponent();
-
- expect(findMultiActions().exists()).toBe(true);
- });
-
- describe('events', () => {
- beforeEach(() => {
- createComponent();
-
- jest.spyOn(eventHub, '$emit').mockImplementation(() => {});
- });
-
- it('should emit retryPipeline event', () => {
- findRetryBtn().vm.$emit('click');
-
- expect(eventHub.$emit).toHaveBeenCalledWith(
- 'retryPipeline',
- defaultProps.pipeline.retry_path,
- );
- });
-
- it('should emit openConfirmationModal event', () => {
- findCancelBtn().vm.$emit('click');
-
- expect(eventHub.$emit).toHaveBeenCalledWith('openConfirmationModal', {
- pipeline: defaultProps.pipeline,
- endpoint: defaultProps.pipeline.cancel_path,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_tabs_spec.js b/spec/frontend/pipelines/pipeline_tabs_spec.js
deleted file mode 100644
index 8d1cd98e981..00000000000
--- a/spec/frontend/pipelines/pipeline_tabs_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import { createAppOptions } from '~/pipelines/pipeline_tabs';
-
-jest.mock('~/lib/utils/url_utility', () => ({
- removeParams: () => 'gitlab.com',
- joinPaths: () => {},
- setUrlFragment: () => {},
-}));
-
-jest.mock('~/pipelines/utils', () => ({
- getPipelineDefaultTab: () => '',
-}));
-
-describe('~/pipelines/pipeline_tabs.js', () => {
- describe('createAppOptions', () => {
- const SELECTOR = 'SELECTOR';
-
- let el;
-
- const createElement = () => {
- el = document.createElement('div');
- el.id = SELECTOR;
- el.dataset.canGenerateCodequalityReports = 'true';
- el.dataset.codequalityReportDownloadPath = 'codequalityReportDownloadPath';
- el.dataset.downloadablePathForReportType = 'downloadablePathForReportType';
- el.dataset.exposeSecurityDashboard = 'true';
- el.dataset.exposeLicenseScanningData = 'true';
- el.dataset.failedJobsCount = 1;
- el.dataset.graphqlResourceEtag = 'graphqlResourceEtag';
- el.dataset.pipelineIid = '123';
- el.dataset.pipelineProjectPath = 'pipelineProjectPath';
-
- document.body.appendChild(el);
- };
-
- afterEach(() => {
- el = null;
- });
-
- it("extracts the properties from the element's dataset", () => {
- createElement();
- const options = createAppOptions(`#${SELECTOR}`, null);
-
- expect(options).toMatchObject({
- el,
- provide: {
- canGenerateCodequalityReports: true,
- codequalityReportDownloadPath: 'codequalityReportDownloadPath',
- downloadablePathForReportType: 'downloadablePathForReportType',
- exposeSecurityDashboard: true,
- exposeLicenseScanningData: true,
- failedJobsCount: '1',
- graphqlResourceEtag: 'graphqlResourceEtag',
- pipelineIid: '123',
- pipelineProjectPath: 'pipelineProjectPath',
- },
- });
- });
-
- it('returns `null` if el does not exist', () => {
- expect(createAppOptions('foo', null)).toBe(null);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_triggerer_spec.js b/spec/frontend/pipelines/pipeline_triggerer_spec.js
deleted file mode 100644
index 856c0484075..00000000000
--- a/spec/frontend/pipelines/pipeline_triggerer_spec.js
+++ /dev/null
@@ -1,76 +0,0 @@
-import { GlAvatar, GlAvatarLink } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import pipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_triggerer.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-describe('Pipelines Triggerer', () => {
- let wrapper;
-
- const mockData = {
- pipeline: {
- user: {
- name: 'foo',
- avatar_url: '/avatar',
- path: '/path',
- },
- },
- };
-
- const createComponent = (props) => {
- wrapper = shallowMountExtended(pipelineTriggerer, {
- propsData: {
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective('gl-tooltip'),
- },
- });
- };
-
- const findAvatarLink = () => wrapper.findComponent(GlAvatarLink);
- const findAvatar = () => wrapper.findComponent(GlAvatar);
- const findTriggerer = () => wrapper.findByText('API');
-
- describe('when user was a triggerer', () => {
- beforeEach(() => {
- createComponent(mockData);
- });
-
- it('should render pipeline triggerer table cell', () => {
- expect(wrapper.find('[data-testid="pipeline-triggerer"]').exists()).toBe(true);
- });
-
- it('should render only user avatar', () => {
- expect(findAvatarLink().exists()).toBe(true);
- expect(findTriggerer().exists()).toBe(false);
- });
-
- it('should set correct props on avatar link component', () => {
- expect(findAvatarLink().attributes()).toMatchObject({
- title: mockData.pipeline.user.name,
- href: mockData.pipeline.user.path,
- });
- });
-
- it('should add tooltip to avatar link', () => {
- const tooltip = getBinding(findAvatarLink().element, 'gl-tooltip');
-
- expect(tooltip).toBeDefined();
- });
-
- it('should set correct props on avatar component', () => {
- expect(findAvatar().attributes().src).toBe(mockData.pipeline.user.avatar_url);
- });
- });
-
- describe('when API was a triggerer', () => {
- beforeEach(() => {
- createComponent({ pipeline: {} });
- });
-
- it('should render label only', () => {
- expect(findAvatarLink().exists()).toBe(false);
- expect(findTriggerer().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipeline_url_spec.js b/spec/frontend/pipelines/pipeline_url_spec.js
deleted file mode 100644
index 797ec676ccc..00000000000
--- a/spec/frontend/pipelines/pipeline_url_spec.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import { merge } from 'lodash';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import PipelineUrlComponent from '~/pipelines/components/pipelines_list/pipeline_url.vue';
-import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-import { mockPipeline, mockPipelineBranch, mockPipelineTag } from './mock_data';
-
-const projectPath = 'test/test';
-
-describe('Pipeline Url Component', () => {
- let wrapper;
- let trackingSpy;
-
- const findTableCell = () => wrapper.findByTestId('pipeline-url-table-cell');
- const findPipelineUrlLink = () => wrapper.findByTestId('pipeline-url-link');
- const findRefName = () => wrapper.findByTestId('merge-request-ref');
- const findCommitShortSha = () => wrapper.findByTestId('commit-short-sha');
- const findCommitIcon = () => wrapper.findByTestId('commit-icon');
- const findCommitIconType = () => wrapper.findByTestId('commit-icon-type');
- const findCommitRefName = () => wrapper.findByTestId('commit-ref-name');
-
- const findCommitTitleContainer = () => wrapper.findByTestId('commit-title-container');
- const findPipelineNameContainer = () => wrapper.findByTestId('pipeline-name-container');
- const findCommitTitle = (commitWrapper) => commitWrapper.find('[data-testid="commit-title"]');
-
- const defaultProps = { ...mockPipeline(projectPath), refClass: 'gl-text-black' };
-
- const createComponent = (props) => {
- wrapper = shallowMountExtended(PipelineUrlComponent, {
- propsData: { ...defaultProps, ...props },
- provide: {
- targetProjectFullPath: projectPath,
- },
- });
- };
-
- it('should render pipeline url table cell', () => {
- createComponent();
-
- expect(findTableCell().exists()).toBe(true);
- });
-
- it('should render a link the provided path and id', () => {
- createComponent();
-
- expect(findPipelineUrlLink().attributes('href')).toBe('foo');
-
- expect(findPipelineUrlLink().text()).toBe('#1');
- });
-
- it('should render the pipeline name instead of commit title', () => {
- createComponent(merge(mockPipeline(projectPath), { pipeline: { name: 'Build pipeline' } }));
-
- expect(findCommitTitleContainer().exists()).toBe(false);
- expect(findPipelineNameContainer().exists()).toBe(true);
- expect(findRefName().exists()).toBe(true);
- expect(findCommitShortSha().exists()).toBe(true);
- });
-
- it('should render the commit title when pipeline has no name', () => {
- createComponent();
-
- const commitWrapper = findCommitTitleContainer();
-
- expect(findCommitTitle(commitWrapper).exists()).toBe(true);
- expect(findRefName().exists()).toBe(true);
- expect(findCommitShortSha().exists()).toBe(true);
- expect(findPipelineNameContainer().exists()).toBe(false);
- });
-
- it('should pass the refClass prop to merge request link', () => {
- createComponent();
-
- expect(findRefName().classes()).toContain(defaultProps.refClass);
- });
-
- it('should pass the refClass prop to the commit ref name link', () => {
- createComponent(mockPipelineBranch());
-
- expect(findCommitRefName().classes()).toContain(defaultProps.refClass);
- });
-
- describe('commit user avatar', () => {
- it('renders when commit author exists', () => {
- const pipelineBranch = mockPipelineBranch();
- const { avatar_url: imgSrc, name, path } = pipelineBranch.pipeline.commit.author;
- createComponent(pipelineBranch);
-
- const component = wrapper.findComponent(UserAvatarLink);
- expect(component.exists()).toBe(true);
- expect(component.props()).toMatchObject({
- imgSize: 16,
- imgSrc,
- imgAlt: name,
- linkHref: path,
- tooltipText: name,
- });
- });
-
- it('does not render when commit author does not exist', () => {
- createComponent();
-
- expect(wrapper.findComponent(UserAvatarLink).exists()).toBe(false);
- });
- });
-
- it('should render commit icon tooltip', () => {
- createComponent();
-
- expect(findCommitIcon().attributes('title')).toBe('Commit');
- });
-
- it.each`
- pipeline | expectedTitle
- ${mockPipelineTag()} | ${'Tag'}
- ${mockPipelineBranch()} | ${'Branch'}
- ${mockPipeline()} | ${'Merge Request'}
- `('should render tooltip $expectedTitle for commit icon type', ({ pipeline, expectedTitle }) => {
- createComponent(pipeline);
-
- expect(findCommitIconType().attributes('title')).toBe(expectedTitle);
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks pipeline id click', () => {
- createComponent();
-
- findPipelineUrlLink().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_pipeline_id', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks merge request ref click', () => {
- createComponent();
-
- findRefName().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_mr_ref', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks commit ref name click', () => {
- createComponent(mockPipelineBranch());
-
- findCommitRefName().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_name', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks commit title click', () => {
- createComponent(merge(mockPipelineBranch(), { pipeline: { name: null } }));
-
- findCommitTitle(findCommitTitleContainer()).vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_title', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks commit short sha click', () => {
- createComponent(mockPipelineBranch());
-
- findCommitShortSha().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_commit_sha', {
- label: TRACKING_CATEGORIES.table,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipelines_artifacts_spec.js b/spec/frontend/pipelines/pipelines_artifacts_spec.js
deleted file mode 100644
index 1abc2887682..00000000000
--- a/spec/frontend/pipelines/pipelines_artifacts_spec.js
+++ /dev/null
@@ -1,64 +0,0 @@
-import {
- GlDisclosureDropdown,
- GlDisclosureDropdownItem,
- GlDisclosureDropdownGroup,
- GlSprintf,
-} from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import PipelineArtifacts from '~/pipelines/components/pipelines_list/pipelines_artifacts.vue';
-
-describe('Pipelines Artifacts dropdown', () => {
- let wrapper;
-
- const artifacts = [
- {
- name: 'job my-artifact',
- path: '/download/path',
- },
- {
- name: 'job-2 my-artifact-2',
- path: '/download/path-two',
- },
- ];
- const pipelineId = 108;
-
- const createComponent = ({ mockArtifacts = artifacts } = {}) => {
- wrapper = shallowMount(PipelineArtifacts, {
- propsData: {
- pipelineId,
- artifacts: mockArtifacts,
- },
- stubs: {
- GlSprintf,
- GlDisclosureDropdown,
- GlDisclosureDropdownItem,
- GlDisclosureDropdownGroup,
- },
- });
- };
-
- const findGlDropdown = () => wrapper.findComponent(GlDisclosureDropdown);
- const findFirstGlDropdownItem = () => wrapper.findComponent(GlDisclosureDropdownItem);
-
- it('should render a dropdown with all the provided artifacts', () => {
- createComponent();
-
- const [{ items }] = findGlDropdown().props('items');
- expect(items).toHaveLength(artifacts.length);
- });
-
- it('should render a link with the provided path', () => {
- createComponent();
-
- expect(findFirstGlDropdownItem().props('item').href).toBe(artifacts[0].path);
- expect(findFirstGlDropdownItem().text()).toBe(artifacts[0].name);
- });
-
- describe('with no artifacts', () => {
- it('should not render the dropdown', () => {
- createComponent({ mockArtifacts: [] });
-
- expect(findGlDropdown().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipelines_manual_actions_spec.js b/spec/frontend/pipelines/pipelines_manual_actions_spec.js
deleted file mode 100644
index 82cab88c9eb..00000000000
--- a/spec/frontend/pipelines/pipelines_manual_actions_spec.js
+++ /dev/null
@@ -1,216 +0,0 @@
-import { GlDropdown, GlDropdownItem, GlLoadingIcon } from '@gitlab/ui';
-import MockAdapter from 'axios-mock-adapter';
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import mockPipelineActionsQueryResponse from 'test_fixtures/graphql/pipelines/get_pipeline_actions.query.graphql.json';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/alert';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import { confirmAction } from '~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal';
-import PipelinesManualActions from '~/pipelines/components/pipelines_list/pipelines_manual_actions.vue';
-import getPipelineActionsQuery from '~/pipelines/graphql/queries/get_pipeline_actions.query.graphql';
-import { TRACKING_CATEGORIES } from '~/pipelines/constants';
-import GlCountdown from '~/vue_shared/components/gl_countdown.vue';
-
-Vue.use(VueApollo);
-
-jest.mock('~/alert');
-jest.mock('~/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal');
-
-describe('Pipeline manual actions', () => {
- let wrapper;
- let mock;
-
- const queryHandler = jest.fn().mockResolvedValue(mockPipelineActionsQueryResponse);
- const {
- data: {
- project: {
- pipeline: {
- jobs: { nodes },
- },
- },
- },
- } = mockPipelineActionsQueryResponse;
-
- const mockPath = nodes[2].playPath;
-
- const createComponent = (limit = 50) => {
- wrapper = shallowMountExtended(PipelinesManualActions, {
- provide: {
- fullPath: 'root/ci-project',
- manualActionsLimit: limit,
- },
- propsData: {
- iid: 100,
- },
- stubs: {
- GlDropdown,
- },
- apolloProvider: createMockApollo([[getPipelineActionsQuery, queryHandler]]),
- });
- };
-
- const findDropdown = () => wrapper.findComponent(GlDropdown);
- const findAllDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
- const findAllCountdowns = () => wrapper.findAllComponents(GlCountdown);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findLimitMessage = () => wrapper.findByTestId('limit-reached-msg');
-
- it('skips calling query on mount', () => {
- createComponent();
-
- expect(queryHandler).not.toHaveBeenCalled();
- });
-
- describe('loading', () => {
- beforeEach(() => {
- createComponent();
-
- findDropdown().vm.$emit('shown');
- });
-
- it('display loading state while actions are being fetched', () => {
- expect(findAllDropdownItems().at(0).text()).toBe('Loading...');
- expect(findLoadingIcon().exists()).toBe(true);
- expect(findAllDropdownItems()).toHaveLength(1);
- });
- });
-
- describe('loaded', () => {
- beforeEach(async () => {
- mock = new MockAdapter(axios);
-
- createComponent();
-
- findDropdown().vm.$emit('shown');
-
- await waitForPromises();
- });
-
- afterEach(() => {
- mock.restore();
- confirmAction.mockReset();
- });
-
- it('displays dropdown with the provided actions', () => {
- expect(findAllDropdownItems()).toHaveLength(3);
- });
-
- it("displays a disabled action when it's not playable", () => {
- expect(findAllDropdownItems().at(0).attributes('disabled')).toBeDefined();
- });
-
- describe('on action click', () => {
- it('makes a request and toggles the loading state', async () => {
- mock.onPost(mockPath).reply(HTTP_STATUS_OK);
-
- findAllDropdownItems().at(1).vm.$emit('click');
-
- await nextTick();
-
- expect(findDropdown().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findDropdown().props('loading')).toBe(false);
- });
-
- it('makes a failed request and toggles the loading state', async () => {
- mock.onPost(mockPath).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR);
-
- findAllDropdownItems().at(1).vm.$emit('click');
-
- await nextTick();
-
- expect(findDropdown().props('loading')).toBe(true);
-
- await waitForPromises();
-
- expect(findDropdown().props('loading')).toBe(false);
- expect(createAlert).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('tracking', () => {
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks manual actions click', () => {
- const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- findDropdown().vm.$emit('shown');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_manual_actions', {
- label: TRACKING_CATEGORIES.table,
- });
- });
- });
-
- describe('scheduled jobs', () => {
- beforeEach(() => {
- jest
- .spyOn(Date, 'now')
- .mockImplementation(() => new Date('2063-04-04T00:42:00Z').getTime());
- });
-
- it('makes post request after confirming', async () => {
- mock.onPost(mockPath).reply(HTTP_STATUS_OK);
-
- confirmAction.mockResolvedValueOnce(true);
-
- findAllDropdownItems().at(2).vm.$emit('click');
-
- expect(confirmAction).toHaveBeenCalled();
-
- await waitForPromises();
-
- expect(mock.history.post).toHaveLength(1);
- });
-
- it('does not make post request if confirmation is cancelled', async () => {
- mock.onPost(mockPath).reply(HTTP_STATUS_OK);
-
- confirmAction.mockResolvedValueOnce(false);
-
- findAllDropdownItems().at(2).vm.$emit('click');
-
- expect(confirmAction).toHaveBeenCalled();
-
- await waitForPromises();
-
- expect(mock.history.post).toHaveLength(0);
- });
-
- it('displays the remaining time in the dropdown', () => {
- expect(findAllCountdowns().at(0).props('endDateString')).toBe(nodes[2].scheduledAt);
- });
- });
- });
-
- describe('limit message', () => {
- it('limit message does not show', async () => {
- createComponent();
-
- findDropdown().vm.$emit('shown');
-
- await waitForPromises();
-
- expect(findLimitMessage().exists()).toBe(false);
- });
-
- it('limit message does show', async () => {
- createComponent(3);
-
- findDropdown().vm.$emit('shown');
-
- await waitForPromises();
-
- expect(findLimitMessage().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipelines_spec.js b/spec/frontend/pipelines/pipelines_spec.js
deleted file mode 100644
index cc85d6d99e0..00000000000
--- a/spec/frontend/pipelines/pipelines_spec.js
+++ /dev/null
@@ -1,850 +0,0 @@
-import '~/commons';
-import {
- GlButton,
- GlEmptyState,
- GlFilteredSearch,
- GlLoadingIcon,
- GlPagination,
- GlCollapsibleListbox,
-} from '@gitlab/ui';
-import * as Sentry from '@sentry/browser';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { chunk } from 'lodash';
-import { nextTick } from 'vue';
-import mockPipelinesResponse from 'test_fixtures/pipelines/pipelines.json';
-import setWindowLocation from 'helpers/set_window_location_helper';
-import { TEST_HOST } from 'helpers/test_constants';
-import { mockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import Api from '~/api';
-import { createAlert, VARIANT_WARNING } from '~/alert';
-import setSortPreferenceMutation from '~/issues/list/queries/set_sort_preference.mutation.graphql';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_INTERNAL_SERVER_ERROR, HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import NavigationControls from '~/pipelines/components/pipelines_list/nav_controls.vue';
-import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue';
-import PipelinesCiTemplates from '~/pipelines/components/pipelines_list/empty_state/pipelines_ci_templates.vue';
-import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
-import { RAW_TEXT_WARNING, TRACKING_CATEGORIES } from '~/pipelines/constants';
-import Store from '~/pipelines/stores/pipelines_store';
-import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
-import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
-import {
- setIdTypePreferenceMutationResponse,
- setIdTypePreferenceMutationResponseWithErrors,
-} from 'jest/issues/list/mock_data';
-
-import { stageReply, users, mockSearch, branches } from './mock_data';
-
-jest.mock('@sentry/browser');
-jest.mock('~/alert');
-
-const mockProjectPath = 'twitter/flight';
-const mockProjectId = '21';
-const mockDefaultBranchName = 'main';
-const mockPipelinesEndpoint = `/${mockProjectPath}/pipelines.json`;
-const mockPipelinesIds = mockPipelinesResponse.pipelines.map(({ id }) => id);
-const mockPipelineWithStages = mockPipelinesResponse.pipelines.find(
- (p) => p.details.stages && p.details.stages.length,
-);
-
-describe('Pipelines', () => {
- let wrapper;
- let mockApollo;
- let mock;
- let trackingSpy;
-
- const paths = {
- emptyStateSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg',
- errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
- noPipelinesSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg',
- ciLintPath: '/ci/lint',
- resetCachePath: `${mockProjectPath}/settings/ci_cd/reset_cache`,
- newPipelinePath: `${mockProjectPath}/pipelines/new`,
-
- ciRunnerSettingsPath: `${mockProjectPath}/-/settings/ci_cd#js-runners-settings`,
- };
-
- const noPermissions = {
- emptyStateSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg',
- errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
- noPipelinesSvgPath: '/assets/illustrations/empty-state/empty-pipeline-md.svg',
- };
-
- const defaultProps = {
- hasGitlabCi: true,
- canCreatePipeline: true,
- ...paths,
- };
-
- const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
- const findEmptyState = () => wrapper.findComponent(GlEmptyState);
- const findNavigationTabs = () => wrapper.findComponent(NavigationTabs);
- const findNavigationControls = () => wrapper.findComponent(NavigationControls);
- const findPipelinesTable = () => wrapper.findComponent(PipelinesTableComponent);
- const findTablePagination = () => wrapper.findComponent(TablePagination);
- const findPipelineKeyCollapsibleBoxVue = () => wrapper.findComponent(GlCollapsibleListbox);
-
- const findTab = (tab) => wrapper.findByTestId(`pipelines-tab-${tab}`);
- const findPipelineKeyCollapsibleBox = () => wrapper.findByTestId('pipeline-key-collapsible-box');
- const findRunPipelineButton = () => wrapper.findByTestId('run-pipeline-button');
- const findCiLintButton = () => wrapper.findByTestId('ci-lint-button');
- const findCleanCacheButton = () => wrapper.findByTestId('clear-cache-button');
- const findStagesDropdownToggle = () =>
- wrapper.find('[data-testid="mini-pipeline-graph-dropdown"] .dropdown-toggle');
- const findPipelineUrlLinks = () => wrapper.findAll('[data-testid="pipeline-url-link"]');
-
- const createComponent = (props = defaultProps) => {
- const { mutationMock, ...restProps } = props;
- mockApollo = createMockApollo([[setSortPreferenceMutation, mutationMock]]);
-
- wrapper = extendedWrapper(
- mount(PipelinesComponent, {
- provide: {
- pipelineEditorPath: '',
- suggestedCiTemplates: [],
- ciRunnerSettingsPath: paths.ciRunnerSettingsPath,
- anyRunnersAvailable: true,
- },
- propsData: {
- store: new Store(),
- projectId: mockProjectId,
- defaultBranchName: mockDefaultBranchName,
- endpoint: mockPipelinesEndpoint,
- params: {},
- ...restProps,
- },
- apolloProvider: mockApollo,
- }),
- );
- };
-
- beforeEach(() => {
- setWindowLocation(TEST_HOST);
- });
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- jest.spyOn(window.history, 'pushState');
- jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
- });
-
- afterEach(() => {
- mock.reset();
- mockApollo = null;
- window.history.pushState.mockReset();
- });
-
- describe('when pipelines are not yet loaded', () => {
- beforeEach(async () => {
- createComponent();
- await nextTick();
- });
-
- it('shows loading state when the app is loading', () => {
- expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
- });
-
- it('does not display tabs when the first request has not yet been made', () => {
- expect(findNavigationTabs().exists()).toBe(false);
- });
-
- it('does not display buttons', () => {
- expect(findNavigationControls().exists()).toBe(false);
- });
- });
-
- describe('when there are pipelines in the project', () => {
- beforeEach(() => {
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .reply(HTTP_STATUS_OK, mockPipelinesResponse);
- });
-
- describe('when user has no permissions', () => {
- beforeEach(async () => {
- createComponent({ hasGitlabCi: true, canCreatePipeline: false, ...noPermissions });
- await waitForPromises();
- });
-
- it('renders "All" tab with count different from "0"', () => {
- expect(findTab('all').text()).toMatchInterpolatedText('All 3');
- });
-
- it('does not render buttons', () => {
- expect(findNavigationControls().exists()).toBe(false);
-
- expect(findRunPipelineButton().exists()).toBe(false);
- expect(findCiLintButton().exists()).toBe(false);
- expect(findCleanCacheButton().exists()).toBe(false);
- });
-
- it('renders pipelines in a table', () => {
- expect(findPipelinesTable().exists()).toBe(true);
-
- expect(findPipelineUrlLinks()).toHaveLength(mockPipelinesIds.length);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockPipelinesIds[0]}`);
- expect(findPipelineUrlLinks().at(1).text()).toBe(`#${mockPipelinesIds[1]}`);
- expect(findPipelineUrlLinks().at(2).text()).toBe(`#${mockPipelinesIds[2]}`);
- });
- });
-
- describe('when user has permissions', () => {
- beforeEach(async () => {
- createComponent();
- await waitForPromises();
- });
-
- it('should set up navigation tabs', () => {
- expect(findNavigationTabs().props('tabs')).toEqual([
- { name: 'All', scope: 'all', count: '3', isActive: true },
- { name: 'Finished', scope: 'finished', count: undefined, isActive: false },
- { name: 'Branches', scope: 'branches', isActive: false },
- { name: 'Tags', scope: 'tags', isActive: false },
- ]);
- });
-
- it('renders "All" tab with count different from "0"', () => {
- expect(findTab('all').text()).toMatchInterpolatedText('All 3');
- });
-
- it('should render other navigation tabs', () => {
- expect(findTab('finished').text()).toBe('Finished');
- expect(findTab('branches').text()).toBe('Branches');
- expect(findTab('tags').text()).toBe('Tags');
- });
-
- it('shows navigation controls', () => {
- expect(findNavigationControls().exists()).toBe(true);
- });
-
- it('renders Run pipeline link', () => {
- expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
- });
-
- it('renders CI lint link', () => {
- expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
- });
-
- it('renders Clear runner cache button', () => {
- expect(findCleanCacheButton().text()).toBe('Clear runner caches');
- });
-
- it('renders pipelines in a table', () => {
- expect(findPipelinesTable().exists()).toBe(true);
-
- expect(findPipelineUrlLinks()).toHaveLength(mockPipelinesIds.length);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockPipelinesIds[0]}`);
- expect(findPipelineUrlLinks().at(1).text()).toBe(`#${mockPipelinesIds[1]}`);
- expect(findPipelineUrlLinks().at(2).text()).toBe(`#${mockPipelinesIds[2]}`);
- });
-
- describe('when user goes to a tab', () => {
- const goToTab = (tab) => {
- findNavigationTabs().vm.$emit('onChangeTab', tab);
- };
-
- describe('when the scope in the tab has pipelines', () => {
- const mockFinishedPipeline = mockPipelinesResponse.pipelines[0];
-
- beforeEach(async () => {
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } })
- .reply(HTTP_STATUS_OK, {
- pipelines: [mockFinishedPipeline],
- count: mockPipelinesResponse.count,
- });
-
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
-
- goToTab('finished');
-
- await waitForPromises();
- });
-
- it('should filter pipelines', () => {
- expect(findPipelinesTable().exists()).toBe(true);
-
- expect(findPipelineUrlLinks()).toHaveLength(1);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockFinishedPipeline.id}`);
- });
-
- it('should update browser bar', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?scope=finished&page=1`,
- );
- });
-
- it.each(['all', 'finished', 'branches', 'tags'])('tracks %p tab click', async (scope) => {
- goToTab(scope);
-
- await waitForPromises();
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_filter_tabs', {
- label: TRACKING_CATEGORIES.tabs,
- property: scope,
- });
- });
- });
-
- describe('when the scope in the tab is empty', () => {
- beforeEach(async () => {
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'branches', page: '1' } })
- .reply(HTTP_STATUS_OK, {
- pipelines: [],
- count: mockPipelinesResponse.count,
- });
-
- goToTab('branches');
-
- await waitForPromises();
- });
-
- it('should filter pipelines', () => {
- expect(findEmptyState().text()).toBe('There are currently no pipelines.');
- });
-
- it('should update browser bar', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?scope=branches&page=1`,
- );
- });
- });
- });
-
- describe('when user triggers a filtered search', () => {
- const mockFilteredPipeline = mockPipelinesResponse.pipelines[1];
-
- let expectedParams;
-
- beforeEach(async () => {
- expectedParams = {
- page: '1',
- scope: 'all',
- username: 'root',
- ref: 'main',
- status: 'pending',
- };
-
- mock
- .onGet(mockPipelinesEndpoint, {
- params: expectedParams,
- })
- .replyOnce(HTTP_STATUS_OK, {
- pipelines: [mockFilteredPipeline],
- count: mockPipelinesResponse.count,
- });
-
- findFilteredSearch().vm.$emit('submit', mockSearch);
-
- await waitForPromises();
- });
-
- it('requests data with query params on filter submit', () => {
- expect(mock.history.get[1].params).toEqual(expectedParams);
- });
-
- it('renders filtered pipelines', () => {
- expect(findPipelineUrlLinks()).toHaveLength(1);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockFilteredPipeline.id}`);
- });
-
- it('should update browser bar', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?page=1&scope=all&username=root&ref=main&status=pending`,
- );
- });
- });
-
- describe('when user changes Show Pipeline ID to Show Pipeline IID', () => {
- const mockFilteredPipeline = mockPipelinesResponse.pipelines[0];
-
- beforeEach(() => {
- gon.current_user_id = 1;
- });
-
- it('should change the text to Show Pipeline IID', async () => {
- expect(findPipelineKeyCollapsibleBox().exists()).toBe(true);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockFilteredPipeline.id}`);
- findPipelineKeyCollapsibleBoxVue().vm.$emit('select', 'iid');
-
- await waitForPromises();
-
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockFilteredPipeline.iid}`);
- });
-
- it('calls mutation to save idType preference', () => {
- const mutationMock = jest.fn().mockResolvedValue(setIdTypePreferenceMutationResponse);
- createComponent({ ...defaultProps, mutationMock });
-
- findPipelineKeyCollapsibleBoxVue().vm.$emit('select', 'iid');
-
- expect(mutationMock).toHaveBeenCalledWith({ input: { visibilityPipelineIdType: 'IID' } });
- });
-
- it('captures error when mutation response has errors', async () => {
- const mutationMock = jest
- .fn()
- .mockResolvedValue(setIdTypePreferenceMutationResponseWithErrors);
- createComponent({ ...defaultProps, mutationMock });
-
- findPipelineKeyCollapsibleBoxVue().vm.$emit('select', 'iid');
- await waitForPromises();
-
- expect(Sentry.captureException).toHaveBeenCalledWith(new Error('oh no!'));
- });
- });
-
- describe('when user triggers a filtered search with raw text', () => {
- beforeEach(async () => {
- findFilteredSearch().vm.$emit('submit', ['rawText']);
-
- await waitForPromises();
- });
-
- it('requests data with query params on filter submit', () => {
- expect(mock.history.get[1].params).toEqual({ page: '1', scope: 'all' });
- });
-
- it('displays a warning message if raw text search is used', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- expect(createAlert).toHaveBeenCalledWith({
- message: RAW_TEXT_WARNING,
- variant: VARIANT_WARNING,
- });
- });
-
- it('should update browser bar', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?page=1&scope=all`,
- );
- });
- });
- });
- });
-
- describe('when there are multiple pages of pipelines', () => {
- const mockPageSize = 2;
- const mockPageHeaders = ({ page = 1 } = {}) => {
- return {
- 'X-PER-PAGE': `${mockPageSize}`,
- 'X-PREV-PAGE': `${page - 1}`,
- 'X-PAGE': `${page}`,
- 'X-NEXT-PAGE': `${page + 1}`,
- };
- };
- const [firstPage, secondPage] = chunk(mockPipelinesResponse.pipelines, mockPageSize);
-
- const goToPage = (page) => {
- findTablePagination().findComponent(GlPagination).vm.$emit('input', page);
- };
-
- beforeEach(async () => {
- mock.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } }).reply(
- HTTP_STATUS_OK,
- {
- pipelines: firstPage,
- count: mockPipelinesResponse.count,
- },
- mockPageHeaders({ page: 1 }),
- );
- mock.onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '2' } }).reply(
- HTTP_STATUS_OK,
- {
- pipelines: secondPage,
- count: mockPipelinesResponse.count,
- },
- mockPageHeaders({ page: 2 }),
- );
-
- createComponent();
-
- await waitForPromises();
- });
-
- it('shows the first page of pipelines', () => {
- expect(findPipelineUrlLinks()).toHaveLength(firstPage.length);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${firstPage[0].id}`);
- expect(findPipelineUrlLinks().at(1).text()).toBe(`#${firstPage[1].id}`);
- });
-
- it('should not update browser bar', () => {
- expect(window.history.pushState).not.toHaveBeenCalled();
- });
-
- describe('when user goes to next page', () => {
- beforeEach(async () => {
- goToPage(2);
- await waitForPromises();
- });
-
- it('should update page and keep scope the same scope', () => {
- expect(findPipelineUrlLinks()).toHaveLength(secondPage.length);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${secondPage[0].id}`);
- });
-
- it('should update browser bar', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?page=2&scope=all`,
- );
- });
-
- it('should reset page to 1 when filtering pipelines', () => {
- expect(window.history.pushState).toHaveBeenCalledTimes(1);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?page=2&scope=all`,
- );
-
- findFilteredSearch().vm.$emit('submit', [
- { type: 'status', value: { data: 'success', operator: '=' } },
- ]);
-
- expect(window.history.pushState).toHaveBeenCalledTimes(2);
- expect(window.history.pushState).toHaveBeenCalledWith(
- expect.anything(),
- expect.anything(),
- `${window.location.pathname}?page=1&scope=all&status=success`,
- );
- });
- });
- });
-
- describe('when pipelines can be polled', () => {
- beforeEach(() => {
- const emptyResponse = {
- pipelines: [],
- count: { all: '0' },
- };
-
- // Mock no pipelines in the first attempt
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .replyOnce(HTTP_STATUS_OK, emptyResponse, {
- 'POLL-INTERVAL': 100,
- });
- // Mock pipelines in the next attempt
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .reply(HTTP_STATUS_OK, mockPipelinesResponse, {
- 'POLL-INTERVAL': 100,
- });
- });
-
- describe('data is loaded for the first time', () => {
- beforeEach(async () => {
- createComponent();
- await waitForPromises();
- });
-
- it('shows tabs', () => {
- expect(findNavigationTabs().exists()).toBe(true);
- });
-
- it('should update page and keep scope the same scope', () => {
- expect(findPipelineUrlLinks()).toHaveLength(0);
- });
-
- describe('data is loaded for a second time', () => {
- beforeEach(async () => {
- jest.runOnlyPendingTimers();
- await waitForPromises();
- });
-
- it('shows tabs', () => {
- expect(findNavigationTabs().exists()).toBe(true);
- });
-
- it('is loading after a time', () => {
- expect(findPipelineUrlLinks()).toHaveLength(mockPipelinesIds.length);
- expect(findPipelineUrlLinks().at(0).text()).toBe(`#${mockPipelinesIds[0]}`);
- expect(findPipelineUrlLinks().at(1).text()).toBe(`#${mockPipelinesIds[1]}`);
- expect(findPipelineUrlLinks().at(2).text()).toBe(`#${mockPipelinesIds[2]}`);
- });
- });
- });
- });
-
- describe('when no pipelines exist', () => {
- beforeEach(() => {
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'all', page: '1' } })
- .reply(HTTP_STATUS_OK, {
- pipelines: [],
- count: { all: '0' },
- });
- });
-
- describe('when CI is enabled and user has permissions', () => {
- beforeEach(async () => {
- createComponent();
- await waitForPromises();
- });
-
- it('renders tab with count of "0"', () => {
- expect(findNavigationTabs().exists()).toBe(true);
- expect(findTab('all').text()).toMatchInterpolatedText('All 0');
- });
-
- it('renders Run pipeline link', () => {
- expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
- });
-
- it('renders CI lint link', () => {
- expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
- });
-
- it('renders Clear runner cache button', () => {
- expect(findCleanCacheButton().text()).toBe('Clear runner caches');
- });
-
- it('renders empty state', () => {
- expect(findEmptyState().text()).toBe('There are currently no pipelines.');
- });
-
- it('renders filtered search', () => {
- expect(findFilteredSearch().exists()).toBe(true);
- });
-
- it('renders the pipeline key collapsible box', () => {
- expect(findPipelineKeyCollapsibleBox().exists()).toBe(true);
- });
-
- it('renders tab empty state finished scope', async () => {
- mock
- .onGet(mockPipelinesEndpoint, { params: { scope: 'finished', page: '1' } })
- .reply(HTTP_STATUS_OK, {
- pipelines: [],
- count: { all: '0' },
- });
-
- findNavigationTabs().vm.$emit('onChangeTab', 'finished');
-
- await waitForPromises();
-
- expect(findEmptyState().text()).toBe('There are currently no finished pipelines.');
- });
- });
-
- describe('when CI is not enabled and user has permissions', () => {
- beforeEach(async () => {
- createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
- await waitForPromises();
- });
-
- it('renders the CI/CD templates', () => {
- expect(wrapper.findComponent(PipelinesCiTemplates).exists()).toBe(true);
- });
-
- it('does not render filtered search', () => {
- expect(findFilteredSearch().exists()).toBe(false);
- });
-
- it('does not render the pipeline key dropdown', () => {
- expect(findPipelineKeyCollapsibleBox().exists()).toBe(false);
- });
-
- it('does not render tabs nor buttons', () => {
- expect(findNavigationTabs().exists()).toBe(false);
- expect(findTab('all').exists()).toBe(false);
- expect(findRunPipelineButton().exists()).toBe(false);
- expect(findCiLintButton().exists()).toBe(false);
- expect(findCleanCacheButton().exists()).toBe(false);
- });
- });
-
- describe('when CI is not enabled and user has no permissions', () => {
- beforeEach(async () => {
- createComponent({ hasGitlabCi: false, canCreatePipeline: false, ...noPermissions });
- await waitForPromises();
- });
-
- it('renders empty state without button to set CI', () => {
- expect(findEmptyState().text()).toBe(
- 'This project is not currently set up to run pipelines.',
- );
-
- expect(findEmptyState().findComponent(GlButton).exists()).toBe(false);
- });
-
- it('does not render tabs or buttons', () => {
- expect(findTab('all').exists()).toBe(false);
- expect(findRunPipelineButton().exists()).toBe(false);
- expect(findCiLintButton().exists()).toBe(false);
- expect(findCleanCacheButton().exists()).toBe(false);
- });
- });
-
- describe('when CI is enabled and user has no permissions', () => {
- beforeEach(() => {
- createComponent({ hasGitlabCi: true, canCreatePipeline: false, ...noPermissions });
-
- return waitForPromises();
- });
-
- it('renders tab with count of "0"', () => {
- expect(findTab('all').text()).toMatchInterpolatedText('All 0');
- });
-
- it('does not render buttons', () => {
- expect(findRunPipelineButton().exists()).toBe(false);
- expect(findCiLintButton().exists()).toBe(false);
- expect(findCleanCacheButton().exists()).toBe(false);
- });
-
- it('renders empty state', () => {
- expect(findEmptyState().text()).toBe('There are currently no pipelines.');
- });
- });
- });
-
- describe('when a pipeline with stages exists', () => {
- describe('updates results when a staged is clicked', () => {
- let stopMock;
- let restartMock;
- let cancelMock;
-
- beforeEach(() => {
- mock.onGet(mockPipelinesEndpoint, { scope: 'all', page: '1' }).reply(
- HTTP_STATUS_OK,
- {
- pipelines: [mockPipelineWithStages],
- count: { all: '1' },
- },
- {
- 'POLL-INTERVAL': 100,
- },
- );
-
- mock
- .onGet(mockPipelineWithStages.details.stages[0].dropdown_path)
- .reply(HTTP_STATUS_OK, stageReply);
-
- createComponent();
-
- stopMock = jest.spyOn(window, 'clearTimeout');
- restartMock = jest.spyOn(axios, 'get');
- });
-
- describe('when a request is being made', () => {
- beforeEach(async () => {
- mock.onGet(mockPipelinesEndpoint).reply(HTTP_STATUS_OK, mockPipelinesResponse);
-
- await waitForPromises();
- });
-
- it('stops polling, cancels the request, & restarts polling', async () => {
- // Mock init a polling cycle
- wrapper.vm.poll.options.notificationCallback(true);
-
- await findStagesDropdownToggle().trigger('click');
- jest.runOnlyPendingTimers();
-
- // cancelMock is getting overwritten in pipelines_service.js#L29
- // so we have to spy on it again here
- cancelMock = jest.spyOn(axios.CancelToken, 'source');
-
- await waitForPromises();
-
- expect(cancelMock).toHaveBeenCalled();
- expect(stopMock).toHaveBeenCalled();
- expect(restartMock).toHaveBeenCalledWith(
- `${mockPipelinesResponse.pipelines[0].path}/stage.json?stage=build`,
- );
- });
-
- it('stops polling & restarts polling', async () => {
- await findStagesDropdownToggle().trigger('click');
- jest.runOnlyPendingTimers();
- await waitForPromises();
-
- expect(cancelMock).not.toHaveBeenCalled();
- expect(stopMock).toHaveBeenCalled();
- expect(restartMock).toHaveBeenCalledWith(
- `${mockPipelinesResponse.pipelines[0].path}/stage.json?stage=build`,
- );
- });
- });
- });
- });
-
- describe('when pipelines cannot be loaded', () => {
- beforeEach(() => {
- mock.onGet(mockPipelinesEndpoint).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, {});
- });
-
- describe('when user has no permissions', () => {
- beforeEach(async () => {
- createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...noPermissions });
-
- await waitForPromises();
- });
-
- it('renders tabs', () => {
- expect(findNavigationTabs().exists()).toBe(true);
- expect(findTab('all').text()).toBe('All');
- });
-
- it('does not render buttons', () => {
- expect(findRunPipelineButton().exists()).toBe(false);
- expect(findCiLintButton().exists()).toBe(false);
- expect(findCleanCacheButton().exists()).toBe(false);
- });
-
- it('shows error state', () => {
- expect(findEmptyState().props('title')).toBe('There was an error fetching the pipelines.');
- expect(findEmptyState().props('description')).toBe(
- 'Try again in a few moments or contact your support team.',
- );
- });
- });
-
- describe('when user has permissions', () => {
- beforeEach(async () => {
- createComponent();
-
- await waitForPromises();
- });
-
- it('renders tabs', () => {
- expect(findTab('all').text()).toBe('All');
- });
-
- it('renders buttons', () => {
- expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
-
- expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
- expect(findCleanCacheButton().text()).toBe('Clear runner caches');
- });
-
- it('shows error state', () => {
- expect(findEmptyState().props('title')).toBe('There was an error fetching the pipelines.');
- expect(findEmptyState().props('description')).toBe(
- 'Try again in a few moments or contact your support team.',
- );
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipelines_store_spec.js b/spec/frontend/pipelines/pipelines_store_spec.js
deleted file mode 100644
index f374ecd0c0a..00000000000
--- a/spec/frontend/pipelines/pipelines_store_spec.js
+++ /dev/null
@@ -1,80 +0,0 @@
-import PipelineStore from '~/pipelines/stores/pipelines_store';
-
-describe('Pipelines Store', () => {
- let store;
-
- beforeEach(() => {
- store = new PipelineStore();
- });
-
- it('should be initialized with an empty state', () => {
- expect(store.state.pipelines).toEqual([]);
- expect(store.state.count).toEqual({});
- expect(store.state.pageInfo).toEqual({});
- });
-
- describe('storePipelines', () => {
- it('should use the default parameter if none is provided', () => {
- store.storePipelines();
-
- expect(store.state.pipelines).toEqual([]);
- });
-
- it('should store the provided array', () => {
- const array = [
- { id: 1, status: 'running' },
- { id: 2, status: 'success' },
- ];
- store.storePipelines(array);
-
- expect(store.state.pipelines).toEqual(array);
- });
- });
-
- describe('storeCount', () => {
- it('should use the default parameter if none is provided', () => {
- store.storeCount();
-
- expect(store.state.count).toEqual({});
- });
-
- it('should store the provided count', () => {
- const count = { all: 20, finished: 10 };
- store.storeCount(count);
-
- expect(store.state.count).toEqual(count);
- });
- });
-
- describe('storePagination', () => {
- it('should use the default parameter if none is provided', () => {
- store.storePagination();
-
- expect(store.state.pageInfo).toEqual({});
- });
-
- it('should store pagination information normalized and parsed', () => {
- const pagination = {
- 'X-nExt-pAge': '2',
- 'X-page': '1',
- 'X-Per-Page': '1',
- 'X-Prev-Page': '2',
- 'X-TOTAL': '37',
- 'X-Total-Pages': '2',
- };
-
- const expectedResult = {
- perPage: 1,
- page: 1,
- total: 37,
- totalPages: 2,
- nextPage: 2,
- previousPage: 2,
- };
-
- store.storePagination(pagination);
-
- expect(store.state.pageInfo).toEqual(expectedResult);
- });
- });
-});
diff --git a/spec/frontend/pipelines/pipelines_table_spec.js b/spec/frontend/pipelines/pipelines_table_spec.js
deleted file mode 100644
index 950a6b21e16..00000000000
--- a/spec/frontend/pipelines/pipelines_table_spec.js
+++ /dev/null
@@ -1,280 +0,0 @@
-import '~/commons';
-import { GlTableLite } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import fixture from 'test_fixtures/pipelines/pipelines.json';
-import { mockTracking, unmockTracking } from 'helpers/tracking_helper';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import LegacyPipelineMiniGraph from '~/pipelines/components/pipeline_mini_graph/legacy_pipeline_mini_graph.vue';
-import PipelineFailedJobsWidget from '~/pipelines/components/pipelines_list/failure_widget/pipeline_failed_jobs_widget.vue';
-import PipelineOperations from '~/pipelines/components/pipelines_list/pipeline_operations.vue';
-import PipelineTriggerer from '~/pipelines/components/pipelines_list/pipeline_triggerer.vue';
-import PipelineUrl from '~/pipelines/components/pipelines_list/pipeline_url.vue';
-import PipelinesTable from '~/pipelines/components/pipelines_list/pipelines_table.vue';
-import PipelinesTimeago from '~/pipelines/components/pipelines_list/time_ago.vue';
-import {
- PipelineKeyOptions,
- BUTTON_TOOLTIP_RETRY,
- BUTTON_TOOLTIP_CANCEL,
- TRACKING_CATEGORIES,
-} from '~/pipelines/constants';
-
-import CiBadgeLink from '~/vue_shared/components/ci_badge_link.vue';
-
-jest.mock('~/pipelines/event_hub');
-
-describe('Pipelines Table', () => {
- let pipeline;
- let wrapper;
- let trackingSpy;
-
- const defaultProvide = {
- glFeatures: {},
- withFailedJobsDetails: false,
- };
-
- const provideWithDetails = {
- glFeatures: {
- ciJobFailuresInMr: true,
- },
- withFailedJobsDetails: true,
- };
-
- const defaultProps = {
- pipelines: [],
- viewType: 'root',
- pipelineKeyOption: PipelineKeyOptions[0],
- };
-
- const createMockPipeline = () => {
- // Clone fixture as it could be modified by tests
- const { pipelines } = JSON.parse(JSON.stringify(fixture));
- return pipelines.find((p) => p.user !== null && p.commit !== null);
- };
-
- const createComponent = (props = {}, provide = {}) => {
- wrapper = extendedWrapper(
- mount(PipelinesTable, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- provide: {
- ...defaultProvide,
- ...provide,
- },
- stubs: ['PipelineFailedJobsWidget'],
- }),
- );
- };
-
- const findGlTableLite = () => wrapper.findComponent(GlTableLite);
- const findCiBadgeLink = () => wrapper.findComponent(CiBadgeLink);
- const findPipelineInfo = () => wrapper.findComponent(PipelineUrl);
- const findTriggerer = () => wrapper.findComponent(PipelineTriggerer);
- const findLegacyPipelineMiniGraph = () => wrapper.findComponent(LegacyPipelineMiniGraph);
- const findTimeAgo = () => wrapper.findComponent(PipelinesTimeago);
- const findActions = () => wrapper.findComponent(PipelineOperations);
-
- const findPipelineFailureWidget = () => wrapper.findComponent(PipelineFailedJobsWidget);
- const findTableRows = () => wrapper.findAllByTestId('pipeline-table-row');
- const findStatusTh = () => wrapper.findByTestId('status-th');
- const findPipelineTh = () => wrapper.findByTestId('pipeline-th');
- const findStagesTh = () => wrapper.findByTestId('stages-th');
- const findActionsTh = () => wrapper.findByTestId('actions-th');
- const findRetryBtn = () => wrapper.findByTestId('pipelines-retry-button');
- const findCancelBtn = () => wrapper.findByTestId('pipelines-cancel-button');
-
- beforeEach(() => {
- pipeline = createMockPipeline();
- });
-
- describe('Pipelines Table', () => {
- beforeEach(() => {
- createComponent({ pipelines: [pipeline], viewType: 'root' });
- });
-
- it('displays table', () => {
- expect(findGlTableLite().exists()).toBe(true);
- });
-
- it('should render table head with correct columns', () => {
- expect(findStatusTh().text()).toBe('Status');
- expect(findPipelineTh().text()).toBe('Pipeline');
- expect(findStagesTh().text()).toBe('Stages');
- expect(findActionsTh().text()).toBe('Actions');
- });
-
- it('should display a table row', () => {
- expect(findTableRows()).toHaveLength(1);
- });
-
- describe('status cell', () => {
- it('should render a status badge', () => {
- expect(findCiBadgeLink().exists()).toBe(true);
- });
- });
-
- describe('pipeline cell', () => {
- it('should render pipeline information', () => {
- expect(findPipelineInfo().exists()).toBe(true);
- });
-
- it('should display the pipeline id', () => {
- expect(findPipelineInfo().text()).toContain(`#${pipeline.id}`);
- });
- });
-
- describe('stages cell', () => {
- it('should render pipeline mini graph', () => {
- expect(findLegacyPipelineMiniGraph().exists()).toBe(true);
- });
-
- it('should render the right number of stages', () => {
- const stagesLength = pipeline.details.stages.length;
- expect(findLegacyPipelineMiniGraph().props('stages').length).toBe(stagesLength);
- });
-
- it('should render the latest downstream pipelines only', () => {
- // component receives two downstream pipelines. one of them is already outdated
- // because we retried the trigger job, so the mini pipeline graph will only
- // render the newly created downstream pipeline instead
- expect(pipeline.triggered).toHaveLength(2);
- expect(findLegacyPipelineMiniGraph().props('downstreamPipelines')).toHaveLength(1);
- });
-
- describe('when pipeline does not have stages', () => {
- beforeEach(() => {
- pipeline = createMockPipeline();
- pipeline.details.stages = [];
-
- createComponent({ pipelines: [pipeline] });
- });
-
- it('stages are not rendered', () => {
- expect(findLegacyPipelineMiniGraph().props('stages')).toHaveLength(0);
- });
- });
- });
-
- describe('duration cell', () => {
- it('should render duration information', () => {
- expect(findTimeAgo().exists()).toBe(true);
- });
- });
-
- describe('operations cell', () => {
- it('should render pipeline operations', () => {
- expect(findActions().exists()).toBe(true);
- });
-
- it('should render retry action tooltip', () => {
- expect(findRetryBtn().attributes('title')).toBe(BUTTON_TOOLTIP_RETRY);
- });
-
- it('should render cancel action tooltip', () => {
- expect(findCancelBtn().attributes('title')).toBe(BUTTON_TOOLTIP_CANCEL);
- });
- });
-
- describe('triggerer cell', () => {
- it('should render the pipeline triggerer', () => {
- expect(findTriggerer().exists()).toBe(true);
- });
- });
-
- describe('failed jobs details', () => {
- describe('row', () => {
- describe('when the FF is disabled', () => {
- beforeEach(() => {
- createComponent({ pipelines: [pipeline] });
- });
-
- it('does not render', () => {
- expect(findTableRows()).toHaveLength(1);
- expect(findPipelineFailureWidget().exists()).toBe(false);
- });
- });
-
- describe('when the FF is enabled', () => {
- describe('and `withFailedJobsDetails` value is provided', () => {
- beforeEach(() => {
- createComponent({ pipelines: [pipeline] }, provideWithDetails);
- });
-
- it('renders', () => {
- expect(findTableRows()).toHaveLength(2);
- expect(findPipelineFailureWidget().exists()).toBe(true);
- });
-
- it('passes the expected props', () => {
- expect(findPipelineFailureWidget().props()).toStrictEqual({
- failedJobsCount: pipeline.failed_builds.length,
- isPipelineActive: pipeline.active,
- pipelineIid: pipeline.iid,
- pipelinePath: pipeline.path,
- // Make sure the forward slash was removed
- projectPath: 'frontend-fixtures/pipelines-project',
- });
- });
- });
-
- describe('and `withFailedJobsDetails` value is not provided', () => {
- beforeEach(() => {
- createComponent(
- { pipelines: [pipeline] },
- { glFeatures: { ciJobFailuresInMr: true } },
- );
- });
-
- it('does not render', () => {
- expect(findTableRows()).toHaveLength(1);
- expect(findPipelineFailureWidget().exists()).toBe(false);
- });
- });
- });
- });
- });
-
- describe('tracking', () => {
- beforeEach(() => {
- trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
- });
-
- afterEach(() => {
- unmockTracking();
- });
-
- it('tracks status badge click', () => {
- findCiBadgeLink().vm.$emit('ciStatusBadgeClick');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_ci_status_badge', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks retry pipeline button click', () => {
- findRetryBtn().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_retry_button', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks cancel pipeline button click', () => {
- findCancelBtn().vm.$emit('click');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_cancel_button', {
- label: TRACKING_CATEGORIES.table,
- });
- });
-
- it('tracks pipeline mini graph stage click', () => {
- findLegacyPipelineMiniGraph().vm.$emit('miniGraphStageClick');
-
- expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_minigraph', {
- label: TRACKING_CATEGORIES.table,
- });
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/empty_state_spec.js b/spec/frontend/pipelines/test_reports/empty_state_spec.js
deleted file mode 100644
index ee0f8a90a11..00000000000
--- a/spec/frontend/pipelines/test_reports/empty_state_spec.js
+++ /dev/null
@@ -1,45 +0,0 @@
-import { GlEmptyState } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import EmptyState, { i18n } from '~/pipelines/components/test_reports/empty_state.vue';
-
-describe('Test report empty state', () => {
- let wrapper;
-
- const findEmptyState = () => wrapper.findComponent(GlEmptyState);
-
- const createComponent = ({ hasTestReport = true } = {}) => {
- wrapper = shallowMount(EmptyState, {
- provide: {
- emptyStateImagePath: '/image/path',
- hasTestReport,
- },
- stubs: {
- GlEmptyState,
- },
- });
- };
-
- describe('when pipeline has a test report', () => {
- it('should render empty test report message', () => {
- createComponent();
-
- expect(findEmptyState().props()).toMatchObject({
- primaryButtonText: i18n.noTestsButton,
- description: i18n.noTestsDescription,
- title: i18n.noTestsTitle,
- });
- });
- });
-
- describe('when pipeline does not have a test report', () => {
- it('should render no test report message', () => {
- createComponent({ hasTestReport: false });
-
- expect(findEmptyState().props()).toMatchObject({
- primaryButtonText: i18n.noReportsButton,
- description: i18n.noReportsDescription,
- title: i18n.noReportsTitle,
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/mock_data.js b/spec/frontend/pipelines/test_reports/mock_data.js
deleted file mode 100644
index c3ca1429842..00000000000
--- a/spec/frontend/pipelines/test_reports/mock_data.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import { TestStatus } from '~/pipelines/constants';
-
-export default [
- {
- classname: 'spec.test_spec',
- file: 'spec/trace_spec.rb',
- execution_time: 0,
- name: 'Test#skipped text',
- stack_trace: null,
- status: TestStatus.SKIPPED,
- system_output: null,
- },
- {
- classname: 'spec.test_spec',
- file: 'spec/trace_spec.rb',
- execution_time: 0,
- name: 'Test#error text',
- stack_trace: null,
- status: TestStatus.ERROR,
- system_output: null,
- },
- {
- classname: 'spec.test_spec',
- file: 'spec/trace_spec.rb',
- execution_time: 0,
- name: 'Test#unknown text',
- stack_trace: null,
- status: TestStatus.UNKNOWN,
- system_output: null,
- },
-];
diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js
deleted file mode 100644
index e05d2151f0a..00000000000
--- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import { TEST_HOST } from 'helpers/test_constants';
-import testAction from 'helpers/vuex_action_helper';
-import { createAlert } from '~/alert';
-import axios from '~/lib/utils/axios_utils';
-import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
-import * as actions from '~/pipelines/stores/test_reports/actions';
-import * as types from '~/pipelines/stores/test_reports/mutation_types';
-
-jest.mock('~/alert');
-
-describe('Actions TestReports Store', () => {
- let mock;
- let state;
-
- const summary = { total_count: 1 };
-
- const suiteEndpoint = `${TEST_HOST}/tests/suite.json`;
- const summaryEndpoint = `${TEST_HOST}/test_reports/summary.json`;
- const defaultState = {
- suiteEndpoint,
- summaryEndpoint,
- testReports: {},
- selectedSuite: null,
- };
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
- state = { ...defaultState };
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- describe('fetch report summary', () => {
- beforeEach(() => {
- mock.onGet(summaryEndpoint).replyOnce(HTTP_STATUS_OK, summary, {});
- });
-
- it('sets testReports and shows tests', () => {
- return testAction(
- actions.fetchSummary,
- null,
- state,
- [{ type: types.SET_SUMMARY, payload: summary }],
- [{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
- );
- });
-
- it('should create alert on API error', async () => {
- await testAction(
- actions.fetchSummary,
- null,
- { summaryEndpoint: null },
- [],
- [{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
- );
- expect(createAlert).toHaveBeenCalled();
- });
- });
-
- describe('fetch test suite', () => {
- beforeEach(() => {
- const buildIds = [1];
- testReports.test_suites[0].build_ids = buildIds;
- mock
- .onGet(suiteEndpoint, { params: { build_ids: buildIds } })
- .replyOnce(HTTP_STATUS_OK, testReports.test_suites[0], {});
- });
-
- it('sets test suite and shows tests', () => {
- const suite = testReports.test_suites[0];
- const index = 0;
-
- return testAction(
- actions.fetchTestSuite,
- index,
- { ...state, testReports },
- [{ type: types.SET_SUITE, payload: { suite, index } }],
- [{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
- );
- });
-
- it('should call SET_SUITE_ERROR on error', () => {
- const index = 0;
-
- return testAction(
- actions.fetchTestSuite,
- index,
- { ...state, testReports, suiteEndpoint: null },
- [{ type: types.SET_SUITE_ERROR, payload: expect.any(Error) }],
- [{ type: 'toggleLoading' }, { type: 'toggleLoading' }],
- );
- });
-
- describe('when we already have the suite data', () => {
- it('should not fetch suite', () => {
- const index = 0;
- testReports.test_suites[0].hasFullSuite = true;
-
- return testAction(actions.fetchTestSuite, index, { ...state, testReports }, [], []);
- });
- });
- });
-
- describe('set selected suite index', () => {
- it('sets selectedSuiteIndex', () => {
- const selectedSuiteIndex = 0;
-
- return testAction(
- actions.setSelectedSuiteIndex,
- selectedSuiteIndex,
- { ...state, hasFullReport: true },
- [{ type: types.SET_SELECTED_SUITE_INDEX, payload: selectedSuiteIndex }],
- [],
- );
- });
- });
-
- describe('remove selected suite index', () => {
- it('sets selectedSuiteIndex to null', () => {
- return testAction(
- actions.removeSelectedSuiteIndex,
- {},
- state,
- [{ type: types.SET_SELECTED_SUITE_INDEX, payload: null }],
- [],
- );
- });
- });
-
- describe('toggles loading', () => {
- it('sets isLoading to true', () => {
- return testAction(actions.toggleLoading, {}, state, [{ type: types.TOGGLE_LOADING }], []);
- });
-
- it('toggles isLoading to false', () => {
- return testAction(
- actions.toggleLoading,
- {},
- { ...state, isLoading: true },
- [{ type: types.TOGGLE_LOADING }],
- [],
- );
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
deleted file mode 100644
index 70e3a01dbf1..00000000000
--- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js
+++ /dev/null
@@ -1,171 +0,0 @@
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import * as getters from '~/pipelines/stores/test_reports/getters';
-import {
- iconForTestStatus,
- formatFilePath,
- formattedTime,
-} from '~/pipelines/stores/test_reports/utils';
-
-describe('Getters TestReports Store', () => {
- let state;
-
- const defaultState = {
- blobPath: '/test/blob/path',
- testReports,
- selectedSuiteIndex: 0,
- pageInfo: {
- page: 1,
- perPage: 2,
- },
- };
-
- const emptyState = {
- blobPath: '',
- testReports: {},
- selectedSuite: null,
- pageInfo: {
- page: 1,
- perPage: 2,
- },
- };
-
- beforeEach(() => {
- state = {
- testReports,
- };
- });
-
- const setupState = (testState = defaultState) => {
- state = testState;
- };
-
- describe('getTestSuites', () => {
- it('should return the test suites', () => {
- setupState();
-
- const suites = getters.getTestSuites(state);
- const expected = testReports.test_suites.map((x) => ({
- ...x,
- formattedTime: formattedTime(x.total_time),
- }));
-
- expect(suites).toEqual(expected);
- });
-
- it('should return an empty array when testReports is empty', () => {
- setupState(emptyState);
-
- expect(getters.getTestSuites(state)).toEqual([]);
- });
- });
-
- describe('getSelectedSuite', () => {
- it('should return the selected suite', () => {
- setupState();
-
- const selectedSuite = getters.getSelectedSuite(state);
- const expected = testReports.test_suites[state.selectedSuiteIndex];
-
- expect(selectedSuite).toEqual(expected);
- });
- });
-
- describe('getSuiteTests', () => {
- it('should return the current page of test cases inside the suite', () => {
- setupState();
-
- const cases = getters.getSuiteTests(state);
- const expected = testReports.test_suites[0].test_cases
- .map((x) => ({
- ...x,
- filePath: `${state.blobPath}/${formatFilePath(x.file)}`,
- formattedTime: formattedTime(x.execution_time),
- icon: iconForTestStatus(x.status),
- }))
- .slice(0, state.pageInfo.perPage);
-
- expect(cases).toEqual(expected);
- });
-
- it('should return an empty array when testReports is empty', () => {
- setupState(emptyState);
-
- expect(getters.getSuiteTests(state)).toEqual([]);
- });
-
- describe('when a test case classname property is null', () => {
- it('should return an empty string value for the classname property', () => {
- const testCases = testReports.test_suites[0].test_cases;
- setupState({
- ...defaultState,
- testReports: {
- ...testReports,
- test_suites: [
- {
- test_cases: testCases.map((testCase) => ({
- ...testCase,
- classname: null,
- })),
- },
- ],
- },
- });
-
- const expected = testCases
- .map((x) => ({
- ...x,
- classname: '',
- filePath: `${state.blobPath}/${formatFilePath(x.file)}`,
- formattedTime: formattedTime(x.execution_time),
- icon: iconForTestStatus(x.status),
- }))
- .slice(0, state.pageInfo.perPage);
-
- expect(getters.getSuiteTests(state)).toEqual(expected);
- });
- });
-
- describe('when a test case name property is null', () => {
- it('should return an empty string value for the name property', () => {
- const testCases = testReports.test_suites[0].test_cases;
- setupState({
- ...defaultState,
- testReports: {
- ...testReports,
- test_suites: [
- {
- test_cases: testCases.map((testCase) => ({
- ...testCase,
- name: null,
- })),
- },
- ],
- },
- });
-
- const expected = testCases
- .map((x) => ({
- ...x,
- name: '',
- filePath: `${state.blobPath}/${formatFilePath(x.file)}`,
- formattedTime: formattedTime(x.execution_time),
- icon: iconForTestStatus(x.status),
- }))
- .slice(0, state.pageInfo.perPage);
-
- expect(getters.getSuiteTests(state)).toEqual(expected);
- });
- });
- });
-
- describe('getSuiteTestCount', () => {
- it('should return the total number of test cases', () => {
- setupState();
-
- const testCount = getters.getSuiteTestCount(state);
- const expected = testReports.test_suites[0].test_cases.length;
-
- expect(testCount).toEqual(expected);
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
deleted file mode 100644
index 685ac6ea3e5..00000000000
--- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import * as types from '~/pipelines/stores/test_reports/mutation_types';
-import mutations from '~/pipelines/stores/test_reports/mutations';
-import { createAlert } from '~/alert';
-
-jest.mock('~/alert');
-
-describe('Mutations TestReports Store', () => {
- let mockState;
-
- const defaultState = {
- endpoint: '',
- testReports: {},
- selectedSuite: null,
- isLoading: false,
- pageInfo: {
- page: 1,
- perPage: 2,
- },
- };
-
- beforeEach(() => {
- mockState = { ...defaultState };
- });
-
- describe('set page', () => {
- it('should set the current page to display', () => {
- const pageToDisplay = 3;
- mutations[types.SET_PAGE](mockState, pageToDisplay);
-
- expect(mockState.pageInfo.page).toEqual(pageToDisplay);
- });
- });
-
- describe('set suite', () => {
- it('should set the suite at the given index', () => {
- mockState.testReports = testReports;
- const suite = { name: 'test_suite' };
- const index = 0;
- const expectedState = { ...mockState };
- expectedState.testReports.test_suites[index] = { suite, hasFullSuite: true };
- mutations[types.SET_SUITE](mockState, { suite, index });
-
- expect(mockState.testReports.test_suites[index]).toEqual(
- expectedState.testReports.test_suites[index],
- );
- });
- });
-
- describe('set suite error', () => {
- it('should set the error message in state if provided', () => {
- const message = 'Test report artifacts not found';
-
- mutations[types.SET_SUITE_ERROR](mockState, {
- response: { data: { errors: message } },
- });
-
- expect(mockState.errorMessage).toBe(message);
- });
-
- it('should show an alert otherwise', () => {
- mutations[types.SET_SUITE_ERROR](mockState, {});
-
- expect(createAlert).toHaveBeenCalled();
- });
- });
-
- describe('set selected suite index', () => {
- it('should set selectedSuiteIndex', () => {
- const selectedSuiteIndex = 0;
- mutations[types.SET_SELECTED_SUITE_INDEX](mockState, selectedSuiteIndex);
-
- expect(mockState.selectedSuiteIndex).toEqual(selectedSuiteIndex);
- });
- });
-
- describe('set summary', () => {
- it('should set summary', () => {
- const summary = {
- total: { time: 0, count: 10, success: 1, failed: 2, skipped: 3, error: 4 },
- };
- const expectedSummary = {
- ...summary,
- total_time: 0,
- total_count: 10,
- success_count: 1,
- failed_count: 2,
- skipped_count: 3,
- error_count: 4,
- };
- mutations[types.SET_SUMMARY](mockState, summary);
-
- expect(mockState.testReports).toEqual(expectedSummary);
- });
- });
-
- describe('toggle loading', () => {
- it('should set to true', () => {
- const expectedState = { ...mockState, isLoading: true };
- mutations[types.TOGGLE_LOADING](mockState);
-
- expect(mockState.isLoading).toEqual(expectedState.isLoading);
- });
-
- it('should toggle back to false', () => {
- const expectedState = { ...mockState, isLoading: false };
- mockState.isLoading = true;
-
- mutations[types.TOGGLE_LOADING](mockState);
-
- expect(mockState.isLoading).toEqual(expectedState.isLoading);
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/stores/utils_spec.js b/spec/frontend/pipelines/test_reports/stores/utils_spec.js
deleted file mode 100644
index 703fe69026c..00000000000
--- a/spec/frontend/pipelines/test_reports/stores/utils_spec.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import { formatFilePath, formattedTime } from '~/pipelines/stores/test_reports/utils';
-
-describe('Test reports utils', () => {
- describe('formatFilePath', () => {
- it.each`
- file | expected
- ${'./test.js'} | ${'test.js'}
- ${'/test.js'} | ${'test.js'}
- ${'.//////////////test.js'} | ${'test.js'}
- ${'test.js'} | ${'test.js'}
- ${'mock/path./test.js'} | ${'mock/path./test.js'}
- ${'./mock/path./test.js'} | ${'mock/path./test.js'}
- `('should format $file to be $expected', ({ file, expected }) => {
- expect(formatFilePath(file)).toBe(expected);
- });
- });
-
- describe('formattedTime', () => {
- describe('when time is smaller than a second', () => {
- it('should return time in milliseconds fixed to 2 decimals', () => {
- const result = formattedTime(0.4815162342);
- expect(result).toBe('481.52ms');
- });
- });
-
- describe('when time is equal to a second', () => {
- it('should return time in seconds fixed to 2 decimals', () => {
- const result = formattedTime(1);
- expect(result).toBe('1.00s');
- });
- });
-
- describe('when time is greater than a second', () => {
- it('should return time in seconds fixed to 2 decimals', () => {
- const result = formattedTime(4.815162342);
- expect(result).toBe('4.82s');
- });
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/test_case_details_spec.js b/spec/frontend/pipelines/test_reports/test_case_details_spec.js
deleted file mode 100644
index f8663408817..00000000000
--- a/spec/frontend/pipelines/test_reports/test_case_details_spec.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import { GlModal, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import TestCaseDetails from '~/pipelines/components/test_reports/test_case_details.vue';
-import CodeBlock from '~/vue_shared/components/code_block.vue';
-import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
-
-describe('Test case details', () => {
- let wrapper;
- const defaultTestCase = {
- classname: 'spec.test_spec',
- name: 'Test#something cool',
- file: '~/index.js',
- filePath: '/src/javascripts/index.js',
- formattedTime: '10.04ms',
- recent_failures: {
- count: 2,
- base_branch: 'main',
- },
- system_output: 'Line 42 is broken',
- };
-
- const findCopyFileBtn = () => wrapper.findComponent(ModalCopyButton);
- const findModal = () => wrapper.findComponent(GlModal);
- const findName = () => wrapper.findByTestId('test-case-name');
- const findFile = () => wrapper.findByTestId('test-case-file');
- const findFileLink = () => wrapper.findComponent(GlLink);
- const findDuration = () => wrapper.findByTestId('test-case-duration');
- const findRecentFailures = () => wrapper.findByTestId('test-case-recent-failures');
- const findAttachmentUrl = () => wrapper.findByTestId('test-case-attachment-url');
- const findSystemOutput = () => wrapper.findByTestId('test-case-trace');
-
- const createComponent = (testCase = {}) => {
- wrapper = extendedWrapper(
- shallowMount(TestCaseDetails, {
- propsData: {
- modalId: 'my-modal',
- testCase: {
- ...defaultTestCase,
- ...testCase,
- },
- },
- stubs: { CodeBlock, GlModal },
- }),
- );
- };
-
- describe('required details', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders the test case classname as modal title', () => {
- expect(findModal().props('title')).toBe(defaultTestCase.classname);
- });
-
- it('renders the test case name', () => {
- expect(findName().text()).toBe(defaultTestCase.name);
- });
-
- it('renders the test case file', () => {
- expect(findFile().text()).toBe(defaultTestCase.file);
- expect(findFileLink().attributes('href')).toBe(defaultTestCase.filePath);
- });
-
- it('renders copy button for test case file', () => {
- expect(findCopyFileBtn().attributes('text')).toBe(defaultTestCase.file);
- });
-
- it('renders the test case duration', () => {
- expect(findDuration().text()).toBe(defaultTestCase.formattedTime);
- });
- });
-
- describe('when test case has execution time instead of formatted time', () => {
- beforeEach(() => {
- createComponent({ ...defaultTestCase, formattedTime: null, execution_time: 17 });
- });
-
- it('renders the test case duration', () => {
- expect(findDuration().text()).toBe('17 s');
- });
- });
-
- describe('when test case has recent failures', () => {
- describe('has only 1 recent failure', () => {
- it('renders the recent failure', () => {
- createComponent({ recent_failures: { ...defaultTestCase.recent_failures, count: 1 } });
-
- expect(findRecentFailures().text()).toContain(
- `Failed 1 time in ${defaultTestCase.recent_failures.base_branch} in the last 14 days`,
- );
- });
- });
-
- describe('has more than 1 recent failure', () => {
- it('renders the recent failures', () => {
- createComponent();
-
- expect(findRecentFailures().text()).toContain(
- `Failed ${defaultTestCase.recent_failures.count} times in ${defaultTestCase.recent_failures.base_branch} in the last 14 days`,
- );
- });
- });
- });
-
- describe('when test case does not have recent failures', () => {
- it('does not render the recent failures', () => {
- createComponent({ recent_failures: null });
-
- expect(findRecentFailures().exists()).toBe(false);
- });
- });
-
- describe('when test case has attachment URL', () => {
- it('renders the attachment URL as a link', () => {
- const expectedUrl = '/my/path.jpg';
- createComponent({ attachment_url: expectedUrl });
- const attachmentUrl = findAttachmentUrl();
-
- expect(attachmentUrl.exists()).toBe(true);
- expect(attachmentUrl.attributes('href')).toBe(expectedUrl);
- });
- });
-
- describe('when test case does not have attachment URL', () => {
- it('does not render the attachment URL', () => {
- createComponent({ attachment_url: null });
-
- expect(findAttachmentUrl().exists()).toBe(false);
- });
- });
-
- describe('when test case has system output', () => {
- it('renders the test case system output', () => {
- createComponent();
-
- expect(findSystemOutput().text()).toContain(defaultTestCase.system_output);
- });
- });
-
- describe('when test case does not have system output', () => {
- it('does not render the test case system output', () => {
- createComponent({ system_output: null });
-
- expect(findSystemOutput().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js
deleted file mode 100644
index de16f496eff..00000000000
--- a/spec/frontend/pipelines/test_reports/test_reports_spec.js
+++ /dev/null
@@ -1,125 +0,0 @@
-import { GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
-// eslint-disable-next-line no-restricted-imports
-import Vuex from 'vuex';
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import EmptyState from '~/pipelines/components/test_reports/empty_state.vue';
-import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
-import TestSummary from '~/pipelines/components/test_reports/test_summary.vue';
-import TestSummaryTable from '~/pipelines/components/test_reports/test_summary_table.vue';
-import * as getters from '~/pipelines/stores/test_reports/getters';
-
-Vue.use(Vuex);
-
-describe('Test reports app', () => {
- let wrapper;
- let store;
-
- const loadingSpinner = () => wrapper.findComponent(GlLoadingIcon);
- const testsDetail = () => wrapper.findByTestId('tests-detail');
- const emptyState = () => wrapper.findComponent(EmptyState);
- const testSummary = () => wrapper.findComponent(TestSummary);
- const testSummaryTable = () => wrapper.findComponent(TestSummaryTable);
-
- const actionSpies = {
- fetchTestSuite: jest.fn(),
- fetchSummary: jest.fn(),
- setSelectedSuiteIndex: jest.fn(),
- removeSelectedSuiteIndex: jest.fn(),
- };
-
- const createComponent = ({ state = {} } = {}) => {
- store = new Vuex.Store({
- modules: {
- testReports: {
- namespaced: true,
- state: {
- isLoading: false,
- selectedSuiteIndex: null,
- testReports,
- ...state,
- },
- actions: actionSpies,
- getters,
- },
- },
- });
-
- jest.spyOn(store, 'registerModule').mockReturnValue(null);
-
- wrapper = extendedWrapper(
- shallowMount(TestReports, {
- provide: {
- blobPath: '/blob/path',
- summaryEndpoint: '/summary.json',
- suiteEndpoint: '/suite.json',
- },
- store,
- }),
- );
- };
-
- describe('when component is created', () => {
- it('should call fetchSummary when pipeline has test report', () => {
- createComponent();
-
- expect(actionSpies.fetchSummary).toHaveBeenCalled();
- });
- });
-
- describe('when loading', () => {
- beforeEach(() => createComponent({ state: { isLoading: true } }));
-
- it('shows the loading spinner', () => {
- expect(emptyState().exists()).toBe(false);
- expect(testsDetail().exists()).toBe(false);
- expect(loadingSpinner().exists()).toBe(true);
- });
- });
-
- describe('when the api returns no data', () => {
- it('displays empty state component', () => {
- createComponent({ state: { testReports: {} } });
-
- expect(emptyState().exists()).toBe(true);
- });
- });
-
- describe('when the api returns data', () => {
- beforeEach(() => createComponent());
-
- it('sets testReports and shows tests', () => {
- expect(wrapper.vm.testReports).toEqual(expect.any(Object));
- expect(wrapper.vm.showTests).toBe(true);
- });
-
- it('shows tests details', () => {
- expect(testsDetail().exists()).toBe(true);
- });
- });
-
- describe('when a suite is clicked', () => {
- beforeEach(() => {
- createComponent({ state: { hasFullReport: true } });
- testSummaryTable().vm.$emit('row-click', 0);
- });
-
- it('should call setSelectedSuiteIndex and fetchTestSuite', () => {
- expect(actionSpies.setSelectedSuiteIndex).toHaveBeenCalled();
- expect(actionSpies.fetchTestSuite).toHaveBeenCalled();
- });
- });
-
- describe('when clicking back to summary', () => {
- beforeEach(() => {
- createComponent({ state: { selectedSuiteIndex: 0 } });
- testSummary().vm.$emit('on-back-click');
- });
-
- it('should call removeSelectedSuiteIndex', () => {
- expect(actionSpies.removeSelectedSuiteIndex).toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
deleted file mode 100644
index 08b430fa703..00000000000
--- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
+++ /dev/null
@@ -1,169 +0,0 @@
-import { GlButton, GlFriendlyWrap, GlLink, GlPagination, GlEmptyState } from '@gitlab/ui';
-import Vue from 'vue';
-// eslint-disable-next-line no-restricted-imports
-import Vuex from 'vuex';
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import SuiteTable, { i18n } from '~/pipelines/components/test_reports/test_suite_table.vue';
-import { TestStatus } from '~/pipelines/constants';
-import * as getters from '~/pipelines/stores/test_reports/getters';
-import { formatFilePath } from '~/pipelines/stores/test_reports/utils';
-import { ARTIFACTS_EXPIRED_ERROR_MESSAGE } from '~/pipelines/stores/test_reports/constants';
-import skippedTestCases from './mock_data';
-
-Vue.use(Vuex);
-
-describe('Test reports suite table', () => {
- let wrapper;
- let store;
-
- const {
- test_suites: [testSuite],
- } = testReports;
-
- testSuite.test_cases = [...testSuite.test_cases, ...skippedTestCases];
- const testCases = testSuite.test_cases;
- const blobPath = '/test/blob/path';
-
- const noCasesMessage = () => wrapper.findByTestId('no-test-cases');
- const artifactsExpiredMessage = () => wrapper.findByTestId('artifacts-expired');
- const artifactsExpiredEmptyState = () => wrapper.findComponent(GlEmptyState);
- const allCaseRows = () => wrapper.findAllByTestId('test-case-row');
- const findCaseRowAtIndex = (index) => wrapper.findAllByTestId('test-case-row').at(index);
- const findLinkForRow = (row) => row.findComponent(GlLink);
- const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
-
- const createComponent = ({ suite = testSuite, perPage = 20, errorMessage } = {}) => {
- store = new Vuex.Store({
- modules: {
- testReports: {
- namespaced: true,
- state: {
- blobPath,
- testReports: {
- test_suites: [suite],
- },
- selectedSuiteIndex: 0,
- pageInfo: {
- page: 1,
- perPage,
- },
- errorMessage,
- },
- getters,
- },
- },
- });
-
- wrapper = shallowMountExtended(SuiteTable, {
- provide: {
- blobPath: '/blob/path',
- summaryEndpoint: '/summary.json',
- suiteEndpoint: '/suite.json',
- },
- store,
- stubs: { GlFriendlyWrap },
- });
- };
-
- it('should render a message when there are no test cases', () => {
- createComponent({ suite: [] });
-
- expect(noCasesMessage().exists()).toBe(true);
- expect(artifactsExpiredMessage().exists()).toBe(false);
- });
-
- it('should render an empty state when artifacts have expired', () => {
- createComponent({ suite: [], errorMessage: ARTIFACTS_EXPIRED_ERROR_MESSAGE });
- const emptyState = artifactsExpiredEmptyState();
-
- expect(noCasesMessage().exists()).toBe(false);
- expect(artifactsExpiredMessage().exists()).toBe(true);
-
- expect(emptyState.exists()).toBe(true);
- expect(emptyState.props('title')).toBe(i18n.expiredArtifactsTitle);
- });
-
- describe('when a test suite is supplied', () => {
- beforeEach(() => createComponent());
-
- it('renders the correct number of rows', () => {
- expect(allCaseRows()).toHaveLength(testCases.length);
- });
-
- it.each([
- TestStatus.ERROR,
- TestStatus.FAILED,
- TestStatus.SKIPPED,
- TestStatus.SUCCESS,
- 'unknown',
- ])('renders the correct icon for test case with %s status', (status) => {
- const test = testCases.findIndex((x) => x.status === status);
- const row = findCaseRowAtIndex(test);
-
- expect(findIconForRow(row, status).exists()).toBe(true);
- });
-
- it('renders the file name for the test with a copy button', () => {
- const { file } = testCases[0];
- const relativeFile = formatFilePath(file);
- const filePath = `${blobPath}/${relativeFile}`;
- const row = findCaseRowAtIndex(0);
- const fileLink = findLinkForRow(row);
- const button = row.findComponent(GlButton);
-
- expect(fileLink.attributes('href')).toBe(filePath);
- expect(row.text()).toContain(file);
- expect(button.exists()).toBe(true);
- expect(button.attributes('data-clipboard-text')).toBe(file);
- });
- });
-
- describe('when a test suite has more test cases than the pagination size', () => {
- const perPage = 2;
-
- beforeEach(() => {
- createComponent({ testSuite, perPage });
- });
-
- it('renders one page of test cases', () => {
- expect(allCaseRows().length).toBe(perPage);
- });
-
- it('renders a pagination component', () => {
- expect(wrapper.findComponent(GlPagination).exists()).toBe(true);
- });
- });
-
- describe('when a test case classname property is null', () => {
- it('still renders all test cases', () => {
- createComponent({
- testSuite: {
- ...testSuite,
- test_cases: testSuite.test_cases.map((testCase) => ({
- ...testCase,
- classname: null,
- })),
- },
- });
-
- expect(allCaseRows()).toHaveLength(testCases.length);
- });
- });
-
- describe('when a test case name property is null', () => {
- it('still renders all test cases', () => {
- createComponent({
- testSuite: {
- ...testSuite,
- test_cases: testSuite.test_cases.map((testCase) => ({
- ...testCase,
- name: null,
- })),
- },
- });
-
- expect(allCaseRows()).toHaveLength(testCases.length);
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js
deleted file mode 100644
index 7eed6671fb9..00000000000
--- a/spec/frontend/pipelines/test_reports/test_summary_spec.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import { mount } from '@vue/test-utils';
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import Summary from '~/pipelines/components/test_reports/test_summary.vue';
-import { formattedTime } from '~/pipelines/stores/test_reports/utils';
-
-describe('Test reports summary', () => {
- let wrapper;
-
- const {
- test_suites: [testSuite],
- } = testReports;
-
- const backButton = () => wrapper.find('.js-back-button');
- const totalTests = () => wrapper.find('.js-total-tests');
- const failedTests = () => wrapper.find('.js-failed-tests');
- const erroredTests = () => wrapper.find('.js-errored-tests');
- const successRate = () => wrapper.find('.js-success-rate');
- const duration = () => wrapper.find('.js-duration');
-
- const defaultProps = {
- report: testSuite,
- showBack: false,
- };
-
- const createComponent = (props) => {
- wrapper = mount(Summary, {
- propsData: {
- ...defaultProps,
- ...props,
- },
- });
- };
-
- describe('should not render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('a back button by default', () => {
- expect(backButton().exists()).toBe(false);
- });
- });
-
- describe('should render', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('a back button and emit on-back-click event', () => {
- createComponent({
- showBack: true,
- });
-
- expect(backButton().exists()).toBe(true);
- });
- });
-
- describe('when a report is supplied', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('displays the correct total', () => {
- expect(totalTests().text()).toBe('4 tests');
- });
-
- it('displays the correct failure count', () => {
- expect(failedTests().text()).toBe('2 failures');
- });
-
- it('displays the correct error count', () => {
- expect(erroredTests().text()).toBe('0 errors');
- });
-
- it('calculates and displays percentages correctly', () => {
- expect(successRate().text()).toBe('50% success rate');
- });
-
- it('displays the correctly formatted duration', () => {
- expect(duration().text()).toBe(formattedTime(testSuite.total_time));
- });
- });
-
- describe('success percentage calculation', () => {
- it.each`
- name | successCount | totalCount | skippedCount | result
- ${'displays 0 when there are no tests'} | ${0} | ${0} | ${0} | ${'0'}
- ${'displays whole number when possible'} | ${10} | ${50} | ${0} | ${'20'}
- ${'excludes skipped tests from total'} | ${10} | ${50} | ${5} | ${'22.22'}
- ${'rounds to 0.01'} | ${1} | ${16604} | ${0} | ${'0.01'}
- ${'correctly rounds to 50'} | ${8302} | ${16604} | ${0} | ${'50'}
- ${'rounds down for large close numbers'} | ${16603} | ${16604} | ${0} | ${'99.99'}
- ${'correctly displays 100'} | ${16604} | ${16604} | ${0} | ${'100'}
- `('$name', ({ successCount, totalCount, skippedCount, result }) => {
- createComponent({
- report: {
- success_count: successCount,
- skipped_count: skippedCount,
- total_count: totalCount,
- },
- });
-
- expect(successRate().text()).toBe(`${result}% success rate`);
- });
- });
-});
diff --git a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js b/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
deleted file mode 100644
index a45946d5a03..00000000000
--- a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { mount } from '@vue/test-utils';
-import Vue from 'vue';
-// eslint-disable-next-line no-restricted-imports
-import Vuex from 'vuex';
-import testReports from 'test_fixtures/pipelines/test_report.json';
-import SummaryTable from '~/pipelines/components/test_reports/test_summary_table.vue';
-import * as getters from '~/pipelines/stores/test_reports/getters';
-
-Vue.use(Vuex);
-
-describe('Test reports summary table', () => {
- let wrapper;
- let store;
-
- const allSuitesRows = () => wrapper.findAll('.js-suite-row');
- const noSuitesToShow = () => wrapper.find('.js-no-tests-suites');
-
- const defaultProps = {
- testReports,
- };
-
- const createComponent = (reports = null) => {
- store = new Vuex.Store({
- modules: {
- testReports: {
- namespaced: true,
- state: {
- testReports: reports || testReports,
- },
- getters,
- },
- },
- });
-
- wrapper = mount(SummaryTable, {
- provide: {
- blobPath: '/blob/path',
- summaryEndpoint: '/summary.json',
- suiteEndpoint: '/suite.json',
- },
- propsData: defaultProps,
- store,
- });
- };
-
- describe('when test reports are supplied', () => {
- beforeEach(() => createComponent());
- const findErrorIcon = () => wrapper.findComponent({ ref: 'suiteErrorIcon' });
-
- it('renders the correct number of rows', () => {
- expect(noSuitesToShow().exists()).toBe(false);
- expect(allSuitesRows().length).toBe(testReports.test_suites.length);
- });
-
- describe('when there is a suite error', () => {
- beforeEach(() => {
- createComponent({
- test_suites: [
- {
- ...testReports.test_suites[0],
- suite_error: 'Suite Error',
- },
- ],
- });
- });
-
- it('renders error icon', () => {
- expect(findErrorIcon().exists()).toBe(true);
- expect(findErrorIcon().attributes('title')).toEqual('Suite Error');
- });
- });
-
- describe('when there is not a suite error', () => {
- beforeEach(() => {
- createComponent({
- test_suites: [
- {
- ...testReports.test_suites[0],
- suite_error: null,
- },
- ],
- });
- });
-
- it('does not render error icon', () => {
- expect(findErrorIcon().exists()).toBe(false);
- });
- });
- });
-
- describe('when there are no test suites', () => {
- beforeEach(() => {
- createComponent({ test_suites: [] });
- });
-
- it('displays the no suites to show message', () => {
- expect(noSuitesToShow().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/pipelines/time_ago_spec.js b/spec/frontend/pipelines/time_ago_spec.js
deleted file mode 100644
index d2aa340a980..00000000000
--- a/spec/frontend/pipelines/time_ago_spec.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import TimeAgo from '~/pipelines/components/pipelines_list/time_ago.vue';
-
-describe('Timeago component', () => {
- let wrapper;
-
- const defaultProps = { duration: 0, finished_at: '' };
-
- const createComponent = (props = defaultProps, extraProps) => {
- wrapper = extendedWrapper(
- shallowMount(TimeAgo, {
- propsData: {
- pipeline: {
- details: {
- ...props,
- },
- },
- ...extraProps,
- },
- data() {
- return {
- iconTimerSvg: `<svg></svg>`,
- };
- },
- }),
- );
- };
-
- const duration = () => wrapper.find('.duration');
- const finishedAt = () => wrapper.find('.finished-at');
- const findCalendarIcon = () => wrapper.findByTestId('calendar-icon');
-
- describe('with duration', () => {
- beforeEach(() => {
- createComponent({ duration: 10, finished_at: '' });
- });
-
- it('should render duration and timer svg', () => {
- const icon = duration().findComponent(GlIcon);
-
- expect(duration().exists()).toBe(true);
- expect(icon.props('name')).toBe('timer');
- });
- });
-
- describe('without duration', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not render duration and timer svg', () => {
- expect(duration().exists()).toBe(false);
- });
- });
-
- describe('with finishedTime', () => {
- it('should render time', () => {
- createComponent({ duration: 0, finished_at: '2017-04-26T12:40:23.277Z' });
-
- const time = finishedAt().find('time');
-
- expect(finishedAt().exists()).toBe(true);
- expect(time.exists()).toBe(true);
- });
-
- it('should display calendar icon', () => {
- createComponent({ duration: 0, finished_at: '2017-04-26T12:40:23.277Z' });
-
- expect(findCalendarIcon().exists()).toBe(true);
- });
- });
-
- describe('without finishedTime', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('should not render time and calendar icon', () => {
- expect(finishedAt().exists()).toBe(false);
- expect(findCalendarIcon().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
deleted file mode 100644
index d518519a424..00000000000
--- a/spec/frontend/pipelines/tokens/pipeline_branch_name_token_spec.js
+++ /dev/null
@@ -1,142 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
-import { nextTick } from 'vue';
-import { shallowMount } from '@vue/test-utils';
-import waitForPromises from 'helpers/wait_for_promises';
-import Api from '~/api';
-import PipelineBranchNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_branch_name_token.vue';
-import { branches, mockBranchesAfterMap } from '../mock_data';
-
-describe('Pipeline Branch Name Token', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const getBranchSuggestions = () =>
- findAllFilteredSearchSuggestions().wrappers.map((w) => w.text());
-
- const stubs = {
- GlFilteredSearchToken: {
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const defaultProps = {
- config: {
- type: 'ref',
- icon: 'branch',
- title: 'Branch name',
- unique: true,
- projectId: '21',
- defaultBranchName: null,
- disabled: false,
- },
- value: {
- data: '',
- },
- cursorPosition: 'start',
- };
-
- const optionsWithDefaultBranchName = (options) => {
- return {
- propsData: {
- ...defaultProps,
- config: {
- ...defaultProps.config,
- defaultBranchName: 'main',
- },
- },
- ...options,
- };
- };
-
- const createComponent = (options, data) => {
- wrapper = shallowMount(PipelineBranchNameToken, {
- propsData: {
- ...defaultProps,
- },
- data() {
- return {
- ...data,
- };
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
-
- createComponent();
- });
-
- it('passes config correctly', () => {
- expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
- });
-
- it('fetches and sets project branches', () => {
- expect(Api.branches).toHaveBeenCalled();
-
- expect(wrapper.vm.branches).toEqual(mockBranchesAfterMap);
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- describe('displays loading icon correctly', () => {
- it('shows loading icon', () => {
- createComponent({ stubs }, { loading: true });
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not show loading icon', () => {
- createComponent({ stubs }, { loading: false });
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('shows branches correctly', () => {
- it('renders all branches', () => {
- createComponent({ stubs }, { branches, loading: false });
-
- expect(findAllFilteredSearchSuggestions()).toHaveLength(branches.length);
- });
-
- it('renders only the branch searched for', () => {
- const mockBranches = ['main'];
- createComponent({ stubs }, { branches: mockBranches, loading: false });
-
- expect(findAllFilteredSearchSuggestions()).toHaveLength(mockBranches.length);
- });
-
- it('shows the default branch first if no branch was searched for', async () => {
- const mockBranches = [{ name: 'branch-1' }];
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
-
- createComponent(optionsWithDefaultBranchName({ stubs }), { loading: false });
- await nextTick();
- expect(getBranchSuggestions()).toEqual(['main', 'branch-1']);
- });
-
- it('does not show the default branch if a search term was provided', async () => {
- const mockBranches = [{ name: 'branch-1' }];
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
-
- createComponent(optionsWithDefaultBranchName(), { loading: false });
-
- findFilteredSearchToken().vm.$emit('input', { data: 'branch-1' });
- await waitForPromises();
- expect(getBranchSuggestions()).toEqual(['branch-1']);
- });
-
- it('shows the default branch only once if it appears in the results', async () => {
- const mockBranches = [{ name: 'main' }];
- jest.spyOn(Api, 'branches').mockResolvedValue({ data: mockBranches });
-
- createComponent(optionsWithDefaultBranchName({ stubs }), { loading: false });
- await nextTick();
- expect(getBranchSuggestions()).toEqual(['main']);
- });
- });
-});
diff --git a/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js
deleted file mode 100644
index 60abb63a7e0..00000000000
--- a/spec/frontend/pipelines/tokens/pipeline_source_token_spec.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { PIPELINE_SOURCES } from 'ee_else_ce/pipelines/components/pipelines_list/tokens/constants';
-import { stubComponent } from 'helpers/stub_component';
-import PipelineSourceToken from '~/pipelines/components/pipelines_list/tokens/pipeline_source_token.vue';
-
-describe('Pipeline Source Token', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
-
- const defaultProps = {
- config: {
- type: 'source',
- icon: 'trigger-source',
- title: 'Source',
- unique: true,
- },
- value: {
- data: '',
- },
- cursorPosition: 'start',
- };
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineSourceToken, {
- propsData: {
- ...defaultProps,
- },
- stubs: {
- GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
- template: `<div><slot name="suggestions"></slot></div>`,
- }),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- it('passes config correctly', () => {
- expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
- });
-
- describe('shows sources correctly', () => {
- it('renders all pipeline sources available', () => {
- expect(findAllFilteredSearchSuggestions()).toHaveLength(PIPELINE_SOURCES.length);
- });
- });
-});
diff --git a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
deleted file mode 100644
index cf4ccb5ce43..00000000000
--- a/spec/frontend/pipelines/tokens/pipeline_status_token_spec.js
+++ /dev/null
@@ -1,58 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { stubComponent } from 'helpers/stub_component';
-import PipelineStatusToken from '~/pipelines/components/pipelines_list/tokens/pipeline_status_token.vue';
-import {
- TOKEN_TITLE_STATUS,
- TOKEN_TYPE_STATUS,
-} from '~/vue_shared/components/filtered_search_bar/constants';
-
-describe('Pipeline Status Token', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
- const findAllGlIcons = () => wrapper.findAllComponents(GlIcon);
-
- const defaultProps = {
- config: {
- type: TOKEN_TYPE_STATUS,
- icon: 'status',
- title: TOKEN_TITLE_STATUS,
- unique: true,
- },
- value: {
- data: '',
- },
- cursorPosition: 'start',
- };
-
- const createComponent = () => {
- wrapper = shallowMount(PipelineStatusToken, {
- propsData: {
- ...defaultProps,
- },
- stubs: {
- GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
- template: `<div><slot name="suggestions"></slot></div>`,
- }),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- it('passes config correctly', () => {
- expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
- });
-
- describe('shows statuses correctly', () => {
- it('renders all pipeline statuses available', () => {
- expect(findAllFilteredSearchSuggestions()).toHaveLength(wrapper.vm.statuses.length);
- expect(findAllGlIcons()).toHaveLength(wrapper.vm.statuses.length);
- });
- });
-});
diff --git a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js
deleted file mode 100644
index 88c88d8f16f..00000000000
--- a/spec/frontend/pipelines/tokens/pipeline_tag_name_token_spec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Api from '~/api';
-import PipelineTagNameToken from '~/pipelines/components/pipelines_list/tokens/pipeline_tag_name_token.vue';
-import { tags, mockTagsAfterMap } from '../mock_data';
-
-describe('Pipeline Branch Name Token', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- const stubs = {
- GlFilteredSearchToken: {
- template: `<div><slot name="suggestions"></slot></div>`,
- },
- };
-
- const defaultProps = {
- config: {
- type: 'tag',
- icon: 'tag',
- title: 'Tag name',
- unique: true,
- projectId: '21',
- disabled: false,
- },
- value: {
- data: '',
- },
- cursorPosition: 'start',
- };
-
- const createComponent = (options, data) => {
- wrapper = shallowMount(PipelineTagNameToken, {
- propsData: {
- ...defaultProps,
- },
- data() {
- return {
- ...data,
- };
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- jest.spyOn(Api, 'tags').mockResolvedValue({ data: tags });
-
- createComponent();
- });
-
- it('passes config correctly', () => {
- expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
- });
-
- it('fetches and sets project tags', () => {
- expect(Api.tags).toHaveBeenCalled();
-
- expect(wrapper.vm.tags).toEqual(mockTagsAfterMap);
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- describe('displays loading icon correctly', () => {
- it('shows loading icon', () => {
- createComponent({ stubs }, { loading: true });
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not show loading icon', () => {
- createComponent({ stubs }, { loading: false });
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('shows tags correctly', () => {
- it('renders all tags', () => {
- createComponent({ stubs }, { tags, loading: false });
-
- expect(findAllFilteredSearchSuggestions()).toHaveLength(tags.length);
- });
-
- it('renders only the tag searched for', () => {
- const mockTags = ['main-tag'];
- createComponent({ stubs }, { tags: mockTags, loading: false });
-
- expect(findAllFilteredSearchSuggestions()).toHaveLength(mockTags.length);
- });
- });
-});
diff --git a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js b/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
deleted file mode 100644
index e9ec684a350..00000000000
--- a/spec/frontend/pipelines/tokens/pipeline_trigger_author_token_spec.js
+++ /dev/null
@@ -1,99 +0,0 @@
-import { GlFilteredSearchToken, GlFilteredSearchSuggestion, GlLoadingIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { stubComponent } from 'helpers/stub_component';
-import Api from '~/api';
-import PipelineTriggerAuthorToken from '~/pipelines/components/pipelines_list/tokens/pipeline_trigger_author_token.vue';
-import { users } from '../mock_data';
-
-describe('Pipeline Trigger Author Token', () => {
- let wrapper;
-
- const findFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchToken);
- const findAllFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- const defaultProps = {
- config: {
- type: 'username',
- icon: 'user',
- title: 'Trigger author',
- dataType: 'username',
- unique: true,
- triggerAuthors: users,
- },
- value: {
- data: '',
- },
- cursorPosition: 'start',
- };
-
- const createComponent = (data) => {
- wrapper = shallowMount(PipelineTriggerAuthorToken, {
- propsData: {
- ...defaultProps,
- },
- data() {
- return {
- ...data,
- };
- },
- stubs: {
- GlFilteredSearchToken: stubComponent(GlFilteredSearchToken, {
- template: `<div><slot name="suggestions"></slot></div>`,
- }),
- },
- });
- };
-
- beforeEach(() => {
- jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
-
- createComponent();
- });
-
- it('passes config correctly', () => {
- expect(findFilteredSearchToken().props('config')).toEqual(defaultProps.config);
- });
-
- it('fetches and sets project users', () => {
- expect(Api.projectUsers).toHaveBeenCalled();
-
- expect(wrapper.vm.users).toEqual(users);
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- describe('displays loading icon correctly', () => {
- it('shows loading icon', () => {
- createComponent({ loading: true });
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
-
- it('does not show loading icon', () => {
- createComponent({ loading: false });
-
- expect(findLoadingIcon().exists()).toBe(false);
- });
- });
-
- describe('shows trigger authors correctly', () => {
- beforeEach(() => {});
-
- it('renders all trigger authors', () => {
- createComponent({ users, loading: false });
-
- // should have length of all users plus the static 'Any' option
- expect(findAllFilteredSearchSuggestions()).toHaveLength(users.length + 1);
- });
-
- it('renders only the trigger author searched for', () => {
- createComponent({
- users: [{ name: 'Arnold', username: 'admin', state: 'active', avatar_url: 'avatar-link' }],
- loading: false,
- });
-
- expect(findAllFilteredSearchSuggestions()).toHaveLength(2);
- });
- });
-});
diff --git a/spec/frontend/pipelines/unwrapping_utils_spec.js b/spec/frontend/pipelines/unwrapping_utils_spec.js
deleted file mode 100644
index a6ce7d4049f..00000000000
--- a/spec/frontend/pipelines/unwrapping_utils_spec.js
+++ /dev/null
@@ -1,127 +0,0 @@
-import {
- unwrapGroups,
- unwrapNodesWithName,
- unwrapStagesWithNeeds,
-} from '~/pipelines/components/unwrapping_utils';
-
-const groupsArray = [
- {
- name: 'build_a',
- size: 1,
- status: {
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- },
- {
- name: 'bob_the_build',
- size: 1,
- status: {
- label: 'passed',
- group: 'success',
- icon: 'status_success',
- },
- },
-];
-
-const basicStageInfo = {
- name: 'center_stage',
- status: {
- action: null,
- },
-};
-
-const stagesAndGroups = [
- {
- ...basicStageInfo,
- groups: {
- nodes: groupsArray,
- },
- },
-];
-
-const needArray = [
- {
- name: 'build_b',
- },
-];
-
-const elephantArray = [
- {
- name: 'build_b',
- elephant: 'gray',
- },
-];
-
-const baseJobs = {
- name: 'test_d',
- status: {
- icon: 'status_success',
- tooltip: null,
- hasDetails: true,
- detailsPath: '/root/abcd-dag/-/pipelines/162',
- group: 'success',
- action: null,
- },
-};
-
-const jobArrayWithNeeds = [
- {
- ...baseJobs,
- needs: {
- nodes: needArray,
- },
- },
-];
-
-const jobArrayWithElephant = [
- {
- ...baseJobs,
- needs: {
- nodes: elephantArray,
- },
- },
-];
-
-const completeMock = [
- {
- ...basicStageInfo,
- groups: {
- nodes: groupsArray.map((group) => ({ ...group, jobs: { nodes: jobArrayWithNeeds } })),
- },
- },
-];
-
-describe('Shared pipeline unwrapping utils', () => {
- describe('unwrapGroups', () => {
- it('takes stages without nodes and returns the unwrapped groups', () => {
- expect(unwrapGroups(stagesAndGroups)[0].node.groups).toEqual(groupsArray);
- });
-
- it('keeps other stage properties intact', () => {
- expect(unwrapGroups(stagesAndGroups)[0].node).toMatchObject(basicStageInfo);
- });
- });
-
- describe('unwrapNodesWithName', () => {
- it('works with no field argument', () => {
- expect(unwrapNodesWithName(jobArrayWithNeeds, 'needs')[0].needs).toEqual([needArray[0].name]);
- });
-
- it('works with custom field argument', () => {
- expect(unwrapNodesWithName(jobArrayWithElephant, 'needs', 'elephant')[0].needs).toEqual([
- elephantArray[0].elephant,
- ]);
- });
- });
-
- describe('unwrapStagesWithNeeds', () => {
- it('removes nodes from groups, jobs, and needs', () => {
- const firstProcessedGroup = unwrapStagesWithNeeds(completeMock)[0].groups[0];
- expect(firstProcessedGroup).toMatchObject(groupsArray[0]);
- expect(firstProcessedGroup.jobs[0]).toMatchObject(baseJobs);
- expect(firstProcessedGroup.jobs[0].needs[0]).toBe(needArray[0].name);
- });
- });
-});
diff --git a/spec/frontend/pipelines/utils_spec.js b/spec/frontend/pipelines/utils_spec.js
deleted file mode 100644
index 286d79edc6c..00000000000
--- a/spec/frontend/pipelines/utils_spec.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import mockPipelineResponse from 'test_fixtures/pipelines/pipeline_details.json';
-import { createSankey } from '~/pipelines/components/dag/drawing_utils';
-import {
- makeLinksFromNodes,
- filterByAncestors,
- generateColumnsFromLayersListBare,
- keepLatestDownstreamPipelines,
- listByLayers,
- parseData,
- removeOrphanNodes,
- getMaxNodes,
-} from '~/pipelines/components/parsing_utils';
-import { createNodeDict } from '~/pipelines/utils';
-
-import { mockDownstreamPipelinesRest } from '../vue_merge_request_widget/mock_data';
-import { mockDownstreamPipelinesGraphql } from '../commit/mock_data';
-import { mockParsedGraphQLNodes, missingJob } from './components/dag/mock_data';
-import { generateResponse } from './graph/mock_data';
-
-describe('DAG visualization parsing utilities', () => {
- const nodeDict = createNodeDict(mockParsedGraphQLNodes);
- const unfilteredLinks = makeLinksFromNodes(mockParsedGraphQLNodes, nodeDict);
- const parsed = parseData(mockParsedGraphQLNodes);
-
- describe('makeLinksFromNodes', () => {
- it('returns the expected link structure', () => {
- expect(unfilteredLinks[0]).toHaveProperty('source', 'build_a');
- expect(unfilteredLinks[0]).toHaveProperty('target', 'test_a');
- expect(unfilteredLinks[0]).toHaveProperty('value', 10);
- });
-
- it('does not generate a link for non-existing jobs', () => {
- const sources = unfilteredLinks.map(({ source }) => source);
-
- expect(sources.includes(missingJob)).toBe(false);
- });
- });
-
- describe('filterByAncestors', () => {
- const allLinks = [
- { source: 'job1', target: 'job4' },
- { source: 'job1', target: 'job2' },
- { source: 'job2', target: 'job4' },
- ];
-
- const dedupedLinks = [
- { source: 'job1', target: 'job2' },
- { source: 'job2', target: 'job4' },
- ];
-
- const nodeLookup = {
- job1: {
- name: 'job1',
- },
- job2: {
- name: 'job2',
- needs: ['job1'],
- },
- job4: {
- name: 'job4',
- needs: ['job1', 'job2'],
- category: 'build',
- },
- };
-
- it('dedupes links', () => {
- expect(filterByAncestors(allLinks, nodeLookup)).toMatchObject(dedupedLinks);
- });
- });
-
- describe('parseData parent function', () => {
- it('returns an object containing a list of nodes and links', () => {
- // an array of nodes exist and the values are defined
- expect(parsed).toHaveProperty('nodes');
- expect(Array.isArray(parsed.nodes)).toBe(true);
- expect(parsed.nodes.filter(Boolean)).not.toHaveLength(0);
-
- // an array of links exist and the values are defined
- expect(parsed).toHaveProperty('links');
- expect(Array.isArray(parsed.links)).toBe(true);
- expect(parsed.links.filter(Boolean)).not.toHaveLength(0);
- });
- });
-
- describe('removeOrphanNodes', () => {
- it('removes sankey nodes that have no needs and are not needed', () => {
- const layoutSettings = {
- width: 200,
- height: 200,
- nodeWidth: 10,
- nodePadding: 20,
- paddingForLabels: 100,
- };
-
- const sankeyLayout = createSankey(layoutSettings)(parsed);
- const cleanedNodes = removeOrphanNodes(sankeyLayout.nodes);
- /*
- These lengths are determined by the mock data.
- If the data changes, the numbers may also change.
- */
- expect(parsed.nodes).toHaveLength(mockParsedGraphQLNodes.length);
- expect(cleanedNodes).toHaveLength(12);
- });
- });
-
- describe('getMaxNodes', () => {
- it('returns the number of nodes in the most populous generation', () => {
- const layerNodes = [
- { layer: 0 },
- { layer: 0 },
- { layer: 1 },
- { layer: 1 },
- { layer: 0 },
- { layer: 3 },
- { layer: 2 },
- { layer: 4 },
- { layer: 1 },
- { layer: 3 },
- { layer: 4 },
- ];
- expect(getMaxNodes(layerNodes)).toBe(3);
- });
- });
-
- describe('generateColumnsFromLayersList', () => {
- const pipeline = generateResponse(mockPipelineResponse, 'root/fungi-xoxo');
- const { pipelineLayers } = listByLayers(pipeline);
- const columns = generateColumnsFromLayersListBare(pipeline, pipelineLayers);
-
- it('returns stage-like objects with default name, id, and status', () => {
- columns.forEach((col, idx) => {
- expect(col).toMatchObject({
- name: '',
- status: { action: null },
- id: `layer-${idx}`,
- });
- });
- });
-
- it('creates groups that match the list created in listByLayers', () => {
- columns.forEach((col, idx) => {
- const groupNames = col.groups.map(({ name }) => name);
- expect(groupNames).toEqual(pipelineLayers[idx]);
- });
- });
-
- it('looks up the correct group object', () => {
- columns.forEach((col) => {
- col.groups.forEach((group) => {
- const groupStage = pipeline.stages.find((el) => el.name === group.stageName);
- const groupObject = groupStage.groups.find((el) => el.name === group.name);
- expect(group).toBe(groupObject);
- });
- });
- });
- });
-});
-
-describe('linked pipeline utilities', () => {
- describe('keepLatestDownstreamPipelines', () => {
- it('filters data from GraphQL', () => {
- const downstream = mockDownstreamPipelinesGraphql().nodes;
- const latestDownstream = keepLatestDownstreamPipelines(downstream);
-
- expect(downstream).toHaveLength(3);
- expect(latestDownstream).toHaveLength(1);
- });
-
- it('filters data from REST', () => {
- const downstream = mockDownstreamPipelinesRest();
- const latestDownstream = keepLatestDownstreamPipelines(downstream);
-
- expect(downstream).toHaveLength(2);
- expect(latestDownstream).toHaveLength(1);
- });
-
- it('returns downstream pipelines if sourceJob.retried is null', () => {
- const downstream = mockDownstreamPipelinesGraphql({ includeSourceJobRetried: false }).nodes;
- const latestDownstream = keepLatestDownstreamPipelines(downstream);
-
- expect(latestDownstream).toHaveLength(downstream.length);
- });
-
- it('returns downstream pipelines if source_job.retried is null', () => {
- const downstream = mockDownstreamPipelinesRest({ includeSourceJobRetried: false });
- const latestDownstream = keepLatestDownstreamPipelines(downstream);
-
- expect(latestDownstream).toHaveLength(downstream.length);
- });
- });
-});