Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-22 09:09:20 +00:00
parent 054cda9ea6
commit e9a834ee29
54 changed files with 803 additions and 532 deletions

View File

@ -76,6 +76,7 @@
/doc/user/project/web_ide/index.md @aqualls
/doc/user/project/wiki/index.md @aqualls
/doc/user/search/ @marcia @aqualls
/doc/user/workspace/ @fneill
[Docs Create]
/doc/administration/file_hooks.md @aqualls

View File

@ -1,8 +1,7 @@
<script>
import { GlModal, GlButton, GlFormInput } from '@gitlab/ui';
import { escape } from 'lodash';
import { GlModal, GlButton, GlFormInput, GlSprintf } from '@gitlab/ui';
import csrf from '~/lib/utils/csrf';
import { s__, sprintf } from '~/locale';
import { s__ } from '~/locale';
import SplitButton from '~/vue_shared/components/split_button.vue';
const splitButtonActionItems = [
@ -29,6 +28,7 @@ export default {
GlModal,
GlButton,
GlFormInput,
GlSprintf,
},
props: {
clusterPath: {
@ -67,17 +67,11 @@ export default {
: s__('ClusterIntegration|You are about to remove your cluster integration.');
},
confirmationTextLabel() {
return sprintf(
this.confirmCleanup
? s__(
'ClusterIntegration|To remove your integration and resources, type %{clusterName} to confirm:',
)
: s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:'),
{
clusterName: `<code>${escape(this.clusterName)}</code>`,
},
false,
);
return this.confirmCleanup
? s__(
'ClusterIntegration|To remove your integration and resources, type %{clusterName} to confirm:',
)
: s__('ClusterIntegration|To remove your integration, type %{clusterName} to confirm:');
},
canSubmit() {
return this.enteredClusterName === this.clusterName;
@ -140,7 +134,13 @@ export default {
<!-- eslint-enable @gitlab/vue-require-i18n-strings -->
</ul>
</div>
<strong v-html="confirmationTextLabel /* eslint-disable-line vue/no-v-html */"></strong>
<strong>
<gl-sprintf :message="confirmationTextLabel">
<template #clusterName>
<code>{{ clusterName }}</code>
</template>
</gl-sprintf>
</strong>
<form ref="form" :action="clusterPath" method="post" class="gl-mb-5">
<input ref="method" type="hidden" name="_method" value="delete" />
<input :value="csrfToken" type="hidden" name="authenticity_token" />

View File

@ -1,10 +1,8 @@
import { Blockquote } from '@tiptap/extension-blockquote';
import { wrappingInputRule } from 'prosemirror-inputrules';
import { wrappingInputRule } from '@tiptap/core';
import { getParents } from '~/lib/utils/dom_utils';
import { getMarkdownSource } from '../services/markdown_sourcemap';
export const multilineInputRegex = /^\s*>>>\s$/gm;
export default Blockquote.extend({
addAttributes() {
return {
@ -25,9 +23,15 @@ export default Blockquote.extend({
},
addInputRules() {
const multilineInputRegex = /^\s*>>>\s$/gm;
return [
...this.parent?.(),
wrappingInputRule(multilineInputRegex, this.type, () => ({ multiline: true })),
wrappingInputRule({
find: multilineInputRegex,
type: this.type,
getAttributes: () => ({ multiline: true }),
}),
];
},
});

View File

@ -1,7 +1,4 @@
import { Node, mergeAttributes } from '@tiptap/core';
import { wrappingInputRule } from 'prosemirror-inputrules';
export const inputRegex = /^\s*(<dl>)$/;
import { Node, mergeAttributes, wrappingInputRule } from '@tiptap/core';
export default Node.create({
name: 'descriptionList',
@ -18,6 +15,8 @@ export default Node.create({
},
addInputRules() {
return [wrappingInputRule(inputRegex, this.type)];
const inputRegex = /^\s*(<dl>)$/;
return [wrappingInputRule({ find: inputRegex, type: this.type })];
},
});

View File

@ -1,10 +1,7 @@
import { Node } from '@tiptap/core';
import { Node, wrappingInputRule } from '@tiptap/core';
import { VueNodeViewRenderer } from '@tiptap/vue-2';
import { wrappingInputRule } from 'prosemirror-inputrules';
import DetailsWrapper from '../components/wrappers/details.vue';
export const inputRegex = /^\s*(<details>)$/;
export default Node.create({
name: 'details',
content: 'detailsContent+',
@ -24,7 +21,9 @@ export default Node.create({
},
addInputRules() {
return [wrappingInputRule(inputRegex, this.type)];
const inputRegex = /^\s*(<details>)$/;
return [wrappingInputRule({ find: inputRegex, type: this.type })];
},
addCommands() {

View File

@ -1,9 +1,6 @@
import { Node } from '@tiptap/core';
import { InputRule } from 'prosemirror-inputrules';
import { Node, InputRule } from '@tiptap/core';
import { initEmojiMap, getAllEmoji } from '~/emoji';
export const emojiInputRegex = /(?:^|\s)((?::)((?:\w+))(?::))$/;
export default Node.create({
name: 'emoji',
@ -54,23 +51,28 @@ export default Node.create({
},
addInputRules() {
const emojiInputRegex = /(?:^|\s)(:(\w+):)$/;
return [
new InputRule(emojiInputRegex, (state, match, start, end) => {
const [, , name] = match;
const emojis = getAllEmoji();
const emoji = emojis[name];
const { tr } = state;
new InputRule({
find: emojiInputRegex,
handler: ({ state, range: { from, to }, match }) => {
const [, , name] = match;
const emojis = getAllEmoji();
const emoji = emojis[name];
const { tr } = state;
if (emoji) {
tr.replaceWith(start, end, [
state.schema.text(' '),
this.type.create({ name, moji: emoji.e, unicodeVersion: emoji.u, title: emoji.d }),
]);
if (emoji) {
tr.replaceWith(from, to, [
state.schema.text(' '),
this.type.create({ name, moji: emoji.e, unicodeVersion: emoji.u, title: emoji.d }),
]);
return tr;
}
return tr;
}
return null;
return null;
},
}),
];
},

View File

@ -1,10 +1,10 @@
import { nodeInputRule } from '@tiptap/core';
import { HorizontalRule } from '@tiptap/extension-horizontal-rule';
export const hrInputRuleRegExp = /^---$/;
export default HorizontalRule.extend({
addInputRules() {
return [nodeInputRule(hrInputRuleRegExp, this.type)];
const hrInputRuleRegExp = /^---$/;
return [nodeInputRule({ find: hrInputRuleRegExp, type: this.type })];
},
});

View File

@ -60,7 +60,13 @@ export default marks.map((name) =>
},
addInputRules() {
return [markInputRule(markInputRegex(name), this.type, extractMarkAttributesFromMatch)];
return [
markInputRule({
find: markInputRegex(name),
type: this.type,
getAttributes: extractMarkAttributesFromMatch,
}),
];
},
}),
);

View File

@ -1,8 +1,5 @@
import { Mark, markInputRule, mergeAttributes } from '@tiptap/core';
export const inputRegexAddition = /(\{\+(.+?)\+\})$/gm;
export const inputRegexDeletion = /(\{-(.+?)-\})$/gm;
export default Mark.create({
name: 'inlineDiff',
@ -38,9 +35,20 @@ export default Mark.create({
},
addInputRules() {
const inputRegexAddition = /(\{\+(.+?)\+\})$/gm;
const inputRegexDeletion = /(\{-(.+?)-\})$/gm;
return [
markInputRule(inputRegexAddition, this.type, () => ({ type: 'addition' })),
markInputRule(inputRegexDeletion, this.type, () => ({ type: 'deletion' })),
markInputRule({
find: inputRegexAddition,
type: this.type,
getAttributes: () => ({ type: 'addition' }),
}),
markInputRule({
find: inputRegexDeletion,
type: this.type,
getAttributes: () => ({ type: 'deletion' }),
}),
];
},
});

View File

@ -1,9 +1,6 @@
import { markInputRule } from '@tiptap/core';
import { Link } from '@tiptap/extension-link';
export const markdownLinkSyntaxInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
export const urlSyntaxRegExp = /(?:^|\s)(?<href>(?:https?:\/\/|www\.)[\S]+)(?:\s|\n)$/gim;
const extractHrefFromMatch = (match) => {
return { href: match.groups.href };
};
@ -26,9 +23,20 @@ export default Link.extend({
openOnClick: false,
},
addInputRules() {
const markdownLinkSyntaxInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
const urlSyntaxRegExp = /(?:^|\s)(?<href>(?:https?:\/\/|www\.)[\S]+)(?:\s|\n)$/gim;
return [
markInputRule(markdownLinkSyntaxInputRuleRegExp, this.type, extractHrefFromMarkdownLink),
markInputRule(urlSyntaxRegExp, this.type, extractHrefFromMatch),
markInputRule({
find: markdownLinkSyntaxInputRuleRegExp,
type: this.type,
getAttributes: extractHrefFromMarkdownLink,
}),
markInputRule({
find: urlSyntaxRegExp,
type: this.type,
getAttributes: extractHrefFromMatch,
}),
];
},
addAttributes() {

View File

@ -2,8 +2,6 @@ import { Mark, markInputRule } from '@tiptap/core';
import { __ } from '~/locale';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
export const inputRegex = /(?:^|\s)\$`([^`]+)`\$$/gm;
export default Mark.create({
name: 'mathInline',
@ -30,6 +28,8 @@ export default Mark.create({
},
addInputRules() {
return [markInputRule(inputRegex, this.type)];
const inputRegex = /(?:^|\s)\$`([^`]+)`\$$/gm;
return [markInputRule({ find: inputRegex, type: this.type })];
},
});

View File

@ -4,6 +4,12 @@ import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark
export default Subscript.extend({
addInputRules() {
return [markInputRule(markInputRegex('sub'), this.type, extractMarkAttributesFromMatch)];
return [
markInputRule({
find: markInputRegex('sub'),
type: this.type,
getAttributes: extractMarkAttributesFromMatch,
}),
];
},
});

View File

@ -4,6 +4,12 @@ import { markInputRegex, extractMarkAttributesFromMatch } from '../services/mark
export default Superscript.extend({
addInputRules() {
return [markInputRule(markInputRegex('sup'), this.type, extractMarkAttributesFromMatch)];
return [
markInputRule({
find: markInputRegex('sup'),
type: this.type,
getAttributes: extractMarkAttributesFromMatch,
}),
];
},
});

View File

@ -1,10 +1,7 @@
import { Node } from '@tiptap/core';
import { InputRule } from 'prosemirror-inputrules';
import { Node, InputRule } from '@tiptap/core';
import { s__ } from '~/locale';
import { PARSE_HTML_PRIORITY_HIGHEST } from '../constants';
export const inputRuleRegExps = [/^\[\[_TOC_\]\]$/, /^\[TOC\]$/];
export default Node.create({
name: 'tableOfContents',
@ -34,17 +31,21 @@ export default Node.create({
addInputRules() {
const { type } = this;
const inputRuleRegExps = [/^\[\[_TOC_\]\]$/, /^\[TOC\]$/];
return inputRuleRegExps.map(
(regex) =>
new InputRule(regex, (state, match, start, end) => {
const { tr } = state;
new InputRule({
find: regex,
handler: ({ state, range: { from, to }, match }) => {
const { tr } = state;
if (match) {
tr.replaceWith(start - 1, end, type.create());
}
if (match) {
tr.replaceWith(from - 1, to, type.create());
}
return tr;
return tr;
},
}),
);
},

View File

@ -1,7 +1,5 @@
import { Node, mergeAttributes, nodeInputRule } from '@tiptap/core';
export const inputRegex = /^<wbr>$/;
export default Node.create({
name: 'wordBreak',
inline: true,
@ -24,6 +22,8 @@ export default Node.create({
},
addInputRules() {
return [nodeInputRule(inputRegex, this.type)];
const inputRegex = /^<wbr>$/;
return [nodeInputRule({ find: inputRegex, type: this.type })];
},
});

View File

@ -1,5 +1,5 @@
import { mapValues } from 'lodash';
import { InputRule } from 'prosemirror-inputrules';
import { InputRule } from '@tiptap/core';
import { ENTER_KEY, BACKSPACE_KEY } from '~/lib/utils/keys';
import Tracking from '~/tracking';
import {
@ -17,17 +17,20 @@ const trackKeyboardShortcut = (contentType, commandFn, shortcut) => () => {
};
const trackInputRule = (contentType, inputRule) => {
return new InputRule(inputRule.match, (...args) => {
const result = inputRule.handler(...args);
return new InputRule({
find: inputRule.find,
handler: (...args) => {
const result = inputRule.handler(...args);
if (result) {
Tracking.event(undefined, INPUT_RULE_TRACKING_ACTION, {
label: CONTENT_EDITOR_TRACKING_LABEL,
property: contentType,
});
}
if (result !== null) {
Tracking.event(undefined, INPUT_RULE_TRACKING_ACTION, {
label: CONTENT_EDITOR_TRACKING_LABEL,
property: contentType,
});
}
return result;
return result;
},
});
};

View File

@ -64,58 +64,51 @@ export default {
</script>
<template>
<div
v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)"
id="related-merge-requests"
>
<div id="merge-requests" class="card card-slim mt-3">
<div v-if="isFetchingMergeRequests || (!isFetchingMergeRequests && totalCount)">
<div class="card card-slim gl-mt-5">
<div class="card-header">
<div class="card-title mt-0 mb-0 h5 merge-requests-title position-relative">
<div
class="card-title gl-relative gl-display-flex gl-align-items-center gl-line-height-20 gl-font-weight-bold gl-m-0"
>
<gl-link
id="user-content-related-merge-requests"
class="anchor position-absolute text-decoration-none"
class="anchor gl-absolute gl-text-decoration-none"
href="#related-merge-requests"
aria-hidden="true"
aria-labelledby="related-merge-requests"
/>
<span class="mr-1">
<h3 id="related-merge-requests" class="gl-font-base gl-m-0">
{{ __('Related merge requests') }}
</span>
<div v-if="totalCount" class="d-inline-flex lh-100 align-middle">
<div
class="mr-count-badge gl-display-inline-flex gl-align-items-center gl-py-2 gl-px-3"
>
<svg class="s16 mr-1 text-secondary">
<gl-icon name="merge-request" class="mr-1 text-secondary" />
</svg>
<span class="js-items-count">{{ totalCount }}</span>
</div>
</div>
</h3>
<template v-if="totalCount">
<gl-icon name="merge-request" class="gl-ml-5 gl-mr-2 gl-text-gray-500" />
<span data-testid="count">{{ totalCount }}</span>
</template>
</div>
</div>
<div>
<div v-if="isFetchingMergeRequests" class="qa-related-merge-requests-loading-icon">
<gl-loading-icon size="sm" label="Fetching related merge requests" class="py-2" />
</div>
<ul v-else class="content-list related-items-list">
<li v-for="mr in mergeRequests" :key="mr.id" class="list-item pt-0 pb-0">
<related-issuable-item
:id-key="mr.id"
:display-reference="mr.reference"
:title="mr.title"
:milestone="mr.milestone"
:assignees="getAssignees(mr)"
:created-at="mr.created_at"
:closed-at="mr.closed_at"
:merged-at="mr.merged_at"
:path="mr.web_url"
:state="mr.state"
:is-merge-request="true"
:pipeline-status="mr.head_pipeline && mr.head_pipeline.detailed_status"
path-id-separator="!"
/>
</li>
</ul>
</div>
<gl-loading-icon
v-if="isFetchingMergeRequests"
size="sm"
label="Fetching related merge requests"
class="gl-py-3"
/>
<ul v-else class="content-list related-items-list">
<li v-for="mr in mergeRequests" :key="mr.id" class="list-item gl-m-0! gl-p-0!">
<related-issuable-item
:id-key="mr.id"
:display-reference="mr.reference"
:title="mr.title"
:milestone="mr.milestone"
:assignees="getAssignees(mr)"
:created-at="mr.created_at"
:closed-at="mr.closed_at"
:merged-at="mr.merged_at"
:path="mr.web_url"
:state="mr.state"
:is-merge-request="true"
:pipeline-status="mr.head_pipeline && mr.head_pipeline.detailed_status"
path-id-separator="!"
/>
</li>
</ul>
</div>
<div
v-if="hasClosingMergeRequest && !isFetchingMergeRequests"

View File

@ -1,5 +1,5 @@
<script>
import { GlTab, GlTabs, GlSprintf, GlLink } from '@gitlab/ui';
import { GlTab, GlTabs, GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
@ -31,6 +31,7 @@ export default {
AutoDevOpsAlert,
AutoDevOpsEnabledAlert,
FeatureCard,
GlAlert,
GlLink,
GlSprintf,
GlTab,
@ -79,6 +80,7 @@ export default {
data() {
return {
autoDevopsEnabledAlertDismissedProjects: [],
errorMessage: '',
};
},
computed: {
@ -106,6 +108,12 @@ export default {
dismissedProjects.add(this.projectPath);
this.autoDevopsEnabledAlertDismissedProjects = Array.from(dismissedProjects);
},
onError(message) {
this.errorMessage = message;
},
dismissAlert() {
this.errorMessage = '';
},
},
autoDevopsEnabledAlertStorageKey: AUTO_DEVOPS_ENABLED_ALERT_DISMISSED_STORAGE_KEY,
};
@ -113,6 +121,16 @@ export default {
<template>
<article>
<gl-alert
v-if="errorMessage"
sticky
class="gl-top-8 gl-z-index-1"
data-testid="manage-via-mr-error-alert"
variant="danger"
@dismiss="dismissAlert"
>
{{ errorMessage }}
</gl-alert>
<local-storage-sync
v-model="autoDevopsEnabledAlertDismissedProjects"
:storage-key="$options.autoDevopsEnabledAlertStorageKey"
@ -174,6 +192,7 @@ export default {
data-testid="security-testing-card"
:feature="feature"
class="gl-mb-6"
@error="onError"
/>
</template>
</section-layout>
@ -207,6 +226,7 @@ export default {
:key="feature.type"
:feature="feature"
class="gl-mb-6"
@error="onError"
/>
</template>
</section-layout>

View File

@ -66,6 +66,11 @@ export default {
return Boolean(name && description && configurationText);
},
},
methods: {
onError(message) {
this.$emit('error', message);
},
},
i18n: {
enabled: s__('SecurityConfiguration|Enabled'),
notEnabled: s__('SecurityConfiguration|Not enabled'),
@ -129,6 +134,7 @@ export default {
category="primary"
class="gl-mt-5"
:data-qa-selector="`${feature.type}_mr_button`"
@error="onError"
/>
<gl-button

View File

@ -26,6 +26,11 @@ export default {
type: Number,
required: true,
},
injectedArtifacts: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
@ -56,6 +61,9 @@ export default {
isLoadingReportArtifacts() {
return this.$apollo.queries.reportArtifacts.loading;
},
mergedReportArtifacts() {
return [...this.reportArtifacts, ...this.injectedArtifacts];
},
},
methods: {
showError(error) {
@ -77,7 +85,7 @@ export default {
<template>
<security-report-download-dropdown
:title="s__('SecurityReports|Download results')"
:artifacts="reportArtifacts"
:artifacts="mergedReportArtifacts"
:loading="isLoadingReportArtifacts"
/>
</template>

View File

@ -132,16 +132,7 @@ For example, the following two definitions are equal:
When a CI job runs in a Docker container, the `before_script`, `script`, and `after_script` commands run in the `/builds/<project-path>/` directory. Your image may have a different default `WORKDIR` defined. To move to your `WORKDIR`, save the `WORKDIR` as an environment variable so you can reference it in the container during the job's runtime.
### Available settings for `image`
> Introduced in GitLab and GitLab Runner 9.4.
| Setting | Required | Description |
|------------|----------| ----------- |
| `name` | Yes, when used with any other option. | Full name of the image. It should contain the registry part if needed. |
| `entrypoint` | No. | Command or script to execute as the container's entrypoint. It's translated to Docker's `--entrypoint` option while creating the container. The syntax is similar to [`Dockerfile`'s `ENTRYPOINT`](https://docs.docker.com/engine/reference/builder/#entrypoint) directive, where each shell token is a separate string in the array. |
### Overriding the entrypoint of an image
### Override the entrypoint of an image
> Introduced in GitLab and GitLab Runner 9.4. Read more about the [extended configuration options](../docker/using_docker_images.md#extended-docker-configuration-options).

View File

@ -37,7 +37,7 @@ few important details:
- The kaniko debug image is recommended (`gcr.io/kaniko-project/executor:debug`)
because it has a shell, and a shell is required for an image to be used with
GitLab CI/CD.
- The entrypoint needs to be [overridden](using_docker_images.md#overriding-the-entrypoint-of-an-image),
- The entrypoint needs to be [overridden](using_docker_images.md#override-the-entrypoint-of-an-image),
otherwise the build script doesn't run.
- A Docker `config.json` file needs to be created with the authentication
information for the desired container registry.

View File

@ -59,3 +59,8 @@ To make submodules work correctly in CI/CD jobs:
variables:
GIT_SUBMODULE_STRATEGY: recursive
```
If you use the [`CI_JOB_TOKEN`](jobs/ci_job_token.md) to clone a submodule in a
pipeline job, the user executing the job must be assigned to a role that has
[permission](../user/permissions.md#gitlab-cicd-permissions) to trigger a pipeline
in the upstream submodule project.

View File

@ -645,24 +645,87 @@ The following topics explain how to use keywords to configure CI/CD pipelines.
### `image`
Use `image` to specify [a Docker image](../docker/using_docker_images.md#what-is-an-image) to use for the job.
Use `image` to specify a Docker image that the job runs in.
For:
**Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default:` section](#custom-default-keyword-values).
- Usage examples, see [Define `image` in the `.gitlab-ci.yml` file](../docker/using_docker_images.md#define-image-in-the-gitlab-ciyml-file).
- Detailed usage information, refer to [Docker integration](../docker/index.md) documentation.
**Possible inputs**: The name of the image, including the registry path if needed, in one of these formats:
- `<image-name>` (Same as using `<image-name>` with the `latest` tag)
- `<image-name>:<tag>`
- `<image-name>@<digest>`
**Example of `image`**:
```yaml
default:
image: ruby:3.0
rspec:
script: bundle exec rspec
rspec 2.7:
image: registry.example.com/my-group/my-project/ruby:2.7
script: bundle exec rspec
```
In this example, the `ruby:3.0` image is the default for all jobs in the pipeline.
The `rspec 2.7` job does not use the default, because it overrides the default with
a job-specific `image:` section.
**Related topics**:
- [Run your CI/CD jobs in Docker containers](../docker/using_docker_images.md).
#### `image:name`
An [extended Docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
The name of the Docker image that the job runs in. Similar to [`image:`](#image) used by itself.
For more information, see [Available settings for `image`](../docker/using_docker_images.md#available-settings-for-image).
**Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default:` section](#custom-default-keyword-values).
**Possible inputs**: The name of the image, including the registry path if needed, in one of these formats:
- `<image-name>` (Same as using `<image-name>` with the `latest` tag)
- `<image-name>:<tag>`
- `<image-name>@<digest>`
**Example of `image:name`**:
```yaml
image:
name: "registry.example.com/my/image:latest"
```
**Related topics**:
- [Run your CI/CD jobs in Docker containers](../docker/using_docker_images.md).
#### `image:entrypoint`
An [extended Docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
Command or script to execute as the container's entry point.
For more information, see [Available settings for `image`](../docker/using_docker_images.md#available-settings-for-image).
When the Docker container is created, the `entrypoint` is translated to the Docker `--entrypoint` option.
The syntax is similar to the [Dockerfile `ENTRYPOINT` directive](https://docs.docker.com/engine/reference/builder/#entrypoint),
where each shell token is a separate string in the array.
**Keyword type**: Job keyword. You can use it only as part of a job or in the
[`default:` section](#custom-default-keyword-values).
**Possible inputs**: A string.
**Example of `image:entrypoint`**:
```yaml
image:
name: super/sql:experimental
entrypoint: [""]
```
**Related topics**:
- [Override the entrypoint of an image](../docker/using_docker_images.md#override-the-entrypoint-of-an-image).
#### `services`

View File

@ -38,7 +38,7 @@ After the integration is [set up on GitLab and Jira](#configure-the-integration)
- Refer to any Jira issue by its ID (in uppercase) in GitLab branch names,
commit messages, and merge request titles.
- See the linked branches, commits, and merge requests in Jira issues.
- Create GitLab branches from Jira issues ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66032) in GitLab 14.2).
- Create GitLab branches from Jira Cloud issues ([introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66032) in GitLab 14.2).
At this time, merge requests are called "pull requests" in Jira issues.
This name may change in a future Jira release.

View File

@ -83,9 +83,11 @@ can assign, categorize, and track progress on a merge request:
## Add changes to a merge request
If you have permission to add changes to a merge request, you can add your changes
to an existing merge request in several ways, depending on the complexity of your change and whether you need access to a development environment:
to an existing merge request in several ways, depending on the complexity of your
change and whether you need access to a development environment:
- [Edit changes in the Web IDE](../web_ide/index.md) in your browser. Use this
- [Edit changes in the Web IDE](../web_ide/index.md) in your browser with the
<kbd>.</kbd> [keyboard shortcut](../../shortcuts.md). Use this
browser-based method to edit multiple files, or if you are not comfortable with Git commands.
You cannot run tests from the Web IDE.
- [Edit changes in Gitpod](../../../integration/gitpod.md#launch-gitpod-in-gitlab), if you

View File

@ -15,7 +15,8 @@ projects by providing an advanced editor with commit staging.
## Open the Web IDE
You can open the Web IDE when viewing a file, from the repository file list,
Use the <kbd>.</kbd> [keyboard shortcut](../../shortcuts.md) to open the Web IDE.
You can also open the Web IDE when viewing a file, from the repository file list,
and from merge requests:
- *When viewing a file, or the repository file list* -

View File

@ -43,6 +43,7 @@ These shortcuts are available in most areas of GitLab:
| <kbd>Shift</kbd> + <kbd>t</kbd> | Go to your To-Do List page. |
| <kbd>p</kbd> + <kbd>b</kbd> | Show or hide the Performance Bar. |
| <kbd>g</kbd> + <kbd>x</kbd> | Toggle between [GitLab](https://gitlab.com/) and [GitLab Next](https://next.gitlab.com/) (GitLab SaaS only). |
| <kbd>.</kbd> | Open the [Web IDE](project/web_ide/index.md). |
Additionally, the following shortcuts are available when editing text in text
fields (for example, comments, replies, issue descriptions, and merge request
@ -101,6 +102,7 @@ These shortcuts are available when viewing issues and [merge requests](project/m
| <kbd>]</kbd> or <kbd>j</kbd> | Move to next file (merge requests only). |
| <kbd>[</kbd> or <kbd>k</kbd> | Move to previous file (merge requests only). |
| <kbd>b</kbd> | Copy source branch name (merge requests only). |
| <kbd>.</kbd> | Open the [Web IDE](project/web_ide/index.md). |
### Project files
@ -114,6 +116,7 @@ These shortcuts are available when browsing the files in a project (go to
| <kbd>enter</kbd> | Open selection. |
| <kbd>Escape</kbd> | Go back to file list screen (only while searching for files, **Repository > Files**, then select **Find File**). |
| <kbd>y</kbd> | Go to file permalink (only while viewing a file). |
| <kbd>.</kbd> | Open the [Web IDE](project/web_ide/index.md). |
### Web IDE

View File

@ -27,6 +27,8 @@ module Gitlab
path: request.fullpath
)
Rack::Response.new('', 403).finish
rescue Gitlab::Auth::MissingPersonalAccessTokenError
Rack::Response.new('', 401).finish
end
private

View File

@ -9829,7 +9829,7 @@ msgstr ""
msgid "CreateValueStreamForm|Restore stage"
msgstr ""
msgid "CreateValueStreamForm|Save Value Stream"
msgid "CreateValueStreamForm|Save value stream"
msgstr ""
msgid "CreateValueStreamForm|Select end event"
@ -29682,9 +29682,6 @@ msgstr ""
msgid "Save Changes"
msgstr ""
msgid "Save Value Stream"
msgstr ""
msgid "Save application"
msgstr ""
@ -30418,9 +30415,6 @@ msgstr ""
msgid "SecurityReports|Download scanned URLs"
msgstr ""
msgid "SecurityReports|Download scanned resources"
msgstr ""
msgid "SecurityReports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed."
msgstr ""
@ -30631,6 +30625,9 @@ msgstr ""
msgid "SecurityReports|Your feedback is important to us! We will ask again in a week."
msgstr ""
msgid "SecurityReports|scanned resources"
msgstr ""
msgid "See example DevOps Score page in our documentation."
msgstr ""

View File

@ -63,36 +63,36 @@
"@rails/ujs": "6.1.4-1",
"@sentry/browser": "5.30.0",
"@sourcegraph/code-host-integration": "0.0.60",
"@tiptap/core": "^2.0.0-beta.118",
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
"@tiptap/extension-bold": "^2.0.0-beta.15",
"@tiptap/extension-bullet-list": "^2.0.0-beta.15",
"@tiptap/extension-code": "^2.0.0-beta.16",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.41",
"@tiptap/core": "^2.0.0-beta.125",
"@tiptap/extension-blockquote": "^2.0.0-beta.19",
"@tiptap/extension-bold": "^2.0.0-beta.19",
"@tiptap/extension-bullet-list": "^2.0.0-beta.18",
"@tiptap/extension-code": "^2.0.0-beta.20",
"@tiptap/extension-code-block-lowlight": "2.0.0-beta.47",
"@tiptap/extension-document": "^2.0.0-beta.13",
"@tiptap/extension-dropcursor": "^2.0.0-beta.19",
"@tiptap/extension-gapcursor": "^2.0.0-beta.24",
"@tiptap/extension-hard-break": "^2.0.0-beta.21",
"@tiptap/extension-heading": "^2.0.0-beta.15",
"@tiptap/extension-gapcursor": "^2.0.0-beta.27",
"@tiptap/extension-hard-break": "^2.0.0-beta.24",
"@tiptap/extension-heading": "^2.0.0-beta.18",
"@tiptap/extension-history": "^2.0.0-beta.16",
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.21",
"@tiptap/extension-image": "^2.0.0-beta.15",
"@tiptap/extension-italic": "^2.0.0-beta.15",
"@tiptap/extension-link": "^2.0.0-beta.20",
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.24",
"@tiptap/extension-image": "^2.0.0-beta.19",
"@tiptap/extension-italic": "^2.0.0-beta.19",
"@tiptap/extension-link": "^2.0.0-beta.23",
"@tiptap/extension-list-item": "^2.0.0-beta.14",
"@tiptap/extension-ordered-list": "^2.0.0-beta.16",
"@tiptap/extension-ordered-list": "^2.0.0-beta.19",
"@tiptap/extension-paragraph": "^2.0.0-beta.17",
"@tiptap/extension-strike": "^2.0.0-beta.17",
"@tiptap/extension-strike": "^2.0.0-beta.21",
"@tiptap/extension-subscript": "^2.0.0-beta.4",
"@tiptap/extension-superscript": "^2.0.0-beta.4",
"@tiptap/extension-table": "^2.0.0-beta.31",
"@tiptap/extension-table": "^2.0.0-beta.34",
"@tiptap/extension-table-cell": "^2.0.0-beta.15",
"@tiptap/extension-table-header": "^2.0.0-beta.17",
"@tiptap/extension-table-row": "^2.0.0-beta.14",
"@tiptap/extension-task-item": "^2.0.0-beta.18",
"@tiptap/extension-task-list": "^2.0.0-beta.17",
"@tiptap/extension-task-item": "^2.0.0-beta.21",
"@tiptap/extension-task-list": "^2.0.0-beta.18",
"@tiptap/extension-text": "^2.0.0-beta.13",
"@tiptap/vue-2": "^2.0.0-beta.57",
"@tiptap/vue-2": "^2.0.0-beta.60",
"@toast-ui/editor": "^2.5.2",
"@toast-ui/vue-editor": "^2.5.2",
"apollo-cache-inmemory": "^1.6.6",
@ -159,12 +159,11 @@
"popper.js": "^1.16.1",
"portal-vue": "^2.1.7",
"prismjs": "^1.21.0",
"prosemirror-inputrules": "^1.1.3",
"prosemirror-markdown": "^1.6.0",
"prosemirror-model": "^1.14.3",
"prosemirror-state": "^1.3.4",
"prosemirror-tables": "^1.1.1",
"prosemirror-view": "^1.20.2",
"prosemirror-view": "^1.20.3",
"raphael": "^2.2.7",
"raw-loader": "^4.0.2",
"scrollparent": "^2.0.1",
@ -246,7 +245,7 @@
"prettier": "2.2.1",
"prosemirror-schema-basic": "^1.1.2",
"prosemirror-schema-list": "^1.1.6",
"prosemirror-test-builder": "^1.0.4",
"prosemirror-test-builder": "^1.0.5",
"purgecss": "^4.0.3",
"purgecss-from-html": "^4.0.3",
"readdir-enhanced": "^2.2.4",

View File

@ -39,6 +39,8 @@ module QA
# Sign out as admin and sign is as the fork user
Flow::Login.sign_in(as: user)
@api_client = Runtime::API::Client.new(:gitlab, is_new_session: false, user: user)
upstream.visit!
Page::Project::Show.perform(&:fork_project)

View File

@ -53,9 +53,7 @@ RSpec.describe "Internal references", :js do
end
it "doesn't show any references", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
page.within(".issue-details") do
expect(page).not_to have_content("#merge-requests .merge-requests-title")
end
expect(page).not_to have_text 'Related merge requests'
end
end
@ -65,12 +63,9 @@ RSpec.describe "Internal references", :js do
end
it "shows references", :sidekiq_might_not_need_inline do
page.within("#merge-requests .merge-requests-title") do
expect(page).to have_content("Related merge requests")
expect(page).to have_css(".mr-count-badge")
end
expect(page).to have_text 'Related merge requests 1'
page.within("#merge-requests ul") do
page.within('.related-items-list') do
expect(page).to have_content(private_project_merge_request.title)
expect(page).to have_css(".ic-issue-open-m")
end
@ -122,9 +117,7 @@ RSpec.describe "Internal references", :js do
end
it "doesn't show any references", quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/257832' do
page.within(".merge-request-details") do
expect(page).not_to have_content("#merge-requests .merge-requests-title")
end
expect(page).not_to have_text 'Related merge requests'
end
end

View File

@ -1,18 +1,20 @@
import { GlModal } from '@gitlab/ui';
import { GlModal, GlSprintf } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { stubComponent } from 'helpers/stub_component';
import RemoveClusterConfirmation from '~/clusters/components/remove_cluster_confirmation.vue';
import SplitButton from '~/vue_shared/components/split_button.vue';
describe('Remove cluster confirmation modal', () => {
let wrapper;
const createComponent = (props = {}) => {
const createComponent = ({ props = {}, stubs = {} } = {}) => {
wrapper = mount(RemoveClusterConfirmation, {
propsData: {
clusterPath: 'clusterPath',
clusterName: 'clusterName',
...props,
},
stubs,
});
};
@ -27,35 +29,44 @@ describe('Remove cluster confirmation modal', () => {
});
describe('split button dropdown', () => {
const findModal = () => wrapper.find(GlModal).vm;
const findSplitButton = () => wrapper.find(SplitButton);
const findModal = () => wrapper.findComponent(GlModal);
const findSplitButton = () => wrapper.findComponent(SplitButton);
beforeEach(() => {
createComponent({ clusterName: 'my-test-cluster' });
jest.spyOn(findModal(), 'show').mockReturnValue();
createComponent({
props: { clusterName: 'my-test-cluster' },
stubs: { GlSprintf, GlModal: stubComponent(GlModal) },
});
jest.spyOn(findModal().vm, 'show').mockReturnValue();
});
it('opens modal with "cleanup" option', () => {
it('opens modal with "cleanup" option', async () => {
findSplitButton().vm.$emit('remove-cluster-and-cleanup');
return wrapper.vm.$nextTick().then(() => {
expect(findModal().show).toHaveBeenCalled();
expect(wrapper.vm.confirmCleanup).toEqual(true);
});
await wrapper.vm.$nextTick();
expect(findModal().vm.show).toHaveBeenCalled();
expect(wrapper.vm.confirmCleanup).toEqual(true);
expect(findModal().html()).toContain(
'<strong>To remove your integration and resources, type <code>my-test-cluster</code> to confirm:</strong>',
);
});
it('opens modal without "cleanup" option', () => {
it('opens modal without "cleanup" option', async () => {
findSplitButton().vm.$emit('remove-cluster');
return wrapper.vm.$nextTick().then(() => {
expect(findModal().show).toHaveBeenCalled();
expect(wrapper.vm.confirmCleanup).toEqual(false);
});
await wrapper.vm.$nextTick();
expect(findModal().vm.show).toHaveBeenCalled();
expect(wrapper.vm.confirmCleanup).toEqual(false);
expect(findModal().html()).toContain(
'<strong>To remove your integration, type <code>my-test-cluster</code> to confirm:</strong>',
);
});
describe('with cluster management project', () => {
beforeEach(() => {
createComponent({ hasManagementProject: true });
createComponent({ props: { hasManagementProject: true } });
});
it('renders regular button instead', () => {

View File

@ -74,10 +74,10 @@ describe('content_editor/extensions/attachment', () => {
});
it.each`
eventType | propName | eventData | output
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { files: [attachmentFile] } }} | ${true}
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { files: [] } }} | ${undefined}
${'drop'} | ${'handleDrop'} | ${{ dataTransfer: { files: [attachmentFile] } }} | ${true}
eventType | propName | eventData | output
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { getData: jest.fn(), files: [attachmentFile] } }} | ${true}
${'paste'} | ${'handlePaste'} | ${{ clipboardData: { getData: jest.fn(), files: [] } }} | ${undefined}
${'drop'} | ${'handleDrop'} | ${{ dataTransfer: { getData: jest.fn(), files: [attachmentFile] } }} | ${true}
`('handles $eventType properly', ({ eventType, propName, eventData, output }) => {
const event = Object.assign(new Event(eventType), eventData);
const handled = tiptapEditor.view.someProp(propName, (eventHandler) => {

View File

@ -1,19 +1,37 @@
import { multilineInputRegex } from '~/content_editor/extensions/blockquote';
import Blockquote from '~/content_editor/extensions/blockquote';
import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
describe('content_editor/extensions/blockquote', () => {
describe.each`
input | matches
${'>>> '} | ${true}
${' >>> '} | ${true}
${'\t>>> '} | ${true}
${'>> '} | ${false}
${'>>>x '} | ${false}
${'> '} | ${false}
`('multilineInputRegex', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'}: "${input}"`, () => {
const match = new RegExp(multilineInputRegex).test(input);
let tiptapEditor;
let doc;
let p;
let blockquote;
expect(match).toBe(matches);
});
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [Blockquote] });
({
builders: { doc, p, blockquote },
} = createDocBuilder({
tiptapEditor,
names: {
blockquote: { nodeType: Blockquote.name },
},
}));
});
it.each`
input | insertedNode
${'>>> '} | ${() => blockquote({ multiline: true }, p())}
${'> '} | ${() => blockquote(p())}
${' >>> '} | ${() => blockquote({ multiline: true }, p())}
${'>> '} | ${() => p()}
${'>>>x '} | ${() => p()}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const expectedDoc = doc(insertedNode());
triggerNodeInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -1,6 +1,6 @@
import { initEmojiMock } from 'helpers/emoji';
import Emoji from '~/content_editor/extensions/emoji';
import { createTestEditor, createDocBuilder } from '../test_utils';
import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
describe('content_editor/extensions/emoji', () => {
let tiptapEditor;
@ -28,18 +28,16 @@ describe('content_editor/extensions/emoji', () => {
describe('when typing a valid emoji input rule', () => {
it('inserts an emoji node', () => {
const { view } = tiptapEditor;
const { selection } = view.state;
const expectedDoc = doc(
p(
' ',
emoji({ moji: '❤', name: 'heart', title: 'heavy black heart', unicodeVersion: '1.1' }),
),
);
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, ':heart:'));
expect(eq(tiptapEditor.state.doc, expectedDoc)).toBe(true);
triggerNodeInputRule({ tiptapEditor, inputRuleText: ':heart:' });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -1,20 +1,39 @@
import { hrInputRuleRegExp } from '~/content_editor/extensions/horizontal_rule';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
describe('content_editor/extensions/horizontal_rule', () => {
describe.each`
input | matches
${'---'} | ${true}
${'--'} | ${false}
${'---x'} | ${false}
${' ---x'} | ${false}
${' --- '} | ${false}
${'x---x'} | ${false}
${'x---'} | ${false}
`('hrInputRuleRegExp', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'}: "${input}"`, () => {
const match = new RegExp(hrInputRuleRegExp).test(input);
let tiptapEditor;
let doc;
let p;
let horizontalRule;
expect(match).toBe(matches);
});
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [HorizontalRule] });
({
builders: { doc, p, horizontalRule },
} = createDocBuilder({
tiptapEditor,
names: {
horizontalRule: { nodeType: HorizontalRule.name },
},
}));
});
it.each`
input | insertedNodes
${'---'} | ${() => [p(), horizontalRule()]}
${'--'} | ${() => [p()]}
${'---x'} | ${() => [p()]}
${' ---x'} | ${() => [p()]}
${' --- '} | ${() => [p()]}
${'x---x'} | ${() => [p()]}
${'x---'} | ${() => [p()]}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNodes }) => {
const expectedDoc = doc(...insertedNodes());
triggerNodeInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -1,27 +1,43 @@
import { inputRegexAddition, inputRegexDeletion } from '~/content_editor/extensions/inline_diff';
import InlineDiff from '~/content_editor/extensions/inline_diff';
import { createTestEditor, createDocBuilder, triggerMarkInputRule } from '../test_utils';
describe('content_editor/extensions/inline_diff', () => {
describe.each`
inputRegex | description | input | matches
${inputRegexAddition} | ${'inputRegexAddition'} | ${'hello{+world+}'} | ${true}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'hello{+ world +}'} | ${true}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'hello {+ world+}'} | ${true}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'{+hello world +}'} | ${true}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'{+hello with \nnewline+}'} | ${false}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'{+open only'} | ${false}
${inputRegexAddition} | ${'inputRegexAddition'} | ${'close only+}'} | ${false}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'hello{-world-}'} | ${true}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'hello{- world -}'} | ${true}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'hello {- world-}'} | ${true}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'{-hello world -}'} | ${true}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'{+hello with \nnewline+}'} | ${false}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'{-open only'} | ${false}
${inputRegexDeletion} | ${'inputRegexDeletion'} | ${'close only-}'} | ${false}
`('$description', ({ inputRegex, input, matches }) => {
it(`${matches ? 'matches' : 'does not match'}: "${input}"`, () => {
const match = new RegExp(inputRegex).test(input);
let tiptapEditor;
let doc;
let p;
let inlineDiff;
expect(match).toBe(matches);
});
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [InlineDiff] });
({
builders: { doc, p, inlineDiff },
} = createDocBuilder({
tiptapEditor,
names: {
inlineDiff: { markType: InlineDiff.name },
},
}));
});
it.each`
input | insertedNode
${'hello{+world+}'} | ${() => p('hello', inlineDiff('world'))}
${'hello{+ world +}'} | ${() => p('hello', inlineDiff(' world '))}
${'{+hello with \nnewline+}'} | ${() => p('{+hello with newline+}')}
${'{+open only'} | ${() => p('{+open only')}
${'close only+}'} | ${() => p('close only+}')}
${'hello{-world-}'} | ${() => p('hello', inlineDiff({ type: 'deletion' }, 'world'))}
${'hello{- world -}'} | ${() => p('hello', inlineDiff({ type: 'deletion' }, ' world '))}
${'hello {- world-}'} | ${() => p('hello ', inlineDiff({ type: 'deletion' }, ' world'))}
${'{-hello world -}'} | ${() => p(inlineDiff({ type: 'deletion' }, 'hello world '))}
${'{-hello with \nnewline-}'} | ${() => p('{-hello with newline-}')}
${'{-open only'} | ${() => p('{-open only')}
${'close only-}'} | ${() => p('close only-}')}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const expectedDoc = doc(insertedNode());
triggerMarkInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -1,61 +1,46 @@
import {
markdownLinkSyntaxInputRuleRegExp,
urlSyntaxRegExp,
extractHrefFromMarkdownLink,
} from '~/content_editor/extensions/link';
import Link from '~/content_editor/extensions/link';
import { createTestEditor, createDocBuilder, triggerMarkInputRule } from '../test_utils';
describe('content_editor/extensions/link', () => {
describe.each`
input | matches
${'[gitlab](https://gitlab.com)'} | ${true}
${'[documentation](readme.md)'} | ${true}
${'[link 123](readme.md)'} | ${true}
${'[link 123](read me.md)'} | ${true}
${'text'} | ${false}
${'documentation](readme.md'} | ${false}
${'https://www.google.com'} | ${false}
`('markdownLinkSyntaxInputRuleRegExp', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'} ${input}`, () => {
const match = new RegExp(markdownLinkSyntaxInputRuleRegExp).exec(input);
let tiptapEditor;
let doc;
let p;
let link;
expect(Boolean(match?.groups.href)).toBe(matches);
});
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [Link] });
({
builders: { doc, p, link },
} = createDocBuilder({
tiptapEditor,
names: {
link: { markType: Link.name },
},
}));
});
describe.each`
input | matches
${'http://example.com '} | ${true}
${'https://example.com '} | ${true}
${'www.example.com '} | ${true}
${'example.com/ab.html '} | ${false}
${'text'} | ${false}
${' http://example.com '} | ${true}
${'https://www.google.com '} | ${true}
`('urlSyntaxRegExp', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'} ${input}`, () => {
const match = new RegExp(urlSyntaxRegExp).exec(input);
expect(Boolean(match?.groups.href)).toBe(matches);
});
afterEach(() => {
tiptapEditor.destroy();
});
describe('extractHrefFromMarkdownLink', () => {
const input = '[gitlab](https://gitlab.com)';
const href = 'https://gitlab.com';
let match;
let result;
it.each`
input | insertedNode
${'[gitlab](https://gitlab.com)'} | ${() => p(link({ href: 'https://gitlab.com' }, 'gitlab'))}
${'[documentation](readme.md)'} | ${() => p(link({ href: 'readme.md' }, 'documentation'))}
${'[link 123](readme.md)'} | ${() => p(link({ href: 'readme.md' }, 'link 123'))}
${'[link 123](read me.md)'} | ${() => p(link({ href: 'read me.md' }, 'link 123'))}
${'text'} | ${() => p('text')}
${'documentation](readme.md'} | ${() => p('documentation](readme.md')}
${'http://example.com '} | ${() => p(link({ href: 'http://example.com' }, 'http://example.com'))}
${'https://example.com '} | ${() => p(link({ href: 'https://example.com' }, 'https://example.com'))}
${'www.example.com '} | ${() => p(link({ href: 'www.example.com' }, 'www.example.com'))}
${'example.com/ab.html '} | ${() => p('example.com/ab.html')}
${'https://www.google.com '} | ${() => p(link({ href: 'https://www.google.com' }, 'https://www.google.com'))}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const expectedDoc = doc(insertedNode());
beforeEach(() => {
match = new RegExp(markdownLinkSyntaxInputRuleRegExp).exec(input);
result = extractHrefFromMarkdownLink(match);
});
triggerMarkInputRule({ tiptapEditor, inputRuleText: input });
it('extracts the url from a markdown link captured by markdownLinkSyntaxInputRuleRegExp', () => {
expect(result).toEqual({ href });
});
it('makes sure that url text is the last capture group', () => {
expect(match[match.length - 1]).toEqual('gitlab');
});
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -1,5 +1,5 @@
import MathInline from '~/content_editor/extensions/math_inline';
import { createTestEditor, createDocBuilder } from '../test_utils';
import { createTestEditor, createDocBuilder, triggerMarkInputRule } from '../test_utils';
describe('content_editor/extensions/math_inline', () => {
let tiptapEditor;
@ -26,16 +26,9 @@ describe('content_editor/extensions/math_inline', () => {
${'$`a^2`'} | ${() => p('$`a^2`')}
${'`a^2`$'} | ${() => p('`a^2`$')}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const { view } = tiptapEditor;
const expectedDoc = doc(insertedNode());
tiptapEditor.chain().setContent(input).setTextSelection(0).run();
const { state } = tiptapEditor;
const { selection } = state;
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) => f(view, selection.from, input.length + 1, input));
triggerMarkInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});

View File

@ -1,13 +1,17 @@
import TableOfContents from '~/content_editor/extensions/table_of_contents';
import { createTestEditor, createDocBuilder } from '../test_utils';
import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
describe('content_editor/extensions/emoji', () => {
describe('content_editor/extensions/table_of_contents', () => {
let tiptapEditor;
let builders;
let doc;
let tableOfContents;
let p;
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [TableOfContents] });
({ builders } = createDocBuilder({
({
builders: { doc, p, tableOfContents },
} = createDocBuilder({
tiptapEditor,
names: { tableOfContents: { nodeType: TableOfContents.name } },
}));
@ -15,20 +19,16 @@ describe('content_editor/extensions/emoji', () => {
it.each`
input | insertedNode
${'[[_TOC_]]'} | ${'tableOfContents'}
${'[TOC]'} | ${'tableOfContents'}
${'[toc]'} | ${'p'}
${'TOC'} | ${'p'}
${'[_TOC_]'} | ${'p'}
${'[[TOC]]'} | ${'p'}
${'[[_TOC_]]'} | ${() => tableOfContents()}
${'[TOC]'} | ${() => tableOfContents()}
${'[toc]'} | ${() => p()}
${'TOC'} | ${() => p()}
${'[_TOC_]'} | ${() => p()}
${'[[TOC]]'} | ${() => p()}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const { doc } = builders;
const { view } = tiptapEditor;
const { selection } = view.state;
const expectedDoc = doc(builders[insertedNode]());
const expectedDoc = doc(insertedNode());
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, input));
triggerNodeInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});

View File

@ -0,0 +1,35 @@
import WordBreak from '~/content_editor/extensions/word_break';
import { createTestEditor, createDocBuilder, triggerNodeInputRule } from '../test_utils';
describe('content_editor/extensions/word_break', () => {
let tiptapEditor;
let doc;
let p;
let wordBreak;
beforeEach(() => {
tiptapEditor = createTestEditor({ extensions: [WordBreak] });
({
builders: { doc, p, wordBreak },
} = createDocBuilder({
tiptapEditor,
names: {
wordBreak: { nodeType: WordBreak.name },
},
}));
});
it.each`
input | insertedNode
${'<wbr>'} | ${() => p(wordBreak())}
${'<wbr'} | ${() => p()}
${'wbr>'} | ${() => p()}
`('with input=$input, then should insert a $insertedNode', ({ input, insertedNode }) => {
const expectedDoc = doc(insertedNode());
triggerNodeInputRule({ tiptapEditor, inputRuleText: input });
expect(tiptapEditor.getJSON()).toEqual(expectedDoc.toJSON());
});
});

View File

@ -10,7 +10,7 @@ import Heading from '~/content_editor/extensions/heading';
import ListItem from '~/content_editor/extensions/list_item';
import trackInputRulesAndShortcuts from '~/content_editor/services/track_input_rules_and_shortcuts';
import { ENTER_KEY, BACKSPACE_KEY } from '~/lib/utils/keys';
import { createTestEditor } from '../test_utils';
import { createTestEditor, triggerNodeInputRule } from '../test_utils';
describe('content_editor/services/track_input_rules_and_shortcuts', () => {
let trackingSpy;
@ -70,14 +70,7 @@ describe('content_editor/services/track_input_rules_and_shortcuts', () => {
describe('when creating a heading using an input rule', () => {
it('sends a tracking event indicating that a heading was created using an input rule', async () => {
const nodeName = Heading.name;
const { view } = editor;
const { selection } = view.state;
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, '## '));
editor.chain().insertContent(HEADING_TEXT).run();
triggerNodeInputRule({ tiptapEditor: editor, inputRuleText: '## ' });
expect(trackingSpy).toHaveBeenCalledWith(undefined, INPUT_RULE_TRACKING_ACTION, {
label: CONTENT_EDITOR_TRACKING_LABEL,
property: `${nodeName}`,

View File

@ -119,3 +119,26 @@ export const createTestContentEditorExtension = ({ commands = [] } = {}) => {
},
};
};
export const triggerNodeInputRule = ({ tiptapEditor, inputRuleText }) => {
const { view } = tiptapEditor;
const { state } = tiptapEditor;
const { selection } = state;
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, inputRuleText));
};
export const triggerMarkInputRule = ({ tiptapEditor, inputRuleText }) => {
const { view } = tiptapEditor;
tiptapEditor.chain().setContent(inputRuleText).setTextSelection(0).run();
const { state } = tiptapEditor;
const { selection } = state;
// Triggers the event handler that input rules listen to
view.someProp('handleTextInput', (f) =>
f(view, selection.from, inputRuleText.length + 1, inputRuleText),
);
};

View File

@ -1,6 +1,11 @@
/* eslint-disable import/no-deprecated */
import valueStreamAnalyticsStages from 'test_fixtures/projects/analytics/value_stream_analytics/stages.json';
import issueStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/issue.json';
import planStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/plan.json';
import reviewStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/review.json';
import codeStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/code.json';
import testStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/test.json';
import stagingStageFixtures from 'test_fixtures/projects/analytics/value_stream_analytics/events/staging.json';
import { getJSONFixture } from 'helpers/fixtures';
import { TEST_HOST } from 'helpers/test_constants';
import {
DEFAULT_VALUE_STREAM,
@ -20,28 +25,16 @@ export const deepCamelCase = (obj) => convertObjectPropsToCamelCase(obj, { deep:
export const getStageByTitle = (stages, title) =>
stages.find((stage) => stage.title && stage.title.toLowerCase().trim() === title) || {};
const fixtureEndpoints = {
customizableCycleAnalyticsStagesAndEvents:
'projects/analytics/value_stream_analytics/stages.json',
stageEvents: (stage) => `projects/analytics/value_stream_analytics/events/${stage}.json`,
metricsData: 'projects/analytics/value_stream_analytics/summary.json',
};
export const metricsData = getJSONFixture(fixtureEndpoints.metricsData);
export const customizableStagesAndEvents = getJSONFixture(
fixtureEndpoints.customizableCycleAnalyticsStagesAndEvents,
);
export const defaultStages = ['issue', 'plan', 'review', 'code', 'test', 'staging'];
const stageFixtures = defaultStages.reduce((acc, stage) => {
const events = getJSONFixture(fixtureEndpoints.stageEvents(stage));
return {
...acc,
[stage]: events,
};
}, {});
const stageFixtures = {
issue: issueStageFixtures,
plan: planStageFixtures,
review: reviewStageFixtures,
code: codeStageFixtures,
test: testStageFixtures,
staging: stagingStageFixtures,
};
export const summary = [
{ value: '20', title: 'New Issues' },
@ -260,7 +253,7 @@ export const selectedProjects = [
},
];
export const rawValueStreamStages = customizableStagesAndEvents.stages;
export const rawValueStreamStages = valueStreamAnalyticsStages.stages;
export const valueStreamStages = rawValueStreamStages.map((s) =>
convertObjectPropsToCamelCase(s, { deep: true }),

View File

@ -1,3 +1,4 @@
import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json';
import { useFakeDate } from 'helpers/fake_date';
import {
transformStagesForPathNavigation,
@ -14,7 +15,6 @@ import {
stageMedians,
pathNavIssueMetric,
rawStageMedians,
metricsData,
} from './mock_data';
describe('Value stream analytics utils', () => {

View File

@ -1,11 +1,12 @@
import { GlDeprecatedSkeletonLoading as GlSkeletonLoading } from '@gitlab/ui';
import { GlSingleStat } from '@gitlab/ui/dist/charts';
import { shallowMount } from '@vue/test-utils';
import metricsData from 'test_fixtures/projects/analytics/value_stream_analytics/summary.json';
import waitForPromises from 'helpers/wait_for_promises';
import { METRIC_TYPE_SUMMARY } from '~/api/analytics_api';
import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue';
import createFlash from '~/flash';
import { group, metricsData } from './mock_data';
import { group } from './mock_data';
jest.mock('~/flash');

View File

@ -66,8 +66,8 @@ describe('RelatedMergeRequests', () => {
describe('template', () => {
it('should render related merge request items', () => {
expect(wrapper.find('.js-items-count').text()).toEqual('2');
expect(wrapper.findAll(RelatedIssuableItem).length).toEqual(2);
expect(wrapper.find('[data-testid="count"]').text()).toBe('2');
expect(wrapper.findAll(RelatedIssuableItem)).toHaveLength(2);
const props = wrapper.findAll(RelatedIssuableItem).at(1).props();
const data = mockData[1];

View File

@ -1,5 +1,6 @@
import { GlTab } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import { nextTick } from 'vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import { makeMockUserCalloutDismisser } from 'helpers/mock_user_callout_dismisser';
import stubChildren from 'helpers/stub_children';
@ -70,6 +71,7 @@ describe('App component', () => {
const findTabs = () => wrapper.findAllComponents(GlTab);
const findByTestId = (id) => wrapper.findByTestId(id);
const findFeatureCards = () => wrapper.findAllComponents(FeatureCard);
const findManageViaMRErrorAlert = () => wrapper.findByTestId('manage-via-mr-error-alert');
const findLink = ({ href, text, container = wrapper }) => {
const selector = `a[href="${href}"]`;
const link = container.find(selector);
@ -173,6 +175,43 @@ describe('App component', () => {
});
});
describe('Manage via MR Error Alert', () => {
beforeEach(() => {
createComponent({
augmentedSecurityFeatures: securityFeaturesMock,
augmentedComplianceFeatures: complianceFeaturesMock,
});
});
describe('on initial load', () => {
it('should not show Manage via MR Error Alert', () => {
expect(findManageViaMRErrorAlert().exists()).toBe(false);
});
});
describe('when error occurs', () => {
it('should show Alert with error Message', async () => {
expect(findManageViaMRErrorAlert().exists()).toBe(false);
findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(true);
expect(findManageViaMRErrorAlert().text()).toEqual('There was a manage via MR error');
});
it('should hide Alert when it is dismissed', async () => {
findFeatureCards().at(1).vm.$emit('error', 'There was a manage via MR error');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(true);
findManageViaMRErrorAlert().vm.$emit('dismiss');
await nextTick();
expect(findManageViaMRErrorAlert().exists()).toBe(false);
});
});
});
describe('Auto DevOps hint alert', () => {
describe('given the right props', () => {
beforeEach(() => {

View File

@ -80,7 +80,11 @@ describe('FeatureCard component', () => {
describe('basic structure', () => {
beforeEach(() => {
feature = makeFeature();
feature = makeFeature({
type: 'sast',
available: true,
canEnableByMergeRequest: true,
});
createComponent({ feature });
});
@ -97,6 +101,11 @@ describe('FeatureCard component', () => {
expect(links.exists()).toBe(true);
expect(links).toHaveLength(1);
});
it('should catch and emit manage-via-mr-error', () => {
findManageViaMr().vm.$emit('error', 'There was a manage via MR error');
expect(wrapper.emitted('error')).toEqual([['There was a manage via MR error']]);
});
});
describe('status', () => {

View File

@ -147,6 +147,22 @@ RSpec.describe Gitlab::Middleware::Go do
end
end
end
context 'when a personal access token is missing' do
before do
env['REMOTE_ADDR'] = '192.168.0.1'
env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials(current_user.username, 'dummy_password')
end
it 'returns unauthorized' do
expect(Gitlab::Auth).to receive(:find_for_git_client).and_raise(Gitlab::Auth::MissingPersonalAccessTokenError)
response = go
expect(response[0]).to eq(401)
expect(response[1]['Content-Length']).to be_nil
expect(response[2]).to eq([''])
end
end
end
end
end

View File

@ -63,6 +63,10 @@ module CycleAnalyticsHelpers
wait_for_requests
end
def click_save_value_stream_button
click_button(_('Save value stream'))
end
def create_custom_value_stream(custom_value_stream_name)
toggle_value_stream_dropdown
page.find_button(_('Create new Value Stream')).click

278
yarn.lock
View File

@ -1184,10 +1184,10 @@
resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.12.tgz#431ec342a7195622f86688bbda82e3166ce8cb28"
integrity sha512-6RglhutqrGFMO1MNUXp95RBuYIuc8wTnMAV5MUhLmjTOy78ncwOw7RgeQ/HeymkKXRhZd0s2DNrM1rL7unk3MQ==
"@popperjs/core@^2.8.3":
version "2.9.2"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353"
integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==
"@popperjs/core@^2.9.0":
version "2.10.2"
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.10.2.tgz#0798c03351f0dea1a5a4cabddf26a55a7cbee590"
integrity sha512-IXf3XA7+XyN7CP9gGh/XB0UxVMlvARGEgGXLubFICsUMGz6Q+DU+i4gGlpOxTjKvXjkJDJC8YdqdKkDj9qZHEQ==
"@rails/actioncable@6.1.4-1":
version "6.1.4-1"
@ -1310,79 +1310,73 @@
dom-accessibility-api "^0.5.1"
pretty-format "^26.4.2"
"@tiptap/core@^2.0.0-beta.118":
version "2.0.0-beta.118"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.118.tgz#17e990da525a81d957494171c0c33d17e2039823"
integrity sha512-TApketXliv2PZSTf5WP8j/svwzeK795fOf4Ff6A0gDwcVYFPHlny4ZlpawYthyqDoe1fEusyZokVgiDHnb+EzA==
"@tiptap/core@^2.0.0-beta.125":
version "2.0.0-beta.125"
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.125.tgz#672a86f21727614bfe8d5bf41544ce83ea4a7c87"
integrity sha512-I3lju5hcjK6nhC29jJCdGoRdP0C5nPBHGN8y/fbFmCX0oQ/THuzVdMgaGPpkHjralPPMS3DqHD/Rx8Kbo8zClg==
dependencies:
"@types/prosemirror-commands" "^1.0.4"
"@types/prosemirror-inputrules" "^1.0.4"
"@types/prosemirror-keymap" "^1.0.4"
"@types/prosemirror-model" "^1.13.2"
"@types/prosemirror-schema-list" "^1.0.3"
"@types/prosemirror-state" "^1.2.7"
"@types/prosemirror-transform" "^1.1.4"
"@types/prosemirror-view" "^1.19.1"
prosemirror-commands "^1.1.10"
prosemirror-inputrules "^1.1.3"
prosemirror-commands "^1.1.11"
prosemirror-keymap "^1.1.3"
prosemirror-model "^1.14.3"
prosemirror-schema-list "^1.1.6"
prosemirror-state "^1.3.4"
prosemirror-transform "^1.3.3"
prosemirror-view "^1.20.1"
prosemirror-view "^1.20.3"
"@tiptap/extension-blockquote@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.15.tgz#40be203f7db47e027ea1a5ba42bbb0e33bb6c004"
integrity sha512-Cso44KsYsqKqaNveQmx5KVaLy9krq5AzE9WhGVDBSFqWhvuIJkQYrTRBbOTfUDs/st9VuwJrbjTDD65ow50wEw==
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-blockquote@^2.0.0-beta.19":
version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.19.tgz#4b1dfd0ec511f889ddb0f34cd09e57a0db877dc0"
integrity sha512-9y8keXSm4E5mdh/EocdbrJ/H71qyXv2jajRHIsXj1SaJqLaz4JbSQGrX3j2r0ia3KW3YNFFIHl/z33fU70YfGQ==
"@tiptap/extension-bold@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.15.tgz#cf9ddb3fc316be9707753ad4e497bfb8a3ebb0c2"
integrity sha512-jKyV6iiwhxwa0+7uuKD74jNDVNLNOS1GmU14MgaA95pY5e1fyaRBPPX8Gtt89niz2CLOY711AV17RPZTe/e60w==
"@tiptap/extension-bold@^2.0.0-beta.19":
version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.19.tgz#9426b5fcb2bfb79082f9efefbafff955d26d023e"
integrity sha512-pbYMK3Lz78XFi+1OisdjuGQcjRnzHXeYivh9A4xP1fmSOS6t/lQsu2P2uDhFNXtz45dLcLTOkqnE0j5lj3TGMg==
"@tiptap/extension-bubble-menu@^2.0.0-beta.39":
version "2.0.0-beta.39"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.39.tgz#8971feeac93e685fc38564173a83ad078b4e7f2a"
integrity sha512-hmA+ePR+MnRaTJ5MxoZ3yqOcK54cW2KQllZx16ZwSyM+yU9bXVhfMmyZwqRD7GGQFkrfnPm5QnedXDBYJD19OQ==
"@tiptap/extension-bubble-menu@^2.0.0-beta.42":
version "2.0.0-beta.42"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.42.tgz#40c2783860721e861b3eb11aa2a076d1944058c2"
integrity sha512-H+pGpSk0mK4BRm4RbevgT+ir1bEwlS1dEwi2YIPYVl+JSpuGS2P1jTWeZpCou9cewNuxxwrAarhp4aEvJrL8UQ==
dependencies:
prosemirror-state "^1.3.4"
prosemirror-view "^1.20.1"
tippy.js "^6.3.1"
prosemirror-view "^1.20.3"
tippy.js "^6.3.2"
"@tiptap/extension-bullet-list@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.15.tgz#74876851a8d227ba1a031d031631ed621c175e05"
integrity sha512-5i44JzsZOh8Ci6CuYRQy6W3jCpYgX0+VuJKeHvZ6Aomy4Qqrtc9Jk43PBmCj91lNUUtH6Io9l+kDrLCumEFnEg==
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-bullet-list@^2.0.0-beta.18":
version "2.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.18.tgz#ab33426a3ea03cea11863e44aad9e48fc18d478c"
integrity sha512-dOf2Wx9bmgpBQIxhw7b+g1GhbIyIox7FIiIEkkSgqDtx8wPPYlnGwHRxopj4a57VbqRkRtspJZp52/vhP3is5w==
"@tiptap/extension-code-block-lowlight@2.0.0-beta.41":
version "2.0.0-beta.41"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.41.tgz#3ec98f509bbd5df689de6282f2a3881229262ed7"
integrity sha512-2+D/SwcRjWThJ8uFWJT/6B7R+gTUlp4h13/EZqrFMm3YCSOx+bzgTVndog6UJkWyAoNDxkTVwcOyYI4HWdvCiQ==
"@tiptap/extension-code-block-lowlight@2.0.0-beta.47":
version "2.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.47.tgz#2a3253f778375e4a420dd77967931b4da7926913"
integrity sha512-+k0uACctl4PIrZQrZkiPapYL/Uq8Skc6gBhlvtJ3+U9+U798Rm7QZpGxEe9iXW4KC69E4LtD1JNj27Ofns35Cg==
dependencies:
"@tiptap/extension-code-block" "^2.0.0-beta.18"
"@tiptap/extension-code-block" "^2.0.0-beta.24"
"@types/lowlight" "^0.0.3"
lowlight "^1.20.0"
prosemirror-model "^1.14.3"
prosemirror-state "^1.3.4"
prosemirror-view "^1.20.1"
prosemirror-view "^1.20.3"
"@tiptap/extension-code-block@^2.0.0-beta.18":
version "2.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.18.tgz#3b43730cfca1ba26171530951b3cc324a500cb11"
integrity sha512-E2gz7ovl9nXLZzheqLyN3hi7A10fCaodDn4DvIl4wiEbKZpF7WFBNeb+FQetWNay9UWNeDO94SCX9+rT9H+yHA==
"@tiptap/extension-code-block@^2.0.0-beta.24":
version "2.0.0-beta.24"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.24.tgz#f3d45d4ae0cdc2bf94ed5e61a5421c72b2bd3a53"
integrity sha512-Q6KxBe3FB+dMe/prlfeixXSVqGTmnOmIL2/10B1RzSj7Mj9SgzqQEHZFm3dKVqpYuMOYJ6S6edkW33E0Wq9ahQ==
dependencies:
prosemirror-inputrules "^1.1.3"
prosemirror-state "^1.3.4"
"@tiptap/extension-code@^2.0.0-beta.16":
version "2.0.0-beta.16"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.0.0-beta.16.tgz#b258ff90ebe703a4d36ff0c650e6b2cab634028d"
integrity sha512-Kakg/RMiVrxjzIkLVDXtbCzRh/9W8dgSG04IhMZNOI8N9vWn8Z78jdUyxEEDTcL/JyWWcMxn9AsJw2U5ajO3pA==
"@tiptap/extension-code@^2.0.0-beta.20":
version "2.0.0-beta.20"
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.0.0-beta.20.tgz#0f5ff7e827ae09c26d23ddc9e62f6375d2f1c0f8"
integrity sha512-25twg/rsg5CxTOfDgYzg1GbwrtdzNX2vCQyYsauXfPI1kbrWXdVBYWeL4iHdJk5WElfH9WUbQ4kMGh13/KCG/g==
"@tiptap/extension-document@^2.0.0-beta.13":
version "2.0.0-beta.13"
@ -1397,34 +1391,32 @@
"@types/prosemirror-dropcursor" "^1.0.3"
prosemirror-dropcursor "^1.3.5"
"@tiptap/extension-floating-menu@^2.0.0-beta.33":
version "2.0.0-beta.33"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.33.tgz#11068488f10fce697df2a48f79039e9c1d10eb7b"
integrity sha512-8s8DPnHIzXg7E7S/DjuS1AAFZKVYXY0KBKaEd1f2V45YOkKwN9El46Ugk/4Ir3yrrllvnisbP9ol+BAQmI0bMg==
"@tiptap/extension-floating-menu@^2.0.0-beta.36":
version "2.0.0-beta.36"
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.36.tgz#313082e2dd22b6b20c81aa4e98f7c9bcfd47ebe7"
integrity sha512-Pm9KK+Y7YUgMrlvqa/MgxV0WOTBiOp8d2kpt7OwGv/ahjc4amv0HFbei7glsiJ56VtOv8lsjiuBS+m2ctWHWVQ==
dependencies:
prosemirror-state "^1.3.4"
prosemirror-view "^1.20.1"
tippy.js "^6.3.1"
prosemirror-view "^1.20.3"
tippy.js "^6.3.2"
"@tiptap/extension-gapcursor@^2.0.0-beta.24":
version "2.0.0-beta.24"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.24.tgz#dc42a3610cea611755c6521e14a6995555e0ad49"
integrity sha512-/6Ru0wNLIb3fo30Ar3z/rcakoUA2EIJL9sBFiuyHWTAIujeEaBzA6oG5L4PpP+daKd31JF0I6LjeWMSU9CBSFw==
"@tiptap/extension-gapcursor@^2.0.0-beta.27":
version "2.0.0-beta.27"
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.27.tgz#ced6cb7d39a388e60f8ec22889a015beb81b417a"
integrity sha512-Sk041ygN+PhAGiCnDfEtFl8sFmm+clKTHJZJwb6ixcdfgY9xkTpkrgswsAY5fvLnLxZUJvZSDOrsvysbh6M2MQ==
dependencies:
"@types/prosemirror-gapcursor" "^1.0.4"
prosemirror-gapcursor "^1.2.0"
"@tiptap/extension-hard-break@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.21.tgz#3b9108c7703f23ae186c1038033f0b1354f721bf"
integrity sha512-Ukl+wjfLhE0tW7lWRpSPPo2tajjGnEaSc/Irey1JineFf+x/azA9rREzQy0r2AhORTalH7lj/KDmSdG8IT6syA==
"@tiptap/extension-hard-break@^2.0.0-beta.24":
version "2.0.0-beta.24"
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.24.tgz#8518cf253c24a316824fdac41ffe195fe934bfb2"
integrity sha512-0oEHUlQKQZRQmrbKARFPBVVRBWdekR27ro+qg+T6nzEHYSRkJ7dWBswGSNul1v1XEp52JqniZ3el0w1xdYMR0w==
"@tiptap/extension-heading@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.15.tgz#d62f32a2ec8ce5a6d4e716aa7a45dfb707283848"
integrity sha512-UoXDwEdCV9KiPh0wj0jj2Jt6VDqkoTaSU3d9bmEBLwg1Gjgbuv39JDst7oxSqbf9rgbl3txbeOy35wVBKe9CqA==
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-heading@^2.0.0-beta.18":
version "2.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.18.tgz#ffaee32ab0286ac047a3f38a52f870538155e30c"
integrity sha512-JJtB1pNHkqC9z/z/6B+xQpDd1w5EaLp++yG8eoY9NCq3ZCRhwULda+Uq7reA9D0PdEDpASsTSS2qLu8ZAtgUeA==
"@tiptap/extension-history@^2.0.0-beta.16":
version "2.0.0-beta.16"
@ -1434,28 +1426,29 @@
"@types/prosemirror-history" "^1.0.3"
prosemirror-history "^1.2.0"
"@tiptap/extension-horizontal-rule@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.21.tgz#1c73a8547611f53935117ed0079542d958ba37fa"
integrity sha512-fgvRGuNEGWAitbcoz6VZSR9gcVIHksTy2QpXPnQC+N9Mi7havaxreYdMZn+oePW/5kdZoZNRx+jsf5DjKomvoQ==
"@tiptap/extension-horizontal-rule@^2.0.0-beta.24":
version "2.0.0-beta.24"
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.24.tgz#ffd6226a98a62c5314012b8c1cc60c90709d0b9c"
integrity sha512-kRHJySSJl6QgPvnD+MkN3rzwQgInbq5zE4oxPPbgqYkaAcVSL/q7JBQK1dXMMFaslQlYmEgM6Eh3oU5vo9gUdQ==
dependencies:
prosemirror-state "^1.3.4"
"@tiptap/extension-image@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.0.0-beta.15.tgz#07bdc23c9804c830a394f78242648dd7783a469e"
integrity sha512-iMwQ154RPefOxgqTTHjOA735YWesyjBhYvD9fFxRDctM2vLxEYYYFOENXEBCFLNAr94gfaxefCEdkuNYHl9H7g==
"@tiptap/extension-image@^2.0.0-beta.19":
version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.0.0-beta.19.tgz#40bd95d6d4306d28640bf60a074f20c792f375c6"
integrity sha512-17ax4H6Y+xyePfLtL5Z2V2AuWKHziukixHigA+Go4yOdEHlSDvl+x8eNYAZkxy2nH1yFW+uu7Onv8Ln/jWzqLg==
"@tiptap/extension-italic@^2.0.0-beta.15":
version "2.0.0-beta.15"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.15.tgz#9a81f686cf221110478935596f0b47a76d4c2f45"
integrity sha512-ZCz1vCysLdvOUrwODuyBP0BDaemCLh6ib7qTYoSDKdive9kfn0Vc5Fg3o8xgHrtrUfwKIJz/sWOknjDEGIc9cw==
"@tiptap/extension-italic@^2.0.0-beta.19":
version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.19.tgz#8f140e778e8cbd2281df6463d895d3632280c26f"
integrity sha512-gEVDqEz25glLuOPW1IOPJy/AIrTgsm164XSi9lnwS1uZa1bmEAKpoALN0+9VzSVaBOmrg2tycMo+iuOhYxFb7w==
"@tiptap/extension-link@^2.0.0-beta.20":
version "2.0.0-beta.20"
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.0.0-beta.20.tgz#dbb2aa4f01212bbd0c3fb14b563e28f31140eae2"
integrity sha512-VljjF5Pmd8xeUxN+Wbypyh8zGEMBWJTwzHUVMbkAEU8IZTdk2gDDcAOiYPyU9+j0vAVbSXi1EAsBRIIlj0OaKQ==
"@tiptap/extension-link@^2.0.0-beta.23":
version "2.0.0-beta.23"
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.0.0-beta.23.tgz#7591dfe6eb8f65548c2b6a562c2b6ec0568d23c2"
integrity sha512-XnNdu6OyB09M4Qsru5j/GsDwj/EFjLQNmGZSQIS3GoaEcxrOImohnEZBZO9WJ11A5IT2GilpRZn2wHscdKoBdA==
dependencies:
linkifyjs "^3.0.3"
prosemirror-state "^1.3.4"
"@tiptap/extension-list-item@^2.0.0-beta.14":
@ -1463,22 +1456,20 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.14.tgz#65a9ff9daa11bc9ca8bc2989a891abe68081cfbd"
integrity sha512-t6xwEqP+d5443Ul2Jvqz9kXb3ro7bA7yY9HA0vskm3120WxxHW9jxgxZN+82Ot5Tm7nXOAlsN6vuqnt4idnxZQ==
"@tiptap/extension-ordered-list@^2.0.0-beta.16":
version "2.0.0-beta.16"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.16.tgz#3ef25a8dd8ddbd2b1aa5ce89d5a2e5a3ecafcf4e"
integrity sha512-3n0h5FBfQqBrN/zqF/Ngoyd1bZxeIRLwWI7ak4KulpvOg5V/yw3sw5CSxr2f13ZI9AgGaTq8yOsTYs9dkCCnsQ==
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-ordered-list@^2.0.0-beta.19":
version "2.0.0-beta.19"
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.19.tgz#eeab2e8488b84ba7deb93b299370dc187d37c98a"
integrity sha512-PHC5pA1gohxCJF2xMXOzruPt8XWyWLun3vhJL2AIUUzUoGJmSRhsc8Wreeozdlf8HkKrqnsIuk5tp6IEsau6Pw==
"@tiptap/extension-paragraph@^2.0.0-beta.17":
version "2.0.0-beta.17"
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.17.tgz#f8f0263359b95dec9c10078699697908568d9be9"
integrity sha512-qCQVCf9c2hgaeIdfy22PaoZyW5Vare/1aGkOEAaZma5RjrUbV9hrRKwoW9LsDjnh1EN1fIeKdg02yEhnHWtG8A==
"@tiptap/extension-strike@^2.0.0-beta.17":
version "2.0.0-beta.17"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.17.tgz#2280ea4e8c50189c2729814d2ae484e58c712a36"
integrity sha512-+WRd0RuCK4+jFKNVN+4rHTa5VMqqGDO2uc+TknkqhFqWp/z96OAGlpHJOwPrnW1fLbpjEBBQIr1vVYSw6KgcZg==
"@tiptap/extension-strike@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.21.tgz#d4b7f0d52a275bc6a4ddc60fe546495d9f77f041"
integrity sha512-uYU5k05MChVtZUwWMXAl+xp3IGx/N/+8VZaeJDlIZfg0hew+ZdEGVjwzgCQc1PAuEZriHhbpPg1yOdcHjWga8Q==
"@tiptap/extension-subscript@^2.0.0-beta.4":
version "2.0.0-beta.4"
@ -1505,39 +1496,37 @@
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.14.tgz#9ec98c73e309ee966b71ccd140019874d179e0c8"
integrity sha512-mewdlTqgBCyzeZIZ6F08gfuzwsiYjQ7BvABo2UhDfr0+EN2UvfJj0bT3tGgeZhMxT5Js2DXL+c+ZOVJxWJ9faQ==
"@tiptap/extension-table@^2.0.0-beta.31":
version "2.0.0-beta.31"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.0.0-beta.31.tgz#96987fe14017be2fd3e4dbd2ce349eec641724de"
integrity sha512-yMqnbxaq2DjaZ6EOE9FLSQSO+qHH7oE0rA+ahQkJdy9KycSboKthXBY7P9JeXxariTyD2B/My9x41cuDLWes9w==
"@tiptap/extension-table@^2.0.0-beta.34":
version "2.0.0-beta.34"
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.0.0-beta.34.tgz#6669d7d937b981e39f7dfa04b9adbabc18f789bc"
integrity sha512-DyY+m0PcvdVwh0XynDbsHXu/omUoTzQRQPI4z035J+b2HnLQnYjviNqblY/x8/rGX4qGGjBUmKvQZgwPGg7Dfw==
dependencies:
prosemirror-tables "^1.1.1"
prosemirror-view "^1.20.1"
prosemirror-view "^1.20.3"
"@tiptap/extension-task-item@^2.0.0-beta.18":
"@tiptap/extension-task-item@^2.0.0-beta.21":
version "2.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.0.0-beta.21.tgz#37a8d30949cefd79b80f107d426d14eb9aeaf567"
integrity sha512-7pCOc+jSlveTGIUUVQO5LOGZN20vb1fAIFMUKAxG756MMI69eVhpdqrXcmovYT8qs//DcvxC/sEjus+ZzFtKVg==
"@tiptap/extension-task-list@^2.0.0-beta.18":
version "2.0.0-beta.18"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.0.0-beta.18.tgz#f109c15c997038d8099b64dba4cfc4e219b426e4"
integrity sha512-SmXWdfpDFIBGxWH4Xhb9d6jRSK7jJqsw0GqC5KAtIQ9gNvAQS1j5FqAtWfxlb8FkunIfV6MNnxDP2ZgbKvaEug==
dependencies:
prosemirror-inputrules "^1.1.3"
"@tiptap/extension-task-list@^2.0.0-beta.17":
version "2.0.0-beta.17"
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.0.0-beta.17.tgz#c0f40325abf1b6a23868e72ab32f9724a8b42a7b"
integrity sha512-E17VBqW2lXF59hCQ/3/Kg0hYSDGvVC4B3W8miZwCXt5WTOl98Gk6qAiDXl+2mDKZvnsLty/YrgkD88OlZSIEbQ==
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.0.0-beta.18.tgz#8b13593e3818995e7b5058bc400ee04c68314d69"
integrity sha512-EkPM+We5TP6MWwk+dH3FX/aizNRgVzHDTnnGyMisEievaazi11oKmz72svVCfF+BJJaRdVroCbeRoLWYH7by7w==
"@tiptap/extension-text@^2.0.0-beta.13":
version "2.0.0-beta.13"
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.13.tgz#da0af8d9a3f149d20076e15d88c6af21fb6d940f"
integrity sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw==
"@tiptap/vue-2@^2.0.0-beta.57":
version "2.0.0-beta.57"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.57.tgz#242f7aa47d3c99fdef0e66a05a193b6fef1a95a6"
integrity sha512-f8COWq84wOJeLwAmaYsHCqKVbGgMloW+1r4Rz/KhlFb1MNXYeDHibCiW/VtJe7bdae+iRyIwnfmnAp2u5s77hQ==
"@tiptap/vue-2@^2.0.0-beta.60":
version "2.0.0-beta.60"
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.0.0-beta.60.tgz#18b1c3e7f1a94e5a047c6e25f4e176a42b68e2d1"
integrity sha512-6V2BucVL440yPy4YqCVxnaq7ZkiR9RmOtLydcNIt9/Abc/iQTkp2Q8qyDUqbGTfGfMEEdpuHd4m9ZRk3cuZ14g==
dependencies:
"@tiptap/extension-bubble-menu" "^2.0.0-beta.39"
"@tiptap/extension-floating-menu" "^2.0.0-beta.33"
prosemirror-view "^1.20.1"
"@tiptap/extension-bubble-menu" "^2.0.0-beta.42"
"@tiptap/extension-floating-menu" "^2.0.0-beta.36"
prosemirror-view "^1.20.3"
"@toast-ui/editor@^2.5.2":
version "2.5.2"
@ -1738,14 +1727,6 @@
"@types/prosemirror-model" "*"
"@types/prosemirror-state" "*"
"@types/prosemirror-inputrules@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/prosemirror-inputrules/-/prosemirror-inputrules-1.0.4.tgz#4cb75054d954aa0f6f42099be05eb6c0e6958bae"
integrity sha512-lJIMpOjO47SYozQybUkpV6QmfuQt7GZKHtVrvS+mR5UekA8NMC5HRIVMyaIauJLWhKU6oaNjpVaXdw41kh165g==
dependencies:
"@types/prosemirror-model" "*"
"@types/prosemirror-state" "*"
"@types/prosemirror-keymap@^1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz#f73c79810e8d0e0a20d153d84f998f02e5afbc0c"
@ -7604,6 +7585,11 @@ linkify-it@^3.0.1:
dependencies:
uc.micro "^1.0.1"
linkifyjs@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-3.0.3.tgz#51ea2160b4c60c2c87c27757a1e9eacd422c6076"
integrity sha512-ba5opS5wRHSbDC8VaiDdN14nPGm6LqyRsIPQZGG4qXV4scFdrPneT/uoZOaq9QAPBf6W9I9D/6tNSzWH//815Q==
load-json-file@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
@ -9435,10 +9421,10 @@ prosemirror-collab@^1.2.2:
dependencies:
prosemirror-state "^1.0.0"
prosemirror-commands@^1.1.10, prosemirror-commands@^1.1.4:
version "1.1.10"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.10.tgz#406a6589966e6cd80809cea2d801fb998639b37d"
integrity sha512-IWyBBXNAd44RM6NnBPljwq+/CM2oYCQJkF+YhKEAZNwzW0uFdGf4qComhjbKZzqFdu6Iub2ZhNsXgwPibA0lCQ==
prosemirror-commands@^1.1.11, prosemirror-commands@^1.1.4:
version "1.1.11"
resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.1.11.tgz#369252fcca5397ee7b011b963cc4da45b0b7cb70"
integrity sha512-uXDVkOGJbFHocdacMJihrnQCT7tHswO48ewq6ByqLxTwOrI8Y4B4aHvwUbM4epwElv/YjgC+DuqXm/gEHPym4w==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-state "^1.0.0"
@ -9503,14 +9489,14 @@ prosemirror-model@^1.0.0, prosemirror-model@^1.13.1, prosemirror-model@^1.14.3,
dependencies:
orderedmap "^1.1.0"
prosemirror-schema-basic@^1.1.2:
prosemirror-schema-basic@^1.0.0, prosemirror-schema-basic@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prosemirror-schema-basic/-/prosemirror-schema-basic-1.1.2.tgz#4bde5c339c845e0d08ec8fe473064e372ca51ae3"
integrity sha512-G4q8WflNsR1Q33QAV4MQO0xWrHLOJ+BQcKswGXMy626wlQj6c/1n1v4eC9ns+h2y1r/fJHZEgSZnsNhm9lbrDw==
dependencies:
prosemirror-model "^1.2.0"
prosemirror-schema-list@^1.1.4, prosemirror-schema-list@^1.1.6:
prosemirror-schema-list@^1.0.0, prosemirror-schema-list@^1.1.4, prosemirror-schema-list@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz#c3e13fe2f74750e4a53ff88d798dc0c4ccca6707"
integrity sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==
@ -9537,10 +9523,14 @@ prosemirror-tables@^1.1.1:
prosemirror-transform "^1.2.1"
prosemirror-view "^1.13.3"
prosemirror-test-builder@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prosemirror-test-builder/-/prosemirror-test-builder-1.0.4.tgz#68d1d1cedcd90cc2fdd976d736ce87b7a5f1e873"
integrity sha512-d5ZwYcf+vd1YI0tgoB7UZ2B2iwb4jmxLGzkHz6ZbkAmPwAY48WpS1frv9BofC2m/MRIMp5SERhISNIANYPsVTg==
prosemirror-test-builder@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/prosemirror-test-builder/-/prosemirror-test-builder-1.0.5.tgz#d05e8f77484aef192d705c46fe648d0319d69000"
integrity sha512-mymXkqJlhcB4JtQpM5buHvfn7SawJ2vRwYzuIUMkky00ILBOPUnwUCAbjA7L8o4hsaQeyHljRCNdpjXKK8KsOw==
dependencies:
prosemirror-model "^1.0.0"
prosemirror-schema-basic "^1.0.0"
prosemirror-schema-list "^1.0.0"
prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.2.8, prosemirror-transform@^1.3.3:
version "1.3.3"
@ -9549,10 +9539,10 @@ prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transfor
dependencies:
prosemirror-model "^1.0.0"
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.20.1, prosemirror-view@^1.20.2:
version "1.20.2"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.20.2.tgz#fc073def4358fdbd617ea11f5cd4217c5123635d"
integrity sha512-zh67dsGCI7QKWDbtLEAdZLmadxBJYRArM8E0z2wfuNGpx4i6ObVGzHjbnblZs2n88IwqHdEA47rEhyUqZ+kAbg==
prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.16.5, prosemirror-view@^1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.20.3.tgz#9781fe59cf0728e749ff4116f8a69d30d8cea943"
integrity sha512-2ImL9K/tIEk+aC2GT8shzfmT2U0Y8UQZ13L5AY0A4Tcj09o/ICGE362gKUE3Ze/Xr/nMw61Zv5JMSQUszAj9dw==
dependencies:
prosemirror-model "^1.14.3"
prosemirror-state "^1.0.0"
@ -11180,12 +11170,12 @@ tiny-emitter@^2.0.0:
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
integrity sha512-2NM0auVBGft5tee/OxP4PI3d8WItkDM+fPnaRAVo6xTDI2knbz9eC5ArWGqtGlYqiH3RU5yMpdyTTO7MguC4ow==
tippy.js@^6.3.1:
version "6.3.1"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.1.tgz#3788a007be7015eee0fd589a66b98fb3f8f10181"
integrity sha512-JnFncCq+rF1dTURupoJ4yPie5Cof978inW6/4S6kmWV7LL9YOSEVMifED3KdrVPEG+Z/TFH2CDNJcQEfaeuQww==
tippy.js@^6.3.2:
version "6.3.2"
resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.2.tgz#c03a0b88f170dffeba42f569771801dddc1f6340"
integrity sha512-35XVQI7Zl/jHZ51+8eHu/vVRXBjWYGobPm5G9FxOchj4r5dWhghKGS0nm0ARUKZTF96V7pPn7EbXS191NTwldw==
dependencies:
"@popperjs/core" "^2.8.3"
"@popperjs/core" "^2.9.0"
tiptap-commands@^1.17.1:
version "1.17.1"