summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--changelogs/unreleased/59729-estimate-quick-action-does-not-produce-correct-time-for-1mo.yml5
-rw-r--r--config/initializers/chronic_duration.rb4
-rw-r--r--doc/administration/database_load_balancing.md6
-rw-r--r--doc/ci/examples/README.md2
-rw-r--r--doc/ci/examples/browser_performance.md157
-rw-r--r--doc/user/application_security/sast/index.md1
-rw-r--r--doc/user/project/merge_requests/browser_performance_testing.md173
-rw-r--r--lib/gitlab/patch/chronic_duration.rb35
-rw-r--r--spec/frontend/environment.js1
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js (renamed from spec/javascripts/monitoring/components/dashboard_spec.js)380
-rw-r--r--spec/frontend/test_setup.js3
-rw-r--r--spec/lib/gitlab/patch/chronic_duration_spec.rb27
-rw-r--r--spec/lib/gitlab/time_tracking_formatter_spec.rb8
13 files changed, 440 insertions, 362 deletions
diff --git a/changelogs/unreleased/59729-estimate-quick-action-does-not-produce-correct-time-for-1mo.yml b/changelogs/unreleased/59729-estimate-quick-action-does-not-produce-correct-time-for-1mo.yml
new file mode 100644
index 00000000000..1962a662179
--- /dev/null
+++ b/changelogs/unreleased/59729-estimate-quick-action-does-not-produce-correct-time-for-1mo.yml
@@ -0,0 +1,5 @@
+---
+title: Fix parsing of months in time tracking commands
+merge_request: 32165
+author:
+type: fixed
diff --git a/config/initializers/chronic_duration.rb b/config/initializers/chronic_duration.rb
index b65b06c813a..aa43eef434c 100644
--- a/config/initializers/chronic_duration.rb
+++ b/config/initializers/chronic_duration.rb
@@ -1 +1,5 @@
+# frozen_string_literal: true
+
ChronicDuration.raise_exceptions = true
+
+ChronicDuration.prepend Gitlab::Patch::ChronicDuration
diff --git a/doc/administration/database_load_balancing.md b/doc/administration/database_load_balancing.md
index 50ccebffe73..f643d853d10 100644
--- a/doc/administration/database_load_balancing.md
+++ b/doc/administration/database_load_balancing.md
@@ -146,7 +146,11 @@ The following options can be set:
| `use_tcp` | Lookup DNS resources using TCP instead of UDP | false |
If `record_type` is set to `SRV`, GitLab will continue to use a round-robin algorithm
-and will ignore the `weight` and `priority` in the record.
+and will ignore the `weight` and `priority` in the record. Since SRV records usually
+return hostnames instead of IPs, GitLab will look for the IPs of returned hostnames
+in the additional section of the SRV response. If no IP is found for a hostname, Gitlab
+will query the configured `nameserver` for ANY record for each such hostname looking for A or AAAA records, eventually
+dropping this hostname from rotation if it can't resolve its IP.
The `interval` value specifies the _minimum_ time between checks. If the A
record has a TTL greater than this value, then service discovery will honor said
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 00995f881da..f9612d0c53d 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -22,7 +22,7 @@ The following table lists examples with step-by-step tutorials that are containe
| Use case | Resource |
|:----------------------------|:---------------------------------------------------------------------------------------------------------------------------|
-| Browser performance testing | [Browser Performance Testing with the Sitespeed.io container](browser_performance.md). |
+| Browser performance testing | [Browser Performance Testing with the Sitespeed.io container](../../user/project/merge_requests/browser_performance_testing.md). |
| Clojure | [Test a Clojure application with GitLab CI/CD](test-clojure-application.md). |
| Deployment with Dpl | [Using `dpl` as deployment tool](deployment/README.md). |
| Elixir | [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md). |
diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md
index 3266e5dc62e..4a73fe2e62c 100644
--- a/doc/ci/examples/browser_performance.md
+++ b/doc/ci/examples/browser_performance.md
@@ -1,158 +1,5 @@
---
-type: howto
+redirect_to: '../../user/project/merge_requests/browser_performance_testing.md#configuring-browser-performance-testing'
---
-# Browser Performance Testing with the sitespeed.io container
-
-NOTE: **Note:**
-The job definition shown below is supported on GitLab 11.5 and later versions.
-It also requires the GitLab Runner 11.5 or later.
-For earlier versions, use the [previous job definitions](#previous-job-definitions).
-
-This example shows how to run the
-[sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/) on
-your code by using GitLab CI/CD and [sitespeed.io](https://www.sitespeed.io)
-using Docker-in-Docker.
-
-First, you need GitLab Runner with
-[docker-in-docker build](../docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor).
-
-Once you set up the Runner, add a new job to `.gitlab-ci.yml` that
-generates the expected report:
-
-```yaml
-performance:
- stage: performance
- image: docker:git
- variables:
- URL: https://example.com
- services:
- - docker:stable-dind
- script:
- - mkdir gitlab-exporter
- - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
- - mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL
- - mv sitespeed-results/data/performance.json performance.json
- artifacts:
- paths:
- - sitespeed-results/
- reports:
- performance: performance.json
-```
-
-The above example will create a `performance` job in your CI/CD pipeline and will run
-sitespeed.io against the webpage you defined in `URL` to gather key metrics.
-The [GitLab plugin](https://gitlab.com/gitlab-org/gl-performance) for
-sitespeed.io is downloaded in order to save the report as a
-[Performance report artifact](../yaml/README.md#artifactsreportsperformance-premium)
-that you can later download and analyze.
-Due to implementation limitations we always take the latest Performance artifact available.
-
-The full HTML sitespeed.io report will also be saved as an artifact, and if you have
-[GitLab Pages](../../user/project/pages/index.md) enabled, it can be viewed
-directly in your browser.
-
-For further customization options for sitespeed.io, including the ability to
-provide a list of URLs to test, please see the
-[Sitespeed.io Configuration](https://www.sitespeed.io/documentation/sitespeed.io/configuration/) documentation.
-
-TIP: **Tip:**
-For [GitLab Premium](https://about.gitlab.com/pricing/) users, key metrics are automatically
-extracted and shown right in the merge request widget.
-[Learn more on Browser Performance Testing in merge requests](../../user/project/merge_requests/browser_performance_testing.md).
-
-## Performance testing on Review Apps
-
-The above CI YML is great for testing against static environments, and it can
-be extended for dynamic environments. There are a few extra steps to take to
-set this up:
-
-1. The `performance` job should run after the dynamic environment has started.
-1. In the `review` job, persist the hostname and upload it as an artifact so
- it's available to the `performance` job (the same can be done for static
- environments like staging and production to unify the code path). Saving it
- as an artifact is as simple as `echo $CI_ENVIRONMENT_URL > environment_url.txt`
- in your job's `script`.
-1. In the `performance` job, read the previous artifact into an environment
- variable, like `$CI_ENVIRONMENT_URL`, and use it to parameterize the test
- URLs.
-1. You can now run the sitespeed.io container against the desired hostname and
- paths.
-
-Your `.gitlab-ci.yml` file would look like:
-
-```yaml
-stages:
- - deploy
- - performance
-
-review:
- stage: deploy
- environment:
- name: review/$CI_COMMIT_REF_SLUG
- url: http://$CI_COMMIT_REF_SLUG.$APPS_DOMAIN
- script:
- - run_deploy_script
- - echo $CI_ENVIRONMENT_URL > environment_url.txt
- artifacts:
- paths:
- - environment_url.txt
- only:
- - branches
- except:
- - master
-
-performance:
- stage: performance
- image: docker:git
- services:
- - docker:stable-dind
- dependencies:
- - review
- script:
- - export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
- - mkdir gitlab-exporter
- - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
- - mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
- - mv sitespeed-results/data/performance.json performance.json
- artifacts:
- paths:
- - sitespeed-results/
- reports:
- performance: performance.json
-```
-
-A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml).
-
-## Previous job definitions
-
-CAUTION: **Caution:**
-Before GitLab 11.5, Performance job and artifact had to be named specifically
-to automatically extract report data and show it in the merge request widget.
-While these old job definitions are still maintained they have been deprecated
-and may be removed in next major release, GitLab 12.0.
-You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
-
-For GitLab 11.4 and earlier, the job should look like:
-
-```yaml
-performance:
- stage: performance
- image: docker:git
- variables:
- URL: https://example.com
- services:
- - docker:stable-dind
- script:
- - mkdir gitlab-exporter
- - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
- - mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL
- - mv sitespeed-results/data/performance.json performance.json
- artifacts:
- paths:
- - performance.json
- - sitespeed-results/
-```
+This document was moved to [another location](../../user/project/merge_requests/browser_performance_testing.md#configuring-browser-performance-testing).
diff --git a/doc/user/application_security/sast/index.md b/doc/user/application_security/sast/index.md
index fbc130689e0..15a21bb82e0 100644
--- a/doc/user/application_security/sast/index.md
+++ b/doc/user/application_security/sast/index.md
@@ -205,6 +205,7 @@ Some analyzers can be customized with environment variables.
| `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). |
diff --git a/doc/user/project/merge_requests/browser_performance_testing.md b/doc/user/project/merge_requests/browser_performance_testing.md
index 49b9826a52a..2339cfa0db8 100644
--- a/doc/user/project/merge_requests/browser_performance_testing.md
+++ b/doc/user/project/merge_requests/browser_performance_testing.md
@@ -4,8 +4,7 @@ type: reference, howto
# Browser Performance Testing **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3507)
-in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3507) in [GitLab Premium](https://about.gitlab.com/pricing/) 10.3.
If your application offers a web interface and you are using
[GitLab CI/CD](../../../ci/README.md), you can quickly determine the performance
@@ -25,18 +24,20 @@ for [additional metrics](https://gitlab.com/gitlab-org/gitlab-ee/issues/4370)
in a future release.
Going a step further, GitLab can show the Performance report right
-in the merge request widget area:
+in the merge request widget area (see below).
## Use cases
For instance, consider the following workflow:
-1. A member of the marketing team is attempting to track engagement by adding a new tool
-1. With browser performance metrics, they see how their changes are impacting the usability of the page for end users
-1. The metrics show that after their changes the performance score of the page has gone down
-1. When looking at the detailed report, they see that the new Javascript library was included in `<head>` which affects loading page speed
-1. They ask a front end developer to help them, who sets the library to load asynchronously
-1. The frontend developer approves the merge request and authorizes its deployment to production
+1. A member of the marketing team is attempting to track engagement by adding a new tool.
+1. With browser performance metrics, they see how their changes are impacting the usability
+ of the page for end users.
+1. The metrics show that after their changes the performance score of the page has gone down.
+1. When looking at the detailed report, they see that the new JavaScript library was
+ included in `<head>` which affects loading page speed.
+1. They ask a front end developer to help them, who sets the library to load asynchronously.
+1. The frontend developer approves the merge request and authorizes its deployment to production.
## How it works
@@ -48,15 +49,165 @@ example on [Testing Browser Performance](../../../ci/examples/browser_performanc
GitLab then checks this report, compares key performance metrics for each page
between the source and target branches, and shows the information right on the merge request.
->**Note:**
+NOTE: **Note:**
If the Performance report doesn't have anything to compare to, no information
will be displayed in the merge request area. That is the case when you add the
Performance job in your `.gitlab-ci.yml` for the very first time.
-Consecutive merge requests will have something to compare to and the Performance
+Consecutive merge requests will have something to compare to, and the Performance
report will be shown properly.
![Performance Widget](img/browser_performance_testing.png)
+## Configuring Browser Performance Testing
+
+NOTE: **Note:**
+The job definition shown below is supported in GitLab 11.5 and later versions.
+It also requires GitLab Runner 11.5 or later. For earlier versions, use the
+[previous job definitions](#previous-job-definitions).
+
+This example shows how to run the [sitespeed.io container](https://hub.docker.com/r/sitespeedio/sitespeed.io/)
+on your code by using GitLab CI/CD and [sitespeed.io](https://www.sitespeed.io)
+using Docker-in-Docker.
+
+First, you need GitLab Runner with
+[docker-in-docker build](../../../ci/docker/using_docker_build.md#use-docker-in-docker-workflow-with-docker-executor).
+
+Once you set up the Runner, add a new job to `.gitlab-ci.yml` that generates the
+expected report:
+
+```yaml
+performance:
+ stage: performance
+ image: docker:git
+ variables:
+ URL: https://example.com
+ services:
+ - docker:stable-dind
+ script:
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - sitespeed-results/
+ reports:
+ performance: performance.json
+```
+
+The above example will create a `performance` job in your CI/CD pipeline and will run
+sitespeed.io against the webpage you defined in `URL` to gather key metrics.
+The [GitLab plugin for sitespeed.io](https://gitlab.com/gitlab-org/gl-performance)
+is downloaded in order to save the report as a [Performance report artifact](../../../ci/yaml/README.md#artifactsreportsperformance-premium)
+that you can later download and analyze. Due to implementation limitations we always
+take the latest Performance artifact available.
+
+The full HTML sitespeed.io report will also be saved as an artifact, and if you have
+[GitLab Pages](../pages/index.md) enabled, it can be viewed directly in your browser.
+
+For further customization options for sitespeed.io, including the ability to provide a
+list of URLs to test, please see the [Sitespeed.io Configuration](https://www.sitespeed.io/documentation/sitespeed.io/configuration/)
+documentation.
+
+TIP: **Tip:**
+Key metrics are automatically extracted and shown in the merge request widget.
+
+### Performance testing on Review Apps
+
+The above CI YML is great for testing against static environments, and it can
+be extended for dynamic environments. There are a few extra steps to take to
+set this up:
+
+1. The `performance` job should run after the dynamic environment has started.
+1. In the `review` job, persist the hostname and upload it as an artifact so
+ it's available to the `performance` job (the same can be done for static
+ environments like staging and production to unify the code path). Saving it
+ as an artifact is as simple as `echo $CI_ENVIRONMENT_URL > environment_url.txt`
+ in your job's `script`.
+1. In the `performance` job, read the previous artifact into an environment
+ variable, like `$CI_ENVIRONMENT_URL`, and use it to parameterize the test
+ URLs.
+1. You can now run the sitespeed.io container against the desired hostname and
+ paths.
+
+Your `.gitlab-ci.yml` file would look like:
+
+```yaml
+stages:
+ - deploy
+ - performance
+
+review:
+ stage: deploy
+ environment:
+ name: review/$CI_COMMIT_REF_SLUG
+ url: http://$CI_COMMIT_REF_SLUG.$APPS_DOMAIN
+ script:
+ - run_deploy_script
+ - echo $CI_ENVIRONMENT_URL > environment_url.txt
+ artifacts:
+ paths:
+ - environment_url.txt
+ only:
+ - branches
+ except:
+ - master
+
+performance:
+ stage: performance
+ image: docker:git
+ services:
+ - docker:stable-dind
+ dependencies:
+ - review
+ script:
+ - export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - sitespeed-results/
+ reports:
+ performance: performance.json
+```
+
+A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml).
+
+### Previous job definitions
+
+CAUTION: **Caution:**
+Before GitLab 11.5, Performance job and artifact had to be named specifically
+to automatically extract report data and show it in the merge request widget.
+While these old job definitions are still maintained they have been deprecated
+and may be removed in next major release, GitLab 12.0.
+You are advised to update your current `.gitlab-ci.yml` configuration to reflect that change.
+
+For GitLab 11.4 and earlier, the job should look like:
+
+```yaml
+performance:
+ stage: performance
+ image: docker:git
+ variables:
+ URL: https://example.com
+ services:
+ - docker:stable-dind
+ script:
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results $URL
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - performance.json
+ - sitespeed-results/
+```
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/lib/gitlab/patch/chronic_duration.rb b/lib/gitlab/patch/chronic_duration.rb
new file mode 100644
index 00000000000..ab3cba3657f
--- /dev/null
+++ b/lib/gitlab/patch/chronic_duration.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+# Fixes a bug where parsing months doesn't take into account
+# the ChronicDuration.days_per_week setting
+#
+# We can remove this when we do a refactor and push upstream in
+# https://gitlab.com/gitlab-org/gitlab-ce/issues/66637
+
+module Gitlab
+ module Patch
+ module ChronicDuration
+ extend ActiveSupport::Concern
+
+ class_methods do
+ def duration_units_seconds_multiplier(unit)
+ return 0 unless duration_units_list.include?(unit)
+
+ case unit
+ when 'months'
+ 3600 * ::ChronicDuration.hours_per_day * ::ChronicDuration.days_per_month
+ else
+ super
+ end
+ end
+
+ # ChronicDuration#output uses 1mo = 4w as the conversion so we do the same here.
+ # We do need to add a special case for the default days_per_week value because
+ # we want to retain existing behavior for the default case
+ def days_per_month
+ ::ChronicDuration.days_per_week == 7 ? 30 : ::ChronicDuration.days_per_week * 4
+ end
+ end
+ end
+ end
+end
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
index 290c0e797cb..a8e42721bf0 100644
--- a/spec/frontend/environment.js
+++ b/spec/frontend/environment.js
@@ -40,6 +40,7 @@ class CustomEnvironment extends JSDOMEnvironment {
this.global.fixturesBasePath = `${ROOT_PATH}/tmp/tests/frontend/fixtures${IS_EE ? '-ee' : ''}`;
this.global.staticFixturesBasePath = `${ROOT_PATH}/spec/frontend/fixtures`;
+ this.global.IS_EE = IS_EE;
// Not yet supported by JSDOM: https://github.com/jsdom/jsdom/issues/317
this.global.document.createRange = () => ({
diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
index 15e41e2fe93..2a8e0240bf9 100644
--- a/spec/javascripts/monitoring/components/dashboard_spec.js
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -1,19 +1,64 @@
import Vue from 'vue';
import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { GlToast } from '@gitlab/ui';
+import { GlToast, GlDropdownItem } from '@gitlab/ui';
import MockAdapter from 'axios-mock-adapter';
import Dashboard from '~/monitoring/components/dashboard.vue';
-import { timeWindows, timeWindowsKeyNames } from '~/monitoring/constants';
+import GraphGroup from '~/monitoring/components/graph_group.vue';
+import EmptyState from '~/monitoring/components/empty_state.vue';
+import { timeWindows } from '~/monitoring/constants';
import * as types from '~/monitoring/stores/mutation_types';
import { createStore } from '~/monitoring/stores';
import axios from '~/lib/utils/axios_utils';
+
+// TODO: replace with dynamic fixture
+// https://gitlab.com/gitlab-org/gitlab-ce/issues/62785
import MonitoringMock, {
metricsGroupsAPIResponse,
mockApiEndpoint,
environmentData,
singleGroupResponse,
dashboardGitResponse,
-} from '../mock_data';
+} from '../../../../spec/javascripts/monitoring/mock_data';
+
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-undef */
+// see https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/32571#note_211860465
+function setupComponentStore(component) {
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.SET_QUERY_RESULT}`,
+ mockedQueryResultPayload,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+}
+
+// Mock imported files while retaining the original behaviour
+// See https://github.com/facebook/jest/issues/936#issuecomment-265074320
+function mockMonitoringUtils() {
+ const original = require.requireActual('~/monitoring/utils');
+ return {
+ ...original, // Pass down all the exported objects
+ getTimeDiff: jest.spyOn(original, 'getTimeDiff'),
+ };
+}
+jest.mock('~/monitoring/utils', () => mockMonitoringUtils());
+const monitoringUtils = require.requireMock('~/monitoring/utils');
+
+function mockUrlUtility() {
+ const original = require.requireActual('~/lib/utils/url_utility');
+ return {
+ ...original, // Pass down all the exported objects
+ getParameterValues: jest.spyOn(original, 'getParameterValues'),
+ };
+}
+jest.mock('~/lib/utils/url_utility', () => mockUrlUtility());
+const urlUtility = require.requireMock('~/lib/utils/url_utility');
const localVue = createLocalVue();
const propsData = {
@@ -83,7 +128,7 @@ describe('Dashboard', () => {
});
it('shows the environment selector', () => {
- expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy();
});
});
@@ -95,7 +140,7 @@ describe('Dashboard', () => {
store,
});
- expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ expect(component.$el.querySelector('#monitor-environments-dropdown')).toBeTruthy();
});
});
@@ -117,47 +162,27 @@ describe('Dashboard', () => {
});
});
- it('hides the legend when showLegend is false', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showLegend: false,
- },
- store,
- });
-
- setTimeout(() => {
- expect(component.showEmptyState).toEqual(false);
- expect(component.$el.querySelector('.legend-group')).toEqual(null);
- expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
- done();
- });
- });
-
it('hides the group panels when showPanels is false', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ const wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
+ sync: false,
+ localVue,
});
-
- setTimeout(() => {
- expect(component.showEmptyState).toEqual(false);
- expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
- expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
+ setImmediate(() => {
+ expect(wrapper.find(EmptyState).exists()).toBe(false);
+ expect(wrapper.find(GraphGroup).exists()).toBe(true);
+ expect(wrapper.find(GraphGroup).props().showPanels).toBe(false);
done();
});
});
- it('renders the environments dropdown with a number of environments', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ it('renders the environments dropdown with a number of environments', () => {
+ const wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
@@ -166,38 +191,30 @@ describe('Dashboard', () => {
store,
});
- component.$store.commit(
+ store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
- component.$store.commit(
+ store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(component.environments.length).toEqual(environmentData.length);
- expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
-
- Array.from(dropdownMenuEnvironments).forEach((value, index) => {
- if (environmentData[index].metrics_path) {
- expect(value).toHaveAttr('href', environmentData[index].metrics_path);
- }
- });
+ Vue.nextTick(() => {
+ const dropdownMenuEnvironments = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
- done();
- })
- .catch(done.fail);
+ expect(environmentData.length).toBeGreaterThan(0);
+ expect(dropdownMenuEnvironments.length).toEqual(environmentData.length);
+ dropdownMenuEnvironments.wrappers.forEach((value, index) => {
+ expect(value.attributes('href')).toEqual(environmentData[index].metrics_path);
+ });
+ });
});
- it('hides the environments dropdown list when there is no environments', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ it('hides the environments dropdown list when there is no environments', () => {
+ const wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
@@ -206,54 +223,48 @@ describe('Dashboard', () => {
store,
});
- component.$store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
- component.$store.commit(
+ const findEnvironmentsDropdownItems = () => wrapper.find('#monitor-environments-wrapper');
+
+ store.commit(`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`, []);
+ store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(dropdownMenuEnvironments.length).toEqual(0);
- done();
- })
- .catch(done.fail);
+ return Vue.nextTick(() => {
+ expect(findEnvironmentsDropdownItems(wrapper).exists()).toEqual(false);
+ });
});
- it('renders the environments dropdown with a single active element', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ it('renders the environments dropdown with a single active element', () => {
+ const wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
},
store,
+ sync: false,
+ localVue,
});
- component.$store.commit(
+ store.commit(
`monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
environmentData,
);
- component.$store.commit(
+ store.commit(
`monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
singleGroupResponse,
);
- Vue.nextTick()
- .then(() => {
- const dropdownItems = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item.active',
- );
+ Vue.nextTick(() => {
+ const activeDropdownMenuEnvironments = wrapper
+ .find('#monitor-environments-dropdown')
+ .findAll(GlDropdownItem)
+ .filter(item => item.attributes('active') === 'true');
- expect(dropdownItems.length).toEqual(1);
- done();
- })
- .catch(done.fail);
+ expect(activeDropdownMenuEnvironments.length).toEqual(1);
+ });
});
it('hides the dropdown', done => {
@@ -277,33 +288,40 @@ describe('Dashboard', () => {
});
it('renders the time window dropdown with a set of options', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ const wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
showPanels: false,
+ sync: false,
},
store,
});
+
const numberOfTimeWindows = Object.keys(timeWindows).length;
- setTimeout(() => {
- const timeWindowDropdown = component.$el.querySelector('.js-time-window-dropdown');
- const timeWindowDropdownEls = component.$el.querySelectorAll(
- '.js-time-window-dropdown .dropdown-item',
- );
+ setImmediate(() => {
+ const timeWindowDropdown = wrapper.find('.js-time-window-dropdown');
+ const timeWindowDropdownEls = wrapper
+ .find('.js-time-window-dropdown')
+ .findAll(GlDropdownItem);
- expect(timeWindowDropdown).not.toBeNull();
+ expect(timeWindowDropdown.exists()).toBe(true);
expect(timeWindowDropdownEls.length).toEqual(numberOfTimeWindows);
done();
});
});
- it('fetches the metrics data with proper time window', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ it('fetches the metrics data with proper time window', () => {
+ jest.spyOn(store, 'dispatch').mockImplementationOnce(() => {});
+
+ store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+
+ shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
@@ -312,75 +330,49 @@ describe('Dashboard', () => {
store,
});
- spyOn(component.$store, 'dispatch').and.stub();
- const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-
- component.$mount();
-
- Vue.nextTick()
- .then(() => {
- expect(component.$store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalled();
-
- done();
- })
- .catch(done.fail);
+ const defaultRange = monitoringUtils.getTimeDiff();
+ return Vue.nextTick().then(() => {
+ expect(store.dispatch).toHaveBeenCalledWith('monitoringDashboard/fetchData', defaultRange);
+ });
});
it('shows a specific time window selected from the url params', done => {
const start = 1564439536;
const end = 1564441336;
- spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
+ monitoringUtils.getTimeDiff.mockReturnValueOnce({
start,
end,
});
- spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
+ urlUtility.getParameterValues.mockImplementationOnce(param => {
if (param === 'start') return [start];
if (param === 'end') return [end];
return [];
});
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
+ const wrapper = shallowMount(DashboardComponent, {
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: false,
+ },
store,
});
- setTimeout(() => {
- const selectedTimeWindow = component.$el.querySelector('.js-time-window-dropdown .active');
-
- expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
- done();
- });
- });
+ setImmediate(() => {
+ const activeTimeWindowItems = wrapper
+ .find('.js-time-window-dropdown')
+ .findAll(GlDropdownItem)
+ .filter(item => item.attributes('active') === 'true');
- it('defaults to the eight hours time window for non valid url parameters', done => {
- spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([
- '<script>alert("XSS")</script>',
- ]);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- });
-
- Vue.nextTick(() => {
- expect(component.selectedTimeWindowKey).toEqual(timeWindowsKeyNames.eightHours);
+ expect(activeTimeWindowItems.length).toEqual(1);
+ expect(activeTimeWindowItems.wrappers[0].text().trim()).toEqual('30 minutes');
done();
});
});
});
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
- // eslint-disable-next-line jasmine/no-disabled-tests
- xdescribe('link to chart', () => {
+ describe('link to chart', () => {
let wrapper;
const currentDashboard = 'TEST_DASHBOARD';
localVue.use(GlToast);
@@ -392,13 +384,13 @@ describe('Dashboard', () => {
wrapper = shallowMount(DashboardComponent, {
localVue,
- sync: false,
- attachToDocument: true,
propsData: { ...propsData, hasMetrics: true, currentDashboard },
store,
});
- setTimeout(done);
+ setImmediate(() => {
+ done();
+ });
});
afterEach(() => {
@@ -437,7 +429,7 @@ describe('Dashboard', () => {
});
it('creates a toast when clicked', () => {
- spyOn(wrapper.vm.$toast, 'show').and.stub();
+ jest.spyOn(wrapper.vm.$toast, 'show').mockImplementation(() => {});
link().vm.$emit('click');
@@ -448,11 +440,6 @@ describe('Dashboard', () => {
describe('when the window resizes', () => {
beforeEach(() => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- jasmine.clock().install();
- });
-
- afterEach(() => {
- jasmine.clock().uninstall();
});
it('sets elWidth to page width when the sidebar is resized', done => {
@@ -473,7 +460,7 @@ describe('Dashboard', () => {
Vue.nextTick()
.then(() => {
- jasmine.clock().tick(1000);
+ jest.advanceTimersByTime(1000);
return Vue.nextTick();
})
.then(() => {
@@ -485,11 +472,12 @@ describe('Dashboard', () => {
});
describe('external dashboard link', () => {
- beforeEach(() => {
+ let wrapper;
+
+ beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
+ wrapper = shallowMount(DashboardComponent, {
propsData: {
...propsData,
hasMetrics: true,
@@ -498,62 +486,54 @@ describe('Dashboard', () => {
externalDashboardUrl: '/mockUrl',
},
store,
+ sync: false,
+ localVue,
});
+
+ setImmediate(done);
});
- it('shows the link', done => {
- setTimeout(() => {
- expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
- 'View full dashboard',
- );
- done();
- });
+ it('shows the link', () => {
+ expect(wrapper.find('.js-external-dashboard-link').text()).toContain('View full dashboard');
});
});
describe('Dashboard dropdown', () => {
- beforeEach(() => {
+ let wrapper;
+
+ beforeEach(done => {
mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
+ wrapper = shallowMount(DashboardComponent, {
+ propsData: { ...propsData, hasMetrics: true, showPanels: false },
store,
+ sync: false,
+ localVue,
});
- component.$store.dispatch('monitoringDashboard/setFeatureFlags', {
- prometheusEndpoint: false,
- multipleDashboardsEnabled: true,
- });
+ setImmediate(() => {
+ store.dispatch('monitoringDashboard/setFeatureFlags', {
+ prometheusEndpoint: false,
+ multipleDashboardsEnabled: true,
+ });
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
- singleGroupResponse,
- );
-
- component.$store.commit(
- `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
- dashboardGitResponse,
- );
- });
-
- it('shows the dashboard dropdown', done => {
- setTimeout(() => {
- const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
+ store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+ store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ singleGroupResponse,
+ );
- expect(dashboardDropdown).not.toEqual(null);
+ store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, dashboardGitResponse);
done();
});
});
+
+ it('shows the dashboard dropdown', () => {
+ expect(wrapper.find('.js-dashboards-dropdown').exists()).toEqual(true);
+ });
});
describe('when downloading metrics data as CSV', () => {
@@ -577,13 +557,25 @@ describe('Dashboard', () => {
const data = mockGraphData.queries[0].result[0].values;
const firstRow = `${data[0][0]},${data[0][1]}`;
- expect(component.csvText(mockGraphData)).toMatch(`^${header}\r\n${firstRow}`);
+ expect(component.csvText(mockGraphData)).toContain(`${header}\r\n${firstRow}`);
});
});
describe('downloadCsv', () => {
- it('produces a link with a Blob', () => {
- expect(component.downloadCsv(mockGraphData)).toContain(`blob:`);
+ let spy;
+
+ beforeEach(() => {
+ spy = jest.spyOn(window.URL, 'createObjectURL');
+ });
+
+ afterEach(() => {
+ spy.mockRestore();
+ });
+
+ it('creates a string containing a URL that represents the object', () => {
+ component.downloadCsv(mockGraphData);
+
+ expect(spy).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index d52aeb1fe6b..e7944441fe1 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -73,6 +73,9 @@ expect.extend(customMatchers);
// Tech debt issue TBD
testUtilsConfig.logModifiedComponents = false;
+// Stub for URL.createObjectURL
+window.URL.createObjectURL = function createObjectURL() {};
+
// Basic stub for MutationObserver
global.MutationObserver = () => ({
disconnect: () => {},
diff --git a/spec/lib/gitlab/patch/chronic_duration_spec.rb b/spec/lib/gitlab/patch/chronic_duration_spec.rb
new file mode 100644
index 00000000000..541037ec1a2
--- /dev/null
+++ b/spec/lib/gitlab/patch/chronic_duration_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Patch::ChronicDuration do
+ subject { ChronicDuration.parse('1mo') }
+
+ it 'uses default conversions' do
+ expect(subject).to eq(2_592_000)
+ end
+
+ context 'with custom conversions' do
+ before do
+ ChronicDuration.hours_per_day = 8
+ ChronicDuration.days_per_week = 5
+ end
+
+ after do
+ ChronicDuration.hours_per_day = 24
+ ChronicDuration.days_per_week = 7
+ end
+
+ it 'uses custom conversions' do
+ expect(subject).to eq(576_000)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/time_tracking_formatter_spec.rb b/spec/lib/gitlab/time_tracking_formatter_spec.rb
index a85d418777f..cfc804c13a7 100644
--- a/spec/lib/gitlab/time_tracking_formatter_spec.rb
+++ b/spec/lib/gitlab/time_tracking_formatter_spec.rb
@@ -17,6 +17,14 @@ describe Gitlab::TimeTrackingFormatter do
it { expect(subject).to eq(-12_000) }
end
+
+ context 'durations with months' do
+ let(:duration_string) { '1mo' }
+
+ it 'uses our custom conversions' do
+ expect(subject).to eq(576_000)
+ end
+ end
end
describe '#output' do