diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 478fd01f541..8f3064c55f6 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -534,6 +534,50 @@ rspec:feature-flags: run_timed_command "bundle exec scripts/used-feature-flags"; fi +rspec:skipped-flaky-tests-report: + extends: + - .default-retry + - .rails:rules:skipped-flaky-tests-report + image: ruby:2.7-alpine + stage: post-test + # We cannot use needs since it would mean needing 84 jobs (since most are parallelized) + # so we use `dependencies` here. + dependencies: + # FOSS/EE jobs + - rspec migration pg12 + - rspec unit pg12 + - rspec integration pg12 + - rspec system pg12 + # FOSS/EE minimal jobs + - rspec migration pg12 minimal + - rspec unit pg12 minimal + - rspec integration pg12 minimal + - rspec system pg12 minimal + # EE jobs + - rspec-ee migration pg12 + - rspec-ee unit pg12 + - rspec-ee integration pg12 + - rspec-ee system pg12 + # EE minimal jobs + - rspec-ee migration pg12 minimal + - rspec-ee unit pg12 minimal + - rspec-ee integration pg12 minimal + - rspec-ee system pg12 minimal + # Geo jobs + - rspec-ee unit pg12 geo + - rspec-ee integration pg12 geo + - rspec-ee system pg12 geo + # Geo minimal jobs + - rspec-ee unit pg12 geo minimal + - rspec-ee integration pg12 geo minimal + - rspec-ee system pg12 geo minimal + script: + - cat rspec_flaky/skipped_flaky_tests_*_report.txt >> skipped_flaky_tests_report.txt + artifacts: + expire_in: 31d + paths: + - skipped_flaky_tests_report.txt + # EE/FOSS: default refs (MRs, default branch, schedules) jobs # ####################################################### diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 3e9d977cd83..91320b8988f 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1352,6 +1352,13 @@ when: never - changes: *code-backstage-patterns +.rails:rules:skipped-flaky-tests-report: + rules: + - <<: *if-not-ee + when: never + - if: '$SKIP_FLAKY_TESTS_AUTOMATICALLY == "true"' + changes: *code-backstage-patterns + ######################### # Static analysis rules # ######################### diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index 016a2771cc5..c104cc574c6 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -7b9cd199b0851fd1b6615e0798f2aafddafd63cb +460a880c6993ab5f76cac951fccc02efd5cbd444 diff --git a/Gemfile b/Gemfile index 109c3c1cf5b..4ef4501b909 100644 --- a/Gemfile +++ b/Gemfile @@ -342,7 +342,7 @@ group :development do gem 'lefthook', '~> 0.7.0', require: false gem 'solargraph', '~> 0.43', require: false - gem 'letter_opener_web', '~> 1.4.1' + gem 'letter_opener_web', '~> 2.0.0' # Better errors handler gem 'better_errors', '~> 2.9.0' diff --git a/Gemfile.lock b/Gemfile.lock index 339579dccc7..7f1bf38a9c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -700,10 +700,11 @@ GEM lefthook (0.7.5) letter_opener (1.7.0) launchy (~> 2.2) - letter_opener_web (1.4.1) - actionmailer (>= 3.2) - letter_opener (~> 1.0) - railties (>= 3.2) + letter_opener_web (2.0.0) + actionmailer (>= 5.2) + letter_opener (~> 1.7) + railties (>= 5.2) + rexml libyajl2 (1.2.0) license_finder (6.0.0) bundler @@ -1516,7 +1517,7 @@ DEPENDENCIES kramdown (~> 2.3.1) kubeclient (~> 4.9.2) lefthook (~> 0.7.0) - letter_opener_web (~> 1.4.1) + letter_opener_web (~> 2.0.0) license_finder (~> 6.0) licensee (~> 9.14.1) lockbox (~> 0.6.2) diff --git a/app/assets/javascripts/analytics/devops_report/components/devops_score.vue b/app/assets/javascripts/analytics/devops_reports/components/devops_score.vue similarity index 100% rename from app/assets/javascripts/analytics/devops_report/components/devops_score.vue rename to app/assets/javascripts/analytics/devops_reports/components/devops_score.vue diff --git a/app/assets/javascripts/analytics/devops_report/components/devops_score_callout.vue b/app/assets/javascripts/analytics/devops_reports/components/devops_score_callout.vue similarity index 100% rename from app/assets/javascripts/analytics/devops_report/components/devops_score_callout.vue rename to app/assets/javascripts/analytics/devops_reports/components/devops_score_callout.vue diff --git a/app/assets/javascripts/analytics/devops_report/components/service_ping_disabled.vue b/app/assets/javascripts/analytics/devops_reports/components/service_ping_disabled.vue similarity index 100% rename from app/assets/javascripts/analytics/devops_report/components/service_ping_disabled.vue rename to app/assets/javascripts/analytics/devops_reports/components/service_ping_disabled.vue diff --git a/app/assets/javascripts/analytics/devops_report/constants.js b/app/assets/javascripts/analytics/devops_reports/constants.js similarity index 100% rename from app/assets/javascripts/analytics/devops_report/constants.js rename to app/assets/javascripts/analytics/devops_reports/constants.js diff --git a/app/assets/javascripts/analytics/devops_report/devops_score.js b/app/assets/javascripts/analytics/devops_reports/devops_score.js similarity index 100% rename from app/assets/javascripts/analytics/devops_report/devops_score.js rename to app/assets/javascripts/analytics/devops_reports/devops_score.js diff --git a/app/assets/javascripts/analytics/devops_report/devops_score_disabled_service_ping.js b/app/assets/javascripts/analytics/devops_reports/devops_score_disabled_service_ping.js similarity index 100% rename from app/assets/javascripts/analytics/devops_report/devops_score_disabled_service_ping.js rename to app/assets/javascripts/analytics/devops_reports/devops_score_disabled_service_ping.js diff --git a/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql b/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql deleted file mode 100644 index 1c382c4747b..00000000000 --- a/app/assets/javascripts/boards/graphql/group_board_iterations.query.graphql +++ /dev/null @@ -1,10 +0,0 @@ -query GroupBoardIterations($fullPath: ID!, $title: String) { - group(fullPath: $fullPath) { - iterations(includeAncestors: true, title: $title) { - nodes { - id - title - } - } - } -} diff --git a/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql b/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql deleted file mode 100644 index 078151a275a..00000000000 --- a/app/assets/javascripts/boards/graphql/project_board_iterations.query.graphql +++ /dev/null @@ -1,10 +0,0 @@ -query ProjectBoardIterations($fullPath: ID!, $title: String) { - project(fullPath: $fullPath) { - iterations(includeAncestors: true, title: $title) { - nodes { - id - title - } - } - } -} diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index c81e3cb79db..3a96e535cf7 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -36,13 +36,11 @@ import { } from '../boards_util'; import { gqlClient } from '../graphql'; import boardLabelsQuery from '../graphql/board_labels.query.graphql'; -import groupBoardIterationsQuery from '../graphql/group_board_iterations.query.graphql'; import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql'; import groupProjectsQuery from '../graphql/group_projects.query.graphql'; import issueCreateMutation from '../graphql/issue_create.mutation.graphql'; import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql'; import listsIssuesQuery from '../graphql/lists_issues.query.graphql'; -import projectBoardIterationsQuery from '../graphql/project_board_iterations.query.graphql'; import projectBoardMilestonesQuery from '../graphql/project_board_milestones.query.graphql'; import * as types from './mutation_types'; @@ -203,52 +201,6 @@ export default { }); }, - fetchIterations({ state, commit }, title) { - commit(types.RECEIVE_ITERATIONS_REQUEST); - - const { fullPath, boardType } = state; - - const variables = { - fullPath, - title, - }; - - let query; - if (boardType === BoardType.project) { - query = projectBoardIterationsQuery; - } - if (boardType === BoardType.group) { - query = groupBoardIterationsQuery; - } - - if (!query) { - // eslint-disable-next-line @gitlab/require-i18n-strings - throw new Error('Unknown board type'); - } - - return gqlClient - .query({ - query, - variables, - }) - .then(({ data }) => { - const errors = data[boardType]?.errors; - const iterations = data[boardType]?.iterations.nodes; - - if (errors?.[0]) { - throw new Error(errors[0]); - } - - commit(types.RECEIVE_ITERATIONS_SUCCESS, iterations); - - return iterations; - }) - .catch((e) => { - commit(types.RECEIVE_ITERATIONS_FAILURE); - throw e; - }); - }, - fetchMilestones({ state, commit }, searchTerm) { commit(types.RECEIVE_MILESTONES_REQUEST); diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js index 928cece19f7..31b78014525 100644 --- a/app/assets/javascripts/boards/stores/mutation_types.js +++ b/app/assets/javascripts/boards/stores/mutation_types.js @@ -41,7 +41,3 @@ export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS'; export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS'; export const RESET_BOARD_ITEM_SELECTION = 'RESET_BOARD_ITEM_SELECTION'; export const SET_ERROR = 'SET_ERROR'; - -export const RECEIVE_ITERATIONS_REQUEST = 'RECEIVE_ITERATIONS_REQUEST'; -export const RECEIVE_ITERATIONS_SUCCESS = 'RECEIVE_ITERATIONS_SUCCESS'; -export const RECEIVE_ITERATIONS_FAILURE = 'RECEIVE_ITERATIONS_FAILURE'; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index ef5b84b4575..2a2ce7652e6 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -64,20 +64,6 @@ export default { ); }, - [mutationTypes.RECEIVE_ITERATIONS_REQUEST](state) { - state.iterationsLoading = true; - }, - - [mutationTypes.RECEIVE_ITERATIONS_SUCCESS](state, iterations) { - state.iterations = iterations; - state.iterationsLoading = false; - }, - - [mutationTypes.RECEIVE_ITERATIONS_FAILURE](state) { - state.iterationsLoading = false; - state.error = __('Failed to load iterations.'); - }, - [mutationTypes.SET_ACTIVE_ID](state, { id, sidebarType }) { state.activeId = id; state.sidebarType = sidebarType; diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 66d06a3a1b6..f405b82b05b 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -44,6 +44,7 @@ import { TRACKING_MULTIPLE_FILES_MODE, } from '../constants'; +import { discussionIntersectionObserverHandlerFactory } from '../utils/discussions'; import diffsEventHub from '../event_hub'; import { reviewStatuses } from '../utils/file_reviews'; import { diffsApp } from '../utils/performance'; @@ -86,6 +87,9 @@ export default { ALERT_MERGE_CONFLICT, ALERT_COLLAPSED_FILES, }, + provide: { + discussionObserverHandler: discussionIntersectionObserverHandlerFactory(), + }, props: { endpoint: { type: String, diff --git a/app/assets/javascripts/diffs/utils/discussions.js b/app/assets/javascripts/diffs/utils/discussions.js new file mode 100644 index 00000000000..c404705d209 --- /dev/null +++ b/app/assets/javascripts/diffs/utils/discussions.js @@ -0,0 +1,76 @@ +function normalize(processable) { + const { entry } = processable; + const offset = entry.rootBounds.bottom - entry.boundingClientRect.top; + const direction = + offset < 0 ? 'Up' : 'Down'; /* eslint-disable-line @gitlab/require-i18n-strings */ + + return { + ...processable, + entry: { + time: entry.time, + type: entry.isIntersecting ? 'intersection' : `scroll${direction}`, + }, + }; +} + +function sort({ entry: alpha }, { entry: beta }) { + const diff = alpha.time - beta.time; + let order = 0; + + if (diff < 0) { + order = -1; + } else if (diff > 0) { + order = 1; + } else if (alpha.type === 'intersection' && beta.type === 'scrollUp') { + order = 2; + } else if (alpha.type === 'scrollUp' && beta.type === 'intersection') { + order = -2; + } + + return order; +} + +function filter(entry) { + return entry.type !== 'scrollDown'; +} + +export function discussionIntersectionObserverHandlerFactory() { + let unprocessed = []; + let timer = null; + + return (processable) => { + unprocessed.push(processable); + + if (timer) { + clearTimeout(timer); + } + + timer = setTimeout(() => { + unprocessed + .map(normalize) + .filter(filter) + .sort(sort) + .forEach((discussionObservationContainer) => { + const { + entry: { type }, + currentDiscussion, + isFirstUnresolved, + isDiffsPage, + functions: { setCurrentDiscussionId, getPreviousUnresolvedDiscussionId }, + } = discussionObservationContainer; + + if (type === 'intersection') { + setCurrentDiscussionId(currentDiscussion.id); + } else if (type === 'scrollUp') { + setCurrentDiscussionId( + isFirstUnresolved + ? null + : getPreviousUnresolvedDiscussionId(currentDiscussion.id, isDiffsPage), + ); + } + }); + + unprocessed = []; + }, 0); + }; +} diff --git a/app/assets/javascripts/notes/components/discussion_notes.vue b/app/assets/javascripts/notes/components/discussion_notes.vue index 6fcfa66ea49..d1df4eb848b 100644 --- a/app/assets/javascripts/notes/components/discussion_notes.vue +++ b/app/assets/javascripts/notes/components/discussion_notes.vue @@ -1,5 +1,6 @@ @@ -122,33 +152,35 @@ export default { @mouseleave="handleMouseLeave(discussion)" >