Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-06-09 06:08:43 +00:00
parent 423e5c973b
commit 8288587742
10 changed files with 208 additions and 208 deletions

View File

@ -1,4 +1,6 @@
fragment PageInfo on PageInfo { fragment PageInfo on PageInfo {
hasNextPage hasNextPage
hasPreviousPage
startCursor
endCursor endCursor
} }

View File

@ -54,7 +54,10 @@ export default {
<ci-icon v-else :status="iconStatus" :size="24" /> <ci-icon v-else :status="iconStatus" :size="24" />
</div> </div>
<div class="report-block-list-issue-description"> <div class="report-block-list-issue-description">
<div class="report-block-list-issue-description-text"> <div
class="report-block-list-issue-description-text"
data-testid="test-summary-row-description"
>
{{ summary {{ summary
}}<span v-if="popoverOptions" class="text-nowrap" }}<span v-if="popoverOptions" class="text-nowrap"
>&nbsp;<popover v-if="popoverOptions" :options="popoverOptions" class="align-top" /> >&nbsp;<popover v-if="popoverOptions" :options="popoverOptions" class="align-top" />

View File

@ -26,7 +26,7 @@ export default {
</script> </script>
<template> <template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5"> <div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text"> <div class="report-block-list-issue-description-text" data-testid="test-issue-body-description">
<button <button
type="button" type="button"
class="btn-link btn-blank text-left break-link vulnerability-name-button" class="btn-link btn-blank text-left break-link vulnerability-name-button"

View File

@ -95,8 +95,11 @@ The following are example projects that demonstrate Review App configuration:
- [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx). - [NGINX](https://gitlab.com/gitlab-examples/review-apps-nginx).
- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift). - [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Other examples of Review Apps:
See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example.
- <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
[Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw).
- [Review Apps for Android](https://about.gitlab.com/blog/2020/05/06/how-to-create-review-apps-for-android-with-gitlab-fastlane-and-appetize-dot-io/).
## Route Maps ## Route Maps

View File

@ -20,12 +20,12 @@ that can be reused in different scripts.
Variables are useful for customizing your jobs in GitLab CI/CD. Variables are useful for customizing your jobs in GitLab CI/CD.
When you use variables, you don't have to hard-code values. When you use variables, you don't have to hard-code values.
For more information about advanced use of GitLab CI/CD: > For more information about advanced use of GitLab CI/CD:
>
- Get to productivity faster with these [7 advanced GitLab CI workflow hacks](https://about.gitlab.com/webcast/7cicd-hacks/) > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Get to productivity faster with these [7 advanced GitLab CI workflow hacks](https://about.gitlab.com/webcast/7cicd-hacks/)
shared by GitLab engineers. > shared by GitLab engineers.
- Learn how the Cloud Native Computing Foundation (CNCF) [eliminates the complexity](https://about.gitlab.com/customers/cncf/) > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Learn how the Cloud Native Computing Foundation (CNCF) [eliminates the complexity](https://about.gitlab.com/customers/cncf/)
of managing projects across many cloud providers with GitLab CI/CD. > of managing projects across many cloud providers with GitLab CI/CD.
## Predefined environment variables ## Predefined environment variables

View File

@ -25,13 +25,13 @@ We have complete examples of configuring pipelines:
- For a collection of examples, see [GitLab CI/CD Examples](../examples/README.md). - For a collection of examples, see [GitLab CI/CD Examples](../examples/README.md).
- To see a large `.gitlab-ci.yml` file used in an enterprise, see the [`.gitlab-ci.yml` file for `gitlab`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab-ci.yml). - To see a large `.gitlab-ci.yml` file used in an enterprise, see the [`.gitlab-ci.yml` file for `gitlab`](https://gitlab.com/gitlab-org/gitlab/blob/master/.gitlab-ci.yml).
For some additional information about GitLab CI/CD: > For some additional information about GitLab CI/CD:
>
- Watch the [CI/CD Ease of configuration](https://www.youtube.com/embed/opdLqwz6tcE) video. > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch the [CI/CD Ease of configuration](https://www.youtube.com/embed/opdLqwz6tcE) video.
- Watch the [Making the case for CI/CD in your organization](https://about.gitlab.com/compare/github-actions-alternative/) > - Watch the [Making the case for CI/CD in your organization](https://about.gitlab.com/compare/github-actions-alternative/)
webcast to learn the benefits of CI/CD and how to measure the results of CI/CD automation. > webcast to learn the benefits of CI/CD and how to measure the results of CI/CD automation.
- Learn how [Verizon reduced rebuilds](https://about.gitlab.com/blog/2019/02/14/verizon-customer-story/) > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Learn how [Verizon reduced rebuilds](https://about.gitlab.com/blog/2019/02/14/verizon-customer-story/)
from 30 days to under 8 hours with GitLab. > from 30 days to under 8 hours with GitLab.
NOTE: **Note:** NOTE: **Note:**
If you have a [mirrored repository where GitLab pulls from](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository-starter), If you have a [mirrored repository where GitLab pulls from](../../user/project/repository/repository_mirroring.md#pulling-from-a-remote-repository-starter),
@ -661,6 +661,44 @@ job:
[YAML anchors for `before_script` and `after_script`](#yaml-anchors-for-before_script-and-after_script) are available. [YAML anchors for `before_script` and `after_script`](#yaml-anchors-for-before_script-and-after_script) are available.
#### Coloring script output
Script output can be colored using [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors),
or by running commands or programs that output ANSI escape codes.
For example, using [Bash with color codes](https://misc.flogisoft.com/bash/tip_colors_and_formatting):
```yaml
job:
script:
- echo -e "\e[31mThis text is red,\e[0m but this text isn't\e[31m however this text is red again."
```
You can define the color codes in Shell variables, or even [custom environment variables](../variables/README.md#custom-environment-variables),
which makes the commands easier to read and reusable.
For example, using the same example as above and variables defined in a `before_script`:
```yaml
job:
before_script:
- TXT_RED="\e[31m" && TXT_CLEAR="\e[0m"
script:
- echo -e "${TXT_RED}This text is red,${TXT_CLEAR} but this part isn't${TXT_RED} however this part is again."
- echo "This text is not colored"
```
Or with [PowerShell color codes](https://superuser.com/a/1259916):
```yaml
job:
before_script:
- $esc="$([char]27)"; $TXT_RED="$esc[31m"; $TXT_CLEAR="$esc[0m"
script:
- Write-Host $TXT_RED"This text is red,"$TXT_CLEAR" but this text isn't"$TXT_RED" however this text is red again."
- Write-Host "This text is not colored"
```
### `stage` ### `stage`
`stage` is defined per-job and relies on [`stages`](#stages) which is defined `stage` is defined per-job and relies on [`stages`](#stages) which is defined

View File

@ -24,7 +24,7 @@ can download GitLab's [Git Cheat Sheet](https://about.gitlab.com/images/press/gi
> For more information about the advantages of working with Git and GitLab: > For more information about the advantages of working with Git and GitLab:
> >
> - Watch the [GitLab Source Code Management Walkthrough](https://www.youtube.com/watch?v=wTQ3aXJswtM) video. > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Watch the [GitLab Source Code Management Walkthrough](https://www.youtube.com/watch?v=wTQ3aXJswtM) video.
> - Learn how GitLab became the backbone of [Worldline](https://about.gitlab.com/customers/worldline/)s development environment. > - Learn how GitLab became the backbone of [Worldline](https://about.gitlab.com/customers/worldline/)s development environment.
TIP: **Tip:** TIP: **Tip:**

View File

@ -28,7 +28,7 @@ and access them with direct commit ID. Read more about _[redoing the undo](#redo
> For more information about working with Git and GitLab: > For more information about working with Git and GitLab:
> >
> - Learn why [North Western Mutual chose GitLab](https://youtu.be/kPNMyxKRRoM) for their Enterprise source code management. > - <i class="fa fa-youtube-play youtube" aria-hidden="true"></i>&nbsp;Learn why [North Western Mutual chose GitLab](https://youtu.be/kPNMyxKRRoM) for their Enterprise source code management.
> - Learn how to [get started with Git](https://about.gitlab.com/resources/whitepaper-moving-to-git/). > - Learn how to [get started with Git](https://about.gitlab.com/resources/whitepaper-moving-to-git/).
## Introduction ## Introduction

View File

@ -1,260 +1,214 @@
import Vue from 'vue'; import { mount, createLocalVue } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import Vuex from 'vuex';
import axios from '~/lib/utils/axios_utils'; import GroupedTestReportsApp from '~/reports/components/grouped_test_reports_app.vue';
import state from '~/reports/store/state'; import store from '~/reports/store';
import component from '~/reports/components/grouped_test_reports_app.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
import { failedReport } from '../mock_data/mock_data'; import { failedReport } from '../mock_data/mock_data';
import successTestReports from '../mock_data/no_failures_report.json';
import newFailedTestReports from '../mock_data/new_failures_report.json'; import newFailedTestReports from '../mock_data/new_failures_report.json';
import newErrorsTestReports from '../mock_data/new_errors_report.json'; import newErrorsTestReports from '../mock_data/new_errors_report.json';
import successTestReports from '../mock_data/no_failures_report.json';
import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json'; import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json';
import resolvedFailures from '../mock_data/resolved_failures.json'; import resolvedFailures from '../mock_data/resolved_failures.json';
describe('Grouped Test Reports App', () => { const localVue = createLocalVue();
let vm; localVue.use(Vuex);
let mock;
const Component = Vue.extend(component); describe('Grouped test reports app', () => {
const endpoint = 'endpoint.json';
const Component = localVue.extend(GroupedTestReportsApp);
let wrapper;
let mockStore;
const mountComponent = () => {
wrapper = mount(Component, {
store: mockStore,
localVue,
propsData: {
endpoint,
},
methods: {
fetchReports: () => {},
},
});
};
const setReports = reports => {
mockStore.state.status = reports.status;
mockStore.state.summary = reports.summary;
mockStore.state.reports = reports.suites;
};
const findHeader = () => wrapper.find('[data-testid="report-section-code-text"]');
const findSummaryDescription = () => wrapper.find('[data-testid="test-summary-row-description"]');
const findIssueDescription = () => wrapper.find('[data-testid="test-issue-body-description"]');
const findAllIssueDescriptions = () =>
wrapper.findAll('[data-testid="test-issue-body-description"]');
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mockStore = store();
mountComponent();
}); });
afterEach(() => { afterEach(() => {
vm.$store.replaceState(state()); wrapper.destroy();
vm.$destroy(); wrapper = null;
mock.restore();
}); });
describe('with success result', () => { describe('with success result', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, successTestReports, {}); setReports(successTestReports);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders success summary text', done => { it('renders success summary text', () => {
setImmediate(() => { expect(findHeader().text()).toBe(
expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); 'Test summary contained no changed test results out of 11 total tests',
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( );
'Test summary contained no changed test results out of 11 total tests',
);
expect(vm.$el.textContent).toContain(
'rspec:pg found no changed test results out of 8 total tests',
);
expect(vm.$el.textContent).toContain(
'java ant found no changed test results out of 3 total tests',
);
done();
});
});
});
describe('with 204 result', () => {
beforeEach(() => {
mock.onGet('test_results.json').reply(204, {}, {});
vm = mountComponent(Component, {
endpoint: 'test_results.json',
});
});
it('renders success summary text', done => {
setImmediate(() => {
expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
done();
});
}); });
}); });
describe('with new failed result', () => { describe('with new failed result', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, newFailedTestReports, {}); setReports(newFailedTestReports);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders failed summary text + new badge', done => { it('renders failed summary text', () => {
setImmediate(() => { expect(findHeader().text()).toBe('Test summary contained 2 failed out of 11 total tests');
expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); });
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed out of 11 total tests',
);
expect(vm.$el.textContent).toContain('rspec:pg found 2 failed out of 8 total tests'); it('renders failed test suite', () => {
expect(findSummaryDescription().text()).toContain(
'rspec:pg found 2 failed out of 8 total tests',
);
});
expect(vm.$el.textContent).toContain('New'); it('renders failed issue in list', () => {
expect(vm.$el.textContent).toContain( expect(findIssueDescription().text()).toContain('New');
'java ant found no changed test results out of 3 total tests', expect(findIssueDescription().text()).toContain(
); 'Test#sum when a is 1 and b is 2 returns summary',
done(); );
});
}); });
}); });
describe('with new error result', () => { describe('with new error result', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, newErrorsTestReports, {}); setReports(newErrorsTestReports);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders error summary text + new badge', done => { it('renders error summary text', () => {
setImmediate(() => { expect(findHeader().text()).toBe('Test summary contained 2 errors out of 11 total tests');
expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); });
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 errors out of 11 total tests',
);
expect(vm.$el.textContent).toContain('karma found 2 errors out of 3 total tests'); it('renders error test suite', () => {
expect(findSummaryDescription().text()).toContain(
'karma found 2 errors out of 3 total tests',
);
});
expect(vm.$el.textContent).toContain('New'); it('renders error issue in list', () => {
expect(vm.$el.textContent).toContain( expect(findIssueDescription().text()).toContain('New');
'rspec:pg found no changed test results out of 8 total tests', expect(findIssueDescription().text()).toContain(
); 'Test#sum when a is 1 and b is 2 returns summary',
done(); );
});
}); });
}); });
describe('with mixed results', () => { describe('with mixed results', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, mixedResultsTestReports, {}); setReports(mixedResultsTestReports);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders summary text', done => { it('renders summary text', () => {
setImmediate(() => { expect(findHeader().text()).toBe(
expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); 'Test summary contained 2 failed and 2 fixed test results out of 11 total tests',
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( );
'Test summary contained 2 failed and 2 fixed test results out of 11 total tests', });
);
expect(vm.$el.textContent).toContain( it('renders failed test suite', () => {
'rspec:pg found 1 failed and 2 fixed test results out of 8 total tests', expect(findSummaryDescription().text()).toContain(
); 'rspec:pg found 1 failed and 2 fixed test results out of 8 total tests',
);
});
expect(vm.$el.textContent).toContain('New'); it('renders failed issue in list', () => {
expect(vm.$el.textContent).toContain(' java ant found 1 failed out of 3 total tests'); expect(findIssueDescription().text()).toContain('New');
done(); expect(findIssueDescription().text()).toContain(
}); 'Test#subtract when a is 2 and b is 1 returns correct result',
);
}); });
}); });
describe('with resolved failures and resolved errors', () => { describe('with resolved failures and resolved errors', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, resolvedFailures, {}); setReports(resolvedFailures);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders summary text', done => { it('renders summary text', () => {
setImmediate(() => { expect(findHeader().text()).toBe(
expect(vm.$el.querySelector('.gl-spinner')).toBeNull(); 'Test summary contained 4 fixed test results out of 11 total tests',
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual( );
'Test summary contained 4 fixed test results out of 11 total tests',
);
expect(vm.$el.textContent).toContain(
'rspec:pg found 4 fixed test results out of 8 total tests',
);
done();
});
}); });
it('renders resolved failures', done => { it('renders resolved test suite', () => {
setImmediate(() => { expect(findSummaryDescription().text()).toContain(
expect(vm.$el.querySelector('.report-block-container').textContent).toContain( 'rspec:pg found 4 fixed test results out of 8 total tests',
resolvedFailures.suites[0].resolved_failures[0].name, );
);
expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
resolvedFailures.suites[0].resolved_failures[1].name,
);
done();
});
}); });
it('renders resolved errors', done => { it('renders resolved failures', () => {
setImmediate(() => { expect(findIssueDescription().text()).toContain(
expect(vm.$el.querySelector('.report-block-container').textContent).toContain( resolvedFailures.suites[0].resolved_failures[0].name,
resolvedFailures.suites[0].resolved_errors[0].name, );
); });
expect(vm.$el.querySelector('.report-block-container').textContent).toContain( it('renders resolved errors', () => {
resolvedFailures.suites[0].resolved_errors[1].name, expect(
); findAllIssueDescriptions()
done(); .at(2)
}); .text(),
).toContain(resolvedFailures.suites[0].resolved_errors[0].name);
}); });
}); });
describe('with a report that failed to load', () => { describe('with a report that failed to load', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, failedReport, {}); setReports(failedReport);
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders an error status for the report', done => { it('renders an error status for the report', () => {
setImmediate(() => { const { name } = failedReport.suites[0];
const { name } = failedReport.suites[0];
expect(vm.$el.querySelector('.report-block-list-issue').textContent).toContain( expect(findSummaryDescription().text()).toContain(
`An error occurred while loading ${name} results`, `An error occurred while loading ${name} result`,
); );
done();
});
}); });
}); });
describe('with error', () => { describe('with error', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(500, {}, {}); mockStore.state.isLoading = false;
vm = mountComponent(Component, { mockStore.state.hasError = true;
endpoint: 'test_results.json', mountComponent();
});
}); });
it('renders loading summary text with loading icon', done => { it('renders loading state', () => {
setImmediate(() => { expect(findHeader().text()).toBe('Test summary failed loading results');
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary failed loading results',
);
done();
});
}); });
}); });
describe('while loading', () => { describe('while loading', () => {
beforeEach(() => { beforeEach(() => {
mock.onGet('test_results.json').reply(200, {}, {}); mockStore.state.isLoading = true;
vm = mountComponent(Component, { mountComponent();
endpoint: 'test_results.json',
});
}); });
it('renders loading summary text with loading icon', done => { it('renders loading state', () => {
expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull(); expect(findHeader().text()).toBe('Test summary results are being parsed');
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
setImmediate(() => {
done();
});
}); });
}); });
}); });

View File

@ -1,16 +1,6 @@
{ {
"summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 }, "summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 },
"suites": [ "suites": [
{
"name": "rspec:pg",
"summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
"new_failures": [],
"resolved_failures": [],
"existing_failures": [],
"new_errors": [],
"resolved_errors": [],
"existing_errors": []
},
{ {
"name": "karma", "name": "karma",
"summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 }, "summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 },
@ -33,6 +23,16 @@
], ],
"resolved_errors": [], "resolved_errors": [],
"existing_errors": [] "existing_errors": []
},
{
"name": "rspec:pg",
"summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
"new_failures": [],
"resolved_failures": [],
"existing_failures": [],
"new_errors": [],
"resolved_errors": [],
"existing_errors": []
} }
] ]
} }