Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
ae192a2f14
commit
f4ea1f8998
61 changed files with 800 additions and 269 deletions
|
@ -52,6 +52,7 @@ qa:nightly-auto-quarantine-dequarantine:
|
|||
- bundle exec confiner -r .confiner/nightly.yml
|
||||
allow_failure: true
|
||||
|
||||
|
||||
qa:selectors-as-if-foss:
|
||||
extends:
|
||||
- qa:selectors
|
||||
|
@ -67,28 +68,6 @@ update-qa-cache:
|
|||
script:
|
||||
- echo "Cache has been updated and ready to be uploaded."
|
||||
|
||||
populate-qa-tests-var:
|
||||
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
|
||||
stage: prepare
|
||||
script:
|
||||
- tooling/bin/qa/check_if_qa_only_spec_changes ${CHANGES_FILE} ${ONLY_QA_CHANGES_FILE}
|
||||
- '[ -f $ONLY_QA_CHANGES_FILE ] && export QA_TESTS="`cat $ONLY_QA_CHANGES_FILE`"'
|
||||
- 'echo "QA_TESTS=$QA_TESTS" >> qa_tests_var.env'
|
||||
- 'echo "QA_TESTS: $QA_TESTS"'
|
||||
artifacts:
|
||||
expire_in: 2d
|
||||
reports:
|
||||
dotenv: qa_tests_var.env
|
||||
paths:
|
||||
- ${CHANGES_FILE}
|
||||
- ${ONLY_QA_CHANGES_FILE}
|
||||
- qa_tests_var.env
|
||||
variables:
|
||||
CHANGES_FILE: tmp/changed_files.txt
|
||||
ONLY_QA_CHANGES_FILE: tmp/qa_only_changed_files.txt
|
||||
needs:
|
||||
- detect-tests
|
||||
|
||||
.package-and-qa-base:
|
||||
image: ${GITLAB_DEPENDENCY_PROXY}ruby:2.7-alpine
|
||||
stage: qa
|
||||
|
@ -98,6 +77,8 @@ populate-qa-tests-var:
|
|||
- install_gitlab_gem
|
||||
- tooling/bin/find_change_diffs ${CHANGES_DIFFS_DIR}
|
||||
script:
|
||||
- tooling/bin/qa/check_if_qa_only_spec_changes ${CHANGES_FILE} ${ONLY_QA_CHANGES_FILE}
|
||||
- '[ -f $ONLY_QA_CHANGES_FILE ] && export QA_TESTS="`cat $ONLY_QA_CHANGES_FILE`"'
|
||||
- 'echo "QA_TESTS: $QA_TESTS"'
|
||||
- exit_code=0 && tooling/bin/qa/package_and_qa_check ${CHANGES_DIFFS_DIR} || exit_code=$?
|
||||
- echo $exit_code
|
||||
|
@ -118,13 +99,16 @@ populate-qa-tests-var:
|
|||
artifacts: false
|
||||
- job: build-assets-image
|
||||
artifacts: false
|
||||
- job: populate-qa-tests-var
|
||||
- detect-tests
|
||||
artifacts:
|
||||
expire_in: 7d
|
||||
paths:
|
||||
- ${CHANGES_FILE}
|
||||
- ${ONLY_QA_CHANGES_FILE}
|
||||
- ${CHANGES_DIFFS_DIR}/*
|
||||
variables:
|
||||
CHANGES_FILE: tmp/changed_files.txt
|
||||
ONLY_QA_CHANGES_FILE: tmp/qa_only_changed_files.txt
|
||||
CHANGES_DIFFS_DIR: tmp/diffs
|
||||
ALLURE_JOB_NAME: $CI_JOB_NAME
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import PipelinesService from '~/pipelines/services/pipelines_service';
|
|||
import PipelineStore from '~/pipelines/stores/pipelines_store';
|
||||
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
|
||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { s__, __ } from '~/locale';
|
||||
|
||||
export default {
|
||||
PipelineKeyOptions,
|
||||
|
@ -170,6 +171,20 @@ export default {
|
|||
}
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
actionPrimary: {
|
||||
text: s__('Pipeline|Run pipeline'),
|
||||
attributes: {
|
||||
variant: 'danger',
|
||||
},
|
||||
},
|
||||
actionCancel: {
|
||||
text: __('Cancel'),
|
||||
attributes: {
|
||||
variant: 'default',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
|
@ -229,9 +244,9 @@ export default {
|
|||
ref="modal"
|
||||
:modal-id="modalId"
|
||||
:title="s__('Pipelines|Are you sure you want to run this pipeline?')"
|
||||
:ok-title="s__('Pipeline|Run pipeline')"
|
||||
ok-variant="danger"
|
||||
@ok="onClickRunPipeline"
|
||||
:action-primary="$options.modal.actionPrimary"
|
||||
:action-cancel="$options.modal.actionCancel"
|
||||
@primary="onClickRunPipeline"
|
||||
>
|
||||
<p>
|
||||
{{
|
||||
|
|
|
@ -384,14 +384,26 @@ export default {
|
|||
|
||||
this.unwatchDiscussions = this.$watch(
|
||||
() => `${this.diffFiles.length}:${this.$store.state.notes.discussions.length}`,
|
||||
() => this.setDiscussions(),
|
||||
() => {
|
||||
this.setDiscussions();
|
||||
|
||||
if (
|
||||
this.$store.state.notes.doneFetchingBatchDiscussions &&
|
||||
window.gon?.features?.paginatedMrDiscussions
|
||||
) {
|
||||
this.unwatchDiscussions();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
this.unwatchRetrievingBatches = this.$watch(
|
||||
() => `${this.retrievingBatches}:${this.$store.state.notes.discussions.length}`,
|
||||
() => {
|
||||
if (!this.retrievingBatches && this.$store.state.notes.discussions.length) {
|
||||
this.unwatchDiscussions();
|
||||
if (!window.gon?.features?.paginatedMrDiscussions) {
|
||||
this.unwatchDiscussions();
|
||||
}
|
||||
|
||||
this.unwatchRetrievingBatches();
|
||||
}
|
||||
},
|
||||
|
|
|
@ -90,7 +90,10 @@ export const fetchDiscussions = ({ commit, dispatch }, { path, filter, persistFi
|
|||
? { params: { notes_filter: filter, persist_filter: persistFilter } }
|
||||
: null;
|
||||
|
||||
if (window.gon?.features?.paginatedIssueDiscussions) {
|
||||
if (
|
||||
window.gon?.features?.paginatedIssueDiscussions ||
|
||||
window.gon?.features?.paginatedMrDiscussions
|
||||
) {
|
||||
return dispatch('fetchDiscussionsBatch', { path, config, perPage: 20 });
|
||||
}
|
||||
|
||||
|
@ -128,6 +131,7 @@ export const fetchDiscussionsBatch = ({ commit, dispatch }, { path, config, curs
|
|||
});
|
||||
}
|
||||
|
||||
commit(types.SET_DONE_FETCHING_BATCH_DISCUSSIONS, true);
|
||||
commit(types.SET_FETCHING_DISCUSSIONS, false);
|
||||
dispatch('updateResolvableDiscussionsCounts');
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ export default () => ({
|
|||
currentDiscussionId: null,
|
||||
batchSuggestionsInfo: [],
|
||||
currentlyFetchingDiscussions: false,
|
||||
doneFetchingBatchDiscussions: false,
|
||||
/**
|
||||
* selectedCommentPosition & selectedCommentPositionHover structures are the same as `position.line_range`:
|
||||
* {
|
||||
|
|
|
@ -41,6 +41,7 @@ export const SET_SELECTED_COMMENT_POSITION = 'SET_SELECTED_COMMENT_POSITION';
|
|||
export const SET_SELECTED_COMMENT_POSITION_HOVER = 'SET_SELECTED_COMMENT_POSITION_HOVER';
|
||||
export const SET_FETCHING_DISCUSSIONS = 'SET_FETCHING_DISCUSSIONS';
|
||||
export const SET_RESOLVING_DISCUSSION = 'SET_RESOLVING_DISCUSSION';
|
||||
export const SET_DONE_FETCHING_BATCH_DISCUSSIONS = 'SET_DONE_FETCHING_BATCH_DISCUSSIONS';
|
||||
|
||||
// Issue
|
||||
export const CLOSE_ISSUE = 'CLOSE_ISSUE';
|
||||
|
|
|
@ -436,4 +436,7 @@ export default {
|
|||
[types.SET_FETCHING_DISCUSSIONS](state, value) {
|
||||
state.currentlyFetchingDiscussions = value;
|
||||
},
|
||||
[types.SET_DONE_FETCHING_BATCH_DISCUSSIONS](state, value) {
|
||||
state.doneFetchingBatchDiscussions = value;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import { initShow } from '~/issues';
|
||||
import initRelatedIssues from '~/related_issues';
|
||||
import initSidebarBundle from '~/sidebar/sidebar_bundle';
|
||||
import initWorkItemLinks from '~/work_items/components/work_item_links';
|
||||
|
||||
initShow();
|
||||
initSidebarBundle();
|
||||
initRelatedIssues();
|
||||
initWorkItemLinks();
|
||||
|
|
|
@ -2,7 +2,9 @@ import { initShow } from '~/issues';
|
|||
import { store } from '~/notes/stores';
|
||||
import initRelatedIssues from '~/related_issues';
|
||||
import initSidebarBundle from '~/sidebar/sidebar_bundle';
|
||||
import initWorkItemLinks from '~/work_items/components/work_item_links';
|
||||
|
||||
initShow();
|
||||
initSidebarBundle(store);
|
||||
initRelatedIssues();
|
||||
initWorkItemLinks();
|
||||
|
|
|
@ -23,6 +23,8 @@ import JobItem from './job_item.vue';
|
|||
export default {
|
||||
i18n: {
|
||||
stage: __('Stage:'),
|
||||
loadingTextLineOne: __('Loading, please wait.'),
|
||||
loadingTextLineTwo: __('Cue dramatic background music...'),
|
||||
},
|
||||
dropdownPopperOpts: {
|
||||
placement: 'bottom',
|
||||
|
@ -133,7 +135,17 @@ export default {
|
|||
class="gl-align-items-center gl-display-inline-flex gl-z-index-1"
|
||||
/>
|
||||
</template>
|
||||
<gl-loading-icon v-if="isLoading" size="sm" />
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="gl-display-flex gl-justify-content-center gl-p-3"
|
||||
data-testid="pipeline-stage-loading-state"
|
||||
>
|
||||
<gl-loading-icon size="sm" class="gl-mr-3" />
|
||||
<div>
|
||||
<p class="gl-mb-0">{{ $options.i18n.loadingTextLineOne }}</p>
|
||||
<p class="gl-mb-0">{{ $options.i18n.loadingTextLineTwo }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<ul
|
||||
v-else
|
||||
class="js-builds-dropdown-list scrollable-menu"
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import Vue from 'vue';
|
||||
import WorkItemLinks from './work_item_links.vue';
|
||||
|
||||
export default function initWorkItemLinks() {
|
||||
if (!window.gon.features.workItemsHierarchy) {
|
||||
return;
|
||||
}
|
||||
|
||||
const workItemLinksRoot = document.querySelector('.js-work-item-links-root');
|
||||
|
||||
if (!workItemLinksRoot) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: workItemLinksRoot,
|
||||
name: 'WorkItemLinksRoot',
|
||||
components: {
|
||||
workItemLinks: WorkItemLinks,
|
||||
},
|
||||
render: (createElement) =>
|
||||
createElement('work-item-links', {
|
||||
props: {
|
||||
issuableId: parseInt(workItemLinksRoot.dataset.issuableId, 10),
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
|
@ -14,11 +14,16 @@ export default {
|
|||
required: false,
|
||||
default: null,
|
||||
},
|
||||
issuableId: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isShownAddForm: false,
|
||||
isOpen: false,
|
||||
isOpen: true,
|
||||
children: [],
|
||||
};
|
||||
},
|
||||
|
|
|
@ -184,7 +184,8 @@ module IssuableActions
|
|||
|
||||
def paginated_discussions
|
||||
return if params[:per_page].blank?
|
||||
return unless issuable.instance_of?(Issue) && Feature.enabled?(:paginated_issue_discussions, project)
|
||||
return if issuable.instance_of?(Issue) && Feature.disabled?(:paginated_issue_discussions, project)
|
||||
return if issuable.instance_of?(MergeRequest) && Feature.disabled?(:paginated_mr_discussions, project)
|
||||
|
||||
strong_memoize(:paginated_discussions) do
|
||||
issuable
|
||||
|
|
|
@ -50,6 +50,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
push_frontend_feature_flag(:realtime_labels, project)
|
||||
push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_items_mvc_2)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, project)
|
||||
end
|
||||
|
||||
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
|
||||
|
|
|
@ -48,6 +48,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
push_frontend_feature_flag(:mr_attention_requests, current_user)
|
||||
push_frontend_feature_flag(:remove_diff_header_icons, project)
|
||||
push_frontend_feature_flag(:moved_mr_sidebar, project)
|
||||
push_frontend_feature_flag(:paginated_mr_discussions, project)
|
||||
end
|
||||
|
||||
before_action do
|
||||
|
|
|
@ -44,6 +44,7 @@ class ProjectsController < Projects::ApplicationController
|
|||
push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?)
|
||||
push_frontend_feature_flag(:work_items_mvc_2)
|
||||
push_frontend_feature_flag(:package_registry_access_level)
|
||||
push_frontend_feature_flag(:work_items_hierarchy, @project)
|
||||
end
|
||||
|
||||
before_action only: :edit do
|
||||
|
|
|
@ -45,7 +45,7 @@ module Registrations
|
|||
end
|
||||
|
||||
def update_params
|
||||
params.require(:user).permit(:role, :other_role, :setup_for_company)
|
||||
params.require(:user).permit(:role, :setup_for_company)
|
||||
end
|
||||
|
||||
def requires_confirmation?(user)
|
||||
|
|
|
@ -338,7 +338,6 @@ class User < ApplicationRecord
|
|||
|
||||
delegate :path, to: :namespace, allow_nil: true, prefix: true
|
||||
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
|
||||
delegate :other_role, :other_role=, to: :user_detail, allow_nil: true
|
||||
delegate :bio, :bio=, to: :user_detail, allow_nil: true
|
||||
delegate :webauthn_xid, :webauthn_xid=, to: :user_detail, allow_nil: true
|
||||
delegate :pronouns, :pronouns=, to: :user_detail, allow_nil: true
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
class UserDetail < ApplicationRecord
|
||||
extend ::Gitlab::Utils::Override
|
||||
include IgnorableColumns
|
||||
|
||||
ignore_columns :other_role, remove_after: '2022-07-22', remove_with: '15.3'
|
||||
|
||||
REGISTRATION_OBJECTIVE_PAIRS = { basics: 0, move_repository: 1, code_storage: 2, exploring: 3, ci: 4, other: 5, joining_team: 6 }.freeze
|
||||
|
||||
|
|
2
app/views/projects/issues/_work_item_links.html.haml
Normal file
2
app/views/projects/issues/_work_item_links.html.haml
Normal file
|
@ -0,0 +1,2 @@
|
|||
- if Feature.enabled?(:work_items_hierarchy, @project)
|
||||
.js-work-item-links-root{ data: { issuable_id: @issue.id } }
|
|
@ -65,7 +65,7 @@
|
|||
- if Feature.enabled?(:paginated_notes, @project)
|
||||
- add_page_startup_api_call notes_url
|
||||
- else
|
||||
- add_page_startup_api_call discussions_path(@merge_request)
|
||||
- add_page_startup_api_call Feature.enabled?(:paginated_mr_discussions, @project) ? discussions_path(@merge_request, per_page: 20) : discussions_path(@merge_request)
|
||||
- add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json)
|
||||
- add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json)
|
||||
#js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request, Feature.enabled?(:paginated_notes, @project)).to_json,
|
||||
|
|
|
@ -24,11 +24,6 @@
|
|||
.form-group.col-sm-12
|
||||
= f.label :role, _('Role'), class: 'label-bold'
|
||||
= f.select :role, ::User.roles.keys.map { |role| [role.titleize, role] }, { include_blank: _('Select a role') }, class: 'form-control js-user-role-dropdown', autofocus: true, required: true, data: { qa_selector: 'role_dropdown' }
|
||||
- if Feature.enabled?(:user_other_role_details)
|
||||
.row
|
||||
.form-group.col-sm-12.js-other-role-group.hidden
|
||||
= f.label :other_role, _('What is your job title? (optional)')
|
||||
= f.text_field :other_role, class: 'form-control'
|
||||
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
|
||||
= render_if_exists "registrations/welcome/setup_for_company", f: f
|
||||
= render_if_exists "registrations/welcome/joining_project"
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
= render 'projects/issues/design_management'
|
||||
|
||||
= render_if_exists 'projects/issues/work_item_links'
|
||||
= render_if_exists 'projects/issues/related_issues'
|
||||
|
||||
#js-related-merge-requests{ data: { endpoint: expose_path(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: issuable.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: fe_epic_board_total_weight
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/89390
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364503
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::product planning
|
||||
default_enabled: false
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: paginated_mr_discussions
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/88905
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/364497
|
||||
milestone: '15.1'
|
||||
type: development
|
||||
group: group::code review
|
||||
default_enabled: false
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: user_other_role_details
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45635
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/growth/team-tasks/-/issues/282
|
||||
milestone: '13.7'
|
||||
type: development
|
||||
group: group::conversion
|
||||
default_enabled: false
|
14
db/migrate/20220601091804_add_semver_column_to_ci_runners.rb
Normal file
14
db/migrate/20220601091804_add_semver_column_to_ci_runners.rb
Normal file
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddSemverColumnToCiRunners < Gitlab::Database::Migration[2.0]
|
||||
# rubocop:disable Migration/AddLimitToTextColumns
|
||||
# limit is added in 20220601091805_add_text_limit_to_ci_runners_semver
|
||||
def up
|
||||
add_column :ci_runners, :semver, :text, null: true
|
||||
end
|
||||
# rubocop:enable Migration/AddLimitToTextColumns
|
||||
|
||||
def down
|
||||
remove_column :ci_runners, :semver
|
||||
end
|
||||
end
|
|
@ -0,0 +1,13 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddTextLimitToCiRunnersSemver < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
add_text_limit :ci_runners, :semver, 16
|
||||
end
|
||||
|
||||
def down
|
||||
remove_text_limit :ci_runners, :semver
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddIndexOnRunnerIdAndSemverColumns < Gitlab::Database::Migration[2.0]
|
||||
disable_ddl_transaction!
|
||||
|
||||
INDEX_NAME = 'index_ci_runners_on_id_and_semver_cidr'
|
||||
|
||||
def up
|
||||
add_concurrent_index :ci_runners,
|
||||
'id, (semver::cidr)',
|
||||
name: INDEX_NAME
|
||||
end
|
||||
|
||||
def down
|
||||
remove_concurrent_index_by_name :ci_runners, INDEX_NAME
|
||||
end
|
||||
end
|
1
db/schema_migrations/20220601091804
Normal file
1
db/schema_migrations/20220601091804
Normal file
|
@ -0,0 +1 @@
|
|||
3d3c9b4aa88008c907b583db08e1246cd227414147b41f45b63e4ca1cc24de66
|
1
db/schema_migrations/20220601091805
Normal file
1
db/schema_migrations/20220601091805
Normal file
|
@ -0,0 +1 @@
|
|||
930a6a853626c3a9f5a529105bf4e4cb8cef9b6f948ccb4faaaf0dcb7a5a2544
|
1
db/schema_migrations/20220601101800
Normal file
1
db/schema_migrations/20220601101800
Normal file
|
@ -0,0 +1 @@
|
|||
c75a7375240fdd785f873b1a39173efec51d23e16808d23c24a6550604f080ad
|
|
@ -13082,6 +13082,8 @@ CREATE TABLE ci_runners (
|
|||
maintainer_note text,
|
||||
token_expires_at timestamp with time zone,
|
||||
allowed_plans text[] DEFAULT '{}'::text[] NOT NULL,
|
||||
semver text,
|
||||
CONSTRAINT check_a4f24953fd CHECK ((char_length(semver) <= 16)),
|
||||
CONSTRAINT check_ce275cee06 CHECK ((char_length(maintainer_note) <= 1024))
|
||||
);
|
||||
|
||||
|
@ -27448,6 +27450,8 @@ CREATE INDEX index_ci_runners_on_created_at_desc_and_id_desc ON ci_runners USING
|
|||
|
||||
CREATE INDEX index_ci_runners_on_description_trigram ON ci_runners USING gin (description gin_trgm_ops);
|
||||
|
||||
CREATE INDEX index_ci_runners_on_id_and_semver_cidr ON ci_runners USING btree (id, ((semver)::cidr));
|
||||
|
||||
CREATE INDEX index_ci_runners_on_locked ON ci_runners USING btree (locked);
|
||||
|
||||
CREATE INDEX index_ci_runners_on_runner_type ON ci_runners USING btree (runner_type);
|
||||
|
|
|
@ -141,6 +141,8 @@ and the existing GLFM parser and render implementations. They may also be
|
|||
manually updated as necessary to test-drive incomplete implementations.
|
||||
Regarding the terminology used here:
|
||||
|
||||
<!-- vale gitlab.InclusionCultural = NO -->
|
||||
|
||||
1. The Markdown snapshot tests can be considered a form of the
|
||||
[Golden Master Testing approach](https://www.google.com/search?q=golden+master+testing),
|
||||
which is also referred to as Approval Testing or Characterization Testing.
|
||||
|
@ -167,6 +169,11 @@ Regarding the terminology used here:
|
|||
they are colocated under the `spec/fixtures` directory with the rest of
|
||||
the fixture data for the GitLab Rails application.
|
||||
|
||||
<!-- vale gitlab.InclusionCultural = YES -->
|
||||
|
||||
See also the section on [normalization](#normalization) below, which is an important concept used
|
||||
in the Markdown snapshot testing.
|
||||
|
||||
## Parsing and Rendering
|
||||
|
||||
The Markdown dialect used in the GitLab application has a dual requirement for rendering:
|
||||
|
@ -268,6 +275,49 @@ HTML. (For example, when they are represented as an image.) In these cases, the
|
|||
conformance test for the example can be skipped by setting `skip_update_example_snapshots: true`
|
||||
for the example in `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml`.
|
||||
|
||||
### Normalization
|
||||
|
||||
Versions of the rendered HTML and ProseMirror JSON can vary for a number of reasons.
|
||||
Differences in styling or HTML structure can occur, but the values of attributes or nodes may
|
||||
also vary across different test runs or environments. For example:
|
||||
|
||||
1. Database record identifiers
|
||||
1. Namespace or project identifiers
|
||||
1. Portions of URIs
|
||||
1. File paths or names
|
||||
1. Random values
|
||||
|
||||
For the [Markdown snapshot testing](#markdown-snapshot-testing) to work
|
||||
properly, you must account for these differences in a way that ensures the tests are reliable,
|
||||
and always behave the same across different test runs or environments.
|
||||
|
||||
To account for these differences, there is a process called **_normalization_**. Normalization
|
||||
allows custom regular expressions with
|
||||
[_capturing groups_](https://ruby-doc.org/core-3.1.2/Regexp.html#class-Regexp-label-Capturing)
|
||||
to be applied to two different versions of HTML or JSON for a given Markdown example,
|
||||
and the contents of the captured groups can be replaced with the same fixed values.
|
||||
|
||||
Then, the two normalized versions can be compared to each other to ensure all other non-variable
|
||||
content is identical.
|
||||
|
||||
NOTE:
|
||||
We don't care about verifying specific attribute values here, so
|
||||
it's OK if the normalizations discard and replace these variable values with fixed values.
|
||||
Different testing levels have different purposes:
|
||||
|
||||
1. [Markdown snapshot testing](#markdown-snapshot-testing) is intended to enforce the structure of
|
||||
the rendered HTML/JSON, and to ensure that it conforms to the canonical specification.
|
||||
1. Individual unit tests of the implementation for a specific Markdown example are responsible for
|
||||
specific and targeted testing of these variable values.
|
||||
|
||||
We also use this same regex capture-and-replace normalization approach for
|
||||
[canonicalization of HTML](#canonicalization-of-html), because it is essentially the same process.
|
||||
With canonicalization, instead of just replacing variable values, we are removing non-canonical
|
||||
portions of the HTML.
|
||||
|
||||
Refer to [`glfm_example_normalizations.yml`](#glfm_example_normalizationsyml) for a detailed explanation
|
||||
of how the normalizations are specified.
|
||||
|
||||
## Goals
|
||||
|
||||
Given the constraints above, we have a few goals related to the GLFM
|
||||
|
@ -593,7 +643,7 @@ controls the behavior of the [scripts](#scripts) and [tests](#types-of-markdown-
|
|||
- The `skip_running_*` control allow Markdown conformance tests or
|
||||
Markdown snapshot tests to be skipped for individual examples.
|
||||
- This allows control over skipping this processing or testing of various examples when they
|
||||
are unimplemented, partially implemented, broken, or cannot be generated or tested for some reason.
|
||||
are unimplemented, partially implemented, broken, cannot be generated, or cannot be tested for some reason.
|
||||
- All entries default to false. They can be set to true by specifying a Ruby
|
||||
value which evaluates as truthy. This could be the boolean `true` value, but ideally should
|
||||
be a string describing why the example's updating or testing is being skipped.
|
||||
|
@ -641,6 +691,63 @@ The following optional entries are supported for each example. They all default
|
|||
skip_running_snapshot_prosemirror_json_tests: 'An explanation of the reason for skipping.'
|
||||
```
|
||||
|
||||
##### `glfm_example_normalizations.yml`
|
||||
|
||||
[`glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml)
|
||||
controls the [normalization](#normalization) process. It allows one or more `regex`/`replacement` pairs
|
||||
to be specified for a Markdown example.
|
||||
|
||||
- It is manually updated.
|
||||
- It has a nested structure corresponding to the example and type of entry it refers to.
|
||||
- It extensively uses [YAML anchors and aliases](https://yaml.org/spec/1.2.2/#692-node-anchors)
|
||||
to avoid duplication of `regex`/`replacement` pairs and allow them to be shared across multiple examples.
|
||||
- The YAML anchors use a naming convention based on the index number of the example, to
|
||||
ensure unique anchor names and avoid naming conflicts.
|
||||
|
||||
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_normalizations.yml` sample entries:
|
||||
|
||||
```yaml
|
||||
# NOTE: All YAML anchors which are shared across one or more examples are defined in the `00_shared` section.
|
||||
00_shared:
|
||||
00_uri: &00_uri
|
||||
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
|
||||
replacement: '\1\2URI_PREFIX\4'
|
||||
01_01__section_one__example_containing_a_uri__001:
|
||||
html:
|
||||
static:
|
||||
canonical:
|
||||
01_01_uri: *00_uri
|
||||
snapshot:
|
||||
01_01_uri: *00_uri
|
||||
wysiwyg:
|
||||
01_01_uri: *00_uri
|
||||
prosemirror_json:
|
||||
01_01_uri: *00_uri
|
||||
07_01__gitlab_specific_markdown__footnotes__001:
|
||||
# YAML anchors which are only shared within a single example should be defined within the example
|
||||
shared:
|
||||
07_01_href: &07_01_href
|
||||
- regex: '(href)(=")(.+?)(")'
|
||||
replacement: '\1\2REF\4'
|
||||
07_01_id: &07_01_id
|
||||
- regex: '(id)(=")(.+?)(")'
|
||||
replacement: '\1\2ID\4'
|
||||
html:
|
||||
static:
|
||||
canonical:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
||||
snapshot:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
||||
wysiwyg:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
||||
prosemirror_json:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
||||
```
|
||||
|
||||
#### Output specification files
|
||||
|
||||
The `glfm_specification/output` directory contains the CommonMark standard format
|
||||
|
@ -654,7 +761,8 @@ are colocated under the same parent folder `glfm_specification` with the other
|
|||
a mix of manually edited and generated files.
|
||||
|
||||
In GFM, `spec.txt` is [located in the test dir](https://github.com/github/cmark-gfm/blob/master/test/spec.txt),
|
||||
and in CommonMark it's located [in the project root](https://github.com/github/cmark-gfm/blob/master/test/spec.txt). No precedent exists for a standard location. In the future, we may decide to
|
||||
and in CommonMark it's located [in the project root](https://github.com/github/cmark-gfm/blob/master/test/spec.txt).
|
||||
No precedent exists for a standard location. In the future, we may decide to
|
||||
move or copy a hosted version of the rendered HTML `spec.html` version to another location or site.
|
||||
|
||||
##### spec.txt
|
||||
|
|
|
@ -333,7 +333,7 @@ GitLab has the following retention policies for vulnerabilities on non-default b
|
|||
|
||||
To view vulnerabilities, either:
|
||||
|
||||
- Re-run the pipeline.
|
||||
- Run a new pipeline.
|
||||
- Download the related CI job artifacts if they are available.
|
||||
|
||||
NOTE:
|
||||
|
|
|
@ -208,6 +208,54 @@ it 'searches' do
|
|||
end
|
||||
```
|
||||
|
||||
## Avoid multiple actions in `expect do ... raise_error` blocks
|
||||
|
||||
When you wrap multiple actions in a single `expect do ... end.not_to raise_error` or `expect do ... end.to raise_error` block,
|
||||
it can be hard to debug the actual cause of the failure, because of how the logs are printed. Important information can be truncated
|
||||
or missing altogether.
|
||||
|
||||
For example, if you encapsulate some actions and expectations in a private method in the test, like `expect_owner_permissions_allow_delete_issue`:
|
||||
|
||||
```ruby
|
||||
it "has Owner role with Owner permissions" do
|
||||
Page::Dashboard::Projects.perform do |projects|
|
||||
projects.filter_by_name(project.name)
|
||||
|
||||
expect(projects).to have_project_with_access_role(project.name, 'Owner')
|
||||
end
|
||||
|
||||
expect_owner_permissions_allow_delete_issue
|
||||
end
|
||||
```
|
||||
|
||||
Then, in the method itself:
|
||||
|
||||
```ruby
|
||||
#=> Good
|
||||
def expect_owner_permissions_allow_delete_issue
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end
|
||||
|
||||
#=> Bad
|
||||
def expect_owner_permissions_allow_delete_issue
|
||||
expect do
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end.not_to raise_error
|
||||
end
|
||||
```
|
||||
|
||||
## Prefer to split tests across multiple files
|
||||
|
||||
Our framework includes a couple of parallelization mechanisms that work by executing spec files in parallel.
|
||||
|
|
|
@ -246,3 +246,33 @@ A banned user can be unbanned using the Admin Area. To do this:
|
|||
|
||||
The user's state is set to active and they consume a
|
||||
[seat](../../subscriptions/self_managed/index.md#billable-users).
|
||||
|
||||
### Delete a user
|
||||
|
||||
Use the Admin Area to delete users.
|
||||
|
||||
1. On the top bar, select **Menu > Admin**.
|
||||
1. On the left sidebar, select **Overview > Users**.
|
||||
1. Select the **Banned** tab.
|
||||
1. Optional. Select a user.
|
||||
1. Select the **{settings}** **User administration** dropdown list.
|
||||
1. Select **Delete user**.
|
||||
1. Type the username.
|
||||
1. Select **Delete user**.
|
||||
|
||||
NOTE:
|
||||
You can only delete a user if there are inherited or direct owners of a group. You cannot delete a user if they are the only group owner.
|
||||
|
||||
You can also delete a user and their contributions, such as merge requests, issues, and groups of which they are the only group owner.
|
||||
|
||||
1. On the top bar, select **Menu > Admin**.
|
||||
1. On the left sidebar, select **Overview > Users**.
|
||||
1. Select the **Banned** tab.
|
||||
1. Optional. Select a user.
|
||||
1. Select the **{settings}** **User administration** dropdown list.
|
||||
1. Select **Delete user and contributions**.
|
||||
1. Type the username.
|
||||
1. Select **Delete user and contributions**.
|
||||
|
||||
NOTE:
|
||||
Before 15.1, additionally groups of which deleted user were the only owner among direct members were deleted.
|
||||
|
|
|
@ -1,23 +1,20 @@
|
|||
# First GitLab-Specific Section with Examples
|
||||
# GitLab-Specific Markdown
|
||||
|
||||
## Strong but with two asterisks
|
||||
Currently, only some of the GitLab-specific markdown features are
|
||||
listed in this section. We will eventually add all
|
||||
GitLab-specific features currently listed as supported in the
|
||||
[user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html).
|
||||
|
||||
```````````````````````````````` example
|
||||
**bold**
|
||||
.
|
||||
<p><strong>bold</strong></p>
|
||||
````````````````````````````````
|
||||
|
||||
# Second GitLab-Specific Section with Examples
|
||||
|
||||
## Strong but with HTML
|
||||
|
||||
```````````````````````````````` example
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
.
|
||||
<p><strong>
|
||||
bold
|
||||
</strong></p>
|
||||
There is currently only this single top-level heading, but the
|
||||
examples may be split into multiple top-level headings in the future.
|
||||
|
||||
## Footnotes
|
||||
|
||||
See
|
||||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example gitlab footnote
|
||||
footnote reference tag [^1]
|
||||
|
||||
[^1]: footnote text
|
||||
````````````````````````````````
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
# See the following documentation for more info on normalization:
|
||||
#
|
||||
# - https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#normalization
|
||||
# - https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#glfm_example_normalizationsyml
|
||||
#
|
||||
# NOTE: All YAML anchors which are shared across one or more entries are defined in the `00_shared` section.
|
||||
00_shared:
|
||||
00_uri: &00_uri
|
||||
- regex: '(href|data-src)(=")(.*?)(test-file\.(png|zip)")'
|
||||
replacement: '\1\2URI_PREFIX\4'
|
||||
07_01__gitlab_specific_markdown__footnotes__001:
|
||||
html:
|
||||
static:
|
||||
shared:
|
||||
07_01_href: &07_01_href
|
||||
- regex: '(href)(=")(.+?)(")'
|
||||
replacement: '\1\2REF\4'
|
||||
07_01_id: &07_01_id
|
||||
- regex: '(id)(=")(.+?)(")'
|
||||
replacement: '\1\2ID\4'
|
||||
canonical:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
||||
snapshot:
|
||||
07_01_href: *07_01_href
|
||||
07_01_id: *07_01_id
|
|
@ -9600,28 +9600,25 @@ Multiple spaces
|
|||
````````````````````````````````
|
||||
|
||||
|
||||
# First GitLab-Specific Section with Examples
|
||||
# GitLab-Specific Markdown
|
||||
|
||||
## Strong but with two asterisks
|
||||
Currently, only some of the GitLab-specific markdown features are
|
||||
listed in this section. We will eventually add all
|
||||
GitLab-specific features currently listed as supported in the
|
||||
[user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html).
|
||||
|
||||
```````````````````````````````` example
|
||||
**bold**
|
||||
.
|
||||
<p><strong>bold</strong></p>
|
||||
````````````````````````````````
|
||||
There is currently only this single top-level heading, but the
|
||||
examples may be split into multiple top-level headings in the future.
|
||||
|
||||
# Second GitLab-Specific Section with Examples
|
||||
## Footnotes
|
||||
|
||||
## Strong but with HTML
|
||||
See
|
||||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
.
|
||||
<p><strong>
|
||||
bold
|
||||
</strong></p>
|
||||
```````````````````````````````` example gitlab footnote
|
||||
footnote reference tag [^1]
|
||||
|
||||
[^1]: footnote text
|
||||
````````````````````````````````
|
||||
|
||||
<!-- END TESTS -->
|
||||
|
|
|
@ -32,8 +32,8 @@ module Gitlab
|
|||
def mail_metadata
|
||||
{
|
||||
mail_uid: mail.message_id,
|
||||
from_address: mail.from,
|
||||
to_address: mail.to,
|
||||
from_address: from,
|
||||
to_address: to,
|
||||
mail_key: mail_key,
|
||||
references: Array(mail.references),
|
||||
delivered_to: delivered_to.map(&:value),
|
||||
|
@ -42,7 +42,7 @@ module Gitlab
|
|||
# reduced down to what looks like an email in the received headers
|
||||
received_recipients: recipients_from_received_headers,
|
||||
meta: {
|
||||
client_id: "email/#{mail.from.first}",
|
||||
client_id: "email/#{from.first}",
|
||||
project: handler&.project&.full_path
|
||||
}
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def key_from_to_header
|
||||
mail.to.find do |address|
|
||||
to.find do |address|
|
||||
key = email_class.key_from_address(address)
|
||||
break key if key
|
||||
end
|
||||
|
@ -112,6 +112,14 @@ module Gitlab
|
|||
end
|
||||
end
|
||||
|
||||
def from
|
||||
Array(mail.from)
|
||||
end
|
||||
|
||||
def to
|
||||
Array(mail.to)
|
||||
end
|
||||
|
||||
def delivered_to
|
||||
Array(mail[:delivered_to])
|
||||
end
|
||||
|
|
|
@ -10918,6 +10918,9 @@ msgstr ""
|
|||
msgid "CsvParser|Unable to auto-detect delimiter; defaulted to \",\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Cue dramatic background music..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Current"
|
||||
msgstr ""
|
||||
|
||||
|
@ -23049,6 +23052,9 @@ msgstr ""
|
|||
msgid "Loading the GitLab IDE..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading, please wait."
|
||||
msgstr ""
|
||||
|
||||
msgid "Loading..."
|
||||
msgstr ""
|
||||
|
||||
|
@ -42744,9 +42750,6 @@ msgstr ""
|
|||
msgid "What is squashing?"
|
||||
msgstr ""
|
||||
|
||||
msgid "What is your job title? (optional)"
|
||||
msgstr ""
|
||||
|
||||
msgid "What templates can I create?"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
"@babel/preset-env": "^7.10.1",
|
||||
"@gitlab/at.js": "1.5.7",
|
||||
"@gitlab/favicon-overlay": "2.0.0",
|
||||
"@gitlab/svgs": "2.16.0",
|
||||
"@gitlab/svgs": "2.17.0",
|
||||
"@gitlab/ui": "40.7.1",
|
||||
"@gitlab/visual-review-tools": "1.7.3",
|
||||
"@rails/actioncable": "6.1.4-7",
|
||||
|
|
47
qa/README.md
47
qa/README.md
|
@ -41,19 +41,30 @@ This is the recommended option if you would like to contribute to the tests.
|
|||
|
||||
Note that tests are using `Chrome` web browser by default so it should be installed and present in `PATH`.
|
||||
|
||||
## CI
|
||||
|
||||
Tests are executed in merge request pipelines as part of the development lifecycle.
|
||||
|
||||
- [Review app environment](../doc/development/testing_guide/review_apps.md)
|
||||
- [package-and-qa](../doc/development/testing_guide/end_to_end/index.md#testing-code-in-merge-requests)
|
||||
|
||||
### Logging
|
||||
|
||||
By default tests on CI use `info` log level. `debug` level is still available in case of failure debugging. Logs are stored in jobs artifacts.
|
||||
|
||||
### Writing tests
|
||||
|
||||
- [Writing tests from scratch tutorial](../doc/development/testing_guide/end_to_end/beginners_guide.md)
|
||||
- [Best practices](../doc/development/testing_guide/best_practices.md)
|
||||
- [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md)
|
||||
- [Guidelines](../doc/development/testing_guide/index.md)
|
||||
- [Tests with special setup for local environments](../doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md)
|
||||
- [Best practices](../doc/development/testing_guide/best_practices.md)
|
||||
- [Using page objects](../doc/development/testing_guide/end_to_end/page_objects.md)
|
||||
- [Guidelines](../doc/development/testing_guide/index.md)
|
||||
- [Tests with special setup for local environments](../doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md)
|
||||
|
||||
### Run the end-to-end tests in a local development environment
|
||||
|
||||
1. Follow the instructions to [install GDK](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/main/doc/index.md), your local GitLab development environment.
|
||||
|
||||
1. Navigate to the QA folder and run the following commands.
|
||||
1. Navigate to the QA folder and run the following commands.
|
||||
|
||||
```bash
|
||||
cd gitlab-development-kit/gitlab/qa
|
||||
|
@ -77,7 +88,6 @@ bundle exec bin/qa Test::Instance::All {GDK IP ADDRESS}
|
|||
- Note: If you want to run tests requiring SSH against GDK, you will need to [modify your GDK setup](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md).
|
||||
- Note: If this is your first time running GDK, you can use the password pre-set for `root`. [See supported GitLab environment variables](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#supported-gitlab-environment-variables). If you have changed your `root` password, use that when exporting `GITLAB_INITIAL_ROOT_PASSWORD`.
|
||||
|
||||
|
||||
#### Running EE tests
|
||||
|
||||
When running EE tests you'll need to have a license available. GitLab engineers can [request a license](https://about.gitlab.com/handbook/developer-onboarding/#working-on-gitlab-ee).
|
||||
|
@ -88,12 +98,12 @@ Once you have the license file you can export it as an environment variable and
|
|||
export EE_LICENSE=$(cat /path/to/gitlab_license)
|
||||
```
|
||||
|
||||
### Running specific tests
|
||||
#### Running specific tests
|
||||
|
||||
You can also supply specific tests to run as another parameter. For example, to
|
||||
run the repository-related specs, you can execute:
|
||||
|
||||
```console
|
||||
```shell
|
||||
bundle exec rspec qa/specs/features/browser_ui/3_create/repository
|
||||
```
|
||||
|
||||
|
@ -114,7 +124,7 @@ When running tests against GDK, the default address is `http://127.0.0.1:3000`.
|
|||
QA_GITLAB_URL=https://gdk.test:3000 bundle exec rspec
|
||||
```
|
||||
|
||||
### Overriding the authenticated user
|
||||
#### Overriding the authenticated user
|
||||
|
||||
Unless told otherwise, the QA tests will run as the default `root` user seeded
|
||||
by the GDK.
|
||||
|
@ -147,7 +157,7 @@ GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sa
|
|||
|
||||
All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/what_tests_can_be_run.md#supported-environment-variables).
|
||||
|
||||
### Sending additional cookies
|
||||
#### Sending additional cookies
|
||||
|
||||
The environment variable `QA_COOKIES` can be set to send additional cookies
|
||||
on every request. This is necessary on gitlab.com to direct traffic to the
|
||||
|
@ -155,6 +165,22 @@ canary fleet. To do this set `QA_COOKIES="gitlab_canary=true"`.
|
|||
|
||||
To set multiple cookies, separate them with the `;` character, for example: `QA_COOKIES="cookie1=value;cookie2=value2"`
|
||||
|
||||
#### Headless browser
|
||||
|
||||
By default tests use headless browser. To override that, `WEBDRIVER_HEADLESS` must be set to `false`:
|
||||
|
||||
```shell
|
||||
WEBDRIVER_HEADLESS=false bundle exec rspec
|
||||
```
|
||||
|
||||
#### Log level
|
||||
|
||||
By default, the tests use the `info` log level. To change the test's log level, the environment variable `QA_LOG_LEVEL` can be set:
|
||||
|
||||
```shell
|
||||
QA_LOG_LEVEL=debug bundle exec rspec
|
||||
```
|
||||
|
||||
### Building a Docker image to test
|
||||
|
||||
Once you have made changes to the CE/EE repositories, you may want to build a
|
||||
|
@ -182,7 +208,6 @@ bundle exec rspec --tag quarantine
|
|||
|
||||
`bin/qa` is an additional custom wrapper script that abstracts away some of the more complicated setups that some tests require. This option requires test scenario and test instance's Gitlab address to be specified in the command. For example, to run any `Instance` scenario test, the following command can be used:
|
||||
|
||||
|
||||
```shell
|
||||
bundle exec bin/qa Test::Instance::All http://localhost:3000
|
||||
```
|
||||
|
|
|
@ -73,25 +73,21 @@ module QA
|
|||
private
|
||||
|
||||
def expect_owner_permissions_allow_delete_issue
|
||||
expect do
|
||||
issue.visit!
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
Page::Project::Issue::Show.perform(&:delete_issue)
|
||||
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end.not_to raise_error
|
||||
Page::Project::Issue::Index.perform do |index|
|
||||
expect(index).not_to have_issue(issue)
|
||||
end
|
||||
end
|
||||
|
||||
def expect_maintainer_permissions_do_not_allow_delete_issue
|
||||
expect do
|
||||
issue.visit!
|
||||
issue.visit!
|
||||
|
||||
Page::Project::Issue::Show.perform do |issue|
|
||||
expect(issue).not_to have_delete_issue_button
|
||||
end
|
||||
end.not_to raise_error
|
||||
Page::Project::Issue::Show.perform do |issue|
|
||||
expect(issue).not_to have_delete_issue_button
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -101,6 +101,7 @@ RSpec.describe 'Merge request > User sees versions', :js do
|
|||
outdated_diff_note.save!
|
||||
|
||||
refresh
|
||||
wait_for_requests
|
||||
|
||||
expect(page).to have_css(".diffs .notes[data-discussion-id='#{outdated_diff_note.discussion_id}']")
|
||||
end
|
||||
|
|
|
@ -26,6 +26,8 @@ RSpec.describe 'User comments on a diff', :js do
|
|||
let(:user) { create(:user) }
|
||||
|
||||
before do
|
||||
stub_feature_flags(paginated_notes: false)
|
||||
|
||||
project.add_maintainer(user)
|
||||
sign_in(user)
|
||||
|
||||
|
|
|
@ -2012,9 +2012,6 @@
|
|||
06_15__inlines__textual_content__003:
|
||||
spec_txt_example_position: 673
|
||||
source_specification: commonmark
|
||||
07_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
|
||||
07_01__gitlab_specific_markdown__footnotes__001:
|
||||
spec_txt_example_position: 674
|
||||
source_specification: commonmark
|
||||
08_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
|
||||
spec_txt_example_position: 675
|
||||
source_specification: commonmark
|
||||
source_specification: gitlab
|
||||
|
|
83
spec/fixtures/glfm/example_snapshots/html.yml
vendored
83
spec/fixtures/glfm/example_snapshots/html.yml
vendored
|
@ -130,9 +130,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>foo
|
||||
</p><ul bullet="*"><li><p>bar
|
||||
</p><ul bullet="*"><li><p>baz</p></li></ul></li></ul></li></ul>
|
||||
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p></li></ul></li></ul></li></ul>
|
||||
02_01__preliminaries__tabs__010:
|
||||
canonical: |
|
||||
<h1>Foo</h1>
|
||||
|
@ -1849,7 +1847,7 @@
|
|||
<p data-sourcepos="2:1-2:5" dir="auto"><em>baz</em></p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__030:
|
||||
canonical: |
|
||||
<script>
|
||||
|
@ -1872,7 +1870,7 @@
|
|||
<p data-sourcepos="5:1-5:4" dir="auto">okay</p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__032:
|
||||
canonical: |
|
||||
<?php
|
||||
|
@ -1888,7 +1886,7 @@
|
|||
<p data-sourcepos="6:1-6:4" dir="auto">okay</p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__033:
|
||||
canonical: |
|
||||
<!DOCTYPE html>
|
||||
|
@ -1915,7 +1913,7 @@
|
|||
<p data-sourcepos="13:1-13:4" dir="auto">okay</p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__035:
|
||||
canonical: |2
|
||||
<!-- foo -->
|
||||
|
@ -1927,7 +1925,7 @@
|
|||
foo --></span></code></pre>\n<copy-code></copy-code>\n</div>"
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__036:
|
||||
canonical: |2
|
||||
<div>
|
||||
|
@ -3898,10 +3896,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>foo
|
||||
</p><ul bullet="*"><li><p>bar
|
||||
</p><ul bullet="*"><li><p>baz
|
||||
</p><ul bullet="*"><li><p>boo</p></li></ul></li></ul></li></ul></li></ul>
|
||||
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p><ul bullet="*"><li><p>boo</p></li></ul></li></ul></li></ul></li></ul>
|
||||
05_02__container_blocks__list_items__043:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -3937,8 +3932,7 @@
|
|||
</li>
|
||||
</ol>
|
||||
wysiwyg: |-
|
||||
<ol parens="false"><li><p>foo
|
||||
</p><ul bullet="*"><li><p>bar</p></li></ul></li></ol>
|
||||
<ol parens="false"><li><p>foo</p><ul bullet="*"><li><p>bar</p></li></ul></li></ol>
|
||||
05_02__container_blocks__list_items__045:
|
||||
canonical: |
|
||||
<ol start="10">
|
||||
|
@ -4072,8 +4066,7 @@
|
|||
<li data-sourcepos="9:1-9:5">baz</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "input" not supported by this converter. Please, provide an specification.
|
||||
<ul bullet="*"><li><p>baz</p></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__050:
|
||||
canonical: |
|
||||
<ol>
|
||||
|
@ -4190,9 +4183,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>foo
|
||||
</p><ul bullet="*"><li><p>bar
|
||||
</p><ul bullet="*"><li><p>baz</p><p>bim</p></li></ul></li></ul></li></ul>
|
||||
<ul bullet="*"><li><p>foo</p><ul bullet="*"><li><p>bar</p><ul bullet="*"><li><p>baz</p><p>bim</p></li></ul></li></ul></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__056:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4216,7 +4207,7 @@
|
|||
</ul>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__057:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4248,7 +4239,7 @@
|
|||
</div>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__058:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4508,8 +4499,7 @@
|
|||
<li data-sourcepos="5:1-5:3">d</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>a
|
||||
</p><ul bullet="*"><li><p>b</p><p>c</p></li></ul></li><li><p>d</p></li></ul>
|
||||
<ul bullet="*"><li><p>a</p><ul bullet="*"><li><p>b</p><p>c</p></li></ul></li><li><p>d</p></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__068:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4530,8 +4520,7 @@
|
|||
<li data-sourcepos="4:1-4:3">c</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>a
|
||||
</p><blockquote multiline="false"><p>b</p></blockquote></li><li><p>c</p></li></ul>
|
||||
<ul bullet="*"><li><p>a</p><blockquote multiline="false"><p>b</p></blockquote></li><li><p>c</p></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__069:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4558,8 +4547,7 @@
|
|||
<li data-sourcepos="6:1-6:3">d</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>a
|
||||
</p><blockquote multiline="false"><p>b</p></blockquote><pre class="content-editor-code-block undefined code highlight"><code>c</code></pre></li><li><p>d</p></li></ul>
|
||||
<ul bullet="*"><li><p>a</p><blockquote multiline="false"><p>b</p></blockquote><pre class="content-editor-code-block undefined code highlight"><code>c</code></pre></li><li><p>d</p></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__070:
|
||||
canonical: |
|
||||
<ul>
|
||||
|
@ -4589,8 +4577,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
wysiwyg: |-
|
||||
<ul bullet="*"><li><p>a
|
||||
</p><ul bullet="*"><li><p>b</p></li></ul></li></ul>
|
||||
<ul bullet="*"><li><p>a</p><ul bullet="*"><li><p>b</p></li></ul></li></ul>
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__072:
|
||||
canonical: |
|
||||
<ol>
|
||||
|
@ -7217,7 +7204,7 @@
|
|||
<p data-sourcepos="1:1-2:25" dir="auto">foo </p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__014:
|
||||
canonical: |
|
||||
<p>foo <!-- not a comment -- two hyphens --></p>
|
||||
|
@ -7241,7 +7228,7 @@
|
|||
<p data-sourcepos="1:1-1:21" dir="auto">foo <?php echo $a; ?></p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__017:
|
||||
canonical: |
|
||||
<p>foo <!ELEMENT br EMPTY></p>
|
||||
|
@ -7249,7 +7236,7 @@
|
|||
<p data-sourcepos="1:1-1:23" dir="auto">foo </p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__018:
|
||||
canonical: |
|
||||
<p>foo <![CDATA[>&<]]></p>
|
||||
|
@ -7257,7 +7244,7 @@
|
|||
<p data-sourcepos="1:1-1:19" dir="auto">foo &</p>
|
||||
wysiwyg: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__019:
|
||||
canonical: |
|
||||
<p>foo <a href="ö"></p>
|
||||
|
@ -7462,23 +7449,17 @@
|
|||
<p data-sourcepos="1:1-1:19" dir="auto">Multiple spaces</p>
|
||||
wysiwyg: |-
|
||||
<p>Multiple spaces</p>
|
||||
07_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
|
||||
canonical: |
|
||||
<p><strong>bold</strong></p>
|
||||
07_01__gitlab_specific_markdown__footnotes__001:
|
||||
canonical: ""
|
||||
static: |-
|
||||
<p data-sourcepos="1:1-1:8" dir="auto"><strong>bold</strong></p>
|
||||
<p data-sourcepos="1:1-1:27" dir="auto">footnote reference tag <sup class="footnote-ref"><a href="#fn-1-2118" id="fnref-1-2118" data-footnote-ref>1</a></sup></p>
|
||||
<section data-footnotes class="footnotes">
|
||||
<ol>
|
||||
<li id="fn-1-2118">
|
||||
<p data-sourcepos="3:7-3:19">footnote text <a href="#fnref-1-2118" data-footnote-backref aria-label="Back to content" class="footnote-backref"><gl-emoji title="leftwards arrow with hook" data-name="leftwards_arrow_with_hook" data-unicode-version="1.1">↩</gl-emoji></a></p>
|
||||
</li>
|
||||
</ol>
|
||||
</section>
|
||||
wysiwyg: |-
|
||||
<p><strong>bold</strong></p>
|
||||
08_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001:
|
||||
canonical: |
|
||||
<p><strong>
|
||||
bold
|
||||
</strong></p>
|
||||
static: |-
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
wysiwyg: |-
|
||||
<p><strong>
|
||||
bold
|
||||
</strong></p>
|
||||
Error - check implementation:
|
||||
Hast node of type "sup" not supported by this converter. Please, provide an specification.
|
||||
|
|
|
@ -2195,9 +2195,7 @@
|
|||
Foo χρῆν
|
||||
06_15__inlines__textual_content__003: |
|
||||
Multiple spaces
|
||||
07_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |
|
||||
**bold**
|
||||
08_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
07_01__gitlab_specific_markdown__footnotes__001: |
|
||||
footnote reference tag [^1]
|
||||
|
||||
[^1]: footnote text
|
||||
|
|
|
@ -233,7 +233,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "foo\n"
|
||||
"text": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -251,7 +251,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "bar\n"
|
||||
"text": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3121,16 +3121,16 @@
|
|||
Hast node of type "style" not supported by this converter. Please, provide an specification.
|
||||
04_06__leaf_blocks__html_blocks__029: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__030: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "script" not supported by this converter. Please, provide an specification.
|
||||
04_06__leaf_blocks__html_blocks__031: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__032: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__033: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -3142,10 +3142,10 @@
|
|||
}
|
||||
04_06__leaf_blocks__html_blocks__034: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__035: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
04_06__leaf_blocks__html_blocks__036: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "div" not supported by this converter. Please, provide an specification.
|
||||
|
@ -6691,7 +6691,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "foo\n"
|
||||
"text": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -6709,7 +6709,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "bar\n"
|
||||
"text": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -6727,7 +6727,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "baz\n"
|
||||
"text": "baz"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -6856,7 +6856,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "foo\n"
|
||||
"text": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -7100,8 +7100,33 @@
|
|||
]
|
||||
}
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__049: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "input" not supported by this converter. Please, provide an specification.
|
||||
{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "bulletList",
|
||||
"attrs": {
|
||||
"bullet": "*"
|
||||
},
|
||||
"content": [
|
||||
{
|
||||
"type": "listItem",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "baz"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__050: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -7346,7 +7371,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "foo\n"
|
||||
"text": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -7364,7 +7389,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "bar\n"
|
||||
"text": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -7411,10 +7436,10 @@
|
|||
}
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__056: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__057: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
05_02__container_blocks__list_items__motivation__task_list_items_extension__lists__058: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -8018,7 +8043,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "a\n"
|
||||
"text": "a"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -8091,7 +8116,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "a\n"
|
||||
"text": "a"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -8150,7 +8175,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "a\n"
|
||||
"text": "a"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -8250,7 +8275,7 @@
|
|||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "a\n"
|
||||
"text": "a"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -16576,7 +16601,7 @@
|
|||
}
|
||||
06_11__inlines__raw_html__013: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__014: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -16618,13 +16643,13 @@
|
|||
}
|
||||
06_11__inlines__raw_html__016: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__017: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__018: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "comment" not supported by this converter. Please, provide an specification.
|
||||
Cannot destructure property 'className' of 'hastNode.properties' as it is undefined.
|
||||
06_11__inlines__raw_html__019: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -16988,43 +17013,6 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
07_01__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001: |-
|
||||
{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"marks": [
|
||||
{
|
||||
"type": "bold"
|
||||
}
|
||||
],
|
||||
"text": "bold"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
08_01__second_gitlab_specific_section_with_examples__strong_but_with_html__001: |-
|
||||
{
|
||||
"type": "doc",
|
||||
"content": [
|
||||
{
|
||||
"type": "paragraph",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"marks": [
|
||||
{
|
||||
"type": "bold"
|
||||
}
|
||||
],
|
||||
"text": "\nbold\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
07_01__gitlab_specific_markdown__footnotes__001: |-
|
||||
Error - check implementation:
|
||||
Hast node of type "sup" not supported by this converter. Please, provide an specification.
|
||||
|
|
|
@ -1382,6 +1382,29 @@ describe('Actions Notes Store', () => {
|
|||
],
|
||||
);
|
||||
});
|
||||
|
||||
it('dispatches `fetchDiscussionsBatch` action if `paginatedMrDiscussions` feature flag is enabled', () => {
|
||||
window.gon = { features: { paginatedMrDiscussions: true } };
|
||||
|
||||
return testAction(
|
||||
actions.fetchDiscussions,
|
||||
{ path: 'test-path', filter: 'test-filter', persistFilter: 'test-persist-filter' },
|
||||
null,
|
||||
[],
|
||||
[
|
||||
{
|
||||
type: 'fetchDiscussionsBatch',
|
||||
payload: {
|
||||
config: {
|
||||
params: { notes_filter: 'test-filter', persist_filter: 'test-persist-filter' },
|
||||
},
|
||||
path: 'test-path',
|
||||
perPage: 20,
|
||||
},
|
||||
},
|
||||
],
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchDiscussionsBatch', () => {
|
||||
|
@ -1401,6 +1424,7 @@ describe('Actions Notes Store', () => {
|
|||
null,
|
||||
[
|
||||
{ type: mutationTypes.ADD_OR_UPDATE_DISCUSSIONS, payload: { discussion } },
|
||||
{ type: mutationTypes.SET_DONE_FETCHING_BATCH_DISCUSSIONS, payload: true },
|
||||
{ type: mutationTypes.SET_FETCHING_DISCUSSIONS, payload: false },
|
||||
],
|
||||
[{ type: 'updateResolvableDiscussionsCounts' }],
|
||||
|
|
|
@ -883,4 +883,16 @@ describe('Notes Store mutations', () => {
|
|||
expect(state.discussions[0].position).toEqual(position);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SET_DONE_FETCHING_BATCH_DISCUSSIONS', () => {
|
||||
it('should set doneFetchingBatchDiscussions', () => {
|
||||
const state = {
|
||||
doneFetchingBatchDiscussions: false,
|
||||
};
|
||||
|
||||
mutations.SET_DONE_FETCHING_BATCH_DISCUSSIONS(state, true);
|
||||
|
||||
expect(state.doneFetchingBatchDiscussions).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -5,6 +5,7 @@ import CiIcon from '~/vue_shared/components/ci_icon.vue';
|
|||
import axios from '~/lib/utils/axios_utils';
|
||||
import PipelineStage from '~/pipelines/components/pipelines_list/pipeline_stage.vue';
|
||||
import eventHub from '~/pipelines/event_hub';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { stageReply } from '../../mock_data';
|
||||
|
||||
const dropdownPath = 'path.json';
|
||||
|
@ -58,6 +59,7 @@ describe('Pipelines stage component', () => {
|
|||
const findDropdownMenuTitle = () =>
|
||||
wrapper.find('[data-testid="pipeline-stage-dropdown-menu-title"]');
|
||||
const findMergeTrainWarning = () => wrapper.find('[data-testid="warning-message-merge-trains"]');
|
||||
const findLoadingState = () => wrapper.find('[data-testid="pipeline-stage-loading-state"]');
|
||||
|
||||
const openStageDropdown = () => {
|
||||
findDropdownToggle().trigger('click');
|
||||
|
@ -66,6 +68,29 @@ describe('Pipelines stage component', () => {
|
|||
});
|
||||
};
|
||||
|
||||
describe('loading state', () => {
|
||||
beforeEach(async () => {
|
||||
createComponent({ updateDropdown: true });
|
||||
|
||||
mock.onGet(dropdownPath).reply(200, stageReply);
|
||||
|
||||
await openStageDropdown();
|
||||
});
|
||||
|
||||
it('displays loading state while jobs are being fetched', () => {
|
||||
const expectedLoadingText = `${PipelineStage.i18n.loadingTextLineOne} ${PipelineStage.i18n.loadingTextLineTwo}`;
|
||||
|
||||
expect(findLoadingState().exists()).toBe(true);
|
||||
expect(findLoadingState().text()).toBe(expectedLoadingText);
|
||||
});
|
||||
|
||||
it('does not display loading state after jobs have been fetched', async () => {
|
||||
await waitForPromises();
|
||||
|
||||
expect(findLoadingState().exists()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('default appearance', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
|
|
|
@ -23,32 +23,26 @@ describe('WorkItemLinks', () => {
|
|||
wrapper.destroy();
|
||||
});
|
||||
|
||||
it('is collapsed by default', () => {
|
||||
expect(findToggleButton().props('icon')).toBe('angle-down');
|
||||
expect(findLinksBody().exists()).toBe(false);
|
||||
it('is expanded by default', () => {
|
||||
expect(findToggleButton().props('icon')).toBe('angle-up');
|
||||
expect(findLinksBody().exists()).toBe(true);
|
||||
});
|
||||
|
||||
it('expands on click toggle button', async () => {
|
||||
findToggleButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(findToggleButton().props('icon')).toBe('angle-up');
|
||||
expect(findLinksBody().exists()).toBe(true);
|
||||
expect(findToggleButton().props('icon')).toBe('angle-down');
|
||||
expect(findLinksBody().exists()).toBe(false);
|
||||
});
|
||||
|
||||
it('displays empty state if there are no links', async () => {
|
||||
findToggleButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
it('displays empty state if there are no links', () => {
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
expect(findToggleAddFormButton().exists()).toBe(true);
|
||||
});
|
||||
|
||||
describe('add link form', () => {
|
||||
it('displays form on click add button and hides form on cancel', async () => {
|
||||
findToggleButton().vm.$emit('click');
|
||||
await nextTick();
|
||||
|
||||
expect(findEmptyState().exists()).toBe(true);
|
||||
|
||||
findToggleAddFormButton().vm.$emit('click');
|
||||
|
|
|
@ -11,6 +11,7 @@ RSpec.describe Gitlab::Email::Receiver do
|
|||
let_it_be(:project) { create(:project) }
|
||||
|
||||
let(:handler) { double(:handler, project: project, execute: true, metrics_event: nil, metrics_params: nil) }
|
||||
let(:client_id) { 'email/jake@example.com' }
|
||||
|
||||
it 'correctly finds the mail key' do
|
||||
expect(Gitlab::Email::Handler).to receive(:for).with(an_instance_of(Mail::Message), 'gitlabhq/gitlabhq+auth_token').and_return(handler)
|
||||
|
@ -33,7 +34,7 @@ RSpec.describe Gitlab::Email::Receiver do
|
|||
metadata = receiver.mail_metadata
|
||||
|
||||
expect(metadata.keys).to match_array(%i(mail_uid from_address to_address mail_key references delivered_to envelope_to x_envelope_to meta received_recipients))
|
||||
expect(metadata[:meta]).to include(client_id: 'email/jake@example.com', project: project.full_path)
|
||||
expect(metadata[:meta]).to include(client_id: client_id, project: project.full_path)
|
||||
expect(metadata[meta_key]).to eq(meta_value)
|
||||
end
|
||||
end
|
||||
|
@ -126,6 +127,49 @@ RSpec.describe Gitlab::Email::Receiver do
|
|||
it_behaves_like 'failed receive'
|
||||
end
|
||||
|
||||
context "when the email's To field is blank" do
|
||||
before do
|
||||
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.example.com")
|
||||
end
|
||||
|
||||
let(:email_raw) do
|
||||
<<~EMAIL
|
||||
Delivered-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com
|
||||
From: "jake@example.com" <jake@example.com>
|
||||
Bcc: "support@example.com" <support@example.com>
|
||||
|
||||
Email content
|
||||
EMAIL
|
||||
end
|
||||
|
||||
let(:meta_key) { :delivered_to }
|
||||
let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com"] }
|
||||
|
||||
it_behaves_like 'successful receive'
|
||||
end
|
||||
|
||||
context "when the email's From field is blank" do
|
||||
before do
|
||||
stub_incoming_email_setting(enabled: true, address: "incoming+%{key}@appmail.example.com")
|
||||
end
|
||||
|
||||
let(:email_raw) do
|
||||
<<~EMAIL
|
||||
Delivered-To: incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com
|
||||
To: "support@example.com" <support@example.com>
|
||||
|
||||
Email content
|
||||
EMAIL
|
||||
end
|
||||
|
||||
let(:meta_key) { :delivered_to }
|
||||
let(:meta_value) { ["incoming+gitlabhq/gitlabhq+auth_token@appmail.example.com"] }
|
||||
|
||||
it_behaves_like 'successful receive' do
|
||||
let(:client_id) { 'email/' }
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the email was auto generated with X-Autoreply header' do
|
||||
let(:email_raw) { fixture_file('emails/auto_reply.eml') }
|
||||
let(:expected_error) { Gitlab::Email::AutoGeneratedEmailError }
|
||||
|
|
|
@ -5,6 +5,7 @@ require 'spec_helper'
|
|||
# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
|
||||
# for documentation on this spec.
|
||||
RSpec.describe API::Markdown, 'Snapshot' do
|
||||
glfm_specification_dir = File.expand_path('../../../glfm_specification', __dir__)
|
||||
glfm_example_snapshots_dir = File.expand_path('../../fixtures/glfm/example_snapshots', __dir__)
|
||||
include_context 'API::Markdown Snapshot shared context', glfm_example_snapshots_dir
|
||||
include_context 'with API::Markdown Snapshot shared context', glfm_specification_dir, glfm_example_snapshots_dir
|
||||
end
|
||||
|
|
|
@ -3,6 +3,70 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Projects::MergeRequestsController do
|
||||
describe 'GET #discussions' do
|
||||
let_it_be(:merge_request) { create(:merge_request) }
|
||||
let_it_be(:project) { merge_request.project }
|
||||
let_it_be(:user) { merge_request.author }
|
||||
let_it_be(:discussion) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
|
||||
let_it_be(:discussion_reply) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project, in_reply_to: discussion) }
|
||||
let_it_be(:state_event) { create(:resource_state_event, merge_request: merge_request) }
|
||||
let_it_be(:discussion_2) { create(:discussion_note_on_merge_request, noteable: merge_request, project: project) }
|
||||
let_it_be(:discussion_3) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
|
||||
|
||||
before do
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
context 'pagination' do
|
||||
def get_discussions(**params)
|
||||
get discussions_project_merge_request_path(project, merge_request, params: params.merge(format: :json))
|
||||
end
|
||||
|
||||
it 'returns paginated notes and cursor based on per_page param' do
|
||||
get_discussions(per_page: 2)
|
||||
|
||||
discussions = Gitlab::Json.parse(response.body)
|
||||
notes = discussions.flat_map { |d| d['notes'] }
|
||||
|
||||
expect(discussions.count).to eq(2)
|
||||
expect(notes).to match([
|
||||
a_hash_including('id' => discussion.id.to_s),
|
||||
a_hash_including('id' => discussion_reply.id.to_s),
|
||||
a_hash_including('type' => 'StateNote')
|
||||
])
|
||||
|
||||
cursor = response.header['X-Next-Page-Cursor']
|
||||
expect(cursor).to be_present
|
||||
|
||||
get_discussions(per_page: 1, cursor: cursor)
|
||||
|
||||
discussions = Gitlab::Json.parse(response.body)
|
||||
notes = discussions.flat_map { |d| d['notes'] }
|
||||
|
||||
expect(discussions.count).to eq(1)
|
||||
expect(notes).to match([
|
||||
a_hash_including('id' => discussion_2.id.to_s)
|
||||
])
|
||||
end
|
||||
|
||||
context 'when paginated_mr_discussions is disabled' do
|
||||
before do
|
||||
stub_feature_flags(paginated_mr_discussions: false)
|
||||
end
|
||||
|
||||
it 'returns all discussions and ignores per_page param' do
|
||||
get_discussions(per_page: 2)
|
||||
|
||||
discussions = Gitlab::Json.parse(response.body)
|
||||
notes = discussions.flat_map { |d| d['notes'] }
|
||||
|
||||
expect(discussions.count).to eq(4)
|
||||
expect(notes.count).to eq(5)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'token authentication' do
|
||||
context 'when public project' do
|
||||
let_it_be(:public_project) { create(:project, :public) }
|
||||
|
|
|
@ -53,8 +53,10 @@ end
|
|||
require 'rainbow/ext/string'
|
||||
Rainbow.enabled = false
|
||||
|
||||
require_relative('../ee/spec/spec_helper') if Gitlab.ee?
|
||||
# Require JH first because we need override some EE methods with JH methods,
|
||||
# if we load EE first, we can't find JH modules in prepend_mod method
|
||||
require_relative('../jh/spec/spec_helper') if Gitlab.jh?
|
||||
require_relative('../ee/spec/spec_helper') if Gitlab.ee?
|
||||
|
||||
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||
# in spec/support/ and its subdirectories.
|
||||
|
|
|
@ -4,7 +4,9 @@ require 'spec_helper'
|
|||
|
||||
# See https://docs.gitlab.com/ee/development/gitlab_flavored_markdown/specification_guide/#markdown-snapshot-testing
|
||||
# for documentation on this spec.
|
||||
RSpec.shared_context 'API::Markdown Snapshot shared context' do |glfm_example_snapshots_dir|
|
||||
# rubocop:disable Layout/LineLength
|
||||
RSpec.shared_context 'with API::Markdown Snapshot shared context' do |glfm_specification_dir, glfm_example_snapshots_dir|
|
||||
# rubocop:enable Layout/LineLength
|
||||
include ApiHelpers
|
||||
|
||||
markdown_examples, html_examples = %w[markdown.yml html.yml].map do |file_name|
|
||||
|
@ -12,7 +14,11 @@ RSpec.shared_context 'API::Markdown Snapshot shared context' do |glfm_example_sn
|
|||
YAML.safe_load(yaml, symbolize_names: true, aliases: true)
|
||||
end
|
||||
|
||||
if focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES']
|
||||
normalizations_yaml = File.read(
|
||||
"#{glfm_specification_dir}/input/gitlab_flavored_markdown/glfm_example_normalizations.yml")
|
||||
normalizations_by_example_name = YAML.safe_load(normalizations_yaml, symbolize_names: true, aliases: true)
|
||||
|
||||
if (focused_markdown_examples_string = ENV['FOCUSED_MARKDOWN_EXAMPLES'])
|
||||
focused_markdown_examples = focused_markdown_examples_string.split(',').map(&:strip).map(&:to_sym)
|
||||
markdown_examples.select! { |example_name| focused_markdown_examples.include?(example_name) }
|
||||
end
|
||||
|
@ -20,17 +26,38 @@ RSpec.shared_context 'API::Markdown Snapshot shared context' do |glfm_example_sn
|
|||
markdown_examples.each do |name, markdown|
|
||||
context "for #{name}" do
|
||||
let(:html) { html_examples.fetch(name).fetch(:static) }
|
||||
let(:normalizations) { normalizations_by_example_name.dig(name, :html, :static, :snapshot) }
|
||||
|
||||
it "verifies conversion of GLFM to HTML", :unlimited_max_formatted_output_length do
|
||||
api_url = api "/markdown"
|
||||
|
||||
# noinspection RubyResolve
|
||||
normalized_html = normalize_html(html, normalizations)
|
||||
|
||||
post api_url, params: { text: markdown, gfm: true }
|
||||
expect(response).to be_successful
|
||||
response_body = Gitlab::Json.parse(response.body)
|
||||
# Some requests have the HTML in the `html` key, others in the `body` key.
|
||||
response_html = response_body['body'] ? response_body.fetch('body') : response_body.fetch('html')
|
||||
# noinspection RubyResolve
|
||||
normalized_response_html = normalize_html(response_html, normalizations)
|
||||
|
||||
expect(response_html).to eq(html)
|
||||
expect(normalized_response_html).to eq(normalized_html)
|
||||
end
|
||||
|
||||
def normalize_html(html, normalizations)
|
||||
return html unless normalizations
|
||||
|
||||
normalized_html = html.dup
|
||||
normalizations.each_value do |normalization_entry|
|
||||
normalization_entry.each do |normalization|
|
||||
regex = normalization.fetch(:regex)
|
||||
replacement = normalization.fetch(:replacement)
|
||||
normalized_html.gsub!(%r{#{regex}}, replacement)
|
||||
end
|
||||
end
|
||||
|
||||
normalized_html
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -963,10 +963,10 @@
|
|||
stylelint-declaration-strict-value "1.8.0"
|
||||
stylelint-scss "4.1.0"
|
||||
|
||||
"@gitlab/svgs@2.16.0":
|
||||
version "2.16.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.16.0.tgz#b9551af803487158d232a8b4b806ec2bbc2fb9eb"
|
||||
integrity sha512-VvaB4ttd5mdE0ntiSZM/70dFeBu4iE4CyVJA72OyCfJLzmJEClsn81eYF44bT53BCy8rrZKgJG3lsvvlbeFCAA==
|
||||
"@gitlab/svgs@2.17.0":
|
||||
version "2.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-2.17.0.tgz#56d0d11744859b3e1da80dedab2396a95cd01a02"
|
||||
integrity sha512-+cmn4ptdOFjSC8ByqD41kj1xSQ9/YFYLq/Es+jy5t12HmUtvYL8YRfNTlvApReSJ8SM7scwleVy4S19M15Siqw==
|
||||
|
||||
"@gitlab/ui@40.7.1":
|
||||
version "40.7.1"
|
||||
|
|
Loading…
Reference in a new issue