From 067b3d04573d1473dbc6c81ef775d70c6636ff3f Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Tue, 14 Jun 2022 06:09:22 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .../services/hast_to_prosemirror_converter.js | 2 +- .../services/serialization_helpers.js | 97 ++++-- .../diffs/components/diff_file_header.vue | 13 +- .../issuable_timelogs.fragment.graphql | 3 + .../time_tracking/graphql/cache_update.js | 20 ++ .../mutations/delete_timelog.mutation.graphql | 5 + .../components/time_tracking/report.vue | 90 ++++- .../components/time_tracking/time_tracker.vue | 1 + .../vue_shared/components/web_ide_link.vue | 2 +- .../stylesheets/pages/merge_requests.scss | 15 - .../projects/merge_requests_controller.rb | 1 - .../creations/_new_submit.html.haml | 4 +- .../projects/merge_requests/show.html.haml | 4 +- .../_metadata_issuable_assignee.html.haml | 2 +- .../development/remove_diff_header_icons.yml | 8 - doc/ci/environments/protected_environments.md | 14 +- .../testing_guide/contract/consumer_tests.md | 308 ++++++++++++++++++ .../testing_guide/contract/index.md | 39 +++ .../testing_guide/contract/provider_tests.md | 177 ++++++++++ doc/development/testing_guide/index.md | 4 + .../img/time_tracking_report_v13_12.png | Bin 13073 -> 0 bytes .../img/time_tracking_report_v15_1.png | Bin 0 -> 31669 bytes doc/user/project/time_tracking.md | 22 +- locale/gitlab.pot | 13 +- .../generic_repository_spec.rb | 2 +- spec/contracts/README.md | 15 + .../remark_markdown_processing_spec.js | 67 ++++ .../services/markdown_serializer_spec.js | 83 +++-- .../diffs/components/diff_file_header_spec.js | 11 - .../components/time_tracking/mock_data.js | 28 +- .../components/time_tracking/report_spec.js | 86 ++++- 31 files changed, 985 insertions(+), 151 deletions(-) create mode 100644 app/assets/javascripts/sidebar/components/time_tracking/graphql/cache_update.js create mode 100644 app/assets/javascripts/sidebar/components/time_tracking/graphql/mutations/delete_timelog.mutation.graphql delete mode 100644 config/feature_flags/development/remove_diff_header_icons.yml create mode 100644 doc/development/testing_guide/contract/consumer_tests.md create mode 100644 doc/development/testing_guide/contract/index.md create mode 100644 doc/development/testing_guide/contract/provider_tests.md delete mode 100644 doc/user/project/img/time_tracking_report_v13_12.png create mode 100644 doc/user/project/img/time_tracking_report_v15_1.png create mode 100644 spec/contracts/README.md diff --git a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js index f38e4514393..2c462cdde91 100644 --- a/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js +++ b/app/assets/javascripts/content_editor/services/hast_to_prosemirror_converter.js @@ -63,7 +63,7 @@ function maybeMerge(a, b) { function createSourceMapAttributes(hastNode, source) { const { position } = hastNode; - return position.end + return position && position.end ? { sourceMapKey: `${position.start.offset}:${position.end.offset}`, sourceMarkdown: source.substring(position.start.offset, position.end.offset), diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js index 055a32420b2..88f5192af77 100644 --- a/app/assets/javascripts/content_editor/services/serialization_helpers.js +++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js @@ -12,22 +12,6 @@ const ignoreAttrs = { const tableMap = new WeakMap(); -// Source taken from -// prosemirror-markdown/src/to_markdown.js -export function isPlainURL(link, parent, index, side) { - if (link.attrs.title || !/^\w+:/.test(link.attrs.href)) return false; - const content = parent.child(index + (side < 0 ? -1 : 0)); - if ( - !content.isText || - content.text !== link.attrs.href || - content.marks[content.marks.length - 1] !== link - ) - return false; - if (index === (side < 0 ? 1 : parent.childCount - 1)) return true; - const next = parent.child(index + (side < 0 ? -2 : 1)); - return !link.isInSet(next.marks); -} - function containsOnlyText(node) { if (node.childCount === 1) { const child = node.child(0); @@ -498,10 +482,79 @@ const linkType = (sourceMarkdown) => { return LINK_HTML; }; +const removeUrlProtocol = (url) => url.replace(/^\w+:\/?\/?/, ''); + +const normalizeUrl = (url) => decodeURIComponent(removeUrlProtocol(url)); + +/** + * Validates that the provided URL is well-formed + * + * @param {String} url + * @returns Returns true when the browser’s URL constructor + * can successfully parse the URL string + */ +const isValidUrl = (url) => { + try { + return new URL(url) && true; + } catch { + return false; + } +}; + +const findChildWithMark = (mark, parent) => { + let child; + let offset; + let index; + + parent.forEach((_child, _offset, _index) => { + if (mark.isInSet(_child.marks)) { + child = _child; + offset = _offset; + index = _index; + } + }); + + return child ? { child, offset, index } : null; +}; + +/** + * This function detects whether a link should be serialized + * as an autolink. + * + * See https://github.github.com/gfm/#autolinks-extension- + * to understand the parsing rules of autolinks. + * */ +const isAutoLink = (linkMark, parent) => { + const { title, href } = linkMark.attrs; + + if (title || !/^\w+:/.test(href)) { + return false; + } + + const { child } = findChildWithMark(linkMark, parent); + + if ( + !child || + !child.isText || + !isValidUrl(href) || + normalizeUrl(child.text) !== normalizeUrl(href) + ) { + return false; + } + + return true; +}; + +/** + * Returns true if the user used brackets to the define + * the autolink in the original markdown source + */ +const isBracketAutoLink = (sourceMarkdown) => /^<.+?>$/.test(sourceMarkdown); + export const link = { - open(state, mark, parent, index) { - if (isPlainURL(mark, parent, index, 1)) { - return '<'; + open(state, mark, parent) { + if (isAutoLink(mark, parent)) { + return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '<' : ''; } const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; @@ -518,9 +571,9 @@ export const link = { return openTag('a', attrs); }, - close(state, mark, parent, index) { - if (isPlainURL(mark, parent, index, -1)) { - return '>'; + close(state, mark, parent) { + if (isAutoLink(mark, parent)) { + return isBracketAutoLink(mark.attrs.sourceMarkdown) ? '>' : ''; } const { canonicalSrc, href, title, sourceMarkdown } = mark.attrs; diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue index a75262ee303..07316f9433a 100644 --- a/app/assets/javascripts/diffs/components/diff_file_header.vue +++ b/app/assets/javascripts/diffs/components/diff_file_header.vue @@ -19,8 +19,6 @@ import { scrollToElement } from '~/lib/utils/common_utils'; import { truncateSha } from '~/lib/utils/text_utility'; import { __, s__, sprintf } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; -import FileIcon from '~/vue_shared/components/file_icon.vue'; -import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { DIFF_FILE_AUTOMATIC_COLLAPSE } from '../constants'; import { DIFF_FILE_HEADER } from '../i18n'; @@ -33,7 +31,6 @@ export default { components: { ClipboardButton, GlIcon, - FileIcon, DiffStats, GlBadge, GlButton, @@ -48,7 +45,7 @@ export default { GlTooltip: GlTooltipDirective, SafeHtml: GlSafeHtmlDirective, }, - mixins: [glFeatureFlagsMixin(), IdState({ idProp: (vm) => vm.diffFile.file_hash })], + mixins: [IdState({ idProp: (vm) => vm.diffFile.file_hash })], i18n: { ...DIFF_FILE_HEADER, compareButtonLabel: __('Compare submodule commit revisions'), @@ -301,14 +298,6 @@ export default { :href="titleLink" @click="handleFileNameClick" > -