diff --git a/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md b/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md
index e9e510da11e..3939fca44f1 100644
--- a/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md
+++ b/.gitlab/issue_templates/Navigation - Left Sidebar Proposals.md
@@ -6,7 +6,7 @@
### Checklist
-- [ ] If your proposal includes changes to the top-level menu items within the left sidebar, engage the [Foundations Product Design Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI will work with UX partners in product design, research, and technical writing, as applicable.
+- [ ] If your proposal includes changes to the menu items within the left sidebar, engage the [Foundations Product Manager](https://about.gitlab.com/handbook/product/categories/#foundations-group) for approval. The Foundations DRI will work with UX partners in product design, research, and technical writing, as applicable.
- [ ] Follow the [product development workflow](https://about.gitlab.com/handbook/product-development-flow/#validation-phase-2-problem-validation) validation process to ensure you are solving a well understood problem and that the proposed change is understandable and non-disruptive to users. Navigation-specific research is strongly encouraged.
- [ ] Engage the [Foundations](https://about.gitlab.com/handbook/product/categories/#foundations-group) team to ensure your proposal is in alignment with holistic changes happening to the left side bar.
- [ ] Consider whether you need to communicate the change somehow, or if you will have an interim period in the UI where your nav item will live in more than one place.
diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION
index 30e298c7494..76d05362056 100644
--- a/GITLAB_PAGES_VERSION
+++ b/GITLAB_PAGES_VERSION
@@ -1 +1 @@
-1.61.1
+1.62.0
diff --git a/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue b/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue
index f0726ff3e63..8caf12eac5f 100644
--- a/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue
+++ b/app/assets/javascripts/content_editor/components/bubble_menus/formatting.vue
@@ -10,6 +10,7 @@ import Code from '../../extensions/code';
import CodeBlockHighlight from '../../extensions/code_block_highlight';
import Diagram from '../../extensions/diagram';
import Frontmatter from '../../extensions/frontmatter';
+import ReferenceDefinition from '../../extensions/reference_definition';
import ToolbarButton from '../toolbar_button.vue';
export default {
@@ -35,6 +36,7 @@ export default {
Image.name,
Audio.name,
Video.name,
+ ReferenceDefinition.name,
];
return !exclude.some((type) => editor.isActive(type));
diff --git a/app/assets/javascripts/content_editor/extensions/reference_definition.js b/app/assets/javascripts/content_editor/extensions/reference_definition.js
new file mode 100644
index 00000000000..e2762fe9fd9
--- /dev/null
+++ b/app/assets/javascripts/content_editor/extensions/reference_definition.js
@@ -0,0 +1,29 @@
+import { Node } from '@tiptap/core';
+
+export default Node.create({
+ name: 'referenceDefinition',
+
+ group: 'block',
+
+ content: 'text*',
+
+ marks: '',
+
+ addAttributes() {
+ return {
+ identifier: {
+ default: null,
+ },
+ url: {
+ default: null,
+ },
+ title: {
+ default: null,
+ },
+ };
+ },
+
+ renderHTML() {
+ return ['pre', {}, 0];
+ },
+});
diff --git a/app/assets/javascripts/content_editor/extensions/sourcemap.js b/app/assets/javascripts/content_editor/extensions/sourcemap.js
index 618f17b1c5e..fdac852e55c 100644
--- a/app/assets/javascripts/content_editor/extensions/sourcemap.js
+++ b/app/assets/javascripts/content_editor/extensions/sourcemap.js
@@ -16,6 +16,7 @@ import Link from './link';
import ListItem from './list_item';
import OrderedList from './ordered_list';
import Paragraph from './paragraph';
+import ReferenceDefinition from './reference_definition';
import Strike from './strike';
import TaskList from './task_list';
import TaskItem from './task_item';
@@ -45,6 +46,7 @@ export default Extension.create({
ListItem.name,
OrderedList.name,
Paragraph.name,
+ ReferenceDefinition.name,
Strike.name,
TaskList.name,
TaskItem.name,
diff --git a/app/assets/javascripts/content_editor/services/create_content_editor.js b/app/assets/javascripts/content_editor/services/create_content_editor.js
index 795355abdab..7a289df94ea 100644
--- a/app/assets/javascripts/content_editor/services/create_content_editor.js
+++ b/app/assets/javascripts/content_editor/services/create_content_editor.js
@@ -42,6 +42,7 @@ import OrderedList from '../extensions/ordered_list';
import Paragraph from '../extensions/paragraph';
import PasteMarkdown from '../extensions/paste_markdown';
import Reference from '../extensions/reference';
+import ReferenceDefinition from '../extensions/reference_definition';
import Sourcemap from '../extensions/sourcemap';
import Strike from '../extensions/strike';
import Subscript from '../extensions/subscript';
@@ -128,6 +129,7 @@ export const createContentEditor = ({
Paragraph,
PasteMarkdown,
Reference,
+ ReferenceDefinition,
Sourcemap,
Strike,
Subscript,
diff --git a/app/assets/javascripts/content_editor/services/markdown_serializer.js b/app/assets/javascripts/content_editor/services/markdown_serializer.js
index c1c7af6b1af..1511f122a15 100644
--- a/app/assets/javascripts/content_editor/services/markdown_serializer.js
+++ b/app/assets/javascripts/content_editor/services/markdown_serializer.js
@@ -33,6 +33,7 @@ import MathInline from '../extensions/math_inline';
import OrderedList from '../extensions/ordered_list';
import Paragraph from '../extensions/paragraph';
import Reference from '../extensions/reference';
+import ReferenceDefinition from '../extensions/reference_definition';
import Strike from '../extensions/strike';
import Subscript from '../extensions/subscript';
import Superscript from '../extensions/superscript';
@@ -177,6 +178,25 @@ const defaultSerializerConfig = {
[Reference.name]: (state, node) => {
state.write(node.attrs.originalText || node.attrs.text);
},
+ [ReferenceDefinition.name]: preserveUnchanged({
+ render: (state, node, parent, index, same, sourceMarkdown) => {
+ const nextSibling = parent.maybeChild(index + 1);
+
+ state.text(same ? sourceMarkdown : node.textContent, false);
+
+ /**
+ * Do not insert a blank line between reference definitions
+ * because it isn’t necessary and a more compact text format
+ * is preferred.
+ */
+ if (!nextSibling || nextSibling.type.name !== ReferenceDefinition.name) {
+ state.closeBlock(node);
+ } else {
+ state.ensureNewLine();
+ }
+ },
+ overwriteSourcePreservationStrategy: true,
+ }),
[TableOfContents.name]: (state, node) => {
state.write('[[_TOC_]]');
state.closeBlock(node);
diff --git a/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js b/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
index 8e2c066e011..e38be672063 100644
--- a/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
+++ b/app/assets/javascripts/content_editor/services/remark_markdown_deserializer.js
@@ -170,6 +170,16 @@ const factorySpecs = {
type: 'ignore',
selector: (hastNode) => hastNode.type === 'comment',
},
+
+ referenceDefinition: {
+ type: 'block',
+ selector: 'referencedefinition',
+ getAttrs: (hastNode) => ({
+ title: hastNode.properties.title,
+ url: hastNode.properties.url,
+ identifier: hastNode.properties.identifier,
+ }),
+ },
};
export default () => {
@@ -185,7 +195,7 @@ export default () => {
wrappableTags,
markdown,
}),
- skipRendering: ['footnoteReference', 'footnoteDefinition', 'code'],
+ skipRendering: ['footnoteReference', 'footnoteDefinition', 'code', 'definition'],
});
return { document };
diff --git a/app/assets/javascripts/content_editor/services/serialization_helpers.js b/app/assets/javascripts/content_editor/services/serialization_helpers.js
index 7d5e718b41c..97bad375aea 100644
--- a/app/assets/javascripts/content_editor/services/serialization_helpers.js
+++ b/app/assets/javascripts/content_editor/services/serialization_helpers.js
@@ -1,4 +1,4 @@
-import { uniq, isString, omit } from 'lodash';
+import { uniq, isString, omit, isFunction } from 'lodash';
const defaultAttrs = {
td: { colspan: 1, rowspan: 1, colwidth: null },
@@ -327,16 +327,25 @@ export function renderCodeBlock(state, node) {
state.closeBlock(node);
}
-export function preserveUnchanged(render) {
+const expandPreserveUnchangedConfig = (configOrRender) =>
+ isFunction(configOrRender)
+ ? { render: configOrRender, overwriteSourcePreservationStrategy: false }
+ : configOrRender;
+
+export function preserveUnchanged(configOrRender) {
return (state, node, parent, index) => {
+ const { render, overwriteSourcePreservationStrategy } = expandPreserveUnchangedConfig(
+ configOrRender,
+ );
+
const { sourceMarkdown } = node.attrs;
const same = state.options.changeTracker.get(node);
- if (same) {
+ if (same && !overwriteSourcePreservationStrategy) {
state.write(sourceMarkdown);
state.closeBlock(node);
} else {
- render(state, node, parent, index);
+ render(state, node, parent, index, same, sourceMarkdown);
}
};
}
diff --git a/app/assets/javascripts/lib/gfm/index.js b/app/assets/javascripts/lib/gfm/index.js
index 92118c8929f..05534401a2a 100644
--- a/app/assets/javascripts/lib/gfm/index.js
+++ b/app/assets/javascripts/lib/gfm/index.js
@@ -19,6 +19,16 @@ const skipRenderingHandlers = {
h(node.position, 'codeBlock', { language: node.lang, meta: node.meta }, [
{ type: 'text', value: node.value },
]),
+ definition: (h, node) => {
+ const title = node.title ? ` "${node.title}"` : '';
+
+ return h(
+ node.position,
+ 'referenceDefinition',
+ { identifier: node.identifier, url: node.url, title: node.title },
+ [{ type: 'text', value: `[${node.identifier}]: ${node.url}${title}` }],
+ );
+ },
};
const createParser = ({ skipRendering = [] }) => {
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
index cd4bc35e74e..9513f42d9c9 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
@@ -1,4 +1,6 @@
import Vue from 'vue';
+import { BV_SHOW_MODAL } from '~/lib/utils/constants';
+import PipelineSchedulesTakeOwnershipModal from '~/pipeline_schedules/components/take_ownership_modal.vue';
import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
function initPipelineSchedules() {
@@ -23,4 +25,43 @@ function initPipelineSchedules() {
});
}
+function initTakeownershipModal() {
+ const modalId = 'pipeline-take-ownership-modal';
+ const buttonSelector = 'js-take-ownership-button';
+ const el = document.getElementById(modalId);
+ const takeOwnershipButtons = document.querySelectorAll(`.${buttonSelector}`);
+
+ if (!el) {
+ return;
+ }
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ data() {
+ return {
+ url: '',
+ };
+ },
+ mounted() {
+ takeOwnershipButtons.forEach((button) => {
+ button.addEventListener('click', () => {
+ const { url } = button.dataset;
+
+ this.url = url;
+ this.$root.$emit(BV_SHOW_MODAL, modalId, `.${buttonSelector}`);
+ });
+ });
+ },
+ render(createElement) {
+ return createElement(PipelineSchedulesTakeOwnershipModal, {
+ props: {
+ ownershipUrl: this.url,
+ },
+ });
+ },
+ });
+}
+
initPipelineSchedules();
+initTakeownershipModal();
diff --git a/app/assets/javascripts/pipeline_schedules/components/take_ownership_modal.vue b/app/assets/javascripts/pipeline_schedules/components/take_ownership_modal.vue
new file mode 100644
index 00000000000..7ded3945a32
--- /dev/null
+++ b/app/assets/javascripts/pipeline_schedules/components/take_ownership_modal.vue
@@ -0,0 +1,52 @@
+
+
+ {{ $options.i18n.ownershipMessage }}[foo]: /url "title"
04_07__leaf_blocks__link_reference_definitions__002:
canonical: |
@@ -2151,6 +2152,7 @@
static: |-
wysiwyg: |-
+ [foo]: /url "the title"
04_07__leaf_blocks__link_reference_definitions__003:
canonical: |
@@ -2158,6 +2160,7 @@
static: |-
wysiwyg: |-
+ [foo*bar\]]: my_(url) "title (with parens)"
04_07__leaf_blocks__link_reference_definitions__004:
canonical: |
@@ -2165,6 +2168,7 @@
static: |-
wysiwyg: |-
+ [foo bar]: my url "title"
04_07__leaf_blocks__link_reference_definitions__005:
canonical: |
@@ -2180,6 +2184,11 @@
line2
">foo
[foo]: /url " + title + line1 + line2 + "wysiwyg: |- +
[foo]: /url04_07__leaf_blocks__link_reference_definitions__008: canonical: | @@ -2221,6 +2231,7 @@ static: |- wysiwyg: |- +
[foo]:04_07__leaf_blocks__link_reference_definitions__010: canonical: | @@ -2238,6 +2249,7 @@ static: |- wysiwyg: |- +
[foo]: /url\bar*baz "foo"bar\baz"04_07__leaf_blocks__link_reference_definitions__012: canonical: | @@ -2246,6 +2258,7 @@ wysiwyg: |- +
[foo]: url04_07__leaf_blocks__link_reference_definitions__013: canonical: | @@ -2253,12 +2266,15 @@ wysiwyg: |- +
[foo]: first+
[foo]: second04_07__leaf_blocks__link_reference_definitions__014: canonical: | static: |- wysiwyg: |- +
[foo]: /url04_07__leaf_blocks__link_reference_definitions__015: canonical: | @@ -2266,18 +2282,20 @@ static: |- wysiwyg: |- +
[αγω]: /φου04_07__leaf_blocks__link_reference_definitions__016: canonical: "" static: "" wysiwyg: |- - +
[foo]: /url04_07__leaf_blocks__link_reference_definitions__017: canonical: |
bar
static: |-bar
wysiwyg: |- +[foo]: /url
bar
04_07__leaf_blocks__link_reference_definitions__018: canonical: | @@ -2292,6 +2310,7 @@ static: |-"title" ok
wysiwyg: |- +[foo]: /url
"title" ok
04_07__leaf_blocks__link_reference_definitions__020: canonical: | @@ -2349,6 +2368,7 @@ wysiwyg: |-[foo]: /url
04_07__leaf_blocks__link_reference_definitions__024: canonical: | @@ -2359,6 +2379,7 @@ bar wysiwyg: |- +bar
[foo]: /url
=== foo
wysiwyg: |- +[foo]: /url
=== foo
04_07__leaf_blocks__link_reference_definitions__026: @@ -2381,6 +2403,9 @@ bar, baz wysiwyg: |- +[foo]: /foo-url "foo"+
[bar]: /bar-url "bar"+
[baz]: /baz-url@@ -2395,12 +2420,12 @@ wysiwyg: |- -
+
04_07__leaf_blocks__link_reference_definitions__028: canonical: "" static: "" wysiwyg: |- - +[foo]: /url
[foo]: /url04_08__leaf_blocks__paragraphs__001: canonical: |
aaa
@@ -4543,7 +4568,7 @@ wysiwyg: |- -a
b
d
a
b
[ref]: /url
d
[foo]: /bar* "ti*tle"06_02__inlines__backslash_escapes__013: canonical: |
foo
@@ -4969,6 +4995,7 @@
wysiwyg: |-
+ [foo]: /föö "föö"
06_03__inlines__entity_and_numeric_character_references__010:
canonical: |
foo
@@ -6475,6 +6502,7 @@
wysiwyg: |-
+ [bar]: /url "title"
06_07__inlines__links__044:
canonical: |
@@ -6482,6 +6510,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__045:
canonical: |
@@ -6489,6 +6518,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__046:
canonical: |
@@ -6496,6 +6526,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__047:
canonical: |
@@ -6503,6 +6534,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__048:
canonical: |
@@ -6510,6 +6542,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__049:
canonical: |
@@ -6517,6 +6550,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__050:
canonical: |
*foo*
@@ -6524,6 +6558,7 @@
*foo*
wysiwyg: |-
*foo*
+ [ref]: /uri
06_07__inlines__links__051:
canonical: |
@@ -6531,6 +6566,7 @@
wysiwyg: |-
+ [ref]: /uri
06_07__inlines__links__052:
canonical: |
[foo
@@ -6538,6 +6574,7 @@
[foo
wysiwyg: |-
[foo
+ [ref]: /uri
06_07__inlines__links__053:
canonical: |
[foo][ref]
@@ -6545,6 +6582,7 @@
[foo][ref]
wysiwyg: |-
[foo][ref]
+ [ref]: /uri
06_07__inlines__links__054:
canonical: |
[foohttp://example.com/?search=][ref]
@@ -6552,6 +6590,7 @@
[foohttp://example.com/?search=][ref]
wysiwyg: |-
[foohttp://example.com/?search=][ref]
+ [ref]: /uri
06_07__inlines__links__055:
canonical: |
@@ -6559,6 +6598,7 @@
wysiwyg: |-
+ [bar]: /url "title"
06_07__inlines__links__056:
canonical: |
Толпой is a Russian word.
@@ -6566,12 +6606,14 @@
Толпой is a Russian word.
wysiwyg: |-
Толпой is a Russian word.
+ [толпой]: /url
06_07__inlines__links__057:
canonical: |
static: |-
wysiwyg: |-
+ [foo bar]: /url
06_07__inlines__links__058:
canonical: |
@@ -6580,6 +6622,7 @@
[foo] bar
wysiwyg: |-
[foo] bar
+ [bar]: /url "title"
06_07__inlines__links__059:
canonical: |
[foo]
@@ -6590,12 +6633,15 @@
wysiwyg: |-
[foo]
bar
+ [bar]: /url "title"
06_07__inlines__links__060:
canonical: |
static: |-
wysiwyg: |-
+ [foo]: /url1
+ [foo]: /url2
06_07__inlines__links__061:
canonical: |
@@ -6604,6 +6650,7 @@
[bar][foo!]
wysiwyg: |-
[bar][foo!]
+ [foo!]: /url
06_07__inlines__links__062:
canonical: |
[foo][ref[]
@@ -6641,12 +6688,14 @@
wysiwyg: |-
+ [ref\[]: /uri
06_07__inlines__links__066:
canonical: |
static: |-
wysiwyg: |-
+ [bar\\]: /uri
06_07__inlines__links__067:
canonical: |
@@ -6681,6 +6730,7 @@
wysiwyg: |-
+ [foo]: /url "title"
06_07__inlines__links__070:
canonical: |
@@ -6688,6 +6738,7 @@
wysiwyg: |-
+ [*foo* bar]: /url "title"
06_07__inlines__links__071:
canonical: |
@@ -6695,6 +6746,7 @@
wysiwyg: |-
+ [foo]: /url "title"
06_07__inlines__links__072:
canonical: |
foo
@@ -6705,6 +6757,7 @@
wysiwyg: |-
foo
[]
+ [foo]: /url "title"
06_07__inlines__links__073:
canonical: |
@@ -6712,6 +6765,7 @@
wysiwyg: |-
+ [foo]: /url "title"
06_07__inlines__links__074:
canonical: |
@@ -6719,6 +6773,7 @@
wysiwyg: |-
+ [*foo* bar]: /url "title"
06_07__inlines__links__075:
canonical: |
[foo bar]
@@ -6726,6 +6781,7 @@
[foo bar]
wysiwyg: |-
[foo bar]
+ [*foo* bar]: /url "title"
06_07__inlines__links__076:
canonical: |
[[bar foo
@@ -6733,6 +6789,7 @@
[[bar foo
wysiwyg: |-
[[bar foo
+ [foo]: /url
06_07__inlines__links__077:
canonical: |
@@ -6740,6 +6797,7 @@
wysiwyg: |-
+ [foo]: /url "title"
06_07__inlines__links__078:
canonical: |
foo bar
@@ -6747,6 +6805,7 @@
foo bar
wysiwyg: |-
foo bar
+ [foo]: /url
06_07__inlines__links__079:
canonical: |
[foo]
@@ -6754,12 +6813,14 @@
[foo]
wysiwyg: |-
[foo]
+ [foo]: /url "title"
06_07__inlines__links__080:
canonical: |
*foo*
static: |-
*foo*
wysiwyg: |-
+ [foo*]: /url
*foo*
06_07__inlines__links__081:
canonical: |
@@ -6768,6 +6829,8 @@
wysiwyg: |-
+ [foo]: /url1
+ [bar]: /url2
06_07__inlines__links__082:
canonical: |
@@ -6775,6 +6838,7 @@
wysiwyg: |-
+ [foo]: /url1
06_07__inlines__links__083:
canonical: |
@@ -6782,6 +6846,7 @@
wysiwyg: |-
+ [foo]: /url1
06_07__inlines__links__084:
canonical: |
foo(not a link)
@@ -6789,6 +6854,7 @@
foo(not a link)
wysiwyg: |-
foo(not a link)
+ [foo]: /url1
06_07__inlines__links__085:
canonical: |
[foo]bar
@@ -6796,6 +6862,7 @@
[foo]bar
wysiwyg: |-
[foo]bar
+ [baz]: /url
06_07__inlines__links__086:
canonical: |
@@ -6803,6 +6870,8 @@
wysiwyg: |-
+ [baz]: /url1
+ [bar]: /url2
06_07__inlines__links__087:
canonical: |
[foo]bar
@@ -6810,6 +6879,8 @@
[foo]bar
wysiwyg: |-
[foo]bar
+ [baz]: /url1
+ [foo]: /url2
06_08__inlines__images__001:
canonical: |
![title foo](/url)
@@ -6824,6 +6895,7 @@
wysiwyg: |-
![train & tracks foo bar](train.jpg)
+ [foo *bar*]: train.jpg "train & tracks"
06_08__inlines__images__003:
canonical: |
![foo bar](/url2)
@@ -6845,6 +6917,7 @@
wysiwyg: |-
![train & tracks foo bar](train.jpg)
+ [foo *bar*]: train.jpg "train & tracks"
06_08__inlines__images__006:
canonical: |
![train & tracks foo bar](train.jpg)
@@ -6852,6 +6925,7 @@
wysiwyg: |-
![train & tracks foo bar](train.jpg)
+ [foobar]: train.jpg "train & tracks"
06_08__inlines__images__007:
canonical: |
![foo](train.jpg)
@@ -6887,6 +6961,7 @@
wysiwyg: |-
![foo](/url)
+ [bar]: /url
06_08__inlines__images__012:
canonical: |
![foo](/url)
@@ -6894,6 +6969,7 @@
wysiwyg: |-
![foo](/url)
+ [bar]: /url
06_08__inlines__images__013:
canonical: |
![title foo](/url)
@@ -6901,6 +6977,7 @@
wysiwyg: |-
![title foo](/url)
+ [foo]: /url "title"
06_08__inlines__images__014:
canonical: |
![title foo bar](/url)
@@ -6908,6 +6985,7 @@
wysiwyg: |-
![title foo bar](/url)
+ [*foo* bar]: /url "title"
06_08__inlines__images__015:
canonical: |
![title Foo](/url)
@@ -6915,6 +6993,7 @@
wysiwyg: |-
![title Foo](/url)
+ [foo]: /url "title"
06_08__inlines__images__016:
canonical: |
@@ -6925,6 +7004,7 @@
wysiwyg: |-
[]
+ [foo]: /url "title"
06_08__inlines__images__017:
canonical: |
![title foo](/url)
@@ -6932,6 +7012,7 @@
wysiwyg: |-
![title foo](/url)
+ [foo]: /url "title"
06_08__inlines__images__018:
canonical: |
![title foo bar](/url)
@@ -6939,6 +7020,7 @@
wysiwyg: |-
![title foo bar](/url)
+ [*foo* bar]: /url "title"
06_08__inlines__images__019:
canonical: |
![[foo]]
@@ -6956,6 +7038,7 @@
wysiwyg: |-
![title Foo](/url)
+ [foo]: /url "title"
06_08__inlines__images__021:
canonical: |
![foo]
@@ -6963,6 +7046,7 @@
![foo]
wysiwyg: |-
![foo]
+ [foo]: /url "title"
06_08__inlines__images__022:
canonical: |
!foo
@@ -6970,6 +7054,7 @@
!foo
wysiwyg: |-
!foo
+ [foo]: /url "title"
06_09__inlines__autolinks__001:
canonical: |
diff --git a/glfm_specification/example_snapshots/prosemirror_json.yml b/glfm_specification/example_snapshots/prosemirror_json.yml
index f770d341c42..7cb4757b368 100644
--- a/glfm_specification/example_snapshots/prosemirror_json.yml
+++ b/glfm_specification/example_snapshots/prosemirror_json.yml
@@ -3883,6 +3883,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -3910,6 +3924,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "the title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"the title\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -3937,6 +3965,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo*bar\\]",
+ "url": "my_(url)",
+ "title": "title (with parens)"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo*bar\\]]: my_(url) \"title (with parens)\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -3964,6 +4006,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo bar",
+ "url": "my url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo bar]: my url \"title\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -3991,6 +4047,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "\ntitle\nline1\nline2\n"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"\ntitle\nline1\nline2\n\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4051,6 +4121,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4102,6 +4186,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: "
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4153,6 +4251,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url\\bar*baz",
+ "title": "foo\"bar\\baz"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url\\bar*baz \"foo\"bar\\baz\""
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4200,6 +4312,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: url"
+ }
+ ]
}
]
}
@@ -4227,6 +4353,34 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "first",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: first"
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "second",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: second"
+ }
+ ]
}
]
}
@@ -4234,6 +4388,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4261,6 +4429,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "αγω",
+ "url": "/φου",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[αγω]: /φου"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4289,7 +4471,18 @@
"type": "doc",
"content": [
{
- "type": "paragraph"
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
}
]
}
@@ -4297,6 +4490,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4327,6 +4534,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4446,6 +4667,20 @@
}
]
},
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "blockquote",
"attrs": {
@@ -4469,6 +4704,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "heading",
"attrs": {
@@ -4508,6 +4757,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4539,6 +4802,48 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/foo-url",
+ "title": "foo"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /foo-url \"foo\""
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/bar-url",
+ "title": "bar"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /bar-url \"bar\""
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "baz",
+ "url": "/baz-url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[baz]: /baz-url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -4634,7 +4939,18 @@
},
"content": [
{
- "type": "paragraph"
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
}
]
}
@@ -4645,7 +4961,18 @@
"type": "doc",
"content": [
{
- "type": "paragraph"
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
}
]
}
@@ -9572,6 +9899,20 @@
"text": "b"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /url"
+ }
+ ]
}
]
},
@@ -10416,6 +10757,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/bar*",
+ "title": "ti*tle"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /bar* \"ti*tle\""
+ }
+ ]
}
]
}
@@ -10588,6 +10943,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/föö",
+ "title": "föö"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /föö \"föö\""
+ }
+ ]
}
]
}
@@ -15592,6 +15961,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -15619,6 +16002,20 @@
"text": "link [foo [bar]]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15646,6 +16043,20 @@
"text": "link [bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15739,6 +16150,20 @@
"text": "#"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15772,6 +16197,20 @@
]
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15823,6 +16262,20 @@
"text": "ref"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15886,6 +16339,20 @@
"text": "ref"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15917,6 +16384,20 @@
"text": "foo*"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15944,6 +16425,20 @@
"text": "foo *bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15959,6 +16454,20 @@
"text": "[foo "
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -15983,6 +16492,20 @@
"text": "][ref]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -16014,6 +16537,20 @@
"text": "http://example.com/?search=][ref]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref]: /uri"
+ }
+ ]
}
]
}
@@ -16041,6 +16578,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16072,6 +16623,20 @@
"text": " is a Russian word."
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "толпой",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[толпой]: /url"
+ }
+ ]
}
]
}
@@ -16079,6 +16644,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo bar",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo bar]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -16130,6 +16709,20 @@
"text": "bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16161,6 +16754,20 @@
"text": "bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16168,6 +16775,34 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url1"
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url2",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url2"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -16203,6 +16838,20 @@
"text": "[bar][foo!]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo!",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo!]: /url"
+ }
+ ]
}
]
}
@@ -16302,6 +16951,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "ref\\[",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[ref\\[]: /uri"
+ }
+ ]
}
]
}
@@ -16309,6 +16972,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar\\\\",
+ "url": "/uri",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar\\\\]: /uri"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -16404,6 +17081,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16450,6 +17141,20 @@
"text": " bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "*foo* bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[*foo* bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16477,6 +17182,20 @@
"text": "Foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16508,6 +17227,20 @@
"text": "\n[]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16535,6 +17268,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16581,6 +17328,20 @@
"text": " bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "*foo* bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[*foo* bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16635,6 +17396,20 @@
"text": "]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "*foo* bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[*foo* bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16666,6 +17441,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
}
]
}
@@ -16693,6 +17482,20 @@
"text": "Foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16724,6 +17527,20 @@
"text": " bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url"
+ }
+ ]
}
]
}
@@ -16739,6 +17556,20 @@
"text": "[foo]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -16746,6 +17577,20 @@
{
"type": "doc",
"content": [
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo*",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo*]: /url"
+ }
+ ]
+ },
{
"type": "paragraph",
"content": [
@@ -16797,6 +17642,34 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url1"
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url2",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url2"
+ }
+ ]
}
]
}
@@ -16824,6 +17697,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url1"
+ }
+ ]
}
]
}
@@ -16851,6 +17738,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url1"
+ }
+ ]
}
]
}
@@ -16882,6 +17783,20 @@
"text": "(not a link)"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url1"
+ }
+ ]
}
]
}
@@ -16913,6 +17828,20 @@
"text": "bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "baz",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[baz]: /url"
+ }
+ ]
}
]
}
@@ -16956,6 +17885,34 @@
"text": "baz"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "baz",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[baz]: /url1"
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url2",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url2"
+ }
+ ]
}
]
}
@@ -16987,6 +17944,34 @@
"text": "bar"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "baz",
+ "url": "/url1",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[baz]: /url1"
+ }
+ ]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url2",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url2"
+ }
+ ]
}
]
}
@@ -17029,6 +18014,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo *bar*",
+ "url": "train.jpg",
+ "title": "train & tracks"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo *bar*]: train.jpg \"train & tracks\""
+ }
+ ]
}
]
}
@@ -17092,6 +18091,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo *bar*",
+ "url": "train.jpg",
+ "title": "train & tracks"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo *bar*]: train.jpg \"train & tracks\""
+ }
+ ]
}
]
}
@@ -17113,6 +18126,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foobar",
+ "url": "train.jpg",
+ "title": "train & tracks"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foobar]: train.jpg \"train & tracks\""
+ }
+ ]
}
]
}
@@ -17222,6 +18249,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url"
+ }
+ ]
}
]
}
@@ -17243,6 +18284,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "bar",
+ "url": "/url",
+ "title": null
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[bar]: /url"
+ }
+ ]
}
]
}
@@ -17264,6 +18319,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17285,6 +18354,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "*foo* bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[*foo* bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17306,6 +18389,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17331,6 +18428,20 @@
"text": "\n[]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17352,6 +18463,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17373,6 +18498,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "*foo* bar",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[*foo* bar]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17418,6 +18557,20 @@
}
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17433,6 +18586,20 @@
"text": "![foo]"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
@@ -17464,6 +18631,20 @@
"text": "foo"
}
]
+ },
+ {
+ "type": "referenceDefinition",
+ "attrs": {
+ "identifier": "foo",
+ "url": "/url",
+ "title": "title"
+ },
+ "content": [
+ {
+ "type": "text",
+ "text": "[foo]: /url \"title\""
+ }
+ ]
}
]
}
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 0bc643b40a3..a71a1cc40e6 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -584,12 +584,12 @@ module API
end
end
- def log_artifact_size(file)
+ def log_artifact_file_size(file)
Gitlab::ApplicationContext.push(artifact: file.model)
end
def present_artifacts_file!(file, **args)
- log_artifact_size(file) if file
+ log_artifact_file_size(file) if file
present_carrierwave_file!(file, **args)
end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 8c55652da43..5583c896803 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -41,8 +41,7 @@ module Gitlab
@unfolded = false
# Ensure items are collected in the the batch
- new_blob_lazy
- old_blob_lazy
+ add_blobs_to_batch_loader
end
def use_semantic_ipynb_diff?
@@ -382,6 +381,11 @@ module Gitlab
file_path.ends_with?('.ipynb')
end
+ def add_blobs_to_batch_loader
+ new_blob_lazy
+ old_blob_lazy
+ end
+
private
def diffable_by_attribute?
diff --git a/lib/gitlab/memory/reports_daemon.rb b/lib/gitlab/memory/reports_daemon.rb
index 779c19b3a1e..8788bee27a6 100644
--- a/lib/gitlab/memory/reports_daemon.rb
+++ b/lib/gitlab/memory/reports_daemon.rb
@@ -36,12 +36,16 @@ module Gitlab
sleep interval_with_jitter
reports.select(&:active?).each do |report|
- tms = Benchmark.measure do
- report.run
- end
+ start_monotonic_time = Gitlab::Metrics::System.monotonic_time
+ start_thread_cpu_time = Gitlab::Metrics::System.thread_cpu_time
- log_report(report_label(report), tms)
- @report_duration_counter.increment({ report: report_label(report) }, tms.real)
+ report.run
+
+ cpu_s = Gitlab::Metrics::System.thread_cpu_duration(start_thread_cpu_time)
+ duration_s = Gitlab::Metrics::System.monotonic_time - start_monotonic_time
+
+ log_report(label: report_label(report), cpu_s: cpu_s, duration_s: duration_s)
+ @report_duration_counter.increment({ report: report_label(report) }, duration_s)
sleep sleep_between_reports_s
end
@@ -58,15 +62,14 @@ module Gitlab
sleep_s + rand(sleep_max_delta_s)
end
- def log_report(report_label, tms)
+ def log_report(label:, duration_s:, cpu_s:)
Gitlab::AppLogger.info(
message: 'finished',
pid: $$,
worker_id: worker_id,
- perf_report: report_label,
- duration_s: tms.real.round(2),
- cpu_s: tms.utime.round(2),
- sys_cpu_s: tms.stime.round(2)
+ perf_report: label,
+ duration_s: duration_s.round(2),
+ cpu_s: cpu_s.round(2)
)
end
diff --git a/lib/gitlab/usage_data_counters/hll_redis_counter.rb b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
index 40581bda81b..4647b19554d 100644
--- a/lib/gitlab/usage_data_counters/hll_redis_counter.rb
+++ b/lib/gitlab/usage_data_counters/hll_redis_counter.rb
@@ -114,6 +114,10 @@ module Gitlab
@categories ||= known_events.map { |event| event[:category] }.uniq
end
+ def categories_collected_from_metrics_definitions
+ CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS
+ end
+
# @param category [String] the category name
# @return [Array] list of event names for given category
def events_for_category(category)
@@ -164,7 +168,7 @@ module Gitlab
def categories_pending_migration
if ::Feature.enabled?(:use_redis_hll_instrumentation_classes)
- (categories - CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS)
+ (categories - categories_collected_from_metrics_definitions)
else
categories
end
diff --git a/lib/gitlab/usage_data_counters/known_events/common.yml b/lib/gitlab/usage_data_counters/known_events/common.yml
index 88c9f44c165..d647573ec9f 100644
--- a/lib/gitlab/usage_data_counters/known_events/common.yml
+++ b/lib/gitlab/usage_data_counters/known_events/common.yml
@@ -1,25 +1,5 @@
---
# Compliance category
-- name: g_compliance_dashboard
- redis_slot: compliance
- category: compliance
- aggregation: weekly
-- name: g_compliance_audit_events
- category: compliance
- redis_slot: compliance
- aggregation: weekly
-- name: i_compliance_audit_events
- category: compliance
- redis_slot: compliance
- aggregation: weekly
-- name: i_compliance_credential_inventory
- category: compliance
- redis_slot: compliance
- aggregation: weekly
-- name: a_compliance_audit_events_api
- category: compliance
- redis_slot: compliance
- aggregation: weekly
- name: g_edit_by_web_ide
category: ide_edit
redis_slot: edit
diff --git a/lib/gitlab/utils/batch_loader.rb b/lib/gitlab/utils/batch_loader.rb
new file mode 100644
index 00000000000..67ade0633e2
--- /dev/null
+++ b/lib/gitlab/utils/batch_loader.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Utils
+ module BatchLoader
+ # Clears batched items under the specified batch key
+ # https://github.com/exAspArk/batch-loader#batch-key
+ def self.clear_key(batch_key)
+ return if ::BatchLoader::Executor.current.nil?
+
+ items_to_clear = ::BatchLoader::Executor.current.items_by_block.select do |k, v|
+ # The Hash key here is [source_location, batch_key], so we just check k[1]
+ k[1] == batch_key
+ end
+
+ items_to_clear.each do |k, v|
+ ::BatchLoader::Executor.current.items_by_block.delete(k)
+ ::BatchLoader::Executor.current.loaded_values_by_block.delete(k)
+ end
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 958d0a7489f..4dc897d3ddf 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -11694,9 +11694,21 @@ msgstr ""
msgid "DastConfig|Enable DAST to automatically test for vulnerabilities in your project's running application, website, or API, in the CI/CD pipeline. Configuration changes must be applied to your .gitlab-ci.yml file to take effect. For details of all configuration options, see the %{linkStart}GitLab DAST documentation%{linkEnd}."
msgstr ""
+msgid "DastConfig|Enabled"
+msgstr ""
+
msgid "DastConfig|Generate code snippet"
msgstr ""
+msgid "DastConfig|Last scan triggered %{runTimeAgo} in pipeline "
+msgstr ""
+
+msgid "DastConfig|No previous scans found for this project"
+msgstr ""
+
+msgid "DastConfig|Not enabled"
+msgstr ""
+
msgid "DastProfiles|A passive scan monitors all HTTP messages (requests and responses) sent to the target. An active scan attacks the target to find potential vulnerabilities."
msgstr ""
@@ -28580,6 +28592,9 @@ msgstr ""
msgid "PipelineSchedules|None"
msgstr ""
+msgid "PipelineSchedules|Only the owner of a pipeline schedule can make changes to it. Do you want to take ownership of this schedule?"
+msgstr ""
+
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr ""
@@ -28592,6 +28607,9 @@ msgstr ""
msgid "PipelineSchedules|Variables"
msgstr ""
+msgid "PipelineSchedule|Take ownership to edit"
+msgstr ""
+
msgid "PipelineSource|API"
msgstr ""
diff --git a/package.json b/package.json
index 4ba8caee76e..d30b3f803c9 100644
--- a/package.json
+++ b/package.json
@@ -91,7 +91,6 @@
"@tiptap/vue-2": "^2.0.0-beta.84",
"apollo-upload-client": "15.0.0",
"autosize": "^5.0.1",
- "aws-sdk": "^2.637.0",
"axios": "^0.24.0",
"babel-loader": "^8.2.5",
"babel-plugin-lodash": "^3.3.4",
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 59b2168c41a..edb07bbdce6 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -82,6 +82,22 @@ RSpec.describe Projects::CommitController do
expect(response).to be_successful
end
+ it 'only loads blobs in the current page' do
+ stub_feature_flags(async_commit_diff_files: false)
+ stub_const('Projects::CommitController::COMMIT_DIFFS_PER_PAGE', 1)
+
+ commit = project.commit('1a0b36b3cdad1d2ee32457c102a8c0b7056fa863')
+
+ expect_next_instance_of(Repository) do |repository|
+ # This commit contains 3 changed files but we expect only the blobs for the first one to be loaded
+ expect(repository).to receive(:blobs_at).with([[commit.id, '.gitignore']], anything).and_call_original
+ end
+
+ go(id: commit.id)
+
+ expect(response).to be_ok
+ end
+
shared_examples "export as" do |format|
it "does generally work" do
go(id: commit.id, format: format)
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index e6e0307d0ca..6ed6f7017e3 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -226,8 +226,8 @@ RSpec.describe Projects::CompareController do
context 'when page is valid' do
let(:from_project_id) { nil }
- let(:from_ref) { '08f22f25' }
- let(:to_ref) { '66eceea0' }
+ let(:from_ref) { '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9' }
+ let(:to_ref) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' }
let(:page) { 1 }
it 'shows the diff' do
@@ -237,6 +237,21 @@ RSpec.describe Projects::CompareController do
expect(assigns(:diffs).diff_files.first).to be_present
expect(assigns(:commits).length).to be >= 1
end
+
+ it 'only loads blobs in the current page' do
+ stub_const('Projects::CompareController::COMMIT_DIFFS_PER_PAGE', 1)
+
+ expect_next_instance_of(Repository) do |repository|
+ # This comparison contains 4 changed files but we expect only the blobs for the first one to be loaded
+ expect(repository).to receive(:blobs_at).with(
+ contain_exactly([from_ref, '.gitmodules'], [to_ref, '.gitmodules']), anything
+ ).and_call_original
+ end
+
+ show_request
+
+ expect(response).to be_successful
+ end
end
context 'when page is not valid' do
diff --git a/spec/events/projects/project_transfered_event_spec.rb b/spec/events/projects/project_transfered_event_spec.rb
new file mode 100644
index 00000000000..51c8bf4765f
--- /dev/null
+++ b/spec/events/projects/project_transfered_event_spec.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Projects::ProjectTransferedEvent do
+ where(:data, :valid) do
+ valid_event = {
+ project_id: 1,
+ old_namespace_id: 2,
+ old_root_namespace_id: 3,
+ new_namespace_id: 4,
+ new_root_namespace_id: 5
+ }
+
+ # All combinations of missing keys
+ with_missing_keys = 0.upto(valid_event.size - 1)
+ .flat_map { |size| valid_event.keys.combination(size).to_a }
+ .map { |keys| [valid_event.slice(*keys), false] }
+
+ [
+ [valid_event, true],
+ *with_missing_keys,
+ [{ project_id: 'foo', namespace_id: 2 }, false],
+ [{ project_id: 1, namespace_id: 'foo' }, false],
+ [{ project_id: [], namespace_id: 2 }, false],
+ [{ project_id: 1, namespace_id: [] }, false],
+ [{ project_id: {}, namespace_id: 2 }, false],
+ [{ project_id: 1, namespace_id: {} }, false],
+ ['foo', false],
+ [123, false],
+ [[], false]
+ ]
+ end
+
+ with_them do
+ it 'validates data' do
+ constructor = -> { described_class.new(data: data) }
+
+ if valid
+ expect { constructor.call }.not_to raise_error
+ else
+ expect { constructor.call }.to raise_error(Gitlab::EventStore::InvalidEvent)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 8cf6d5bd29b..0711a30e974 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -109,7 +109,12 @@ RSpec.describe 'Pipeline Schedules', :js do
end
it 'changes ownership of the pipeline' do
- click_link 'Take ownership'
+ click_button 'Take ownership'
+
+ page.within('#pipeline-take-ownership-modal') do
+ click_link 'Take ownership'
+ end
+
page.within('.pipeline-schedule-table-row') do
expect(page).not_to have_content('No owner')
expect(page).to have_link('Sidney Jones')
diff --git a/spec/frontend/content_editor/remark_markdown_processing_spec.js b/spec/frontend/content_editor/remark_markdown_processing_spec.js
index 48adceaab58..ddf49a4c18e 100644
--- a/spec/frontend/content_editor/remark_markdown_processing_spec.js
+++ b/spec/frontend/content_editor/remark_markdown_processing_spec.js
@@ -15,6 +15,7 @@ import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
import Paragraph from '~/content_editor/extensions/paragraph';
+import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
import Sourcemap from '~/content_editor/extensions/sourcemap';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
@@ -45,6 +46,7 @@ const tiptapEditor = createTestEditor({
Link,
ListItem,
OrderedList,
+ ReferenceDefinition,
Sourcemap,
Strike,
Table,
@@ -78,6 +80,7 @@ const {
listItem,
orderedList,
pre,
+ referenceDefinition,
strike,
table,
tableRow,
@@ -105,6 +108,7 @@ const {
listItem: { nodeType: ListItem.name },
orderedList: { nodeType: OrderedList.name },
paragraph: { nodeType: Paragraph.name },
+ referenceDefinition: { nodeType: ReferenceDefinition.name },
strike: { nodeType: Strike.name },
table: { nodeType: Table.name },
tableCell: { nodeType: TableCell.name },
@@ -1079,6 +1083,32 @@ _world_.
),
),
},
+ {
+ markdown: `
+[GitLab][gitlab-url]
+
+[gitlab-url]: https://gitlab.com "GitLab"
+
+ `,
+ expectedDoc: doc(
+ paragraph(
+ source('[GitLab][gitlab-url]'),
+ link(
+ { ...source('[GitLab][gitlab-url]'), href: 'https://gitlab.com', title: 'GitLab' },
+ 'GitLab',
+ ),
+ ),
+ referenceDefinition(
+ {
+ ...source('[gitlab-url]: https://gitlab.com "GitLab"'),
+ identifier: 'gitlab-url',
+ url: 'https://gitlab.com',
+ title: 'GitLab',
+ },
+ '[gitlab-url]: https://gitlab.com "GitLab"',
+ ),
+ ),
+ },
];
const runOnly = examples.find((example) => example.only === true);
diff --git a/spec/frontend/content_editor/render_html_and_json_for_all_examples.js b/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
index 116a26cf7d5..782158810dd 100644
--- a/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
+++ b/spec/frontend/content_editor/render_html_and_json_for_all_examples.js
@@ -26,6 +26,7 @@ import Italic from '~/content_editor/extensions/italic';
import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
+import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
import TableCell from '~/content_editor/extensions/table_cell';
@@ -63,6 +64,7 @@ const tiptapEditor = createTestEditor({
Link,
ListItem,
OrderedList,
+ ReferenceDefinition,
Strike,
Table,
TableCell,
diff --git a/spec/frontend/content_editor/services/markdown_serializer_spec.js b/spec/frontend/content_editor/services/markdown_serializer_spec.js
index 29f1f219971..422eb3f311b 100644
--- a/spec/frontend/content_editor/services/markdown_serializer_spec.js
+++ b/spec/frontend/content_editor/services/markdown_serializer_spec.js
@@ -24,6 +24,7 @@ import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
import Paragraph from '~/content_editor/extensions/paragraph';
+import ReferenceDefinition from '~/content_editor/extensions/reference_definition';
import Sourcemap from '~/content_editor/extensions/sourcemap';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
@@ -63,6 +64,7 @@ const tiptapEditor = createTestEditor({
Link,
ListItem,
OrderedList,
+ ReferenceDefinition,
Sourcemap,
Strike,
Table,
@@ -104,6 +106,7 @@ const {
listItem,
orderedList,
paragraph,
+ referenceDefinition,
strike,
table,
tableCell,
@@ -139,6 +142,7 @@ const {
listItem: { nodeType: ListItem.name },
orderedList: { nodeType: OrderedList.name },
paragraph: { nodeType: Paragraph.name },
+ referenceDefinition: { nodeType: ReferenceDefinition.name },
strike: { markType: Strike.name },
table: { nodeType: Table.name },
tableCell: { nodeType: TableCell.name },
@@ -1163,6 +1167,38 @@ Oranges are orange [^1]
);
});
+ it('correctly serializes reference definition', () => {
+ expect(
+ serialize(
+ referenceDefinition('[gitlab]: https://gitlab.com'),
+ referenceDefinition('[foobar]: foobar.com'),
+ ),
+ ).toBe(
+ `
+[gitlab]: https://gitlab.com
+[foobar]: foobar.com`.trimLeft(),
+ );
+ });
+
+ it('correctly adds a space between a reference definition and a block content', () => {
+ expect(
+ serialize(
+ paragraph('paragraph'),
+ referenceDefinition('[gitlab]: https://gitlab.com'),
+ referenceDefinition('[foobar]: foobar.com'),
+ heading({ level: 2 }, 'heading'),
+ ),
+ ).toBe(
+ `
+paragraph
+
+[gitlab]: https://gitlab.com
+[foobar]: foobar.com
+
+## heading`.trimLeft(),
+ );
+ });
+
const defaultEditAction = (initialContent) => {
tiptapEditor.chain().setContent(initialContent.toJSON()).insertContent(' modified').run();
};
diff --git a/spec/frontend/lib/gfm/index_spec.js b/spec/frontend/lib/gfm/index_spec.js
index b722315d63a..624744b9a92 100644
--- a/spec/frontend/lib/gfm/index_spec.js
+++ b/spec/frontend/lib/gfm/index_spec.js
@@ -96,28 +96,60 @@ describe('gfm', () => {
);
});
});
- });
- describe('when skipping the rendering of code blocks', () => {
- it('transforms code nodes into codeblock html tags', async () => {
- const result = await markdownToAST(
- `
+ describe('when skipping the rendering of code blocks', () => {
+ it('transforms code nodes into codeblock html tags', async () => {
+ const result = await markdownToAST(
+ `
\`\`\`javascript
console.log('Hola');
\`\`\`\
`,
- ['code'],
- );
+ ['code'],
+ );
- expectInRoot(
- result,
- expect.objectContaining({
- tagName: 'codeblock',
- properties: {
- language: 'javascript',
- },
- }),
- );
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ tagName: 'codeblock',
+ properties: {
+ language: 'javascript',
+ },
+ }),
+ );
+ });
+ });
+
+ describe('when skipping the rendering of reference definitions', () => {
+ it('transforms code nodes into codeblock html tags', async () => {
+ const result = await markdownToAST(
+ `
+[gitlab][gitlab]
+
+[gitlab]: https://gitlab.com "GitLab"
+ `,
+ ['definition'],
+ );
+
+ expectInRoot(
+ result,
+ expect.objectContaining({
+ type: 'element',
+ tagName: 'referencedefinition',
+ properties: {
+ identifier: 'gitlab',
+ title: 'GitLab',
+ url: 'https://gitlab.com',
+ },
+ children: [
+ {
+ type: 'text',
+ value: '[gitlab]: https://gitlab.com "GitLab"',
+ },
+ ],
+ }),
+ );
+ });
});
});
});
diff --git a/spec/frontend/pipeline_schedules/components/take_ownership_modal_spec.js b/spec/frontend/pipeline_schedules/components/take_ownership_modal_spec.js
new file mode 100644
index 00000000000..23bfdef13c6
--- /dev/null
+++ b/spec/frontend/pipeline_schedules/components/take_ownership_modal_spec.js
@@ -0,0 +1,54 @@
+import { GlModal } from '@gitlab/ui';
+import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
+import TakeOwnershipModal from '~/pipeline_schedules/components/take_ownership_modal.vue';
+
+describe('Take ownership modal', () => {
+ let wrapper;
+ const url = `/root/job-log-tester/-/pipeline_schedules/3/take_ownership`;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMountExtended(TakeOwnershipModal, {
+ propsData: {
+ ownershipUrl: url,
+ ...props,
+ },
+ });
+ };
+
+ const findModal = () => wrapper.findComponent(GlModal);
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('has a primary action set to a url and a post data-method', () => {
+ const actionPrimary = findModal().props('actionPrimary');
+
+ expect(actionPrimary.attributes).toEqual(
+ expect.objectContaining([
+ {
+ category: 'primary',
+ variant: 'confirm',
+ href: url,
+ 'data-method': 'post',
+ },
+ ]),
+ );
+ });
+
+ it('shows a take ownership message', () => {
+ expect(findModal().text()).toBe(
+ 'Only the owner of a pipeline schedule can make changes to it. Do you want to take ownership of this schedule?',
+ );
+ });
+
+ it('emits the cancel event when clicking on cancel', async () => {
+ findModal().vm.$emit('cancel');
+
+ expect(findModal().emitted('cancel')).toBeTruthy();
+ });
+});
diff --git a/spec/frontend_integration/content_editor/content_editor_integration_spec.js b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
index 89b8d8d6d94..4d400a383e3 100644
--- a/spec/frontend_integration/content_editor/content_editor_integration_spec.js
+++ b/spec/frontend_integration/content_editor/content_editor_integration_spec.js
@@ -61,29 +61,38 @@ describe('content_editor', () => {
});
});
- it('renders footnote ids alongside the footnote definition', async () => {
- buildWrapper();
+ describe('when preserveUnchangedMarkdown feature flag is enabled', () => {
+ beforeEach(() => {
+ gon.features = { preserveUnchangedMarkdown: true };
+ });
+ afterEach(() => {
+ gon.features = { preserveUnchangedMarkdown: false };
+ });
- renderMarkdown.mockResolvedValue(`
-
- This reference tag is a mix of letters and numbers. 2
-
-
-
- -
-
This is another footnote. ↩
-
-
-
+ it('processes and renders footnote ids alongside the footnote definition', async () => {
+ buildWrapper();
+
+ await contentEditorService.setSerializedContent(`
+This reference tag is a mix of letters and numbers [^footnote].
+
+[^footnote]: This is another footnote.
`);
+ await nextTick();
- await contentEditorService.setSerializedContent(`
- This reference tag is a mix of letters and numbers [^footnote].
+ expect(wrapper.text()).toContain('footnote: This is another footnote');
+ });
- [^footnote]: This is another footnote.
- `);
- await nextTick();
+ it('processes and displays reference definitions', async () => {
+ buildWrapper();
- expect(wrapper.text()).toContain('footnote: This is another footnote');
+ await contentEditorService.setSerializedContent(`
+[GitLab][gitlab]
+
+[gitlab]: https://gitlab.com
+ `);
+ await nextTick();
+
+ expect(wrapper.find('pre').text()).toContain('[gitlab]: https://gitlab.com');
+ });
});
});
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index b5b572e9719..b27954de0d4 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -153,16 +153,24 @@ RSpec.describe CommitsHelper do
end
describe "#conditionally_paginate_diff_files" do
- let(:diffs_collection) { instance_double(Gitlab::Diff::FileCollection::Commit, diff_files: diff_files) }
- let(:diff_files) { Gitlab::Git::DiffCollection.new(files) }
- let(:page) { nil }
+ let_it_be(:project) { create(:project, :repository) }
+ let(:diffs_collection) { instance_double(Gitlab::Diff::FileCollection::Commit, diff_files: decorated_diff_files, project: project) }
+ let(:decorated_diff_files) do
+ diffs.map do |diff|
+ Gitlab::Diff::File.new(diff, repository: project.repository)
+ end
+ end
+
+ let(:diffs) { Gitlab::Git::DiffCollection.new(files) }
let(:files) do
Array.new(85).map do
{ too_large: false, diff: "" }
end
end
+ let(:page) { nil }
+
subject { helper.conditionally_paginate_diff_files(diffs_collection, paginate: paginate, page: page, per: Projects::CommitController::COMMIT_DIFFS_PER_PAGE) }
before do
@@ -203,8 +211,8 @@ RSpec.describe CommitsHelper do
context "pagination is disabled" do
let(:paginate) { false }
- it "returns a standard DiffCollection" do
- expect(subject).to be_a(Gitlab::Git::DiffCollection)
+ it "returns the unpaginated collection" do
+ expect(subject.size).to eq(85)
end
end
end
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index 5f5e7f211f8..b160f322fb8 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -727,6 +727,7 @@ RSpec.describe Gitlab::Auth::OAuth::User do
context 'signup with linked omniauth and LDAP account' do
before do
stub_omniauth_config(auto_link_ldap_user: true)
+ stub_ldap_setting(enabled: true)
allow(ldap_user).to receive(:uid) { uid }
allow(ldap_user).to receive(:username) { uid }
allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] }
diff --git a/spec/lib/gitlab/memory/reports_daemon_spec.rb b/spec/lib/gitlab/memory/reports_daemon_spec.rb
index 75334834c5b..6af317f2771 100644
--- a/spec/lib/gitlab/memory/reports_daemon_spec.rb
+++ b/spec/lib/gitlab/memory/reports_daemon_spec.rb
@@ -32,7 +32,6 @@ RSpec.describe Gitlab::Memory::ReportsDaemon do
hash_including(
:duration_s,
:cpu_s,
- :sys_cpu_s,
message: 'finished',
pid: Process.pid,
worker_id: 'worker_1',
diff --git a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
index 54d49b432f4..d45ee0113e8 100644
--- a/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
+++ b/spec/lib/gitlab/usage_data_counters/hll_redis_counter_spec.rb
@@ -82,7 +82,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
stub_feature_flags(use_redis_hll_instrumentation_classes: true)
expect(described_class.unique_events_data.keys)
- .not_to include(*described_class::CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS)
+ .not_to include(*described_class.categories_collected_from_metrics_definitions)
end
end
@@ -91,18 +91,17 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
stub_feature_flags(use_redis_hll_instrumentation_classes: false)
expect(described_class.unique_events_data.keys)
- .to include(*described_class::CATEGORIES_COLLECTED_FROM_METRICS_DEFINITIONS)
+ .to include(*described_class.categories_collected_from_metrics_definitions)
end
end
end
end
describe '.categories' do
- it 'gets all unique category names' do
- expect(described_class.categories).to contain_exactly(
+ it 'gets CE unique category names' do
+ expect(described_class.categories).to include(
'deploy_token_packages',
'user_packages',
- 'compliance',
'ecosystem',
'analytics',
'ide_edit',
@@ -483,7 +482,7 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
describe '.weekly_redis_keys' do
using RSpec::Parameterized::TableSyntax
- let(:weekly_event) { 'g_compliance_dashboard' }
+ let(:weekly_event) { 'i_search_total' }
let(:redis_event) { described_class.send(:event_for, weekly_event) }
subject(:weekly_redis_keys) { described_class.send(:weekly_redis_keys, events: [redis_event], start_date: DateTime.parse(start_date), end_date: DateTime.parse(end_date)) }
@@ -493,13 +492,13 @@ RSpec.describe Gitlab::UsageDataCounters::HLLRedisCounter, :clean_gitlab_redis_s
'2020-12-21' | '2020-12-20' | []
'2020-12-21' | '2020-11-21' | []
'2021-01-01' | '2020-12-28' | []
- '2020-12-21' | '2020-12-28' | ['g_{compliance}_dashboard-2020-52']
- '2020-12-21' | '2021-01-01' | ['g_{compliance}_dashboard-2020-52']
- '2020-12-27' | '2021-01-01' | ['g_{compliance}_dashboard-2020-52']
- '2020-12-26' | '2021-01-04' | ['g_{compliance}_dashboard-2020-52', 'g_{compliance}_dashboard-2020-53']
- '2020-12-26' | '2021-01-11' | ['g_{compliance}_dashboard-2020-52', 'g_{compliance}_dashboard-2020-53', 'g_{compliance}_dashboard-2021-01']
- '2020-12-26' | '2021-01-17' | ['g_{compliance}_dashboard-2020-52', 'g_{compliance}_dashboard-2020-53', 'g_{compliance}_dashboard-2021-01']
- '2020-12-26' | '2021-01-18' | ['g_{compliance}_dashboard-2020-52', 'g_{compliance}_dashboard-2020-53', 'g_{compliance}_dashboard-2021-01', 'g_{compliance}_dashboard-2021-02']
+ '2020-12-21' | '2020-12-28' | ['i_{search}_total-2020-52']
+ '2020-12-21' | '2021-01-01' | ['i_{search}_total-2020-52']
+ '2020-12-27' | '2021-01-01' | ['i_{search}_total-2020-52']
+ '2020-12-26' | '2021-01-04' | ['i_{search}_total-2020-52', 'i_{search}_total-2020-53']
+ '2020-12-26' | '2021-01-11' | ['i_{search}_total-2020-52', 'i_{search}_total-2020-53', 'i_{search}_total-2021-01']
+ '2020-12-26' | '2021-01-17' | ['i_{search}_total-2020-52', 'i_{search}_total-2020-53', 'i_{search}_total-2021-01']
+ '2020-12-26' | '2021-01-18' | ['i_{search}_total-2020-52', 'i_{search}_total-2020-53', 'i_{search}_total-2021-01', 'i_{search}_total-2021-02']
end
with_them do
diff --git a/spec/lib/gitlab/utils/batch_loader_spec.rb b/spec/lib/gitlab/utils/batch_loader_spec.rb
new file mode 100644
index 00000000000..c1f6d6df07a
--- /dev/null
+++ b/spec/lib/gitlab/utils/batch_loader_spec.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+require 'batch-loader'
+
+RSpec.describe Gitlab::Utils::BatchLoader do
+ let(:stubbed_loader) do
+ double( # rubocop:disable RSpec/VerifiedDoubles
+ 'Loader',
+ load_lazy_method: [],
+ load_lazy_method_same_batch_key: [],
+ load_lazy_method_other_batch_key: []
+ )
+ end
+
+ let(:test_module) do
+ Module.new do
+ def self.lazy_method(id)
+ BatchLoader.for(id).batch(key: :my_batch_name) do |ids, loader|
+ stubbed_loader.load_lazy_method(ids)
+
+ ids.each { |id| loader.call(id, id) }
+ end
+ end
+
+ def self.lazy_method_same_batch_key(id)
+ BatchLoader.for(id).batch(key: :my_batch_name) do |ids, loader|
+ stubbed_loader.load_lazy_method_same_batch_key(ids)
+
+ ids.each { |id| loader.call(id, id) }
+ end
+ end
+
+ def self.lazy_method_other_batch_key(id)
+ BatchLoader.for(id).batch(key: :other_batch_name) do |ids, loader|
+ stubbed_loader.load_lazy_method_other_batch_key(ids)
+
+ ids.each { |id| loader.call(id, id) }
+ end
+ end
+ end
+ end
+
+ before do
+ BatchLoader::Executor.clear_current
+ allow(test_module).to receive(:stubbed_loader).and_return(stubbed_loader)
+ end
+
+ describe '.clear_key' do
+ it 'clears batched items which match the specified batch key' do
+ test_module.lazy_method(1)
+ test_module.lazy_method_same_batch_key(2)
+ test_module.lazy_method_other_batch_key(3)
+
+ described_class.clear_key(:my_batch_name)
+
+ test_module.lazy_method(4).to_i
+ test_module.lazy_method_same_batch_key(5).to_i
+ test_module.lazy_method_other_batch_key(6).to_i
+
+ expect(stubbed_loader).to have_received(:load_lazy_method).with([4])
+ expect(stubbed_loader).to have_received(:load_lazy_method_same_batch_key).with([5])
+ expect(stubbed_loader).to have_received(:load_lazy_method_other_batch_key).with([3, 6])
+ end
+
+ it 'clears loaded values which match the specified batch key' do
+ test_module.lazy_method(1).to_i
+ test_module.lazy_method_same_batch_key(2).to_i
+ test_module.lazy_method_other_batch_key(3).to_i
+
+ described_class.clear_key(:my_batch_name)
+
+ test_module.lazy_method(1).to_i
+ test_module.lazy_method_same_batch_key(2).to_i
+ test_module.lazy_method_other_batch_key(3).to_i
+
+ expect(stubbed_loader).to have_received(:load_lazy_method).with([1]).twice
+ expect(stubbed_loader).to have_received(:load_lazy_method_same_batch_key).with([2]).twice
+ expect(stubbed_loader).to have_received(:load_lazy_method_other_batch_key).with([3])
+ end
+ end
+end
diff --git a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
index 5767fa4326e..cd8c3dd2806 100644
--- a/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
+++ b/spec/requests/api/ci/runner/jobs_artifacts_spec.rb
@@ -844,7 +844,13 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
'Content-Disposition' => %q(attachment; filename="ci_build_artifacts.zip"; filename*=UTF-8''ci_build_artifacts.zip) }
end
+ before do
+ allow(Gitlab::ApplicationContext).to receive(:push).and_call_original
+ end
+
it 'downloads artifacts' do
+ expect(Gitlab::ApplicationContext).to receive(:push).with(artifact: an_instance_of(Ci::JobArtifact)).once.and_call_original
+
download_artifact
expect(response).to have_gitlab_http_status(:ok)
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index ecf9f92d74f..49073931c02 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -64,6 +64,28 @@ RSpec.describe Projects::TransferService do
expect(project.namespace).to eq(group)
end
+ context 'EventStore' do
+ let(:group) do
+ create(:group, :nested).tap { |g| g.add_owner(user) }
+ end
+
+ let(:target) do
+ create(:group, :nested).tap { |g| g.add_owner(user) }
+ end
+
+ it 'publishes a ProjectTransferedEvent' do
+ expect { execute_transfer }
+ .to publish_event(Projects::ProjectTransferedEvent)
+ .with(
+ project_id: kind_of(Numeric),
+ old_namespace_id: group.id,
+ old_root_namespace_id: group.parent_id,
+ new_namespace_id: target.id,
+ new_root_namespace_id: target.parent_id
+ )
+ end
+ end
+
context 'when project has an associated project namespace' do
it 'keeps project namespace in sync with project' do
transfer_result = execute_transfer
@@ -299,6 +321,11 @@ RSpec.describe Projects::TransferService do
)
end
+ it 'does not publish a ProjectTransferedEvent' do
+ expect { attempt_project_transfer }
+ .not_to publish_event(Projects::ProjectTransferedEvent)
+ end
+
context 'when project has pending builds', :sidekiq_inline do
let!(:other_project) { create(:project) }
let!(:pending_build) { create(:ci_pending_build, project: project.reload) }
diff --git a/spec/services/protected_branches/cache_service_spec.rb b/spec/services/protected_branches/cache_service_spec.rb
new file mode 100644
index 00000000000..4c05d6dcbd9
--- /dev/null
+++ b/spec/services/protected_branches/cache_service_spec.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+# rubocop:disable Style/RedundantFetchBlock
+#
+require 'spec_helper'
+
+RSpec.describe ProtectedBranches::CacheService, :clean_gitlab_redis_cache do
+ subject(:service) { described_class.new(project, user) }
+
+ let_it_be(:project) { create(:project) }
+ let_it_be(:user) { project.first_owner }
+
+ let(:immediate_expiration) { 0 }
+
+ describe '#fetch' do
+ it 'caches the value' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
+ end
+
+ it 'sets expiry on the key' do
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
+
+ it 'does not set an expiry on the key after the hash is already created' do
+ expect(service.fetch('main') { true }).to eq(true)
+
+ stub_const("#{described_class.name}::CACHE_EXPIRE_IN", immediate_expiration)
+
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
+ end
+
+ context 'when CACHE_LIMIT is exceeded' do
+ before do
+ stub_const("#{described_class.name}::CACHE_LIMIT", 2)
+ end
+
+ it 'recreates cache' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ # Uses cached values
+ expect(service.fetch('main') { false }).to eq(true)
+ expect(service.fetch('not-found') { true }).to eq(false)
+
+ # Overflow
+ expect(service.fetch('new-branch') { true }).to eq(true)
+
+ # Refreshes values
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
+ end
+ end
+
+ describe '#refresh' do
+ it 'clears cached values' do
+ expect(service.fetch('main') { true }).to eq(true)
+ expect(service.fetch('not-found') { false }).to eq(false)
+
+ service.refresh
+
+ # Recreates cache
+ expect(service.fetch('main') { false }).to eq(false)
+ expect(service.fetch('not-found') { true }).to eq(true)
+ end
+ end
+end
+# rubocop:enable Style/RedundantFetchBlock
diff --git a/spec/services/protected_branches/create_service_spec.rb b/spec/services/protected_branches/create_service_spec.rb
index 3ac42d41377..5a371b68f0c 100644
--- a/spec/services/protected_branches/create_service_spec.rb
+++ b/spec/services/protected_branches/create_service_spec.rb
@@ -24,6 +24,14 @@ RSpec.describe ProtectedBranches::CreateService do
expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
+
+ service.execute
+ end
+
context 'when protecting a branch with a name that contains HTML tags' do
let(:name) { 'foobar<\b>' }
diff --git a/spec/services/protected_branches/destroy_service_spec.rb b/spec/services/protected_branches/destroy_service_spec.rb
index 4e55c72f312..492b91dbb8b 100644
--- a/spec/services/protected_branches/destroy_service_spec.rb
+++ b/spec/services/protected_branches/destroy_service_spec.rb
@@ -16,6 +16,14 @@ RSpec.describe ProtectedBranches::DestroyService do
expect(protected_branch).to be_destroyed
end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
+
+ service.execute(protected_branch)
+ end
+
context 'when a policy restricts rule deletion' do
before do
policy = instance_double(ProtectedBranchPolicy, allowed?: false)
diff --git a/spec/services/protected_branches/update_service_spec.rb b/spec/services/protected_branches/update_service_spec.rb
index 4405af35c37..ee61382bd89 100644
--- a/spec/services/protected_branches/update_service_spec.rb
+++ b/spec/services/protected_branches/update_service_spec.rb
@@ -18,6 +18,14 @@ RSpec.describe ProtectedBranches::UpdateService do
expect(result.reload.name).to eq(params[:name])
end
+ it 'refreshes the cache' do
+ expect_next_instance_of(ProtectedBranches::CacheService) do |cache_service|
+ expect(cache_service).to receive(:refresh)
+ end
+
+ result
+ end
+
context 'when updating name of a protected branch to one that contains HTML tags' do
let(:new_name) { 'foobar<\b>' }
let(:result) { service.execute(protected_branch) }
diff --git a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
index e650e183bc8..37c9908af1d 100644
--- a/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
+++ b/spec/views/projects/pipeline_schedules/_pipeline_schedule.html.haml_spec.rb
@@ -28,7 +28,7 @@ RSpec.describe 'projects/pipeline_schedules/_pipeline_schedule' do
it 'non-owner can take ownership of pipeline' do
render
- expect(rendered).to have_link('Take ownership')
+ expect(rendered).to have_button('Take ownership')
end
end
@@ -42,7 +42,7 @@ RSpec.describe 'projects/pipeline_schedules/_pipeline_schedule' do
it 'owner cannot take ownership of pipeline' do
render
- expect(rendered).not_to have_link('Take ownership')
+ expect(rendered).not_to have_button('Take ownership')
end
end
end
diff --git a/yarn.lock b/yarn.lock
index ca755429f19..4d7aae26d5b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2744,21 +2744,6 @@ autosize@^5.0.1:
resolved "https://registry.yarnpkg.com/autosize/-/autosize-5.0.1.tgz#ed269b0fa9b7eb47627048a1bb3299e99e003a0f"
integrity sha512-UIWUlE4TOVPNNj2jjrU39wI4hEYbneUypEqcyRmRFIx5CC2gNdg3rQr+Zh7/3h6egbBvm33TDQjNQKtj9Tk1HA==
-aws-sdk@^2.637.0:
- version "2.637.0"
- resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.637.0.tgz#810e25e53acf2250d35fc74498f9d4492e154217"
- integrity sha512-e7EYX5rNtQyEaleQylUtLSNKXOmvOwfifQ4bYkfF80mFsVI3DSydczLHXrqPzXoEJaS/GI/9HqVnlQcPs6Q3ew==
- dependencies:
- buffer "4.9.1"
- events "1.1.1"
- ieee754 "1.1.13"
- jmespath "0.15.0"
- querystring "0.2.0"
- sax "1.2.1"
- url "0.10.3"
- uuid "3.3.2"
- xml2js "0.4.19"
-
axios-mock-adapter@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-1.15.0.tgz#fbc06825d8302c95c3334d21023bba996255d45d"
@@ -3137,7 +3122,7 @@ buffer-xor@^1.0.3:
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=
-buffer@4.9.1, buffer@^4.3.0:
+buffer@^4.3.0:
version "4.9.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298"
integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=
@@ -5444,11 +5429,6 @@ eventemitter3@^4.0.0:
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
-events@1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
- integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=
-
events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
@@ -6582,7 +6562,7 @@ icss-utils@^4.1.0:
dependencies:
postcss "^7.0.14"
-ieee754@1.1.13, ieee754@^1.1.13, ieee754@^1.1.4:
+ieee754@^1.1.13, ieee754@^1.1.4:
version "1.1.13"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
@@ -7528,11 +7508,6 @@ jest@^26.5.2:
import-local "^3.0.2"
jest-cli "^26.5.2"
-jmespath@0.15.0:
- version "0.15.0"
- resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.15.0.tgz#a3f222a9aae9f966f5d27c796510e28091764217"
- integrity sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=
-
jquery.caret@^0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/jquery.caret/-/jquery.caret-0.3.1.tgz#9c093318faf327eff322e826ca9f3241368bc7b8"
@@ -10598,11 +10573,6 @@ sass@^1.49.9:
immutable "^4.0.0"
source-map-js ">=0.6.2 <2.0.0"
-sax@1.2.1, sax@>=0.6.0:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
- integrity sha1-e45lYZCyKOgaZq6nSEgNgozS03o=
-
saxes@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
@@ -12004,14 +11974,6 @@ url-loader@^4.1.1:
mime-types "^2.1.27"
schema-utils "^3.0.0"
-url@0.10.3:
- version "0.10.3"
- resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
- integrity sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=
- dependencies:
- punycode "1.3.2"
- querystring "0.2.0"
-
url@^0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -12049,11 +12011,6 @@ utils-merge@1.0.1:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
-uuid@3.3.2:
- version "3.3.2"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
- integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
-
uuid@8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d"
@@ -12634,24 +12591,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
-xml2js@0.4.19:
- version "0.4.19"
- resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
- integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
- dependencies:
- sax ">=0.6.0"
- xmlbuilder "~9.0.1"
-
xml@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5"
integrity sha1-eLpyAgApxbyHuKgaPPzXS0ovweU=
-xmlbuilder@~9.0.1:
- version "9.0.7"
- resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
- integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
-
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"