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 {
hasNextPage
hasPreviousPage
startCursor
endCursor
}

View File

@ -54,7 +54,10 @@ export default {
<ci-icon v-else :status="iconStatus" :size="24" />
</div>
<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
}}<span v-if="popoverOptions" class="text-nowrap"
>&nbsp;<popover v-if="popoverOptions" :options="popoverOptions" class="align-top" />

View File

@ -26,7 +26,7 @@ export default {
</script>
<template>
<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
type="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).
- [OpenShift](https://gitlab.com/gitlab-examples/review-apps-openshift).
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
See also the video [Demo: Cloud Native Development with GitLab](https://www.youtube.com/watch?v=jfIyQEwrocw), which includes a Review Apps example.
Other examples of Review Apps:
- <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

View File

@ -20,12 +20,12 @@ that can be reused in different scripts.
Variables are useful for customizing your jobs in GitLab CI/CD.
When you use variables, you don't have to hard-code values.
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/)
shared by GitLab engineers.
- 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.
> For more information about advanced use of GitLab CI/CD:
>
> - <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.
> - <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.
## 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).
- 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:
- 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/)
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/)
from 30 days to under 8 hours with GitLab.
> For some additional information about GitLab CI/CD:
>
> - <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/)
> webcast to learn the benefits of CI/CD and how to measure the results of CI/CD automation.
> - <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.
NOTE: **Note:**
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.
#### 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` 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:
>
> - 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.
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:
>
> - 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/).
## Introduction

View File

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

View File

@ -1,16 +1,6 @@
{
"summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 },
"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",
"summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 },
@ -33,6 +23,16 @@
],
"resolved_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": []
}
]
}