summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2019-11-22 15:06:39 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2019-11-22 15:06:39 +0000
commit68b6846fa6c7b630cc8dab7a8474dcc34e4d67d4 (patch)
treef592f2a5fed915184154ffd05e4e44298a192207
parent4db9eeb44af5644eb1d080b4ccf4aff8b90656b9 (diff)
downloadgitlab-ce-68b6846fa6c7b630cc8dab7a8474dcc34e4d67d4.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/assets/javascripts/pipelines/stores/test_reports/utils.js2
-rw-r--r--app/models/concerns/issuable.rb2
-rw-r--r--app/models/epic.rb2
-rw-r--r--app/models/project_services/prometheus_service.rb20
-rw-r--r--app/services/issuable/clone/attributes_rewriter.rb8
-rw-r--r--changelogs/unreleased/34157-apm_snowplow_events.yml5
-rw-r--r--changelogs/unreleased/36611-gitlab-container-registry-repository-names-regex-is-not-at-parity-w.yml5
-rw-r--r--changelogs/unreleased/remove_milestone_id_from_epics.yml5
-rw-r--r--config/pseudonymizer.yml1
-rw-r--r--doc/api/graphql/getting_started.md2
-rw-r--r--doc/user/application_security/sast/analyzers.md37
-rw-r--r--doc/user/application_security/sast/index.md44
-rw-r--r--lib/gitlab/database_importers/self_monitoring/project/create_service.rb3
-rw-r--r--lib/gitlab/regex.rb2
-rw-r--r--package.json2
-rw-r--r--spec/controllers/projects/tags_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb3
-rw-r--r--spec/features/projects/tags/user_edits_tags_spec.rb12
-rw-r--r--spec/features/tags/developer_deletes_tag_spec.rb8
-rw-r--r--spec/features/tags/developer_updates_tag_spec.rb4
-rw-r--r--spec/finders/tags_finder_spec.rb27
-rw-r--r--spec/frontend/fixtures/test_report.rb30
-rw-r--r--spec/frontend/pipelines/test_reports/mock_data.js113
-rw-r--r--spec/frontend/pipelines/test_reports/stores/actions_spec.js4
-rw-r--r--spec/frontend/pipelines/test_reports/stores/getters_spec.js22
-rw-r--r--spec/frontend/pipelines/test_reports/stores/mutations_spec.js12
-rw-r--r--spec/frontend/pipelines/test_reports/test_reports_spec.js4
-rw-r--r--spec/frontend/pipelines/test_reports/test_suite_table_spec.js12
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_spec.js10
-rw-r--r--spec/frontend/pipelines/test_reports/test_summary_table_spec.js6
-rw-r--r--spec/frontend/vue_shared/components/time_ago_tooltip_spec.js48
-rw-r--r--spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb110
-rw-r--r--spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb6
-rw-r--r--spec/lib/gitlab/regex_spec.rb6
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb24
-rw-r--r--spec/models/repository_spec.rb8
-rw-r--r--spec/requests/api/tags_spec.rb10
-rw-r--r--yarn.lock8
38 files changed, 394 insertions, 235 deletions
diff --git a/app/assets/javascripts/pipelines/stores/test_reports/utils.js b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
index 95466587d6b..16fa6935cbe 100644
--- a/app/assets/javascripts/pipelines/stores/test_reports/utils.js
+++ b/app/assets/javascripts/pipelines/stores/test_reports/utils.js
@@ -1,7 +1,7 @@
import { TestStatus } from '~/pipelines/constants';
import { formatTime, secondsToMilliseconds } from '~/lib/utils/datetime_utility';
-function iconForTestStatus(status) {
+export function iconForTestStatus(status) {
switch (status) {
case 'success':
return 'status_success_borderless';
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 6ea12e1cd59..71338fedbe9 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -173,7 +173,7 @@ module Issuable
private
def milestone_is_valid
- errors.add(:milestone_id, message: "is invalid") if milestone_id.present? && !milestone_available?
+ errors.add(:milestone_id, message: "is invalid") if respond_to?(:milestone_id) && milestone_id.present? && !milestone_available?
end
def description_max_length_for_new_records_is_valid
diff --git a/app/models/epic.rb b/app/models/epic.rb
index 46723462590..01ef8bd100e 100644
--- a/app/models/epic.rb
+++ b/app/models/epic.rb
@@ -3,6 +3,8 @@
# Placeholder class for model that is implemented in EE
# It reserves '&' as a reference prefix, but the table does not exists in CE
class Epic < ApplicationRecord
+ self.ignored_columns += %i[milestone_id]
+
def self.link_reference_pattern
nil
end
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index a0273fe0e5a..3e0606fd34a 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -22,6 +22,8 @@ class PrometheusService < MonitoringService
after_save :clear_reactive_cache!
+ after_commit :track_events
+
def initialize_properties
if properties.nil?
self.properties = {}
@@ -116,4 +118,22 @@ class PrometheusService < MonitoringService
true
end
+
+ def track_events
+ if enabled_manual_prometheus?
+ Gitlab::Tracking.event('cluster:services:prometheus', 'enabled_manual_prometheus')
+ elsif disabled_manual_prometheus?
+ Gitlab::Tracking.event('cluster:services:prometheus', 'disabled_manual_prometheus')
+ end
+
+ true
+ end
+
+ def enabled_manual_prometheus?
+ manual_configuration_changed? && manual_configuration?
+ end
+
+ def disabled_manual_prometheus?
+ manual_configuration_changed? && !manual_configuration?
+ end
end
diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb
index 10c89c62bf1..1f5d83917cc 100644
--- a/app/services/issuable/clone/attributes_rewriter.rb
+++ b/app/services/issuable/clone/attributes_rewriter.rb
@@ -10,7 +10,13 @@ module Issuable
end
def execute
- new_entity.update(milestone: cloneable_milestone, labels: cloneable_labels)
+ update_attributes = { labels: cloneable_labels }
+
+ milestone = cloneable_milestone
+ update_attributes[:milestone] = milestone if milestone.present?
+
+ new_entity.update(update_attributes)
+
copy_resource_label_events
end
diff --git a/changelogs/unreleased/34157-apm_snowplow_events.yml b/changelogs/unreleased/34157-apm_snowplow_events.yml
new file mode 100644
index 00000000000..6dfa7ffce5c
--- /dev/null
+++ b/changelogs/unreleased/34157-apm_snowplow_events.yml
@@ -0,0 +1,5 @@
+---
+title: Add snowplow events for APM
+merge_request: 19463
+author:
+type: added
diff --git a/changelogs/unreleased/36611-gitlab-container-registry-repository-names-regex-is-not-at-parity-w.yml b/changelogs/unreleased/36611-gitlab-container-registry-repository-names-regex-is-not-at-parity-w.yml
new file mode 100644
index 00000000000..b8f2c402d90
--- /dev/null
+++ b/changelogs/unreleased/36611-gitlab-container-registry-repository-names-regex-is-not-at-parity-w.yml
@@ -0,0 +1,5 @@
+---
+title: Update Container Registry naming restrictions to allow for sequential '-'
+merge_request: 20318
+author:
+type: fixed
diff --git a/changelogs/unreleased/remove_milestone_id_from_epics.yml b/changelogs/unreleased/remove_milestone_id_from_epics.yml
new file mode 100644
index 00000000000..f124a01c9dd
--- /dev/null
+++ b/changelogs/unreleased/remove_milestone_id_from_epics.yml
@@ -0,0 +1,5 @@
+---
+title: Remove milestone_id from epics
+merge_request: 20187
+author: Lee Tickett
+type: other
diff --git a/config/pseudonymizer.yml b/config/pseudonymizer.yml
index 1c06366c237..1f4ed9a8421 100644
--- a/config/pseudonymizer.yml
+++ b/config/pseudonymizer.yml
@@ -47,7 +47,6 @@ tables:
epics:
whitelist:
- id
- - milestone_id
- group_id
- author_id
- assignee_id
diff --git a/doc/api/graphql/getting_started.md b/doc/api/graphql/getting_started.md
index cebedf9fd69..aab8c26ae99 100644
--- a/doc/api/graphql/getting_started.md
+++ b/doc/api/graphql/getting_started.md
@@ -97,7 +97,7 @@ query {
### The root node
-Any field defined in [`QueryType`](app/graphql/types/query_type.rb) will be exposed as a root node.
+Any field defined in [`QueryType`](https://gitlab.com/gitlab-org/gitlab/tree/master/app/graphql/types/query_type.rb) will be exposed as a root node.
When retrieving child nodes use:
- the `edges { node { } }` syntax.
diff --git a/doc/user/application_security/sast/analyzers.md b/doc/user/application_security/sast/analyzers.md
index c766ca4cffc..a42cf7f09ff 100644
--- a/doc/user/application_security/sast/analyzers.md
+++ b/doc/user/application_security/sast/analyzers.md
@@ -18,6 +18,7 @@ SAST supports the following official analyzers:
- [`eslint`](https://gitlab.com/gitlab-org/security-products/analyzers/eslint) (ESLint (JavaScript and React))
- [`flawfinder`](https://gitlab.com/gitlab-org/security-products/analyzers/flawfinder) (Flawfinder)
- [`gosec`](https://gitlab.com/gitlab-org/security-products/analyzers/gosec) (Gosec)
+- [`kubesec`](https://gitlab.com/gitlab-org/security-products/analyzers/kubesec) (Kubesec)
- [`nodejs-scan`](https://gitlab.com/gitlab-org/security-products/analyzers/nodejs-scan) (NodeJsScan)
- [`phpcs-security-audit`](https://gitlab.com/gitlab-org/security-products/analyzers/phpcs-security-audit) (PHP CS security-audit)
- [`pmd-apex`](https://gitlab.com/gitlab-org/security-products/analyzers/pmd-apex) (PMD (Apex only))
@@ -116,24 +117,24 @@ Custom analyzers are not spawned automatically when [Docker In Docker](index.md#
## Analyzers Data
-| Property \ Tool | Apex | Bandit | Brakeman | ESLint security | Find Sec Bugs | Flawfinder | Go AST Scanner | NodeJsScan | Php CS Security Audit | Security code Scan (.NET) | TSLint Security | Sobelow |
-| --------------------------------------- | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :---------------------: | :-------------------------: | :-------------: | :----------------: |
-| Severity | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | 𐄂 |
-| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Description | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | 𐄂 | ✓ | ✓ |
-| File | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| Start line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
-| End line | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
-| Start column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | 𐄂 |
-| End column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
-| External id (e.g. CVE) | 𐄂 | 𐄂 | ⚠ | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
-| URLs | ✓ | 𐄂 | ✓ | 𐄂 | ⚠ | 𐄂 | ⚠ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
-| Internal doc/explanation | ✓ | ⚠ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
-| Solution | ✓ | 𐄂 | 𐄂 | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
-| Confidence | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
-| Affected item (e.g. class or package) | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
-| Source code extract | 𐄂 | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
-| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | ✓ |
+| Property \ Tool | Apex | Bandit | Brakeman | ESLint security | Find Sec Bugs | Flawfinder | Go AST Scanner | Kubesec Scanner | NodeJsScan | Php CS Security Audit | Security code Scan (.NET) | Sobelow | TSLint Security |
+| --------------------------------------- | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :------------------: | :---------------------: | :-------------------------: | :----------------: | :-------------: |
+| Severity | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | ✓ |
+| Title | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Description | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | ✓ |
+| File | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
+| Start line | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | ✓ | ✓ |
+| End line | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
+| Start column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | ✓ |
+| End column | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ |
+| External id (e.g. CVE) | 𐄂 | 𐄂 | ⚠ | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| URLs | ✓ | 𐄂 | ✓ | 𐄂 | ⚠ | 𐄂 | ⚠ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Internal doc/explanation | ✓ | ⚠ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
+| Solution | ✓ | 𐄂 | 𐄂 | 𐄂 | ⚠ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Affected item (e.g. class or package) | ✓ | 𐄂 | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Confidence | 𐄂 | ✓ | ✓ | 𐄂 | ✓ | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | ✓ | 𐄂 |
+| Source code extract | 𐄂 | ✓ | ✓ | ✓ | 𐄂 | ✓ | ✓ | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 | 𐄂 |
+| Internal ID | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | 𐄂 | 𐄂 | ✓ | ✓ | ✓ | ✓ |
- ✓ => we have that data
- ⚠ => we have that data but it's partially reliable, or we need to extract it from unstructured content
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index 8b8d578a78e..c0885e27da6 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -73,6 +73,7 @@ The following table shows which languages, package managers and frameworks are s
| Groovy ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 11.3 (Gradle) & 11.9 (Ant, Maven, SBT) |
| Java ([Ant](https://ant.apache.org/), [Gradle](https://gradle.org/), [Maven](https://maven.apache.org/) and [SBT](https://www.scala-sbt.org/)) | [SpotBugs](https://spotbugs.github.io/) with the [find-sec-bugs](https://find-sec-bugs.github.io/) plugin | 10.6 (Maven), 10.8 (Gradle) & 11.9 (Ant, SBT) |
| JavaScript | [ESLint security plugin](https://github.com/nodesecurity/eslint-plugin-security) | 11.8 |
+| Kubernetes manifests | [Kubesec](https://github.com/controlplaneio/kubesec) | 12.6 |
| Node.js | [NodeJsScan](https://github.com/ajinabraham/NodeJsScan) | 11.1 |
| PHP | [phpcs-security-audit](https://github.com/FloeDesignTechnologies/phpcs-security-audit) | 10.8 |
| Python ([pip](https://pip.pypa.io/en/stable/)) | [bandit](https://github.com/PyCQA/bandit) | 10.3 |
@@ -185,6 +186,22 @@ variables:
This will create individual `<analyzer-name>-sast` jobs for each analyzer that runs in your CI/CD pipeline.
+#### Enabling kubesec analyzer
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12752) in GitLab Ultimate 12.6.
+
+When [Docker in Docker is disabled](#disabling-docker-in-docker-for-sast),
+you will need to set `SCAN_KUBERNETES_MANIFESTS` to `"true"` to enable the
+kubesec analyzer. In `.gitlab-ci.yml`, define:
+
+```yaml
+include:
+ template: SAST.gitlab-ci.yml
+
+variables:
+ SCAN_KUBERNETES_MANIFESTS: "true"
+```
+
### Available variables
SAST can be [configured](#customizing-the-sast-settings) using environment variables.
@@ -232,19 +249,20 @@ Timeout variables are not applicable for setups with [disabled Docker In Docker]
Some analyzers can be customized with environment variables.
-| Environment variable | Analyzer | Description |
-|-------------------------|----------|----------|
-| `ANT_HOME` | spotbugs | The `ANT_HOME` environment variable. |
-| `ANT_PATH` | spotbugs | Path to the `ant` executable. |
-| `GRADLE_PATH` | spotbugs | Path to the `gradle` executable. |
-| `JAVA_OPTS` | spotbugs | Additional arguments for the `java` executable. |
-| `JAVA_PATH` | spotbugs | Path to the `java` executable. |
-| `SAST_JAVA_VERSION` | spotbugs | Which Java version to use. Supported versions are `8` and `11`. Defaults to `8`. |
-| `MAVEN_CLI_OPTS` | spotbugs | Additional arguments for the `mvn` or `mvnw` executable. |
-| `MAVEN_PATH` | spotbugs | Path to the `mvn` executable. |
-| `MAVEN_REPO_PATH` | spotbugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
-| `SBT_PATH` | spotbugs | Path to the `sbt` executable. |
-| `FAIL_NEVER` | spotbugs | Set to `1` to ignore compilation failure. |
+| Environment variable | Analyzer | Description |
+|-----------------------------|----------|-------------|
+| `SCAN_KUBERNETES_MANIFESTS` | kubesec | Set to `"true"` to scan Kubernetes manifests when [Docker in Docker](#disabling-docker-in-docker-for-sast) is disabled. |
+| `ANT_HOME` | spotbugs | The `ANT_HOME` environment variable. |
+| `ANT_PATH` | spotbugs | Path to the `ant` executable. |
+| `GRADLE_PATH` | spotbugs | Path to the `gradle` executable. |
+| `JAVA_OPTS` | spotbugs | Additional arguments for the `java` executable. |
+| `JAVA_PATH` | spotbugs | Path to the `java` executable. |
+| `SAST_JAVA_VERSION` | spotbugs | Which Java version to use. Supported versions are `8` and `11`. Defaults to `8`. |
+| `MAVEN_CLI_OPTS` | spotbugs | Additional arguments for the `mvn` or `mvnw` executable. |
+| `MAVEN_PATH` | spotbugs | Path to the `mvn` executable. |
+| `MAVEN_REPO_PATH` | spotbugs | Path to the Maven local repository (shortcut for the `maven.repo.local` property). |
+| `SBT_PATH` | spotbugs | Path to the `sbt` executable. |
+| `FAIL_NEVER` | spotbugs | Set to `1` to ignore compilation failure. |
#### Custom environment variables
diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
index 8cd9694b741..fbf252b7ec3 100644
--- a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
+++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
@@ -29,10 +29,11 @@ module Gitlab
def execute!
result = execute_steps
-
if result[:status] == :success
+ ::Gitlab::Tracking.event("self_monitoring", "project_created")
result
elsif STEPS_ALLOWED_TO_FAIL.include?(result[:last_step])
+ ::Gitlab::Tracking.event("self_monitoring", "project_created")
success
else
raise StandardError, result[:message]
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index e3a434dfe35..d9300da38a5 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -19,7 +19,7 @@ module Gitlab
# See https://github.com/docker/distribution/blob/master/reference/regexp.go.
#
def container_repository_name_regex
- @container_repository_regex ||= %r{\A[a-z0-9]+((?:[._/]|__|[-])[a-z0-9]+)*\Z}
+ @container_repository_regex ||= %r{\A[a-z0-9]+((?:[._/]|__|[-]{0,10})[a-z0-9]+)*\Z}
end
##
diff --git a/package.json b/package.json
index e55b525753a..96d443758d6 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
"@gitlab/ui": "7.15.2",
"@gitlab/visual-review-tools": "1.2.0",
"@sentry/browser": "^5.7.1",
- "@sourcegraph/code-host-integration": "^0.0.13",
+ "@sourcegraph/code-host-integration": "^0.0.14",
"apollo-cache-inmemory": "^1.6.3",
"apollo-client": "^2.6.4",
"apollo-link": "^1.2.11",
diff --git a/spec/controllers/projects/tags_controller_spec.rb b/spec/controllers/projects/tags_controller_spec.rb
index f077b4c99fc..7e5237facf6 100644
--- a/spec/controllers/projects/tags_controller_spec.rb
+++ b/spec/controllers/projects/tags_controller_spec.rb
@@ -13,7 +13,7 @@ describe Projects::TagsController do
end
it 'returns the tags for the page' do
- expect(assigns(:tags).map(&:name)).to eq(['v1.1.0', 'v1.0.0'])
+ expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0')
end
it 'returns releases matching those tags' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index ff0259cd40d..d16201fff5a 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -837,8 +837,7 @@ describe ProjectsController do
get :refs, params: { namespace_id: project.namespace, id: project, sort: 'updated_desc' }
expect(json_response['Branches']).to include('master')
- expect(json_response['Tags'].first).to eq('v1.1.0')
- expect(json_response['Tags'].last).to eq('v1.0.0')
+ expect(json_response['Tags']).to include('v1.0.0')
expect(json_response['Commits']).to be_nil
end
diff --git a/spec/features/projects/tags/user_edits_tags_spec.rb b/spec/features/projects/tags/user_edits_tags_spec.rb
index 63f97eeb4e0..b1cb7685f63 100644
--- a/spec/features/projects/tags/user_edits_tags_spec.rb
+++ b/spec/features/projects/tags/user_edits_tags_spec.rb
@@ -21,23 +21,21 @@ describe 'Project > Tags', :js do
context 'page with tags list' do
it 'shows tag name' do
- page.within first('.tags > .content-list > li') do
- expect(page.find('.row-main-content')).to have_content 'v1.1.0 Version 1.1.0'
- end
+ expect(page).to have_content 'v1.1.0 Version 1.1.0'
end
it 'shows tag edit button' do
- page.within first('.tags > .content-list > li') do
- edit_btn = page.find('.row-fixed-content.controls a.btn-edit')
+ page.within '.tags > .content-list' do
+ edit_btn = page.find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']")
- expect(edit_btn['href']).to have_content '/tags/v1.1.0/release/edit'
+ expect(edit_btn['href']).to end_with("/#{project.full_path}/-/tags/v1.1.0/release/edit")
end
end
end
context 'edit tag release notes' do
before do
- find('.tags > .content-list > li:first-child .row-fixed-content.controls a.btn-edit').click
+ page.find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']").click
end
it 'shows tag name header' do
diff --git a/spec/features/tags/developer_deletes_tag_spec.rb b/spec/features/tags/developer_deletes_tag_spec.rb
index 0fc62a578f9..50eac8ddaed 100644
--- a/spec/features/tags/developer_deletes_tag_spec.rb
+++ b/spec/features/tags/developer_deletes_tag_spec.rb
@@ -17,7 +17,7 @@ describe 'Developer deletes tag' do
it 'deletes the tag' do
expect(page).to have_content 'v1.1.0'
- delete_first_tag
+ delete_tag 'v1.1.0'
expect(page).not_to have_content 'v1.1.0'
end
@@ -46,15 +46,15 @@ describe 'Developer deletes tag' do
end
it 'shows the error message' do
- delete_first_tag
+ delete_tag 'v1.1.0'
expect(page).to have_content('Do not delete tags')
end
end
- def delete_first_tag
+ def delete_tag(tag)
page.within('.content') do
- accept_confirm { first('.btn-remove').click }
+ accept_confirm { find("li > .row-fixed-content.controls a.btn-remove[href='/#{project.full_path}/-/tags/#{tag}']").click }
end
end
end
diff --git a/spec/features/tags/developer_updates_tag_spec.rb b/spec/features/tags/developer_updates_tag_spec.rb
index 0cdd953b9ae..167079c3f31 100644
--- a/spec/features/tags/developer_updates_tag_spec.rb
+++ b/spec/features/tags/developer_updates_tag_spec.rb
@@ -15,9 +15,7 @@ describe 'Developer updates tag' do
context 'from the tags list page' do
it 'updates the release notes' do
- page.within(first('.content-list .controls')) do
- click_link 'Edit release notes'
- end
+ find("li > .row-fixed-content.controls a.btn-edit[href='/#{project.full_path}/-/tags/v1.1.0/release/edit']").click
fill_in 'release_description', with: 'Awesome release notes'
click_button 'Save changes'
diff --git a/spec/finders/tags_finder_spec.rb b/spec/finders/tags_finder_spec.rb
index e9f29ab2441..582d82bbf79 100644
--- a/spec/finders/tags_finder_spec.rb
+++ b/spec/finders/tags_finder_spec.rb
@@ -95,24 +95,25 @@ describe TagsFinder do
end
context 'filter and sort' do
- it 'filters tags by name and sorts by recently_updated' do
- params = { sort: 'updated_desc', search: 'v1' }
- tags_finder = described_class.new(repository, params)
+ let(:tags_to_compare) { %w[v1.0.0 v1.1.0] }
+ subject { described_class.new(repository, params).execute.select { |tag| tags_to_compare.include?(tag.name) } }
- result = tags_finder.execute
+ context 'when sort by updated_desc' do
+ let(:params) { { sort: 'updated_desc', search: 'v1' } }
- expect(result.first.name).to eq('v1.1.0')
- expect(result.count).to eq(2)
+ it 'filters tags by name' do
+ expect(subject.first.name).to eq('v1.1.0')
+ expect(subject.count).to eq(2)
+ end
end
- it 'filters tags by name and sorts by last_updated' do
- params = { sort: 'updated_asc', search: 'v1' }
- tags_finder = described_class.new(repository, params)
-
- result = tags_finder.execute
+ context 'when sort by updated_asc' do
+ let(:params) { { sort: 'updated_asc', search: 'v1' } }
- expect(result.first.name).to eq('v1.0.0')
- expect(result.count).to eq(2)
+ it 'filters tags by name' do
+ expect(subject.first.name).to eq('v1.0.0')
+ expect(subject.count).to eq(2)
+ end
end
end
end
diff --git a/spec/frontend/fixtures/test_report.rb b/spec/frontend/fixtures/test_report.rb
new file mode 100644
index 00000000000..d26bba9b9d0
--- /dev/null
+++ b/spec/frontend/fixtures/test_report.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require "spec_helper"
+
+describe Projects::PipelinesController, "(JavaScript fixtures)", type: :controller do
+ include JavaScriptFixturesHelpers
+
+ let(:namespace) { create(:namespace, name: "frontend-fixtures") }
+ let(:project) { create(:project, :repository, namespace: namespace, path: "pipelines-project") }
+ let(:commit) { create(:commit, project: project) }
+ let(:user) { create(:user, developer_projects: [project], email: commit.author_email) }
+ let(:pipeline) { create(:ci_pipeline, :with_test_reports, project: project, user: user) }
+
+ render_views
+
+ before do
+ sign_in(user)
+ stub_feature_flags(junit_pipeline_view: true)
+ end
+
+ it "pipelines/test_report.json" do
+ get :test_report, params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: pipeline.id
+ }, format: :json
+
+ expect(response).to be_successful
+ end
+end
diff --git a/spec/frontend/pipelines/test_reports/mock_data.js b/spec/frontend/pipelines/test_reports/mock_data.js
index b0f22bc63fb..1d03f0b655f 100644
--- a/spec/frontend/pipelines/test_reports/mock_data.js
+++ b/spec/frontend/pipelines/test_reports/mock_data.js
@@ -1,41 +1,6 @@
-import { formatTime } from '~/lib/utils/datetime_utility';
import { TestStatus } from '~/pipelines/constants';
-export const testCases = [
- {
- classname: 'spec.test_spec',
- execution_time: 0.000748,
- name: 'Test#subtract when a is 1 and b is 2 raises an error',
- stack_trace: null,
- status: TestStatus.SUCCESS,
- system_output: null,
- },
- {
- classname: 'spec.test_spec',
- execution_time: 0.000064,
- name: 'Test#subtract when a is 2 and b is 1 returns correct result',
- stack_trace: null,
- status: TestStatus.SUCCESS,
- system_output: null,
- },
- {
- classname: 'spec.test_spec',
- execution_time: 0.009292,
- name: 'Test#sum when a is 1 and b is 2 returns summary',
- stack_trace: null,
- status: TestStatus.FAILED,
- system_output:
- "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'",
- },
- {
- classname: 'spec.test_spec',
- execution_time: 0.00018,
- name: 'Test#sum when a is 100 and b is 200 returns summary',
- stack_trace: null,
- status: TestStatus.FAILED,
- system_output:
- "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'",
- },
+export default [
{
classname: 'spec.test_spec',
execution_time: 0,
@@ -45,79 +10,3 @@ export const testCases = [
system_output: null,
},
];
-
-export const testCasesFormatted = [
- {
- ...testCases[2],
- icon: 'status_failed_borderless',
- formattedTime: formatTime(testCases[0].execution_time * 1000),
- },
- {
- ...testCases[3],
- icon: 'status_failed_borderless',
- formattedTime: formatTime(testCases[1].execution_time * 1000),
- },
- {
- ...testCases[4],
- icon: 'status_skipped_borderless',
- formattedTime: formatTime(testCases[2].execution_time * 1000),
- },
- {
- ...testCases[0],
- icon: 'status_success_borderless',
- formattedTime: formatTime(testCases[3].execution_time * 1000),
- },
- {
- ...testCases[1],
- icon: 'status_success_borderless',
- formattedTime: formatTime(testCases[4].execution_time * 1000),
- },
-];
-
-export const testSuites = [
- {
- error_count: 0,
- failed_count: 2,
- name: 'rspec:osx',
- skipped_count: 0,
- success_count: 2,
- test_cases: testCases,
- total_count: 4,
- total_time: 60,
- },
- {
- error_count: 0,
- failed_count: 10,
- name: 'rspec:osx',
- skipped_count: 0,
- success_count: 50,
- test_cases: [],
- total_count: 60,
- total_time: 0.010284,
- },
-];
-
-export const testSuitesFormatted = testSuites.map(x => ({
- ...x,
- formattedTime: formatTime(x.total_time * 1000),
-}));
-
-export const testReports = {
- error_count: 0,
- failed_count: 2,
- skipped_count: 0,
- success_count: 2,
- test_suites: testSuites,
- total_count: 4,
- total_time: 0.010284,
-};
-
-export const testReportsWithNoSuites = {
- error_count: 0,
- failed_count: 2,
- skipped_count: 0,
- success_count: 2,
- test_suites: [],
- total_count: 4,
- total_time: 0.010284,
-};
diff --git a/spec/frontend/pipelines/test_reports/stores/actions_spec.js b/spec/frontend/pipelines/test_reports/stores/actions_spec.js
index c1721e12234..d7007eb7631 100644
--- a/spec/frontend/pipelines/test_reports/stores/actions_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/actions_spec.js
@@ -2,10 +2,10 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import * as actions from '~/pipelines/stores/test_reports/actions';
import * as types from '~/pipelines/stores/test_reports/mutation_types';
+import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from '../../../helpers/test_constants';
import testAction from '../../../helpers/vuex_action_helper';
import createFlash from '~/flash';
-import { testReports } from '../mock_data';
jest.mock('~/flash.js');
@@ -13,6 +13,8 @@ describe('Actions TestReports Store', () => {
let mock;
let state;
+ const testReports = getJSONFixture('pipelines/test_report.json');
+
const endpoint = `${TEST_HOST}/test_reports.json`;
const defaultState = {
endpoint,
diff --git a/spec/frontend/pipelines/test_reports/stores/getters_spec.js b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
index e630a005409..cfd0ecdcb30 100644
--- a/spec/frontend/pipelines/test_reports/stores/getters_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/getters_spec.js
@@ -1,9 +1,12 @@
import * as getters from '~/pipelines/stores/test_reports/getters';
-import { testReports, testSuitesFormatted, testCasesFormatted } from '../mock_data';
+import { iconForTestStatus } from '~/pipelines/stores/test_reports/utils';
+import { getJSONFixture } from 'helpers/fixtures';
describe('Getters TestReports Store', () => {
let state;
+ const testReports = getJSONFixture('pipelines/test_report.json');
+
const defaultState = {
testReports,
selectedSuite: testReports.test_suites[0],
@@ -28,7 +31,13 @@ describe('Getters TestReports Store', () => {
it('should return the test suites', () => {
setupState();
- expect(getters.getTestSuites(state)).toEqual(testSuitesFormatted);
+ const suites = getters.getTestSuites(state);
+ const expected = testReports.test_suites.map(x => ({
+ ...x,
+ formattedTime: '00:00:00',
+ }));
+
+ expect(suites).toEqual(expected);
});
it('should return an empty array when testReports is empty', () => {
@@ -42,7 +51,14 @@ describe('Getters TestReports Store', () => {
it('should return the test cases inside the suite', () => {
setupState();
- expect(getters.getSuiteTests(state)).toEqual(testCasesFormatted);
+ const cases = getters.getSuiteTests(state);
+ const expected = testReports.test_suites[0].test_cases.map(x => ({
+ ...x,
+ formattedTime: '00:00:00',
+ icon: iconForTestStatus(x.status),
+ }));
+
+ expect(cases).toEqual(expected);
});
it('should return an empty array when testReports is empty', () => {
diff --git a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
index ad5b7f91163..b891415f705 100644
--- a/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
+++ b/spec/frontend/pipelines/test_reports/stores/mutations_spec.js
@@ -1,10 +1,12 @@
import * as types from '~/pipelines/stores/test_reports/mutation_types';
import mutations from '~/pipelines/stores/test_reports/mutations';
-import { testReports, testSuites } from '../mock_data';
+import { getJSONFixture } from 'helpers/fixtures';
describe('Mutations TestReports Store', () => {
let mockState;
+ const testReports = getJSONFixture('pipelines/test_report.json');
+
const defaultState = {
endpoint: '',
testReports: {},
@@ -27,7 +29,7 @@ describe('Mutations TestReports Store', () => {
describe('set reports', () => {
it('should set testReports', () => {
- const expectedState = Object.assign({}, mockState, { testReports });
+ const expectedState = { ...mockState, testReports };
mutations[types.SET_REPORTS](mockState, testReports);
expect(mockState.testReports).toEqual(expectedState.testReports);
@@ -36,10 +38,10 @@ describe('Mutations TestReports Store', () => {
describe('set selected suite', () => {
it('should set selectedSuite', () => {
- const expectedState = Object.assign({}, mockState, { selectedSuite: testSuites[0] });
- mutations[types.SET_SELECTED_SUITE](mockState, testSuites[0]);
+ const selectedSuite = testReports.test_suites[0];
+ mutations[types.SET_SELECTED_SUITE](mockState, selectedSuite);
- expect(mockState.selectedSuite).toEqual(expectedState.selectedSuite);
+ expect(mockState.selectedSuite).toEqual(selectedSuite);
});
});
diff --git a/spec/frontend/pipelines/test_reports/test_reports_spec.js b/spec/frontend/pipelines/test_reports/test_reports_spec.js
index 4d6422745a9..033c3300098 100644
--- a/spec/frontend/pipelines/test_reports/test_reports_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_reports_spec.js
@@ -1,13 +1,15 @@
import Vuex from 'vuex';
import TestReports from '~/pipelines/components/test_reports/test_reports.vue';
import { shallowMount } from '@vue/test-utils';
-import { testReports } from './mock_data';
import * as actions from '~/pipelines/stores/test_reports/actions';
+import { getJSONFixture } from 'helpers/fixtures';
describe('Test reports app', () => {
let wrapper;
let store;
+ const testReports = getJSONFixture('pipelines/test_report.json');
+
const loadingSpinner = () => wrapper.find('.js-loading-spinner');
const testsDetail = () => wrapper.find('.js-tests-detail');
const noTestsToShow = () => wrapper.find('.js-no-tests-to-show');
diff --git a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
index b4305719ea8..bc5d8647d6a 100644
--- a/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_suite_table_spec.js
@@ -3,18 +3,26 @@ import SuiteTable from '~/pipelines/components/test_reports/test_suite_table.vue
import * as getters from '~/pipelines/stores/test_reports/getters';
import { TestStatus } from '~/pipelines/constants';
import { shallowMount } from '@vue/test-utils';
-import { testSuites, testCases } from './mock_data';
+import { getJSONFixture } from 'helpers/fixtures';
+import skippedTestCases from './mock_data';
describe('Test reports suite table', () => {
let wrapper;
let store;
+ const {
+ test_suites: [testSuite],
+ } = getJSONFixture('pipelines/test_report.json');
+
+ testSuite.test_cases = [...testSuite.test_cases, ...skippedTestCases];
+ const testCases = testSuite.test_cases;
+
const noCasesMessage = () => wrapper.find('.js-no-test-cases');
const allCaseRows = () => wrapper.findAll('.js-case-row');
const findCaseRowAtIndex = index => wrapper.findAll('.js-case-row').at(index);
const findIconForRow = (row, status) => row.find(`.ci-status-icon-${status}`);
- const createComponent = (suite = testSuites[0]) => {
+ const createComponent = (suite = testSuite) => {
store = new Vuex.Store({
state: {
selectedSuite: suite,
diff --git a/spec/frontend/pipelines/test_reports/test_summary_spec.js b/spec/frontend/pipelines/test_reports/test_summary_spec.js
index 19a7755dbdc..864c7b6f4de 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_spec.js
@@ -1,10 +1,14 @@
import Summary from '~/pipelines/components/test_reports/test_summary.vue';
import { mount } from '@vue/test-utils';
-import { testSuites } from './mock_data';
+import { getJSONFixture } from 'helpers/fixtures';
describe('Test reports summary', () => {
let wrapper;
+ const {
+ test_suites: [testSuite],
+ } = getJSONFixture('pipelines/test_report.json');
+
const backButton = () => wrapper.find('.js-back-button');
const totalTests = () => wrapper.find('.js-total-tests');
const failedTests = () => wrapper.find('.js-failed-tests');
@@ -13,7 +17,7 @@ describe('Test reports summary', () => {
const duration = () => wrapper.find('.js-duration');
const defaultProps = {
- report: testSuites[0],
+ report: testSuite,
showBack: false,
};
@@ -72,7 +76,7 @@ describe('Test reports summary', () => {
});
it('displays the correctly formatted duration', () => {
- expect(duration().text()).toBe('00:01:00');
+ expect(duration().text()).toBe('00:00:00');
});
});
});
diff --git a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js b/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
index e7599d5cdbc..7d06d96fe75 100644
--- a/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
+++ b/spec/frontend/pipelines/test_reports/test_summary_table_spec.js
@@ -2,7 +2,7 @@ import Vuex from 'vuex';
import SummaryTable from '~/pipelines/components/test_reports/test_summary_table.vue';
import * as getters from '~/pipelines/stores/test_reports/getters';
import { mount, createLocalVue } from '@vue/test-utils';
-import { testReports, testReportsWithNoSuites } from './mock_data';
+import { getJSONFixture } from 'helpers/fixtures';
const localVue = createLocalVue();
localVue.use(Vuex);
@@ -11,6 +11,8 @@ describe('Test reports summary table', () => {
let wrapper;
let store;
+ const testReports = getJSONFixture('pipelines/test_report.json');
+
const allSuitesRows = () => wrapper.findAll('.js-suite-row');
const noSuitesToShow = () => wrapper.find('.js-no-tests-suites');
@@ -44,7 +46,7 @@ describe('Test reports summary table', () => {
describe('when there are no test suites', () => {
beforeEach(() => {
- createComponent({ testReportsWithNoSuites });
+ createComponent({ test_suites: [] });
});
it('displays the no suites to show message', () => {
diff --git a/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
index 536bb57b946..f1f231c1a29 100644
--- a/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
@@ -1,44 +1,40 @@
-import Vue from 'vue';
-import timeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
describe('Time ago with tooltip component', () => {
- let TimeagoTooltip;
let vm;
- beforeEach(() => {
- TimeagoTooltip = Vue.extend(timeagoTooltip);
- });
+ const buildVm = (propsData = {}) => {
+ vm = shallowMount(TimeAgoTooltip, {
+ attachToDocument: true,
+ sync: false,
+ propsData,
+ localVue: createLocalVue(),
+ });
+ };
+ const timestamp = '2017-05-08T14:57:39.781Z';
afterEach(() => {
- vm.$destroy();
+ vm.destroy();
});
it('should render timeago with a bootstrap tooltip', () => {
- vm = new TimeagoTooltip({
- propsData: {
- time: '2017-05-08T14:57:39.781Z',
- },
- }).$mount();
-
- expect(vm.$el.tagName).toEqual('TIME');
- expect(vm.$el.getAttribute('data-original-title')).toEqual(
- formatDate('2017-05-08T14:57:39.781Z'),
- );
-
+ buildVm({
+ time: timestamp,
+ });
const timeago = getTimeago();
- expect(vm.$el.textContent.trim()).toEqual(timeago.format('2017-05-08T14:57:39.781Z'));
+ expect(vm.attributes('data-original-title')).toEqual(formatDate(timestamp));
+ expect(vm.text()).toEqual(timeago.format(timestamp));
});
it('should render provided html class', () => {
- vm = new TimeagoTooltip({
- propsData: {
- time: '2017-05-08T14:57:39.781Z',
- cssClass: 'foo',
- },
- }).$mount();
+ buildVm({
+ time: timestamp,
+ cssClass: 'foo',
+ });
- expect(vm.$el.classList.contains('foo')).toEqual(true);
+ expect(vm.classes()).toContain('foo');
});
});
diff --git a/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
new file mode 100644
index 00000000000..c2f9930056a
--- /dev/null
+++ b/spec/lib/gitlab/ci/templates/auto_devops_gitlab_ci_yaml_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Auto-DevOps.gitlab-ci.yml' do
+ subject(:template) { Gitlab::Template::GitlabCiYmlTemplate.find('Auto-DevOps') }
+
+ describe 'the created pipeline' do
+ let(:user) { create(:admin) }
+ let(:default_branch) { 'master' }
+ let(:pipeline_branch) { default_branch }
+ let(:project) { create(:project, :custom_repo, files: { 'README.md' => '' }) }
+ let(:service) { Ci::CreatePipelineService.new(project, user, ref: pipeline_branch ) }
+ let(:pipeline) { service.execute!(:push) }
+ let(:build_names) { pipeline.builds.pluck(:name) }
+
+ before do
+ stub_ci_pipeline_yaml_file(template.content)
+ allow_any_instance_of(Ci::BuildScheduleWorker).to receive(:perform).and_return(true)
+ allow(project).to receive(:default_branch).and_return(default_branch)
+ end
+
+ it 'creates a build and a test job' do
+ expect(build_names).to include('build', 'test')
+ end
+
+ context 'when the project has no active cluster' do
+ it 'only creates a build and a test stage' do
+ expect(pipeline.stages_names).to eq(%w(build test))
+ end
+
+ it 'does not create any deployment-related builds' do
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+
+ context 'when the project has an active cluster' do
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp, projects: [project]) }
+
+ before do
+ allow(cluster).to receive(:active?).and_return(true)
+ end
+
+ describe 'deployment-related builds' do
+ context 'on default branch' do
+ it 'does not include rollout jobs besides production' do
+ expect(build_names).to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+
+ context 'when STAGING_ENABLED=1' do
+ before do
+ create(:ci_variable, project: project, key: 'STAGING_ENABLED', value: '1')
+ end
+
+ it 'includes a staging job and a production_manual job' do
+ expect(build_names).not_to include('production')
+ expect(build_names).to include('production_manual')
+ expect(build_names).to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+
+ context 'when CANARY_ENABLED=1' do
+ before do
+ create(:ci_variable, project: project, key: 'CANARY_ENABLED', value: '1')
+ end
+
+ it 'includes a canary job and a production_manual job' do
+ expect(build_names).not_to include('production')
+ expect(build_names).to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).to include('canary')
+ expect(build_names).not_to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+ end
+
+ context 'outside of default branch' do
+ let(:pipeline_branch) { 'patch-1' }
+
+ before do
+ project.repository.create_branch(pipeline_branch)
+ end
+
+ it 'does not include rollout jobs besides review' do
+ expect(build_names).not_to include('production')
+ expect(build_names).not_to include('production_manual')
+ expect(build_names).not_to include('staging')
+ expect(build_names).not_to include('canary')
+ expect(build_names).to include('review')
+ expect(build_names).not_to include(a_string_matching(/rollout \d+%/))
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
index 5b1a17e734d..ee3c99afdf1 100644
--- a/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
+++ b/spec/lib/gitlab/database_importers/self_monitoring/project/create_service_spec.rb
@@ -279,5 +279,11 @@ describe Gitlab::DatabaseImporters::SelfMonitoring::Project::CreateService do
end
end
end
+
+ it "tracks successful install" do
+ expect(Gitlab::Tracking).to receive(:event).with("self_monitoring", "project_created")
+
+ result
+ end
end
end
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 1397add9f5a..c580b46cf8d 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -61,6 +61,12 @@ describe Gitlab::Regex do
it { is_expected.to match('my/image') }
it { is_expected.to match('my/awesome/image-1') }
it { is_expected.to match('my/awesome/image.test') }
+ it { is_expected.to match('my/awesome/image--test') }
+ # docker distribution allows for infinite `-`
+ # https://github.com/docker/distribution/blob/master/reference/regexp.go#L13
+ # but we have a range of 0,10 to add a reasonable limit.
+ it { is_expected.not_to match('my/image-----------test') }
+ it { is_expected.not_to match('my/image-.test') }
it { is_expected.not_to match('.my/image') }
it { is_expected.not_to match('my/image.') }
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index bc22818ede7..73748b39922 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -262,4 +262,28 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end
end
end
+
+ describe '#track_events after_commit callback' do
+ before do
+ allow(service).to receive(:prometheus_available?).and_return(true)
+ end
+
+ context "enabling manual_configuration" do
+ it "tracks enable event" do
+ service.update!(manual_configuration: false)
+
+ expect(Gitlab::Tracking).to receive(:event).with('cluster:services:prometheus', 'enabled_manual_prometheus')
+
+ service.update!(manual_configuration: true)
+ end
+
+ it "tracks disable event" do
+ service.update!(manual_configuration: true)
+
+ expect(Gitlab::Tracking).to receive(:event).with('cluster:services:prometheus', 'disabled_manual_prometheus')
+
+ service.update!(manual_configuration: false)
+ end
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 92450bfdaff..9cb3229aeb1 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -66,14 +66,16 @@ describe Repository do
end
describe 'tags_sorted_by' do
+ let(:tags_to_compare) { %w[v1.0.0 v1.1.0] }
+
context 'name_desc' do
- subject { repository.tags_sorted_by('name_desc').map(&:name) }
+ subject { repository.tags_sorted_by('name_desc').map(&:name) & tags_to_compare }
it { is_expected.to eq(['v1.1.0', 'v1.0.0']) }
end
context 'name_asc' do
- subject { repository.tags_sorted_by('name_asc').map(&:name) }
+ subject { repository.tags_sorted_by('name_asc').map(&:name) & tags_to_compare }
it { is_expected.to eq(['v1.0.0', 'v1.1.0']) }
end
@@ -115,7 +117,7 @@ describe Repository do
context 'annotated tag pointing to a blob' do
let(:annotated_tag_name) { 'annotated-tag' }
- subject { repository.tags_sorted_by('updated_asc').map(&:name) }
+ subject { repository.tags_sorted_by('updated_asc').map(&:name) & (tags_to_compare + [annotated_tag_name]) }
before do
options = { message: 'test tag message\n',
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
index 3c6ec631664..dca87d5e4ce 100644
--- a/spec/requests/api/tags_spec.rb
+++ b/spec/requests/api/tags_spec.rb
@@ -7,6 +7,7 @@ describe API::Tags do
let(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let(:project) { create(:project, :repository, creator: user, path: 'my.project') }
let(:tag_name) { project.repository.find_tag('v1.1.0').name }
+ let(:tag_message) { project.repository.find_tag('v1.1.0').message }
let(:project_id) { project.id }
let(:current_user) { nil }
@@ -75,7 +76,7 @@ describe API::Tags do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers
- expect(json_response.first['name']).to eq(tag_name)
+ expect(json_response.map { |r| r['name'] }).to include(tag_name)
end
context 'when repository is disabled' do
@@ -135,9 +136,10 @@ describe API::Tags do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/tags')
expect(response).to include_pagination_headers
- expect(json_response.first['name']).to eq(tag_name)
- expect(json_response.first['message']).to eq('Version 1.1.0')
- expect(json_response.first['release']['description']).to eq(description)
+
+ expected_tag = json_response.find { |r| r['name'] == tag_name }
+ expect(expected_tag['message']).to eq(tag_message)
+ expect(expected_tag['release']['description']).to eq(description)
end
end
end
diff --git a/yarn.lock b/yarn.lock
index 4f66345fed0..ab929647c4b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -970,10 +970,10 @@
"@sentry/types" "5.7.1"
tslib "^1.9.3"
-"@sourcegraph/code-host-integration@^0.0.13":
- version "0.0.13"
- resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.13.tgz#4fd5fe1e0088c63b2a26be231c5a2a4ca79b1596"
- integrity sha512-IjF9gb9e8dG8p12DKg5Z7UMOVQO/ClH3AyMCPfX/qH7DH/0b55WH6stYVqZu6y776quFonO4Z9gWYM8pQZjzKw==
+"@sourcegraph/code-host-integration@^0.0.14":
+ version "0.0.14"
+ resolved "https://registry.yarnpkg.com/@sourcegraph/code-host-integration/-/code-host-integration-0.0.14.tgz#e12b08371dc37bf4a468450b008c6e167705e1a8"
+ integrity sha512-S4+K+3RKFd49Btl1D9LOdWXROgXevUwOBwp+vDUuGgzT2d6Y+qjalUJ0t8CjbYzdBdJun+2/Zi1+SXfm+S+xVg==
"@types/anymatch@*":
version "1.3.0"