Commit d7ce7307 authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent e43077ab
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-not-ee: &if-not-ee
if: '$CI_PROJECT_NAME !~ /^gitlab(-ee)?$/'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-default-refs: &if-default-refs
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
# Make sure to update all the similar patterns in other CI config files if you modify these patterns
.code-backstage-patterns: &code-backstage-patterns
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
- "Dangerfile"
- "danger/**/*"
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
- "doc/README.md" # Some RSpec test rely on this file
.dev-fixtures:rules:ee-and-foss:
rules:
- <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success
.dev-fixtures:rules:ee-only:
rules:
- <<: *if-not-ee
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success
.run-dev-fixtures: .run-dev-fixtures:
extends: extends:
- .only-code-rails-job-base - .default-tags
- .default-retry
- .default-cache
- .default-before_script
- .use-pg9 - .use-pg9
stage: test stage: test
needs: ["setup-test-env"] needs: ["setup-test-env"]
...@@ -13,17 +61,19 @@ ...@@ -13,17 +61,19 @@
SIZE: 0 # number of external projects to fork, requires network connection SIZE: 0 # number of external projects to fork, requires network connection
# SEED_NESTED_GROUPS: "false" # requires network connection # SEED_NESTED_GROUPS: "false" # requires network connection
run-dev-fixtures-foss: run-dev-fixtures:
extends: .run-dev-fixtures extends:
- .run-dev-fixtures
- .dev-fixtures:rules:ee-and-foss
script: script:
- scripts/gitaly-test-spawn - scripts/gitaly-test-spawn
- RAILS_ENV=test bundle exec rake db:seed_fu - RAILS_ENV=test bundle exec rake db:seed_fu
run-dev-fixtures-ee: run-dev-fixtures-ee:
extends: extends:
- .only-ee
- .use-pg9-ee
- .run-dev-fixtures - .run-dev-fixtures
- .dev-fixtures:rules:ee-only
- .use-pg9-ee
script: script:
- scripts/gitaly-test-spawn - scripts/gitaly-test-spawn
- cp ee/db/fixtures/development/* $FIXTURE_PATH - cp ee/db/fixtures/development/* $FIXTURE_PATH
......
.only-master: # Make sure to update all the similar conditions in other CI config files if you modify these conditions
only: .if-not-ee: &if-not-ee
refs: if: '$CI_PROJECT_NAME !~ /^gitlab(-ee)?$/'
- master
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.rake-exec: .if-master-refs: &if-master-refs
if: '$CI_COMMIT_REF_NAME == "master"'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-default-refs: &if-default-refs
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-merge-request: &if-merge-request
if: '$CI_MERGE_REQUEST_IID'
# Make sure to update all the similar patterns in other CI config files if you modify these patterns
.code-backstage-patterns: &code-backstage-patterns
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
- "Dangerfile"
- "danger/**/*"
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
- "doc/README.md" # Some RSpec test rely on this file
# Make sure to update all the similar patterns in other CI config files if you modify these patterns
.code-backstage-qa-patterns: &code-backstage-qa-patterns
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
- "Dangerfile"
- "danger/**/*"
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
- "doc/README.md" # Some RSpec test rely on this file
# QA changes
- ".dockerignore"
- "qa/**/*"
.rails:rules:ee-and-foss:
rules:
- <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success
.rails:rules:default-refs-code-backstage-qa:
rules:
- <<: *if-default-refs
changes: *code-backstage-qa-patterns
when: on_success
.rails:rules:master-refs:
rules:
- <<: *if-master-refs
when: on_success
.rails:rules:master-refs-ee-only:
rules:
- <<: *if-not-ee
when: never
- <<: *if-master-refs
when: on_success
.rails:rules:ee-only:
rules:
- <<: *if-not-ee
when: never
- <<: *if-default-refs
changes: *code-backstage-patterns
when: on_success
.rails:needs:setup-and-assets:
needs:
- job: setup-test-env
artifacts: true
- job: compile-assets pull-cache
artifacts: true
.rails-job-base:
extends: extends:
- .default-tags - .default-tags
- .default-retry - .default-retry
- .default-cache - .default-cache
- .default-only
- .default-before_script - .default-before_script
variables:
SETUP_DB: "false"
script:
- bundle exec rake $CI_JOB_NAME
.only-code-rails-job-base:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-only
- .default-before_script
- .only:changes-code-backstage
.only-code-qa-rails-job-base:
extends:
- .default-tags
- .default-retry
- .default-cache
- .default-only
- .default-before_script
- .only:changes-code-backstage-qa
####################
# ee and foss jobs #
setup-test-env: setup-test-env:
extends: extends:
- .only-code-qa-rails-job-base - .rails-job-base
- .rails:rules:default-refs-code-backstage-qa
- .use-pg9 - .use-pg9
stage: prepare stage: prepare
script: script:
...@@ -50,11 +129,48 @@ setup-test-env: ...@@ -50,11 +129,48 @@ setup-test-env:
cache: cache:
policy: pull-push policy: pull-push
static-analysis:
extends:
- .rails-job-base
- .rails:rules:default-refs-code-backstage-qa
- .rails:needs:setup-and-assets
stage: test
variables:
SETUP_DB: "false"
parallel: 2
script:
- scripts/static-analysis
cache:
key: "debian-stretch-ruby-2.6-and-rubocop"
paths:
- vendor/ruby
- tmp/rubocop_cache
policy: pull-push
downtime_check:
extends:
- .rails-job-base
- .rails:needs:setup-and-assets
stage: test
rules:
- <<: *if-merge-request
changes: *code-backstage-patterns
when: on_success
variables:
SETUP_DB: "false"
script:
- bundle exec rake downtime_check
.rspec-base: .rspec-base:
extends: .only-code-rails-job-base extends: .rails-job-base
stage: test stage: test
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache"] needs:
dependencies: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache"] - job: setup-test-env
artifacts: true
- job: retrieve-tests-metadata
artifacts: true
- job: compile-assets pull-cache
artifacts: true
script: script:
- source scripts/rspec_helpers.sh - source scripts/rspec_helpers.sh
- rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration" - rspec_paralellized_job "--tag ~quarantine --tag ~geo --tag ~level:migration"
...@@ -72,26 +188,22 @@ setup-test-env: ...@@ -72,26 +188,22 @@ setup-test-env:
reports: reports:
junit: junit_rspec.xml junit: junit_rspec.xml
.rspec-base-foss: .rspec-base-quarantine:
extends: [".rspec-base", ".only-ee-as-if-foss"]
needs: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache foss"]
dependencies: ["setup-test-env", "retrieve-tests-metadata", "compile-assets pull-cache foss"]
.rspec-base-pg9:
extends: extends:
- .rspec-base - .rspec-base
- .use-pg9 - .use-pg9
variables:
RSPEC_OPTS: "--tag quarantine -- spec/"
script:
- source scripts/rspec_helpers.sh
- rspec_simple_job "${RSPEC_OPTS}"
allow_failure: true
.rspec-base-pg9-foss: .rspec-base-pg9:
extends:
- .rspec-base-foss
- .use-pg9
.rspec-base-pg10:
extends: extends:
- .rspec-base - .rspec-base
- .use-pg10 - .rails:rules:ee-and-foss
- .only-master - .use-pg9
.rspec-base-migration: .rspec-base-migration:
script: script:
...@@ -104,200 +216,33 @@ rspec migration pg9: ...@@ -104,200 +216,33 @@ rspec migration pg9:
- .rspec-base-migration - .rspec-base-migration
parallel: 5 parallel: 5
rspec migration pg9-foss:
extends:
- .rspec-base-pg9-foss
- .rspec-base-migration
parallel: 5
rspec unit pg9: rspec unit pg9:
extends: .rspec-base-pg9 extends: .rspec-base-pg9
parallel: 20 parallel: 20
rspec unit pg9-foss:
extends: .rspec-base-pg9-foss
parallel: 20
rspec integration pg9: rspec integration pg9:
extends: .rspec-base-pg9 extends: .rspec-base-pg9
parallel: 8 parallel: 8
rspec integration pg9-foss:
extends: .rspec-base-pg9-foss
parallel: 8
rspec system pg9: rspec system pg9:
extends: .rspec-base-pg9 extends: .rspec-base-pg9
parallel: 24 parallel: 24
rspec system pg9-foss:
extends: .rspec-base-pg9-foss
parallel: 24
rspec unit pg10:
extends: .rspec-base-pg10
parallel: 20
rspec integration pg10:
extends: .rspec-base-pg10
parallel: 8
rspec system pg10:
extends: .rspec-base-pg10
parallel: 24
.rspec-ee-base-pg9:
extends:
- .rspec-base
- .only-ee
- .use-pg9-ee
.rspec-ee-base-pg10:
extends:
- .rspec-base
- .only-ee
- .use-pg10-ee
rspec-ee migration pg9:
extends:
- .rspec-ee-base-pg9
- .rspec-base-migration
parallel: 2
rspec-ee unit pg9:
extends: .rspec-ee-base-pg9
parallel: 10
rspec-ee integration pg9:
extends: .rspec-ee-base-pg9
parallel: 4
rspec-ee system pg9:
extends: .rspec-ee-base-pg9
parallel: 6
rspec-ee migration pg10:
extends:
- .rspec-ee-base-pg10
- .rspec-base-migration
- .only-master
parallel: 2
rspec-ee unit pg10:
extends:
- .rspec-ee-base-pg10
- .only-master
parallel: 10
rspec-ee integration pg10:
extends:
- .rspec-ee-base-pg10
- .only-master
parallel: 3
rspec-ee system pg10:
extends:
- .rspec-ee-base-pg10
- .only-master
parallel: 5
.rspec-ee-base-geo:
extends:
- .rspec-base
- .only-ee
script:
- source scripts/rspec_helpers.sh
- scripts/prepare_postgres_fdw.sh
- rspec_paralellized_job "--tag ~quarantine --tag geo"
.rspec-ee-base-geo-pg9:
extends:
- .rspec-ee-base-geo
- .use-pg9-ee
.rspec-ee-base-geo-pg10:
extends:
- .rspec-ee-base-geo
- .use-pg10-ee
rspec-ee unit pg9 geo:
extends: .rspec-ee-base-geo-pg9
parallel: 2
rspec-ee integration pg9 geo:
extends: .rspec-ee-base-geo-pg9
rspec-ee system pg9 geo:
extends: .rspec-ee-base-geo-pg9
rspec-ee unit pg10 geo:
extends: .rspec-ee-base-geo-pg10
parallel: 2
rspec-ee integration pg10 geo:
extends: .rspec-ee-base-geo-pg10
rspec-ee system pg10 geo:
extends: .rspec-ee-base-geo-pg10
rspec quarantine pg9:
extends:
- .rspec-base-pg9
- .only-master
variables:
RSPEC_OPTS: "--tag quarantine -- spec/"
script:
- source scripts/rspec_helpers.sh
- rspec_simple_job "${RSPEC_OPTS}"
allow_failure: true
rspec-ee quarantine pg9:
extends:
- rspec quarantine pg9
- .only-ee
variables:
RSPEC_OPTS: "--tag quarantine -- ee/spec/"
rspec fast_spec_helper: rspec fast_spec_helper:
extends: .rspec-base-pg9 extends: .rspec-base-pg9
script: script:
- bin/rspec spec/fast_spec_helper.rb - bin/rspec spec/fast_spec_helper.rb
static-analysis:
extends: .only-code-qa-rails-job-base
stage: test
needs: ["setup-test-env", "compile-assets pull-cache"]
dependencies: ["setup-test-env", "compile-assets pull-cache"]
variables:
SETUP_DB: "false"
parallel: 2
script:
- scripts/static-analysis
cache:
key: "debian-stretch-ruby-2.6-and-rubocop"
paths:
- vendor/ruby
- tmp/rubocop_cache
policy: pull-push
downtime_check:
extends:
- .rake-exec
- .only:changes-code-backstage
- .except:refs-master-tags-stable-deploy
stage: test
needs: ["setup-test-env"]
dependencies: ["setup-test-env"]
.db-job-base: .db-job-base:
extends: extends:
- .only-code-rails-job-base - .rails-job-base
- .rails:rules:ee-and-foss
- .use-pg9 - .use-pg9
stage: test stage: test
needs: ["setup-test-env"] needs:
dependencies: ["setup-test-env"] - job: setup-test-env
artifacts: true
# DB migration, rollback, and seed jobs
db:migrate:reset: db:migrate:reset:
extends: .db-job-base extends: .db-job-base
script: script:
...@@ -358,12 +303,14 @@ gitlab:setup: ...@@ -358,12 +303,14 @@ gitlab:setup:
- log/development.log - log/development.log
coverage: coverage:
extends: .only-code-rails-job-base extends:
cache: - .rails-job-base
policy: pull - .rails:rules:ee-and-foss
stage: post-test
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
stage: post-test cache:
policy: pull
script: script:
- bundle exec scripts/merge-simplecov - bundle exec scripts/merge-simplecov
- bundle exec scripts/gather-test-memory-data - bundle exec scripts/gather-test-memory-data
...@@ -375,11 +322,181 @@ coverage: ...@@ -375,11 +322,181 @@ coverage:
- coverage/index.html - coverage/index.html
- coverage/assets/ - coverage/assets/
- tmp/memory_test/ - tmp/memory_test/
# ee and foss jobs #
####################
####################
# master-only jobs #
rspec quarantine pg9:
extends:
- .rspec-base-quarantine
- .rails:rules:master-refs
.rspec-base-pg10:
extends:
- .rspec-base
- .rails:rules:master-refs
- .use-pg10
rspec unit pg10:
extends: .rspec-base-pg10
parallel: 20
rspec integration pg10:
extends: .rspec-base-pg10
parallel: 8
rspec system pg10:
extends: .rspec-base-pg10
parallel: 24
# master-only jobs #
####################
#########################
# ee + master-only jobs #
rspec-ee quarantine pg9:
extends:
- .rspec-base-quarantine
- .rails:rules:master-refs-ee-only
variables:
RSPEC_OPTS: "--tag quarantine -- ee/spec/"
rspec-ee migration pg10:
extends:
- .rspec-ee-base-pg10
- .rspec-base-migration
- .rails:rules:master-refs
parallel: 2
rspec-ee unit pg10:
extends:
- .rspec-ee-base-pg10
- .rails:rules:master-refs
parallel: 10
rspec-ee integration pg10:
extends:
- .rspec-ee-base-pg10
- .rails:rules:master-refs
parallel: 3
rspec-ee system pg10:
extends:
- .rspec-ee-base-pg10
- .rails:rules:master-refs
parallel: 5
# ee + master-only jobs #
#########################
#################
# ee-only jobs #
.rspec-base-ee:
extends:
- .rspec-base
- .rails:rules:ee-only
.rspec-base-pg9-as-if-foss:
extends:
- .rspec-base-ee
- .as-if-foss
- .use-pg9
needs:
- job: setup-test-env
artifacts: true
- job: retrieve-tests-metadata
artifacts: true
- job: compile-assets pull-cache foss
artifacts: true
.rspec-ee-base-pg9:
extends:
- .rspec-base-ee
- .use-pg9-ee
.rspec-ee-base-pg10:
extends:
- .rspec-base-ee
- .use-pg10-ee
rspec migration pg9-as-if-foss:
extends:
- .rspec-base-pg9-as-if-foss
- .rspec-base-migration
parallel: 5
rspec unit pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
parallel: 20
rspec integration pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
parallel: 8
rspec system pg9-as-if-foss:
extends: .rspec-base-pg9-as-if-foss
parallel: 24
rspec-ee migration pg9:
extends:
- .rspec-ee-base-pg9
- .rspec-base-migration
parallel: 2
rspec-ee unit pg9:
extends: .rspec-ee-base-pg9
parallel: 10
rspec-ee integration pg9:
extends: .rspec-ee-base-pg9
parallel: 4
rspec-ee system pg9:
extends: .rspec-ee-base-pg9
parallel: 6
.rspec-ee-base-geo:
extends: .rspec-base-ee
script:
- source scripts/rspec_helpers.sh
- scripts/prepare_postgres_fdw.sh
- rspec_paralellized_job "--tag ~quarantine --tag geo"
.rspec-ee-base-geo-pg9:
extends:
- .rspec-ee-base-geo
- .use-pg9-ee
.rspec-ee-base-geo-pg10:
extends:
- .rspec-ee-base-geo
- .use-pg10-ee
rspec-ee unit pg9 geo:
extends: .rspec-ee-base-geo-pg9
parallel: 2
rspec-ee integration pg9 geo:
extends: .rspec-ee-base-geo-pg9
rspec-ee system pg9 geo:
extends: .rspec-ee-base-geo-pg9
rspec-ee unit pg10 geo:
extends: .rspec-ee-base-geo-pg10
parallel: 2
rspec-ee integration pg10 geo:
extends: .rspec-ee-base-geo-pg10
rspec-ee system pg10 geo:
extends: .rspec-ee-base-geo-pg10
db:rollback geo: db:rollback geo:
extends: extends:
- db:rollback - db:rollback
- .only-ee - .rails:rules:ee-only
script: script:
- bundle exec rake geo:db:migrate VERSION=20170627195211 - bundle exec rake geo:db:migrate VERSION=20170627195211
- bundle exec rake geo:db:migrate - bundle exec rake geo:db:migrate
# ee-only jobs #
################
<script>
/**
* This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue`
* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
*/
import FileRow from '~/vue_shared/components/file_row.vue';
export default {
components: {
FileRow,
},
};
</script>
<template>
<file-row v-bind="$attrs" v-on="$listeners" />
</template>
...@@ -3,7 +3,8 @@ import { mapActions, mapGetters, mapState } from 'vuex'; ...@@ -3,7 +3,8 @@ import { mapActions, mapGetters, mapState } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui'; import { GlTooltipDirective } from '@gitlab/ui';
import { s__, sprintf } from '~/locale'; import { s__, sprintf } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import FileRow from '~/vue_shared/components/file_row.vue'; import FileTree from '~/vue_shared/components/file_tree.vue';
import DiffFileRow from './diff_file_row.vue';
import FileRowStats from './file_row_stats.vue'; import FileRowStats from './file_row_stats.vue';
export default { export default {
...@@ -12,7 +13,7 @@ export default { ...@@ -12,7 +13,7 @@ export default {
}, },
components: { components: {
Icon, Icon,
FileRow, FileTree,
}, },
props: { props: {
hideFileStats: { hideFileStats: {
...@@ -61,6 +62,7 @@ export default { ...@@ -61,6 +62,7 @@ export default {
searchPlaceholder: sprintf(s__('MergeRequest|Search files (%{modifier_key}P)'), { searchPlaceholder: sprintf(s__('MergeRequest|Search files (%{modifier_key}P)'), {
modifier_key: /Mac/i.test(navigator.userAgent) ? '' : 'Ctrl+', modifier_key: /Mac/i.test(navigator.userAgent) ? '' : 'Ctrl+',
}), }),
DiffFileRow,
}; };
</script> </script>
...@@ -91,7 +93,7 @@ export default { ...@@ -91,7 +93,7 @@ export default {
</div> </div>
<div :class="{ 'pt-0 tree-list-blobs': !renderTreeList }" class="tree-list-scroll"> <div :class="{ 'pt-0 tree-list-blobs': !renderTreeList }" class="tree-list-scroll">
<template v-if="filteredTreeList.length"> <template v-if="filteredTreeList.length">
<file-row <file-tree
v-for="file in filteredTreeList" v-for="file in filteredTreeList"
:key="file.key" :key="file.key"
:file="file" :file="file"
...@@ -99,6 +101,7 @@ export default { ...@@ -99,6 +101,7 @@ export default {
:hide-extra-on-tree="true" :hide-extra-on-tree="true"
:extra-component="fileRowExtraComponent" :extra-component="fileRowExtraComponent"
:show-changed-icon="true" :show-changed-icon="true"
:file-row-component="$options.DiffFileRow"
@toggleTreeOpen="toggleTreeOpen" @toggleTreeOpen="toggleTreeOpen"
@clickFile="scrollToFile" @clickFile="scrollToFile"
/> />
......
<script>
/**
* This component is an iterative step towards refactoring and simplifying `vue_shared/components/file_row.vue`
* https://gitlab.com/gitlab-org/gitlab/-/merge_requests/23720
*/
import FileRow from '~/vue_shared/components/file_row.vue';
export default {
components: {
FileRow,
},
};
</script>
<template>
<file-row v-bind="$attrs" v-on="$listeners" />
</template>
<script> <script>
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { GlSkeletonLoading } from '@gitlab/ui'; import { GlSkeletonLoading } from '@gitlab/ui';
import FileRow from '~/vue_shared/components/file_row.vue'; import FileTree from '~/vue_shared/components/file_tree.vue';
import IdeFileRow from './ide_file_row.vue';
import NavDropdown from './nav_dropdown.vue'; import NavDropdown from './nav_dropdown.vue';
import FileRowExtra from './file_row_extra.vue'; import FileRowExtra from './file_row_extra.vue';
...@@ -9,7 +10,7 @@ export default { ...@@ -9,7 +10,7 @@ export default {
components: { components: {
GlSkeletonLoading, GlSkeletonLoading,
NavDropdown, NavDropdown,
FileRow, FileTree,
}, },
props: { props: {
viewerType: { viewerType: {
...@@ -36,6 +37,7 @@ export default { ...@@ -36,6 +37,7 @@ export default {
...mapActions(['updateViewer', 'toggleTreeOpen']), ...mapActions(['updateViewer', 'toggleTreeOpen']),
}, },
FileRowExtra, FileRowExtra,
IdeFileRow,
}; };
</script> </script>
...@@ -53,12 +55,13 @@ export default { ...@@ -53,12 +55,13 @@ export default {
</header> </header>
<div class="ide-tree-body h-100"> <div class="ide-tree-body h-100">
<template v-if="currentTree.tree.length"> <template v-if="currentTree.tree.length">
<file-row <file-tree
v-for="file in currentTree.tree" v-for="file in currentTree.tree"
:key="file.key" :key="file.key"
:file="file" :file="file"
:level="0" :level="0"
:extra-component="$options.FileRowExtra" :extra-component="$options.FileRowExtra"
:file-row-component="$options.IdeFileRow"
@toggleTreeOpen="toggleTreeOpen" @toggleTreeOpen="toggleTreeOpen"
/> />
</template> </template>
......
...@@ -195,8 +195,8 @@ export default { ...@@ -195,8 +195,8 @@ export default {
</gl-dropdown-item> </gl-dropdown-item>
<gl-dropdown-item <gl-dropdown-item
v-if="clipboardText" v-if="clipboardText"
ref="copyChartLink"
v-track-event="generateLinkToChartOptions(clipboardText)" v-track-event="generateLinkToChartOptions(clipboardText)"
class="js-chart-link"
:data-clipboard-text="clipboardText" :data-clipboard-text="clipboardText"
@click="showToast(clipboardText)" @click="showToast(clipboardText)"
> >
......
...@@ -62,9 +62,6 @@ export default { ...@@ -62,9 +62,6 @@ export default {
'is-open': this.file.opened, 'is-open': this.file.opened,
}; };
}, },
childFilesLevel() {
return this.file.isHeader ? 0 : this.level + 1;
},
}, },
watch: { watch: {
'file.active': function fileActiveWatch(active) { 'file.active': function fileActiveWatch(active) {
...@@ -131,7 +128,6 @@ export default { ...@@ -131,7 +128,6 @@ export default {
</script> </script>
<template> <template>
<div>
<file-header v-if="file.isHeader" :path="file.path" /> <file-header v-if="file.isHeader" :path="file.path" />
<div <div
v-else v-else
...@@ -165,20 +161,6 @@ export default { ...@@ -165,20 +161,6 @@ export default {
/> />
</div> </div>
</div> </div>
<template v-if="file.opened || file.isHeader">
<file-row
v-for="childFile in file.tree"
:key="childFile.key"
:file="childFile"
:level="childFilesLevel"
:hide-extra-on-tree="hideExtraOnTree"
:extra-component="extraComponent"
:show-changed-icon="showChangedIcon"
@toggleTreeOpen="toggleTreeOpen"
@clickFile="clickedFile"
/>
</template>
</div>
</template> </template>
<style> <style>
......
<script>
export default {
name: 'FileTree',
props: {
fileRowComponent: {
type: Object,
required: true,
},
level: {
type: Number,
required: true,
},
file: {
type: Object,
required: true,
},
},
computed: {
childFilesLevel() {
return this.file.isHeader ? 0 : this.level + 1;
},
},
};
</script>
<template>
<div>
<component
:is="fileRowComponent"
:level="level"
:file="file"
v-bind="$attrs"
v-on="$listeners"
/>
<template v-if="file.opened || file.isHeader">
<file-tree
v-for="childFile in file.tree"
:key="childFile.key"
:file-row-component="fileRowComponent"
:level="childFilesLevel"
:file="childFile"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
</div>
</template>
---
title: Expose issue link type in REST API
merge_request: 24175
author:
type: added
...@@ -14,7 +14,7 @@ class MigrateEpicNotesMentionsToDb < ActiveRecord::Migration[5.2] ...@@ -14,7 +14,7 @@ class MigrateEpicNotesMentionsToDb < ActiveRecord::Migration[5.2]
INDEX_NAME = 'epic_mentions_temp_index' INDEX_NAME = 'epic_mentions_temp_index'
INDEX_CONDITION = "note LIKE '%@%'::text AND notes.noteable_type = 'Epic'" INDEX_CONDITION = "note LIKE '%@%'::text AND notes.noteable_type = 'Epic'"
QUERY_CONDITIONS = "#{INDEX_CONDITION} AND epic_user_mentions.epic_id IS NULL" QUERY_CONDITIONS = "#{INDEX_CONDITION} AND epic_user_mentions.epic_id IS NULL"
JOIN = 'LEFT JOIN epic_user_mentions ON notes.id = epic_user_mentions.note_id' JOIN = 'INNER JOIN epics ON epics.id = notes.noteable_id LEFT JOIN epic_user_mentions ON notes.id = epic_user_mentions.note_id'
class Note < ActiveRecord::Base class Note < ActiveRecord::Base
include EachBatch include EachBatch
......
...@@ -288,10 +288,13 @@ For source installations, edit the `gitlab.yml` and set the Sidekiq ...@@ -288,10 +288,13 @@ For source installations, edit the `gitlab.yml` and set the Sidekiq
## `gitlab-shell.log` ## `gitlab-shell.log`
This file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log` for This file lives in `/var/log/gitlab/gitaly/gitlab-shell.log` for
Omnibus GitLab packages or in `/home/git/gitlab-shell/gitlab-shell.log` for Omnibus GitLab packages or in `/home/git/gitaly/gitlab-shell.log` for
installations from source. installations from source.
NOTE: **Note**
For GitLab 12.5 and earlier the file lives in `/var/log/gitlab/gitlab-shell/gitlab-shell.log`.
GitLab Shell is used by GitLab for executing Git commands and provide GitLab Shell is used by GitLab for executing Git commands and provide
SSH access to Git repositories. For example: SSH access to Git repositories. For example:
......
...@@ -67,7 +67,7 @@ POST /projects/:id/issues/:issue_iid/links ...@@ -67,7 +67,7 @@ POST /projects/:id/issues/:issue_iid/links
| `issue_iid` | integer | yes | The internal ID of a project's issue | | `issue_iid` | integer | yes | The internal ID of a project's issue |
| `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project | | `target_project_id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) of a target project |
| `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue | | `target_issue_iid` | integer/string | yes | The internal ID of a target project's issue |
| `link_type` | string | no | The type of the relation ("relates_to", "blocks", "is_blocked_by"), defaults to "relates_to"). Ignored unless `issue_link_types` feature flag is enabled. | | `link_type` | string | no | The type of the relation ("relates_to", "blocks", "is_blocked_by"), defaults to "relates_to"). |
```shell ```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1" curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/4/issues/1/links?target_project_id=5&target_issue_iid=1"
......
...@@ -95,7 +95,7 @@ The following table depicts the various user permission levels in a project. ...@@ -95,7 +95,7 @@ The following table depicts the various user permission levels in a project.
| Stop environments | | | ✓ | ✓ | ✓ | | Stop environments | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ | | Add tags | | | ✓ | ✓ | ✓ |
| Cancel and retry jobs | | | ✓ | ✓ | ✓ | | Cancel and retry jobs | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ | | Create or update commit status | | | ✓ (*5*) | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ | | Update a container registry | | | ✓ | ✓ | ✓ |
| Remove a container registry image | | | ✓ | ✓ | ✓ | | Remove a container registry image | | | ✓ | ✓ | ✓ |
| Create/edit/delete project milestones | | | ✓ | ✓ | ✓ | | Create/edit/delete project milestones | | | ✓ | ✓ | ✓ |
...@@ -144,6 +144,7 @@ The following table depicts the various user permission levels in a project. ...@@ -144,6 +144,7 @@ The following table depicts the various user permission levels in a project.
(*2*): Guest users can only view the confidential issues they created themselves. (*2*): Guest users can only view the confidential issues they created themselves.
(*3*): If **Public pipelines** is enabled in **Project Settings > CI/CD**. (*3*): If **Public pipelines** is enabled in **Project Settings > CI/CD**.
(*4*): Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [Protected Branches](./project/protected_branches.md). (*4*): Not allowed for Guest, Reporter, Developer, Maintainer, or Owner. See [Protected Branches](./project/protected_branches.md).
(*5*): If the [branch is protected](./project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings), this depends on the access Developers and Maintainers are given.
## Project features permissions ## Project features permissions
......
...@@ -13,7 +13,7 @@ module Gitlab ...@@ -13,7 +13,7 @@ module Gitlab
def perform(resource_model, join, conditions, with_notes, start_id, end_id) def perform(resource_model, join, conditions, with_notes, start_id, end_id)
resource_model = "#{ISOLATION_MODULE}::#{resource_model}".constantize if resource_model.is_a?(String) resource_model = "#{ISOLATION_MODULE}::#{resource_model}".constantize if resource_model.is_a?(String)
model = with_notes ? "#{ISOLATION_MODULE}::Note".constantize : resource_model model = with_notes ? Gitlab::BackgroundMigration::UserMentions::Models::Note : resource_model
resource_user_mention_model = resource_model.user_mention_model resource_user_mention_model = resource_model.user_mention_model
records = model.joins(join).where(conditions).where(id: start_id..end_id) records = model.joins(join).where(conditions).where(id: start_id..end_id)
...@@ -21,7 +21,7 @@ module Gitlab ...@@ -21,7 +21,7 @@ module Gitlab
records.in_groups_of(BULK_INSERT_SIZE, false).each do |records| records.in_groups_of(BULK_INSERT_SIZE, false).each do |records|
mentions = [] mentions = []
records.each do |record| records.each do |record|
mentions << record.build_mention_values mentions << record.build_mention_values(resource_user_mention_model.resource_foreign_key)
end end
Gitlab::Database.bulk_insert( Gitlab::Database.bulk_insert(
......
...@@ -65,11 +65,11 @@ module Gitlab ...@@ -65,11 +65,11 @@ module Gitlab
false false
end end
def build_mention_values def build_mention_values(resource_foreign_key)
refs = all_references(author) refs = all_references(author)
{ {
"#{self.user_mention_model.resource_foreign_key}": user_mention_resource_id, "#{resource_foreign_key}": user_mention_resource_id,
note_id: user_mention_note_id, note_id: user_mention_note_id,
mentioned_users_ids: array_to_sql(refs.mentioned_users.pluck(:id)), mentioned_users_ids: array_to_sql(refs.mentioned_users.pluck(:id)),
mentioned_projects_ids: array_to_sql(refs.mentioned_projects.pluck(:id)), mentioned_projects_ids: array_to_sql(refs.mentioned_projects.pluck(:id)),
......
...@@ -19,10 +19,6 @@ module Gitlab ...@@ -19,10 +19,6 @@ module Gitlab
belongs_to :noteable, polymorphic: true belongs_to :noteable, polymorphic: true
belongs_to :project belongs_to :project
def user_mention_model
"#{CreateResourceUserMention::ISOLATION_MODULE}::#{noteable.class}".constantize.user_mention_model
end
def for_personal_snippet? def for_personal_snippet?
noteable.class.name == 'PersonalSnippet' noteable.class.name == 'PersonalSnippet'
end end
......
import { shallowMount } from '@vue/test-utils';
import DiffFileRow from '~/diffs/components/diff_file_row.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
describe('Diff File Row component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(DiffFileRow, {
propsData: { ...props },
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders file row component', () => {
createComponent({
level: 4,
file: {},
});
expect(wrapper.find(FileRow).exists()).toEqual(true);
});
});
import { shallowMount } from '@vue/test-utils';
import IdeFileRow from '~/ide/components/ide_file_row.vue';
import FileRow from '~/vue_shared/components/file_row.vue';
describe('Ide File Row component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(IdeFileRow, {
propsData: { ...props },
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders file row component', () => {
createComponent({
level: 4,
file: {},
});
expect(wrapper.find(FileRow).exists()).toEqual(true);
});
});
import { shallowMount, createLocalVue, mount } from '@vue/test-utils'; import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
import { GlDropdownItem, GlButton, GlToast } from '@gitlab/ui'; import { GlDropdownItem, GlButton } from '@gitlab/ui';
import VueDraggable from 'vuedraggable'; import VueDraggable from 'vuedraggable';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -10,6 +10,7 @@ import Dashboard from '~/monitoring/components/dashboard.vue'; ...@@ -10,6 +10,7 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue'; import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue';
import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue'; import DashboardsDropdown from '~/monitoring/components/dashboards_dropdown.vue';
import GroupEmptyState from '~/monitoring/components/group_empty_state.vue'; import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
import PanelType from 'ee_else_ce/monitoring/components/panel_type.vue';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
import * as types from '~/monitoring/stores/mutation_types'; import * as types from '~/monitoring/stores/mutation_types';
import { setupComponentStore, propsData } from '../init_utils'; import { setupComponentStore, propsData } from '../init_utils';
...@@ -540,37 +541,36 @@ describe('Dashboard', () => { ...@@ -540,37 +541,36 @@ describe('Dashboard', () => {
}); });
}); });
// https://gitlab.com/gitlab-org/gitlab-ce/issues/66922 describe('Clipboard text in panels', () => {
// eslint-disable-next-line jest/no-disabled-tests
describe.skip('link to chart', () => {
const currentDashboard = 'TEST_DASHBOARD'; const currentDashboard = 'TEST_DASHBOARD';
localVue.use(GlToast);
const link = () => wrapper.find('.js-chart-link'); const getClipboardTextAt = i =>
const clipboardText = () => link().element.dataset.clipboardText; wrapper
.findAll(PanelType)
.at(i)
.props('clipboardText');
beforeEach(done => { beforeEach(done => {
createShallowWrapper({ hasMetrics: true, currentDashboard }); createShallowWrapper({ hasMetrics: true, currentDashboard });
setTimeout(done); setupComponentStore(wrapper);
});
it('adds a copy button to the dropdown', () => { wrapper.vm.$nextTick(done);
expect(link().text()).toContain('Generate link to chart');
}); });
it('contains a link to the dashboard', () => { it('contains a link to the dashboard', () => {
expect(clipboardText()).toContain(`dashboard=${currentDashboard}`); expect(getClipboardTextAt(0)).toContain(`dashboard=${currentDashboard}`);
expect(clipboardText()).toContain(`group=`); expect(getClipboardTextAt(0)).toContain(`group=`);
expect(clipboardText()).toContain(`title=`); expect(getClipboardTextAt(0)).toContain(`title=`);
expect(clipboardText()).toContain(`y_label=`); expect(getClipboardTextAt(0)).toContain(`y_label=`);
}); });
it('undefined parameter is stripped', done => { it('strips the undefined parameter', done => {
wrapper.setProps({ currentDashboard: undefined }); wrapper.setProps({ currentDashboard: undefined });
wrapper.vm.$nextTick(() => { wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`); expect(getClipboardTextAt(0)).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`); expect(getClipboardTextAt(0)).toContain(`y_label=`);
done(); done();
}); });
}); });
...@@ -579,18 +579,10 @@ describe('Dashboard', () => { ...@@ -579,18 +579,10 @@ describe('Dashboard', () => {
wrapper.setProps({ currentDashboard: null }); wrapper.setProps({ currentDashboard: null });
wrapper.vm.$nextTick(() => { wrapper.vm.$nextTick(() => {
expect(clipboardText()).not.toContain(`dashboard=`); expect(getClipboardTextAt(0)).not.toContain(`dashboard=`);
expect(clipboardText()).toContain(`y_label=`); expect(getClipboardTextAt(0)).toContain(`y_label=`);
done(); done();
}); });
}); });
it('creates a toast when clicked', () => {
jest.spyOn(wrapper.vm.$toast, 'show').and.stub();
link().vm.$emit('click');
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
});
}); });
}); });
...@@ -3,22 +3,29 @@ import AxiosMockAdapter from 'axios-mock-adapter'; ...@@ -3,22 +3,29 @@ import AxiosMockAdapter from 'axios-mock-adapter';
import { setTestTimeout } from 'helpers/timeout'; import { setTestTimeout } from 'helpers/timeout';
import invalidUrl from '~/lib/utils/invalid_url'; import invalidUrl from '~/lib/utils/invalid_url';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import PanelType from '~/monitoring/components/panel_type.vue'; import PanelType from '~/monitoring/components/panel_type.vue';
import EmptyChart from '~/monitoring/components/charts/empty_chart.vue'; import EmptyChart from '~/monitoring/components/charts/empty_chart.vue';
import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue'; import TimeSeriesChart from '~/monitoring/components/charts/time_series.vue';
import AnomalyChart from '~/monitoring/components/charts/anomaly.vue'; import AnomalyChart from '~/monitoring/components/charts/anomaly.vue';
import { graphDataPrometheusQueryRange } from '../../javascripts/monitoring/mock_data'; import { anomalyMockGraphData, graphDataPrometheusQueryRange } from 'jest/monitoring/mock_data';
import { anomalyMockGraphData } from '../../frontend/monitoring/mock_data';
import { createStore } from '~/monitoring/stores'; import { createStore } from '~/monitoring/stores';
global.IS_EE = true; global.IS_EE = true;
global.URL.createObjectURL = jest.fn(); global.URL.createObjectURL = jest.fn();
const mocks = {
$toast: {
show: jest.fn(),
},
};
describe('Panel Type component', () => { describe('Panel Type component', () => {
let axiosMock; let axiosMock;
let store; let store;
let state; let state;
let wrapper; let wrapper;
const exampleText = 'example_text'; const exampleText = 'example_text';
const createWrapper = props => { const createWrapper = props => {
...@@ -27,6 +34,7 @@ describe('Panel Type component', () => { ...@@ -27,6 +34,7 @@ describe('Panel Type component', () => {
...props, ...props,
}, },
store, store,
mocks,
}); });
}; };
...@@ -88,7 +96,7 @@ describe('Panel Type component', () => { ...@@ -88,7 +96,7 @@ describe('Panel Type component', () => {
}); });
it('sets no clipboard copy link on dropdown by default', () => { it('sets no clipboard copy link on dropdown by default', () => {
const link = () => wrapper.find('.js-chart-link'); const link = () => wrapper.find({ ref: 'copyChartLink' });
expect(link().exists()).toBe(false); expect(link().exists()).toBe(false);
}); });
...@@ -196,6 +204,7 @@ describe('Panel Type component', () => { ...@@ -196,6 +204,7 @@ describe('Panel Type component', () => {
}); });
describe('when cliboard data is available', () => { describe('when cliboard data is available', () => {
const link = () => wrapper.find({ ref: 'copyChartLink' });
const clipboardText = 'A value to copy.'; const clipboardText = 'A value to copy.';
beforeEach(() => { beforeEach(() => {
...@@ -210,11 +219,19 @@ describe('Panel Type component', () => { ...@@ -210,11 +219,19 @@ describe('Panel Type component', () => {
}); });
it('sets clipboard text on the dropdown', () => { it('sets clipboard text on the dropdown', () => {
const link = () => wrapper.find('.js-chart-link');
expect(link().exists()).toBe(true); expect(link().exists()).toBe(true);
expect(link().element.dataset.clipboardText).toBe(clipboardText); expect(link().element.dataset.clipboardText).toBe(clipboardText);
}); });
it('adds a copy button to the dropdown', () => {
expect(link().text()).toContain('Generate link to chart');
});
it('opens a toast on click', () => {
link().vm.$emit('click');
expect(wrapper.vm.$toast.show).toHaveBeenCalled();
});
}); });
describe('when downloading metrics data as CSV', () => { describe('when downloading metrics data as CSV', () => {
......
import { pick } from 'lodash';
import { shallowMount } from '@vue/test-utils';
import FileTree from '~/vue_shared/components/file_tree.vue';
const MockFileRow = {
name: 'MockFileRow',
render() {
return this.$slots.default;
},
};
const TEST_LEVEL = 4;
const TEST_EXTA_ARGS = {
foo: 'lorem-ipsum',
bar: 'zoo',
};
describe('File Tree component', () => {
let wrapper;
const createComponent = (props = {}) => {
wrapper = shallowMount(FileTree, {
propsData: { level: TEST_LEVEL, fileRowComponent: MockFileRow, ...props },
attrs: { ...TEST_EXTA_ARGS },
});
};
const findFileRow = () => wrapper.find(MockFileRow);
const findChildrenTrees = () => wrapper.findAll(FileTree).wrappers.slice(1);
const findChildrenTreeProps = () =>
findChildrenTrees().map(x => ({
...x.props(),
...pick(x.attributes(), Object.keys(TEST_EXTA_ARGS)),
}));
afterEach(() => {
wrapper.destroy();
});
describe('file row component', () => {
beforeEach(() => {
createComponent({ file: {} });
});
it('renders file row component', () => {
expect(findFileRow().exists()).toEqual(true);
});
it('contains the required attribute keys', () => {
const fileRow = findFileRow();
// Checking strings b/c value in attributes are always strings
expect(fileRow.attributes()).toEqual({
file: {}.toString(),
level: TEST_LEVEL.toString(),
...TEST_EXTA_ARGS,
});
});
});
describe('file tree', () => {
const createChildren = () => [{ id: 1 }, { id: 2 }];
const createChildrenExpectation = (props = {}) =>
createChildren().map(file => ({
fileRowComponent: MockFileRow,
file,
...TEST_EXTA_ARGS,
...props,
}));
it.each`
key | value | desc | expectedChildren
${'isHeader'} | ${true} | ${'is shown if file is header'} | ${createChildrenExpectation({ level: 0 })}
${'opened'} | ${true} | ${'is shown if file is open'} | ${createChildrenExpectation({ level: TEST_LEVEL + 1 })}
${'isHeader'} | ${false} | ${'is hidden if file is header'} | ${[]}
${'opened'} | ${false} | ${'is hidden if file is open'} | ${[]}
`('$desc', ({ key, value, expectedChildren }) => {
createComponent({
file: {
[key]: value,
tree: createChildren(),
},
});
expect(findChildrenTreeProps()).toEqual(expectedChildren);
});
});
});
...@@ -19,7 +19,6 @@ describe('File row component', () => { ...@@ -19,7 +19,6 @@ describe('File row component', () => {
const findNewDropdown = () => vm.$el.querySelector('.ide-new-btn .dropdown'); const findNewDropdown = () => vm.$el.querySelector('.ide-new-btn .dropdown');
const findNewDropdownButton = () => vm.$el.querySelector('.ide-new-btn .dropdown button'); const findNewDropdownButton = () => vm.$el.querySelector('.ide-new-btn .dropdown button');
const findFileRow = () => vm.$el.querySelector('.file-row');
it('renders name', () => { it('renders name', () => {
createComponent({ createComponent({
...@@ -42,7 +41,7 @@ describe('File row component', () => { ...@@ -42,7 +41,7 @@ describe('File row component', () => {
}); });
spyOn(vm, '$emit').and.stub(); spyOn(vm, '$emit').and.stub();
vm.$el.querySelector('.file-row').click(); vm.$el.click();
expect(vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', vm.file.path); expect(vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', vm.file.path);
}); });
...@@ -87,7 +86,7 @@ describe('File row component', () => { ...@@ -87,7 +86,7 @@ describe('File row component', () => {
level: 0, level: 0,
}); });
expect(vm.$el.querySelector('.js-file-row-header')).not.toBe(null); expect(vm.$el.classList).toContain('js-file-row-header');
}); });
describe('new dropdown', () => { describe('new dropdown', () => {
...@@ -138,7 +137,7 @@ describe('File row component', () => { ...@@ -138,7 +137,7 @@ describe('File row component', () => {
}); });
it('closes when row triggers mouseleave', () => { it('closes when row triggers mouseleave', () => {
findFileRow().dispatchEvent(new Event('mouseleave')); vm.$el.dispatchEvent(new Event('mouseleave'));
expect(vm.dropdownOpen).toBe(false); expect(vm.dropdownOpen).toBe(false);
}); });
......
# frozen_string_literal: true
shared_examples 'resource mentions migration' do |migration_class, resource_class|
it 'migrates resource mentions' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
expect do
subject.perform(resource_class.name, join, conditions, false, resource_class.minimum(:id), resource_class.maximum(:id))
end.to change { user_mentions.count }.by(1)
user_mention = user_mentions.last
expect(user_mention.mentioned_users_ids.sort).to eq(mentioned_users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that performing the same job twice does not fail and does not change counts
expect do
subject.perform(resource_class.name, join, conditions, false, resource_class.minimum(:id), resource_class.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
shared_examples 'resource notes mentions migration' do |migration_class, resource_class|
before do
note1.becomes(Note).save!
note2.becomes(Note).save!
note3.becomes(Note).save!
# note4.becomes(Note).save(validate: false)
end
it 'migrates mentions from note' do
join = migration_class::JOIN
conditions = migration_class::QUERY_CONDITIONS
# there are 4 notes for each noteable_type, but one does not have mentions and
# another one's noteable_id points to an inexistent resource
expect(notes.where(noteable_type: resource_class.to_s).count).to eq 4
expect do
subject.perform(resource_class.name, join, conditions, true, Note.minimum(:id), Note.maximum(:id))
end.to change { user_mentions.count }.by(2)
# check that the user_mention for regular note is created
user_mention = user_mentions.first
expect(Note.find(user_mention.note_id).system).to be false
expect(user_mention.mentioned_users_ids.sort).to eq(users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that the user_mention for system note is created
user_mention = user_mentions.second
expect(Note.find(user_mention.note_id).system).to be true
expect(user_mention.mentioned_users_ids.sort).to eq(users.pluck(:id).sort)
expect(user_mention.mentioned_groups_ids.sort).to eq([group.id])
expect(user_mention.mentioned_groups_ids.sort).not_to include(inaccessible_group.id)
# check that performing the same job twice does not fail and does not change counts
expect do
subject.perform(resource_class.name, join, conditions, true, Note.minimum(:id), Note.maximum(:id))
end.to change { user_mentions.count }.by(0)
end
end
shared_examples 'schedules resource mentions migration' do |resource_class, is_for_notes|
it 'schedules background migrations' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
migration = described_class::MIGRATION
join = described_class::JOIN
conditions = described_class::QUERY_CONDITIONS
expect(migration).to be_scheduled_delayed_migration(2.minutes, resource_class.name, join, conditions, is_for_notes, resource1.id, resource1.id)
expect(migration).to be_scheduled_delayed_migration(4.minutes, resource_class.name, join, conditions, is_for_notes, resource2.id, resource2.id)
expect(migration).to be_scheduled_delayed_migration(6.minutes, resource_class.name, join, conditions, is_for_notes, resource3.id, resource3.id)
expect(BackgroundMigrationWorker.jobs.size).to eq 3
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment