Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
22d47e2001
commit
f16d40013b
|
|
@ -242,6 +242,10 @@
|
|||
.if-observability-skip-e2e-jobs: &if-observability-skip-e2e-jobs
|
||||
if: '$SKIP_GITLAB_OBSERVABILITY_BACKEND_TRIGGER || $GITLAB_OBSERVABILITY_BACKEND_PIPELINE_TRIGGER_TOKEN == null || $GITLAB_OBSERVABILITY_BACKEND_TOKEN_FOR_CI_SCRIPTS == null'
|
||||
|
||||
.if-dev-internal-release: &if-dev-internal-release
|
||||
if: '$CI_SERVER_HOST == "dev.gitlab.org" && $CI_PROJECT_PATH == "gitlab/gitlab-ee" && $CI_COMMIT_REF_NAME =~
|
||||
/^[\d-]+-stable-ee$/ && $CI_COMMIT_TITLE =~ /^Update VERSION file.*internal/'
|
||||
|
||||
####################
|
||||
# Changes patterns #
|
||||
####################
|
||||
|
|
@ -1098,6 +1102,7 @@
|
|||
- <<: *if-dot-com-gitlab-org-schedule
|
||||
variables:
|
||||
ARCH: amd64,arm64
|
||||
- <<: *if-dev-internal-release
|
||||
- !reference [".build-images:rules:build-qa-image-merge-requests", rules]
|
||||
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env", rules]
|
||||
# Always build on stable branches to serve release-environments pipeline
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
73881b727a2a17e48dc365784b7f0370f9c62376
|
||||
a2d87e9fa4447c81916079cce9beb1785dd1d76c
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import createState from '~/batch_comments/stores/modules/batch_comments/state';
|
||||
import * as types from '../stores/modules/batch_comments/mutation_types';
|
||||
|
||||
const processDraft = (draft) => ({
|
||||
|
|
@ -75,4 +76,7 @@ export default {
|
|||
const draft = this.drafts[draftIndex];
|
||||
this.drafts.splice(draftIndex, 1, { ...draft, isEditing });
|
||||
},
|
||||
reset() {
|
||||
Object.assign(this, createState());
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import * as types from './mutation_types';
|
||||
import createState from './state';
|
||||
|
||||
const processDraft = (draft) => ({
|
||||
...draft,
|
||||
|
|
@ -75,4 +76,7 @@ export default {
|
|||
const draft = state.drafts[draftIndex];
|
||||
state.drafts.splice(draftIndex, 1, { ...draft, isEditing });
|
||||
},
|
||||
reset(state) {
|
||||
Object.assign(state, createState());
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
<script>
|
||||
import { GlIntersperse } from '@gitlab/ui';
|
||||
import NullPresenter from './null.vue';
|
||||
|
||||
export default {
|
||||
name: 'CollectionPresenter',
|
||||
components: {
|
||||
NullPresenter,
|
||||
GlIntersperse,
|
||||
},
|
||||
inject: ['presenter'],
|
||||
props: {
|
||||
|
|
@ -17,10 +19,10 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<span>
|
||||
<gl-intersperse separator=" ">
|
||||
<span v-for="(field, index) in data.nodes" :key="index" class="gl-inline-block gl-pr-2">
|
||||
<component :is="presenter.forField(field)" />
|
||||
</span>
|
||||
<null-presenter v-if="!data.nodes.length" />
|
||||
</span>
|
||||
</gl-intersperse>
|
||||
</template>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
<script>
|
||||
import { GlButton, GlButtonGroup, GlDisclosureDropdown, GlTooltipDirective } from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapGetters as mapVuexGetters } from 'vuex';
|
||||
import { mapActions, mapState } from 'pinia';
|
||||
import { throttle } from 'lodash';
|
||||
import { __ } from '~/locale';
|
||||
|
|
@ -13,6 +11,7 @@ import {
|
|||
import { shouldDisableShortcuts } from '~/behaviors/shortcuts/shortcuts_toggle';
|
||||
import { sanitize } from '~/lib/dompurify';
|
||||
import { useMrNotes } from '~/mr_notes/store/legacy_mr_notes';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import discussionNavigation from '../mixins/discussion_navigation';
|
||||
|
||||
export default {
|
||||
|
|
@ -38,11 +37,10 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapVuexGetters([
|
||||
...mapState(useNotes, [
|
||||
'getNoteableData',
|
||||
'resolvableDiscussionsCount',
|
||||
'unresolvedDiscussionsCount',
|
||||
'allResolvableDiscussions',
|
||||
]),
|
||||
...mapState(useMrNotes, ['allVisibleDiscussionsExpanded']),
|
||||
allResolved() {
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import {
|
|||
GlDisclosureDropdownGroup,
|
||||
GlDisclosureDropdownItem,
|
||||
} from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapGetters, mapActions } from 'vuex';
|
||||
import { mapState, mapActions } from 'pinia';
|
||||
import { getLocationHash, doesHashExistInUrl } from '~/lib/utils/url_utility';
|
||||
import { __ } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import {
|
||||
DISCUSSION_FILTERS_DEFAULT_VALUE,
|
||||
HISTORY_ONLY_FILTER_VALUE,
|
||||
|
|
@ -59,7 +59,7 @@ export default {
|
|||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters([
|
||||
...mapState(useNotes, [
|
||||
'getNotesDataByProp',
|
||||
'timelineEnabled',
|
||||
'isLoading',
|
||||
|
|
@ -97,7 +97,7 @@ export default {
|
|||
window.removeEventListener('hashchange', this.handleLocationHash);
|
||||
},
|
||||
methods: {
|
||||
...mapActions([
|
||||
...mapActions(useNotes, [
|
||||
'filterDiscussion',
|
||||
'setCommentsDisabled',
|
||||
'setTargetNoteHash',
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<script>
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapGetters } from 'vuex';
|
||||
import { mapState } from 'pinia';
|
||||
import SafeHtml from '~/vue_shared/directives/safe_html';
|
||||
import { __, sprintf } from '~/locale';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
|
||||
export default {
|
||||
directives: {
|
||||
SafeHtml,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['getNotesDataByProp']),
|
||||
...mapState(useNotes, ['getNotesDataByProp']),
|
||||
registerLink() {
|
||||
return this.getNotesDataByProp('registerPath');
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
<script>
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapActions } from 'vuex';
|
||||
import { mapActions } from 'pinia';
|
||||
import { TYPE_EPIC, TYPE_ISSUE } from '~/issues/constants';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import { confidentialityQueries } from '~/sidebar/queries/constants';
|
||||
import { defaultClient as gqlClient } from '~/graphql_shared/issuable_client';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -50,7 +50,7 @@ export default {
|
|||
});
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setConfidentiality']),
|
||||
...mapActions(useNotes, ['setConfidentiality']),
|
||||
},
|
||||
render() {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<script>
|
||||
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { mapActions, mapGetters } from 'vuex';
|
||||
import { mapActions, mapState } from 'pinia';
|
||||
import { s__ } from '~/locale';
|
||||
import TrackEventDirective from '~/vue_shared/directives/track_event';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import Tracking from '~/tracking';
|
||||
import { COMMENTS_ONLY_FILTER_VALUE, DESC } from '../constants';
|
||||
import notesEventHub from '../event_hub';
|
||||
import { trackToggleTimelineView } from '../utils';
|
||||
|
|
@ -17,19 +17,23 @@ export default {
|
|||
},
|
||||
directives: {
|
||||
GlTooltip: GlTooltipDirective,
|
||||
TrackEvent: TrackEventDirective,
|
||||
},
|
||||
computed: {
|
||||
...mapGetters(['timelineEnabled', 'sortDirection']),
|
||||
...mapState(useNotes, ['isTimelineEnabled', 'discussionSortOrder']),
|
||||
tooltip() {
|
||||
return this.timelineEnabled ? timelineEnabledTooltip : timelineDisabledTooltip;
|
||||
return this.isTimelineEnabled ? timelineEnabledTooltip : timelineDisabledTooltip;
|
||||
},
|
||||
trackingOptions() {
|
||||
const { category, action, label, property, value } = trackToggleTimelineView(
|
||||
this.isTimelineEnabled,
|
||||
);
|
||||
return [category, action, { label, property, value }];
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['setTimelineView', 'setDiscussionSortDirection']),
|
||||
trackToggleTimelineView,
|
||||
...mapActions(useNotes, ['setTimelineView', 'setDiscussionSortDirection']),
|
||||
setSort() {
|
||||
if (this.timelineEnabled && this.sortDirection !== DESC) {
|
||||
if (this.isTimelineEnabled && this.discussionSortOrder !== DESC) {
|
||||
this.setDiscussionSortDirection({ direction: DESC, persist: false });
|
||||
}
|
||||
},
|
||||
|
|
@ -38,9 +42,10 @@ export default {
|
|||
},
|
||||
toggleTimeline(event) {
|
||||
event.currentTarget.blur();
|
||||
this.setTimelineView(!this.timelineEnabled);
|
||||
this.setTimelineView(!this.isTimelineEnabled);
|
||||
this.setSort();
|
||||
this.setFilter();
|
||||
Tracking.event(...this.trackingOptions);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
@ -49,9 +54,8 @@ export default {
|
|||
<template>
|
||||
<gl-button
|
||||
v-gl-tooltip
|
||||
v-track-event="trackToggleTimelineView(timelineEnabled)"
|
||||
icon="history"
|
||||
:selected="timelineEnabled"
|
||||
:selected="isTimelineEnabled"
|
||||
:title="tooltip"
|
||||
:aria-label="tooltip"
|
||||
data-testid="timeline-toggle-button"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
|
|||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { getLocationHash } from '~/lib/utils/url_utility';
|
||||
import { pinia } from '~/pinia/instance';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import NotesApp from './components/notes_app.vue';
|
||||
import { store } from './stores';
|
||||
import { getNotesFilterData } from './utils/get_notes_filter_data';
|
||||
|
|
@ -42,6 +43,8 @@ export default ({ editorAiActions = [] } = {}) => {
|
|||
|
||||
const notesData = JSON.parse(notesDataset.notesData);
|
||||
|
||||
useNotes().syncWith({ store });
|
||||
|
||||
store.dispatch('setNotesData', notesData);
|
||||
store.dispatch('setNoteableData', noteableData);
|
||||
store.dispatch('setUserData', currentUserData);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { isEqual } from 'lodash';
|
||||
import { STATUS_CLOSED, STATUS_REOPENED } from '~/issues/constants';
|
||||
import { isInMRPage } from '~/lib/utils/common_utils';
|
||||
import createState from '~/notes/stores/state';
|
||||
import * as constants from '../../constants';
|
||||
import * as types from '../../stores/mutation_types';
|
||||
import * as utils from '../../stores/utils';
|
||||
|
|
@ -450,4 +451,7 @@ export default {
|
|||
[types.SET_MERGE_REQUEST_FILTERS](value) {
|
||||
this.mergeRequestFilters = value;
|
||||
},
|
||||
reset() {
|
||||
Object.assign(this, createState());
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import { isEqual } from 'lodash';
|
|||
import { STATUS_CLOSED, STATUS_REOPENED } from '~/issues/constants';
|
||||
import { isInMRPage } from '~/lib/utils/common_utils';
|
||||
import * as constants from '../constants';
|
||||
import createState from './state';
|
||||
import * as types from './mutation_types';
|
||||
import * as utils from './utils';
|
||||
|
||||
|
|
@ -450,4 +451,7 @@ export default {
|
|||
[types.SET_MERGE_REQUEST_FILTERS](state, value) {
|
||||
state.mergeRequestFilters = value;
|
||||
},
|
||||
reset(state) {
|
||||
Object.assign(state, createState());
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
import Vue from 'vue';
|
||||
import { createPinia, PiniaVuePlugin } from 'pinia';
|
||||
import { createPinia, PiniaVuePlugin, setActivePinia } from 'pinia';
|
||||
import { globalAccessorPlugin, syncWithVuex } from '~/pinia/plugins';
|
||||
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
const pinia = createPinia();
|
||||
|
||||
setActivePinia(pinia);
|
||||
|
||||
pinia.use(syncWithVuex);
|
||||
pinia.use(globalAccessorPlugin);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,49 +31,52 @@ export const globalAccessorPlugin = (context) => {
|
|||
*/
|
||||
// use this only for component migration
|
||||
export const syncWithVuex = (context) => {
|
||||
const config = context.options.syncWith;
|
||||
if (!config) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
store: vuexStore,
|
||||
name: vuexName,
|
||||
namespaced,
|
||||
} = /** @type {{ store: VuexStore, [name]: string, [namespaced]: boolean }} */ config;
|
||||
const getVuexState = vuexName ? () => vuexStore.state[vuexName] : () => vuexStore.state;
|
||||
if (!isEqual(context.store.$state, getVuexState())) {
|
||||
Object.entries(getVuexState()).forEach(([key, value]) => {
|
||||
// we can't use store.$patch here because it will merge state, but we need to overwrite it
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
context.store[key] = cloneDeep(value);
|
||||
});
|
||||
}
|
||||
const syncWith = (config) => {
|
||||
const {
|
||||
store: vuexStore,
|
||||
name: vuexName,
|
||||
namespaced,
|
||||
} = /** @type {{ store: VuexStore, [name]: string, [namespaced]: boolean }} */ config;
|
||||
const getVuexState = vuexName ? () => vuexStore.state[vuexName] : () => vuexStore.state;
|
||||
if (!isEqual(context.store.$state, getVuexState())) {
|
||||
Object.entries(getVuexState()).forEach(([key, value]) => {
|
||||
// we can't use store.$patch here because it will merge state, but we need to overwrite it
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
context.store[key] = cloneDeep(value);
|
||||
});
|
||||
}
|
||||
|
||||
let committing = false;
|
||||
let committing = false;
|
||||
|
||||
vuexStore.subscribe(
|
||||
(mutation) => {
|
||||
vuexStore.subscribe(
|
||||
(mutation) => {
|
||||
if (committing) return;
|
||||
const { payload, type } = mutation;
|
||||
const [prefixOrName, mutationName] = type.split('/');
|
||||
committing = true;
|
||||
if (!mutationName && prefixOrName in context.store) {
|
||||
context.store[prefixOrName](cloneDeep(payload));
|
||||
} else if (prefixOrName === vuexName && mutationName in context.store) {
|
||||
context.store[mutationName](cloneDeep(payload));
|
||||
}
|
||||
committing = false;
|
||||
},
|
||||
{ prepend: true },
|
||||
);
|
||||
|
||||
context.store.$onAction(({ name: mutationName, args }) => {
|
||||
if (committing) return;
|
||||
const { payload, type } = mutation;
|
||||
const [prefixOrName, mutationName] = type.split('/');
|
||||
const fullMutationName = namespaced ? `${vuexName}/${mutationName}` : mutationName;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (!(fullMutationName in vuexStore._mutations)) return;
|
||||
committing = true;
|
||||
if (!mutationName && prefixOrName in context.store) {
|
||||
context.store[prefixOrName](cloneDeep(payload));
|
||||
} else if (prefixOrName === vuexName && mutationName in context.store) {
|
||||
context.store[mutationName](cloneDeep(payload));
|
||||
}
|
||||
vuexStore.commit(fullMutationName, ...cloneDeep(args));
|
||||
committing = false;
|
||||
},
|
||||
{ prepend: true },
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
context.store.$onAction(({ name: mutationName, args }) => {
|
||||
if (committing) return;
|
||||
const fullMutationName = namespaced ? `${vuexName}/${mutationName}` : mutationName;
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (!(fullMutationName in vuexStore._mutations)) return;
|
||||
committing = true;
|
||||
vuexStore.commit(fullMutationName, ...cloneDeep(args));
|
||||
committing = false;
|
||||
});
|
||||
const initialConfig = context.options.syncWith;
|
||||
if (initialConfig) syncWith(initialConfig);
|
||||
|
||||
return { syncWith };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ module API
|
|||
class GraphqlExplorerController < BaseActionController
|
||||
include Gitlab::GonHelper
|
||||
include WithPerformanceBar
|
||||
include ViteCSP
|
||||
|
||||
def show
|
||||
# We need gon to setup gon.relative_url_root which is used by our Apollo client
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ class ApplicationController < BaseActionController
|
|||
include StrongPaginationParams
|
||||
include Gitlab::HttpRouter::RuleContext
|
||||
include Gitlab::HttpRouter::RuleMetrics
|
||||
include ViteCSP
|
||||
|
||||
around_action :set_current_ip_address
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module ViteCSP
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
content_security_policy_with_context do |p|
|
||||
next unless helpers.vite_enabled?
|
||||
next if p.directives.blank?
|
||||
|
||||
# We need both Websocket and HTTP URLs because Vite will attempt to ping
|
||||
# the HTTP URL if the Websocket isn't available:
|
||||
# https://github.com/vitejs/vite/blob/899d9b1d272b7057aafc6fa01570d40f288a473b/packages/vite/src/client/client.ts#L320-L327
|
||||
hmr_ws_url = Gitlab::Utils.append_path(helpers.vite_hmr_ws_origin, 'vite-dev/')
|
||||
http_path = Gitlab::Utils.append_path(helpers.vite_origin, 'vite-dev/')
|
||||
|
||||
# http_path is used for openInEditorHost feature
|
||||
# https://devtools.vuejs.org/getting-started/open-in-editor#customize-request
|
||||
p.connect_src(*(Array.wrap(p.directives['connect-src']) | [hmr_ws_url, http_path]))
|
||||
p.worker_src(*(Array.wrap(p.directives['worker-src']) | [http_path]))
|
||||
p.style_src(*(Array.wrap(p.directives['style-src']) | [http_path]))
|
||||
p.font_src(*(Array.wrap(p.directives['font-src']) | [http_path]))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -5,7 +5,6 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
|
|||
include PageLayoutHelper
|
||||
include OauthApplications
|
||||
include InitializesCurrentUserMode
|
||||
include ViteCSP
|
||||
|
||||
# Defined by the `Doorkeeper::ApplicationsController` and is redundant as we call `authenticate_user!` below. Not
|
||||
# defining or skipping this will result in a `403` response to all requests.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
|
|||
include InitializesCurrentUserMode
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
include RequestPayloadLogger
|
||||
include ViteCSP
|
||||
|
||||
alias_method :auth_user, :current_user
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
|
||||
include PageLayoutHelper
|
||||
include ViteCSP
|
||||
|
||||
layout 'profile'
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
module Oauth
|
||||
class DeviceAuthorizationsController < Doorkeeper::DeviceAuthorizationGrant::DeviceAuthorizationsController
|
||||
include ViteCSP
|
||||
|
||||
layout 'minimal'
|
||||
|
||||
def index
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
include Gitlab::RackLoadBalancingHelpers
|
||||
include ::Gitlab::Utils::StrongMemoize
|
||||
include Onboarding::Redirectable
|
||||
include ViteCSP
|
||||
|
||||
layout 'devise'
|
||||
|
||||
|
|
|
|||
|
|
@ -8,15 +8,6 @@ module ViteHelper
|
|||
Gitlab::Utils.to_boolean(ViteRuby.env['VITE_ENABLED'], default: false)
|
||||
end
|
||||
|
||||
def vite_origin
|
||||
ViteRuby.config.origin
|
||||
end
|
||||
|
||||
def vite_hmr_ws_origin
|
||||
protocol = ViteRuby.config.https ? 'wss' : 'ws'
|
||||
"#{protocol}://#{ViteRuby.config.host_with_port}"
|
||||
end
|
||||
|
||||
def vite_page_entrypoint_paths(custom_action_name = nil)
|
||||
action_name = custom_action_name || controller.action_name
|
||||
action = case action_name
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ def feature_mr?
|
|||
end
|
||||
|
||||
def doc_path_to_url(path)
|
||||
path.sub("doc/", "https://docs.gitlab.com/ee/").sub("index.md", "").sub(".md", "/")
|
||||
path.sub("doc/", "https://docs.gitlab.com/").sub("index.md", "").sub(".md", "/")
|
||||
end
|
||||
|
||||
docs_paths_to_review = helper.changes_by_category[:docs]
|
||||
|
|
@ -15,7 +15,8 @@ docs_paths_to_review = helper.changes_by_category[:docs]
|
|||
sections_with_no_tw_review = {
|
||||
'doc/architecture' => [],
|
||||
'doc/development' => [],
|
||||
'doc/solutions' => []
|
||||
'doc/solutions' => [],
|
||||
'doc-locale' => []
|
||||
}.freeze
|
||||
|
||||
# One exception to the exceptions above: Technical Writing docs should get a TW review.
|
||||
|
|
@ -31,7 +32,7 @@ docs_paths_to_review.reject! do |doc|
|
|||
end
|
||||
|
||||
SOLUTIONS_LABELS = %w[Solutions].freeze
|
||||
DEVELOPMENT_LABELS = ['docs::improvement', 'development guidelines'].freeze
|
||||
DEVELOPMENT_LABELS = ['development guidelines'].freeze
|
||||
|
||||
def add_labels(labels)
|
||||
helper.labels_to_add.concat(%w[documentation type::maintenance maintenance::refactor] + labels)
|
||||
|
|
@ -45,6 +46,10 @@ SOLUTIONS_MESSAGE = <<~MSG
|
|||
This MR contains docs in the /doc/solutions directory and should be reviewed by a Solutions Architect approver. You do not need tech writer review.
|
||||
MSG
|
||||
|
||||
LOCALIZATION_MESSAGE = <<~MSG
|
||||
This MR contains files in the /doc-locale directory. These files are translations maintained through a separate process and should not be edited directly. If you are not part of the Localization team, please remove the changes to these files from your MR.
|
||||
MSG
|
||||
|
||||
# For regular pages, prompt for a TW review
|
||||
DOCS_UPDATE_SHORT_MESSAGE = <<~MSG
|
||||
This merge request adds or changes documentation files and requires Technical Writing review. The review should happen before merge, but can be post-merge if the merge request is time sensitive.
|
||||
|
|
@ -91,6 +96,8 @@ if sections_with_no_tw_review["doc/solutions"].any?
|
|||
message(SOLUTIONS_MESSAGE)
|
||||
end
|
||||
|
||||
message(LOCALIZATION_MESSAGE) if sections_with_no_tw_review["doc-locale"].any?
|
||||
|
||||
unless docs_paths_to_review.empty?
|
||||
message(DOCS_UPDATE_SHORT_MESSAGE)
|
||||
markdown(DOCS_UPDATE_LONG_MESSAGE)
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ including:
|
|||
|
||||
| Feature | Shortcut on Windows or Linux | Shortcut on macOS | Details |
|
||||
|----------------------------------|-----------------------------------|------------------------------------------------------|---------|
|
||||
| Keyboard navigation command list | <kbd>f1</kbd> | <kbd>f1</kbd> | A [list of commands](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide#keyboard-navigation) that make the editor easier to use without a mouse. |
|
||||
| Keyboard navigation command list | <kbd>F1</kbd> | <kbd>F1</kbd> | A [list of commands](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide#keyboard-navigation) that make the editor easier to use without a mouse. |
|
||||
| Tab trapping | <kbd>Control</kbd> + <kbd>m</kbd> | <kbd>Control</kbd> + <kbd>Shift</kbd> + <kbd>m</kbd> | Enable [tab trapping](https://github.com/microsoft/monaco-editor/wiki/Monaco-Editor-Accessibility-Guide#tab-trapping) to go to the next focusable element on the page instead of inserting a tab character. |
|
||||
|
||||
## Troubleshooting
|
||||
|
|
|
|||
|
|
@ -210,6 +210,16 @@ In the context of multi-agent workflows, a tool is a utility or application that
|
|||
|
||||
## AI Context Terminology
|
||||
|
||||
### Knowledge Graph
|
||||
|
||||
The [Knowledge Graph](https://gitlab.com/gitlab-org/rust/knowledge-graph) project aims to create a structured, queryable graph database from code repositories to power AI features and enhance developer productivity within GitLab.
|
||||
|
||||
Think of it like creating a detailed blueprint that shows which functions call other functions, how classes relate to each other, and where variables are used throughout the codebase. Instead of GitLab Duo having to read through thousands of files every time you ask it something, it can quickly navigate this pre-built map to give you better code suggestions, find related code snippets, or help debug issues. It gives Duo a much smarter way to understand your codebase so it can assist you more effectively with things like code reviews, refactoring, or finding where to make changes when you're working on a feature.
|
||||
|
||||
### One Parser (GitLab Code Parser)
|
||||
|
||||
The [GitLab Code Parser](https://gitlab.com/gitlab-org/code-creation/gitlab-code-parser#) establishes a single, efficient, and reliable static code analysis library. This library will serve as the foundation for diverse code intelligence features across GitLab, from server-side indexing (Knowledge Graph, Embeddings) to client-side analysis (Language Server, Web IDE). Initially scoped to AI and Editor Features.
|
||||
|
||||
### Advanced Context Resolver
|
||||
|
||||
Advanced context is a comprehensive set of code-related information extending
|
||||
|
|
|
|||
|
|
@ -39,7 +39,12 @@ If you are a new customer in GitLab 18.0 or later, IDE features are automaticall
|
|||
|
||||
If you are a pre-existing customer from GitLab 17.11 or earlier, you must [turn on IDE features](../user/gitlab_duo/turn_on_off.md#change-gitlab-duo-core-availability) to start using GitLab Duo in your IDEs. No further action is needed.
|
||||
|
||||
Users assigned the [Guest role](../administration/guest_users.md) do not have access to GitLab Duo Core.
|
||||
Users assigned the following roles have access to GitLab Duo Core:
|
||||
|
||||
- Reporter
|
||||
- Developer
|
||||
- Maintainer
|
||||
- Owner
|
||||
|
||||
### GitLab Duo Core limits
|
||||
|
||||
|
|
|
|||
|
|
@ -123,12 +123,18 @@ Group permissions for [GitLab Duo](gitlab_duo/_index.md):
|
|||
|
||||
| Action | Non-member | Guest | Planner | Reporter | Developer | Maintainer | Owner | Notes |
|
||||
| --------------------------------------------------------------------------------------------------------- | :--------: | :---: | :-----: | :------: | :-------: | :--------: | :---: | ----- |
|
||||
| Use Duo features | | | | ✓ | ✓ | ✓ | ✓ | Requires [user being assigned a seat to gain access to a Duo add-on](../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats). |
|
||||
| Use Duo features | | | | ✓ | ✓ | ✓ | ✓ | Requirements differ depending on if the user has GitLab Duo Core, Pro, or Enterprise. <sup>1</sup> |
|
||||
| Configure [Duo feature availability](gitlab_duo/turn_on_off.md#for-a-group-or-subgroup) | | | | | | ✓ | ✓ | |
|
||||
| Configure [GitLab Duo Self Hosted](../administration/gitlab_duo_self_hosted/configure_duo_features.md) | | | | | | | ✓ | |
|
||||
| Enable [beta and experimental features](gitlab_duo/turn_on_off.md#turn-on-beta-and-experimental-features) | | | | | | | ✓ | |
|
||||
| Purchase [Duo seats](../subscriptions/subscription-add-ons.md#purchase-additional-gitlab-duo-seats) | | | | | | | ✓ | |
|
||||
|
||||
**Footnotes**
|
||||
|
||||
1. If the user has GitLab Duo Pro or Enterprise, the
|
||||
[user must be assigned a seat to gain access to that Duo add-on](../subscriptions/subscription-add-ons.md#assign-gitlab-duo-seats). If the user has GitLab Duo Core, there are
|
||||
no other requirements.
|
||||
|
||||
### Groups group permissions
|
||||
|
||||
Group permissions for [group features](group/_index.md):
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ module Gitlab
|
|||
GLAB_ENV_SET_WINDOWS = '$env:GITLAB_HOST = $env:CI_SERVER_URL'
|
||||
GLAB_LOGIN_UNIX = 'glab auth login --job-token $CI_JOB_TOKEN --hostname $CI_SERVER_FQDN --api-protocol $CI_SERVER_PROTOCOL'
|
||||
GLAB_LOGIN_WINDOWS = 'glab auth login --job-token $env:CI_JOB_TOKEN --hostname $env:CI_SERVER_FQDN --api-protocol $env:CI_SERVER_PROTOCOL'
|
||||
GLAB_CREATE_UNIX = 'glab -R $CI_PROJECT_PATH release create'
|
||||
GLAB_CREATE_WINDOWS = 'glab -R $env:CI_PROJECT_PATH release create'
|
||||
GLAB_CREATE_UNIX = 'glab release create -R $CI_PROJECT_PATH'
|
||||
GLAB_CREATE_WINDOWS = 'glab release create -R $env:CI_PROJECT_PATH'
|
||||
GLAB_PUBLISH_TO_CATALOG_FLAG = '--publish-to-catalog' # enables publishing to the catalog after creating the release
|
||||
GLAB_NO_UPDATE_FLAG = '--no-update' # disables updating the release if it already exists
|
||||
GLAB_NO_CLOSE_MILESTONE_FLAG = '--no-close-milestone' # disables closing the milestone after creating the release
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ module Gitlab
|
|||
def default_directives
|
||||
directives = default_directives_defaults
|
||||
|
||||
allow_vite_dev_server(directives)
|
||||
allow_development_tooling(directives)
|
||||
allow_websocket_connections(directives)
|
||||
allow_lfs(directives)
|
||||
|
|
@ -73,6 +74,26 @@ module Gitlab
|
|||
append_to_directive(directives, 'connect_src', "#{http_url} #{ws_url}")
|
||||
end
|
||||
|
||||
def allow_vite_dev_server(directives)
|
||||
return unless Rails.env.development? || Rails.env.test?
|
||||
|
||||
protocol = ViteRuby.config.https ? 'wss' : 'ws'
|
||||
ws_origin = "#{protocol}://#{ViteRuby.config.host_with_port}"
|
||||
# We need both Websocket and HTTP URLs because Vite will attempt to ping
|
||||
# the HTTP URL if the Websocket isn't available:
|
||||
# https://github.com/vitejs/vite/blob/899d9b1d272b7057aafc6fa01570d40f288a473b/packages/vite/src/client/client.ts#L320-L327
|
||||
hmr_ws_url = Gitlab::Utils.append_path(ws_origin, 'vite-dev/')
|
||||
http_path = Gitlab::Utils.append_path(ViteRuby.config.origin, 'vite-dev/')
|
||||
|
||||
# http_path is used for openInEditorHost feature
|
||||
# https://devtools.vuejs.org/getting-started/open-in-editor#customize-request
|
||||
|
||||
append_to_directive(directives, 'connect_src', "#{hmr_ws_url} #{http_path}")
|
||||
append_to_directive(directives, 'worker_src', http_path)
|
||||
append_to_directive(directives, 'style_src', http_path)
|
||||
append_to_directive(directives, 'font_src', http_path)
|
||||
end
|
||||
|
||||
def allow_letter_opener(directives)
|
||||
url = Gitlab::Utils.append_path(Gitlab.config.gitlab.url, '/rails/letter_opener/')
|
||||
append_to_directive(directives, 'frame_src', url)
|
||||
|
|
|
|||
|
|
@ -107,3 +107,5 @@ module Sidebars
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
Sidebars::Groups::Menus::PackagesRegistriesMenu.prepend_mod_with('Sidebars::Groups::Menus::PackagesRegistriesMenu')
|
||||
|
|
|
|||
|
|
@ -18,7 +18,8 @@ module Sidebars
|
|||
def configure_menu_items
|
||||
[
|
||||
:packages_registry,
|
||||
:container_registry
|
||||
:container_registry,
|
||||
:virtual_registry
|
||||
].each { |id| add_item(::Sidebars::NilMenuItem.new(item_id: id)) }
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -49018,6 +49018,9 @@ msgstr ""
|
|||
msgid "ProjectTemplates|iOS (Swift)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectToolCoverageDetails|Manage configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "ProjectTransfer|An error occurred fetching the transfer locations, please refresh the page and try again."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -55317,7 +55320,7 @@ msgstr ""
|
|||
msgid "SecurityInventory|This group doesn't have any subgroups."
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityInventory|Tool coverage: %{coverage}%%"
|
||||
msgid "SecurityInventory|Tool coverage: %{coverage}"
|
||||
msgstr ""
|
||||
|
||||
msgid "SecurityInventory|View security coverage and vulnerabilities for all the projects in this group."
|
||||
|
|
@ -64501,7 +64504,7 @@ msgstr ""
|
|||
msgid "Tool Coverage"
|
||||
msgstr ""
|
||||
|
||||
msgid "ToolCoverageDetails|Manage configuration"
|
||||
msgid "ToolCoverage|Project coverage"
|
||||
msgstr ""
|
||||
|
||||
msgid "Topic %{source_topic} was successfully merged into topic %{target_topic}."
|
||||
|
|
@ -67760,6 +67763,9 @@ msgstr ""
|
|||
msgid "Violation Details"
|
||||
msgstr ""
|
||||
|
||||
msgid "Virtual registry"
|
||||
msgstr ""
|
||||
|
||||
msgid "VirtualRegistries|API endpoints rate limit"
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -68031,9 +68037,21 @@ msgstr ""
|
|||
msgid "Vulnerabilities|%{link_start}Download the export%{link_end}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|All records must be instances of Vulnerability"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|Follow the link below to download the export."
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|Internal tracking failed: %{message}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|Missing required attributes: %{attributes}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|No valid vulnerabilities to track"
|
||||
msgstr ""
|
||||
|
||||
msgid "Vulnerabilities|The vulnerabilities list was successfully exported for %{exportable}."
|
||||
msgstr ""
|
||||
|
||||
|
|
@ -74193,6 +74211,9 @@ msgstr ""
|
|||
msgid "objective"
|
||||
msgstr ""
|
||||
|
||||
msgid "of"
|
||||
msgstr ""
|
||||
|
||||
msgid "on or after"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
|||
70
package.json
70
package.json
|
|
@ -82,41 +82,41 @@
|
|||
"@snowplow/browser-plugin-timezone": "^3.24.2",
|
||||
"@snowplow/browser-tracker": "^3.24.2",
|
||||
"@sourcegraph/code-host-integration": "0.0.95",
|
||||
"@tiptap/core": "^2.13.0",
|
||||
"@tiptap/extension-blockquote": "^2.13.0",
|
||||
"@tiptap/extension-bold": "^2.13.0",
|
||||
"@tiptap/extension-bubble-menu": "^2.13.0",
|
||||
"@tiptap/extension-bullet-list": "^2.13.0",
|
||||
"@tiptap/extension-code": "^2.13.0",
|
||||
"@tiptap/extension-code-block": "^2.13.0",
|
||||
"@tiptap/extension-code-block-lowlight": "^2.13.0",
|
||||
"@tiptap/extension-document": "^2.13.0",
|
||||
"@tiptap/extension-dropcursor": "^2.13.0",
|
||||
"@tiptap/extension-gapcursor": "^2.13.0",
|
||||
"@tiptap/extension-hard-break": "^2.13.0",
|
||||
"@tiptap/extension-heading": "^2.13.0",
|
||||
"@tiptap/extension-highlight": "^2.13.0",
|
||||
"@tiptap/extension-history": "^2.13.0",
|
||||
"@tiptap/extension-horizontal-rule": "^2.13.0",
|
||||
"@tiptap/extension-image": "^2.13.0",
|
||||
"@tiptap/extension-italic": "^2.13.0",
|
||||
"@tiptap/extension-link": "^2.13.0",
|
||||
"@tiptap/extension-list-item": "^2.13.0",
|
||||
"@tiptap/extension-ordered-list": "^2.13.0",
|
||||
"@tiptap/extension-paragraph": "^2.13.0",
|
||||
"@tiptap/extension-strike": "^2.13.0",
|
||||
"@tiptap/extension-subscript": "^2.13.0",
|
||||
"@tiptap/extension-superscript": "^2.13.0",
|
||||
"@tiptap/extension-table": "^2.13.0",
|
||||
"@tiptap/extension-table-cell": "^2.13.0",
|
||||
"@tiptap/extension-table-header": "^2.13.0",
|
||||
"@tiptap/extension-table-row": "^2.13.0",
|
||||
"@tiptap/extension-task-item": "^2.13.0",
|
||||
"@tiptap/extension-task-list": "^2.13.0",
|
||||
"@tiptap/extension-text": "^2.13.0",
|
||||
"@tiptap/pm": "^2.13.0",
|
||||
"@tiptap/suggestion": "^2.13.0",
|
||||
"@tiptap/vue-2": "^2.13.0",
|
||||
"@tiptap/core": "^2.14.0",
|
||||
"@tiptap/extension-blockquote": "^2.14.0",
|
||||
"@tiptap/extension-bold": "^2.14.0",
|
||||
"@tiptap/extension-bubble-menu": "^2.14.0",
|
||||
"@tiptap/extension-bullet-list": "^2.14.0",
|
||||
"@tiptap/extension-code": "^2.14.0",
|
||||
"@tiptap/extension-code-block": "^2.14.0",
|
||||
"@tiptap/extension-code-block-lowlight": "^2.14.0",
|
||||
"@tiptap/extension-document": "^2.14.0",
|
||||
"@tiptap/extension-dropcursor": "^2.14.0",
|
||||
"@tiptap/extension-gapcursor": "^2.14.0",
|
||||
"@tiptap/extension-hard-break": "^2.14.0",
|
||||
"@tiptap/extension-heading": "^2.14.0",
|
||||
"@tiptap/extension-highlight": "^2.14.0",
|
||||
"@tiptap/extension-history": "^2.14.0",
|
||||
"@tiptap/extension-horizontal-rule": "^2.14.0",
|
||||
"@tiptap/extension-image": "^2.14.0",
|
||||
"@tiptap/extension-italic": "^2.14.0",
|
||||
"@tiptap/extension-link": "^2.14.0",
|
||||
"@tiptap/extension-list-item": "^2.14.0",
|
||||
"@tiptap/extension-ordered-list": "^2.14.0",
|
||||
"@tiptap/extension-paragraph": "^2.14.0",
|
||||
"@tiptap/extension-strike": "^2.14.0",
|
||||
"@tiptap/extension-subscript": "^2.14.0",
|
||||
"@tiptap/extension-superscript": "^2.14.0",
|
||||
"@tiptap/extension-table": "^2.14.0",
|
||||
"@tiptap/extension-table-cell": "^2.14.0",
|
||||
"@tiptap/extension-table-header": "^2.14.0",
|
||||
"@tiptap/extension-table-row": "^2.14.0",
|
||||
"@tiptap/extension-task-item": "^2.14.0",
|
||||
"@tiptap/extension-task-list": "^2.14.0",
|
||||
"@tiptap/extension-text": "^2.14.0",
|
||||
"@tiptap/pm": "^2.14.0",
|
||||
"@tiptap/suggestion": "^2.14.0",
|
||||
"@tiptap/vue-2": "^2.14.0",
|
||||
"@vue/apollo-components": "^4.0.0-beta.4",
|
||||
"@vue/apollo-option": "^4.0.0-beta.4",
|
||||
"apollo-upload-client": "15.0.0",
|
||||
|
|
|
|||
|
|
@ -61,8 +61,7 @@ fi
|
|||
# Then set DESTINATIONS to the content of VERSION file
|
||||
if [[ "${QA_IMAGE_NAME}" == "gitlab-ee-qa" ]] && \
|
||||
[[ "${CI_REGISTRY}" == "dev.gitlab.org:5005" ]] && \
|
||||
[[ "$(version_file_content)" == *"internal"* ]] && \
|
||||
[[ "${CI_COMMIT_TITLE}" == "Update VERSION files" ]]; then
|
||||
[[ "$CI_COMMIT_TITLE" =~ ^Update\ VERSION\ file\ for.*internal ]]; then
|
||||
QA_IMAGE_FOR_INTERNAL_RELEASE="${BASE_IMAGE_PATH}:$(version_file_content)"
|
||||
DESTINATIONS="${DESTINATIONS} --tag ${QA_IMAGE_FOR_INTERNAL_RELEASE}"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import { GlLabel } from '@gitlab/ui';
|
||||
import { mountExtended } from 'helpers/vue_test_utils_helper';
|
||||
import { stubComponent } from 'helpers/stub_component';
|
||||
import CollectionPresenter from '~/glql/components/presenters/collection.vue';
|
||||
import LabelPresenter from '~/glql/components/presenters/label.vue';
|
||||
import UserPresenter from '~/glql/components/presenters/user.vue';
|
||||
|
|
@ -14,6 +16,11 @@ describe('CollectionPresenter', () => {
|
|||
presenter: new Presenter(),
|
||||
},
|
||||
propsData: { data },
|
||||
stubs: {
|
||||
GlLabel: stubComponent(GlLabel, {
|
||||
template: '<span>{{ title }}</span>',
|
||||
}),
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -30,8 +37,6 @@ describe('CollectionPresenter', () => {
|
|||
expect(presenters.at(0).props('data')).toBe(mockData.nodes[0]);
|
||||
expect(presenters.at(1).props('data')).toBe(mockData.nodes[1]);
|
||||
|
||||
expectedTexts.forEach((text) => {
|
||||
expect(wrapper.text()).toContain(text);
|
||||
});
|
||||
expect(wrapper.text()).toEqual(expectedTexts.join(' '));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ import MockAdapter from 'axios-mock-adapter';
|
|||
import Vue, { nextTick } from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import {
|
||||
extendedWrapper,
|
||||
|
|
@ -26,6 +28,9 @@ import notesModule from '~/notes/stores/modules';
|
|||
import { sprintf } from '~/locale';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import { detectAndConfirmSensitiveTokens } from '~/lib/utils/secret_detection';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { loggedOutnoteableData, notesDataMock, userDataMock, noteableDataMock } from '../mock_data';
|
||||
|
||||
jest.mock('autosize');
|
||||
|
|
@ -38,6 +43,7 @@ jest.mock('~/lib/utils/secret_detection', () => {
|
|||
});
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('issue_comment_form component', () => {
|
||||
useLocalStorageSpy();
|
||||
|
|
@ -45,6 +51,7 @@ describe('issue_comment_form component', () => {
|
|||
let trackingSpy;
|
||||
let wrapper;
|
||||
let axiosMock;
|
||||
let pinia;
|
||||
|
||||
const findCloseReopenButton = () => wrapper.findByTestId('close-reopen-button');
|
||||
const findMarkdownEditor = () => wrapper.findComponent(MarkdownEditor);
|
||||
|
|
@ -126,6 +133,7 @@ describe('issue_comment_form component', () => {
|
|||
};
|
||||
},
|
||||
store,
|
||||
pinia,
|
||||
provide: {
|
||||
glFeatures: features,
|
||||
},
|
||||
|
|
@ -134,6 +142,9 @@ describe('issue_comment_form component', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin], stubActions: false });
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
axiosMock = new MockAdapter(axios);
|
||||
trackingSpy = mockTracking(undefined, null, jest.spyOn);
|
||||
detectAndConfirmSensitiveTokens.mockReturnValue(true);
|
||||
|
|
|
|||
|
|
@ -1,43 +1,37 @@
|
|||
import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import DiscussionCounter from '~/notes/components/discussion_counter.vue';
|
||||
import * as types from '~/notes/stores/mutation_types';
|
||||
import { createStore } from '~/mr_notes/stores';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { useMrNotes } from '~/mr_notes/store/legacy_mr_notes';
|
||||
import { discussionMock, noteableDataMock, notesDataMock } from '../mock_data';
|
||||
import { createDiscussionMock, noteableDataMock, notesDataMock } from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('DiscussionCounter component', () => {
|
||||
let store;
|
||||
let pinia;
|
||||
let wrapper;
|
||||
|
||||
const createComponent = (propsData) => {
|
||||
wrapper = mount(DiscussionCounter, { store, pinia, propsData });
|
||||
wrapper = mount(DiscussionCounter, { pinia, propsData });
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin] });
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin], stubActions: false });
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
useMrNotes();
|
||||
window.mrTabs = {};
|
||||
store = createStore();
|
||||
store.dispatch('setNoteableData', {
|
||||
useNotes().setNoteableData({
|
||||
...noteableDataMock,
|
||||
create_issue_to_resolve_discussions_path: '/test',
|
||||
});
|
||||
store.dispatch('setNotesData', notesDataMock);
|
||||
useNotes().setNotesData(notesDataMock);
|
||||
});
|
||||
|
||||
describe('has no discussions', () => {
|
||||
|
|
@ -50,8 +44,10 @@ describe('DiscussionCounter component', () => {
|
|||
|
||||
describe('has no resolvable discussions', () => {
|
||||
it('does not render', () => {
|
||||
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [{ ...discussionMock, resolvable: false }]);
|
||||
store.dispatch('updateResolvableDiscussionsCounts');
|
||||
useNotes()[types.ADD_OR_UPDATE_DISCUSSIONS]([
|
||||
{ ...createDiscussionMock(), resolvable: false },
|
||||
]);
|
||||
useNotes().updateResolvableDiscussionsCounts();
|
||||
createComponent({ blocksMerge: true });
|
||||
|
||||
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(false);
|
||||
|
|
@ -59,19 +55,15 @@ describe('DiscussionCounter component', () => {
|
|||
});
|
||||
|
||||
describe('has resolvable discussions', () => {
|
||||
const updateStore = (note = {}) => {
|
||||
discussionMock.notes[0] = { ...discussionMock.notes[0], ...note };
|
||||
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussionMock]);
|
||||
store.dispatch('updateResolvableDiscussionsCounts');
|
||||
const addNote = (note = {}) => {
|
||||
const discussion = createDiscussionMock();
|
||||
discussion.notes[0] = { ...discussion.notes[0], ...note };
|
||||
useNotes()[types.ADD_OR_UPDATE_DISCUSSIONS]([discussion]);
|
||||
useNotes().updateResolvableDiscussionsCounts();
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
delete discussionMock.notes[0].resolvable;
|
||||
delete discussionMock.notes[0].resolved;
|
||||
});
|
||||
|
||||
it('renders', () => {
|
||||
updateStore();
|
||||
addNote();
|
||||
createComponent({ blocksMerge: true });
|
||||
|
||||
expect(wrapper.findComponent({ ref: 'discussionCounter' }).exists()).toBe(true);
|
||||
|
|
@ -84,8 +76,8 @@ describe('DiscussionCounter component', () => {
|
|||
`(
|
||||
'changes background color to $color if blocksMerge is $blocksMerge',
|
||||
({ blocksMerge, color }) => {
|
||||
updateStore();
|
||||
store.state.notes.unresolvedDiscussionsCount = 1;
|
||||
addNote();
|
||||
useNotes().unresolvedDiscussionsCount = 1;
|
||||
createComponent({ blocksMerge });
|
||||
|
||||
expect(wrapper.find('[data-testid="discussions-counter-text"]').classes()).toContain(color);
|
||||
|
|
@ -97,7 +89,7 @@ describe('DiscussionCounter component', () => {
|
|||
${'not allResolved'} | ${false} | ${2}
|
||||
${'allResolved'} | ${true} | ${1}
|
||||
`('renders correctly if $title', async ({ resolved, groupLength }) => {
|
||||
updateStore({ resolvable: true, resolved });
|
||||
addNote({ resolvable: true, resolved });
|
||||
createComponent({ blocksMerge: true });
|
||||
await wrapper.findComponent(GlDisclosureDropdown).trigger('click');
|
||||
|
||||
|
|
@ -106,11 +98,11 @@ describe('DiscussionCounter component', () => {
|
|||
|
||||
describe('resolve all with new issue link', () => {
|
||||
it('has correct href prop', async () => {
|
||||
updateStore({ resolvable: true });
|
||||
addNote({ resolvable: true });
|
||||
createComponent({ blocksMerge: true });
|
||||
|
||||
const resolveDiscussionsPath =
|
||||
store.getters.getNoteableData.create_issue_to_resolve_discussions_path;
|
||||
useNotes().getNoteableData.create_issue_to_resolve_discussions_path;
|
||||
|
||||
await wrapper.findComponent(GlDisclosureDropdown).trigger('click');
|
||||
const resolveAllLink = wrapper.find('[data-testid="resolve-all-with-issue-link"]');
|
||||
|
|
@ -125,9 +117,9 @@ describe('DiscussionCounter component', () => {
|
|||
let discussion;
|
||||
|
||||
const updateStoreWithExpanded = async (expanded) => {
|
||||
discussion = { ...discussionMock, expanded };
|
||||
store.commit(types.ADD_OR_UPDATE_DISCUSSIONS, [discussion]);
|
||||
store.dispatch('updateResolvableDiscussionsCounts');
|
||||
discussion = { ...createDiscussionMock(), expanded };
|
||||
useNotes()[types.ADD_OR_UPDATE_DISCUSSIONS]([discussion]);
|
||||
useNotes().updateResolvableDiscussionsCounts();
|
||||
createComponent({ blocksMerge: true });
|
||||
await wrapper.findComponent(GlDisclosureDropdown).trigger('click');
|
||||
toggleAllButton = wrapper.find('[data-testid="toggle-all-discussions-btn"]');
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { GlDisclosureDropdown, GlDisclosureDropdownItem } from '@gitlab/ui';
|
|||
import { mount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import { TEST_HOST } from 'helpers/test_constants';
|
||||
import createEventHub from '~/helpers/event_hub_factory';
|
||||
import * as urlUtility from '~/lib/utils/url_utility';
|
||||
|
|
@ -18,22 +18,22 @@ import {
|
|||
ASC,
|
||||
DESC,
|
||||
} from '~/notes/constants';
|
||||
import notesModule from '~/notes/stores/modules';
|
||||
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { discussionFiltersMock, discussionMock } from '../mock_data';
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
const DISCUSSION_PATH = `${TEST_HOST}/example`;
|
||||
|
||||
describe('DiscussionFilter component', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let pinia;
|
||||
let eventHub;
|
||||
let mock;
|
||||
|
||||
const filterDiscussion = jest.fn();
|
||||
|
||||
const findFilter = (filterType) =>
|
||||
wrapper.find(`.gl-new-dropdown-item[data-filter-type="${filterType}"]`);
|
||||
const findGlDisclosureDropdownItem = () => wrapper.findComponent(GlDisclosureDropdownItem);
|
||||
|
|
@ -49,22 +49,12 @@ describe('DiscussionFilter component', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const defaultStore = { ...notesModule() };
|
||||
useNotes().notesData.discussionsPath = DISCUSSION_PATH;
|
||||
|
||||
store = new Vuex.Store({
|
||||
...defaultStore,
|
||||
actions: {
|
||||
...defaultStore.actions,
|
||||
filterDiscussion,
|
||||
},
|
||||
});
|
||||
|
||||
store.state.notesData.discussionsPath = DISCUSSION_PATH;
|
||||
|
||||
store.state.discussions = discussions;
|
||||
useNotes().discussions = discussions;
|
||||
|
||||
wrapper = mount(DiscussionFilter, {
|
||||
store,
|
||||
pinia,
|
||||
propsData: {
|
||||
filters: discussionFiltersMock,
|
||||
selectedValue: DISCUSSION_FILTERS_DEFAULT_VALUE,
|
||||
|
|
@ -74,6 +64,9 @@ describe('DiscussionFilter component', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin], stubActions: false });
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
mock = new AxiosMockAdapter(axios);
|
||||
|
||||
// We are mocking the discussions retrieval,
|
||||
|
|
@ -90,7 +83,6 @@ describe('DiscussionFilter component', () => {
|
|||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||
});
|
||||
|
||||
it('has local storage sync with the correct props', () => {
|
||||
|
|
@ -100,21 +92,20 @@ describe('DiscussionFilter component', () => {
|
|||
it('calls setDiscussionSortDirection when update is emitted', () => {
|
||||
findLocalStorageSync().vm.$emit('input', ASC);
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', { direction: ASC });
|
||||
expect(useNotes().setDiscussionSortDirection).toHaveBeenCalledWith({ direction: ASC });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when asc', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||
});
|
||||
|
||||
describe('when the dropdown is clicked', () => {
|
||||
it('calls the right actions', () => {
|
||||
wrapper.find('.js-newest-first').vm.$emit('action');
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
|
||||
expect(useNotes().setDiscussionSortDirection).toHaveBeenCalledWith({
|
||||
direction: DESC,
|
||||
});
|
||||
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
|
||||
|
|
@ -127,15 +118,14 @@ describe('DiscussionFilter component', () => {
|
|||
describe('when desc', () => {
|
||||
beforeEach(() => {
|
||||
mountComponent();
|
||||
store.state.discussionSortOrder = DESC;
|
||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||
useNotes().discussionSortOrder = DESC;
|
||||
});
|
||||
|
||||
describe('when the dropdown item is clicked', () => {
|
||||
it('calls the right actions', () => {
|
||||
wrapper.find('.js-oldest-first').vm.$emit('action');
|
||||
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
|
||||
expect(useNotes().setDiscussionSortDirection).toHaveBeenCalledWith({
|
||||
direction: ASC,
|
||||
});
|
||||
expect(Tracking.event).toHaveBeenCalledWith(undefined, 'change_discussion_sort_direction', {
|
||||
|
|
@ -167,7 +157,7 @@ describe('DiscussionFilter component', () => {
|
|||
});
|
||||
|
||||
it('disables the dropdown when discussions are loading', () => {
|
||||
store.state.isLoading = true;
|
||||
useNotes().isLoading = true;
|
||||
|
||||
expect(wrapper.findComponent(GlDisclosureDropdown).props('disabled')).toBe(true);
|
||||
});
|
||||
|
|
@ -183,27 +173,27 @@ describe('DiscussionFilter component', () => {
|
|||
it('only updates when selected filter changes', () => {
|
||||
findFilter(DISCUSSION_FILTER_TYPES.ALL).vm.$emit('action');
|
||||
|
||||
expect(filterDiscussion).not.toHaveBeenCalled();
|
||||
expect(useNotes().filterDiscussion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('disables timeline view if it was enabled', () => {
|
||||
store.state.isTimelineEnabled = true;
|
||||
useNotes().isTimelineEnabled = true;
|
||||
|
||||
findFilter(DISCUSSION_FILTER_TYPES.HISTORY).vm.$emit('action');
|
||||
|
||||
expect(store.state.isTimelineEnabled).toBe(false);
|
||||
expect(useNotes().isTimelineEnabled).toBe(false);
|
||||
});
|
||||
|
||||
it('disables commenting when "Show history only" filter is applied', () => {
|
||||
findFilter(DISCUSSION_FILTER_TYPES.HISTORY).vm.$emit('action');
|
||||
|
||||
expect(store.state.commentsDisabled).toBe(true);
|
||||
expect(useNotes().commentsDisabled).toBe(true);
|
||||
});
|
||||
|
||||
it('enables commenting when "Show history only" filter is not applied', () => {
|
||||
findFilter(DISCUSSION_FILTER_TYPES.ALL).vm.$emit('action');
|
||||
|
||||
expect(store.state.commentsDisabled).toBe(false);
|
||||
expect(useNotes().commentsDisabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -262,10 +252,9 @@ describe('DiscussionFilter component', () => {
|
|||
|
||||
it('does not fetch discussions when there is no hash', async () => {
|
||||
mountComponent();
|
||||
const dispatchSpy = jest.spyOn(store, 'dispatch');
|
||||
|
||||
await nextTick();
|
||||
expect(dispatchSpy).not.toHaveBeenCalled();
|
||||
expect(useNotes().filterDiscussion).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('selected value is not default state', () => {
|
||||
|
|
@ -276,12 +265,11 @@ describe('DiscussionFilter component', () => {
|
|||
});
|
||||
it('fetch discussions when there is hash', async () => {
|
||||
jest.spyOn(urlUtility, 'getLocationHash').mockReturnValueOnce('note_123');
|
||||
const dispatchSpy = jest.spyOn(store, 'dispatch');
|
||||
|
||||
window.dispatchEvent(new Event('hashchange'));
|
||||
|
||||
await nextTick();
|
||||
expect(dispatchSpy).toHaveBeenCalledWith('filterDiscussion', {
|
||||
expect(useNotes().filterDiscussion).toHaveBeenCalledWith({
|
||||
filter: 0,
|
||||
path: 'http://test.host/example',
|
||||
persistFilter: false,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,24 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import NoteSignedOutWidget from '~/notes/components/note_signed_out_widget.vue';
|
||||
import createStore from '~/notes/stores';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { notesDataMock } from '../mock_data';
|
||||
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('NoteSignedOutWidget component', () => {
|
||||
let wrapper;
|
||||
let pinia;
|
||||
|
||||
beforeEach(() => {
|
||||
const store = createStore();
|
||||
store.dispatch('setNotesData', notesDataMock);
|
||||
wrapper = shallowMount(NoteSignedOutWidget, { store });
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin], stubActions: false });
|
||||
useLegacyDiffs();
|
||||
useNotes().setNotesData(notesDataMock);
|
||||
wrapper = shallowMount(NoteSignedOutWidget, { pinia });
|
||||
});
|
||||
|
||||
it('renders sign in link provided in the store', () => {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import Vue from 'vue';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
|
||||
import DiscussionFilter from '~/notes/components/discussion_filter.vue';
|
||||
import TimelineToggle from '~/notes/components/timeline_toggle.vue';
|
||||
import createStore from '~/notes/stores';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { notesFilters } from '../mock_data';
|
||||
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('~/notes/components/notes_activity_header.vue', () => {
|
||||
let wrapper;
|
||||
let pinia;
|
||||
|
||||
const findTitle = () => wrapper.find('h2');
|
||||
|
||||
|
|
@ -19,10 +28,19 @@ describe('~/notes/components/notes_activity_header.vue', () => {
|
|||
},
|
||||
// why: Rendering async timeline toggle requires store
|
||||
store: createStore(),
|
||||
pinia,
|
||||
...options,
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
pinia = createTestingPinia({
|
||||
plugins: [globalAccessorPlugin],
|
||||
});
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
});
|
||||
|
||||
describe('default', () => {
|
||||
beforeEach(() => {
|
||||
createComponent();
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ import { createTestingPinia } from '@pinia/testing';
|
|||
import AxiosMockAdapter from 'axios-mock-adapter';
|
||||
import $ from 'jquery';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import setWindowLocation from 'helpers/set_window_location_helper';
|
||||
import { mockTracking } from 'helpers/tracking_helper';
|
||||
import waitForPromises from 'helpers/wait_for_promises';
|
||||
import DraftNote from '~/batch_comments/components/draft_note.vue';
|
||||
import batchComments from '~/batch_comments/stores/modules/batch_comments';
|
||||
import axios from '~/lib/utils/axios_utils';
|
||||
import { HTTP_STATUS_OK } from '~/lib/utils/http_status';
|
||||
import { getLocationHash } from '~/lib/utils/url_utility';
|
||||
|
|
@ -20,7 +21,7 @@ import NotesApp from '~/notes/components/notes_app.vue';
|
|||
import NotesActivityHeader from '~/notes/components/notes_activity_header.vue';
|
||||
import NoteableDiscussion from '~/notes/components/noteable_discussion.vue';
|
||||
import * as constants from '~/notes/constants';
|
||||
import createStore from '~/notes/stores';
|
||||
import store from '~/mr_notes/stores';
|
||||
import OrderedLayout from '~/notes/components/ordered_layout.vue';
|
||||
// TODO: use generated fixture (https://gitlab.com/gitlab-org/gitlab-foss/issues/62491)
|
||||
import { CopyAsGFM } from '~/behaviors/markdown/copy_as_gfm';
|
||||
|
|
@ -29,9 +30,12 @@ import { ISSUABLE_COMMENT_OR_REPLY, keysFor } from '~/behaviors/shortcuts/keybin
|
|||
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { globalAccessorPlugin, syncWithVuex } from '~/pinia/plugins';
|
||||
import createMockApollo from 'helpers/mock_apollo_helper';
|
||||
import noteQuery from '~/notes/graphql/note.query.graphql';
|
||||
import { useBatchComments } from '~/batch_comments/store';
|
||||
import * as types from '~/notes/stores/mutation_types';
|
||||
import { SET_BATCH_COMMENTS_DRAFTS } from '~/batch_comments/stores/modules/batch_comments/mutation_types';
|
||||
import * as mockData from '../mock_data';
|
||||
|
||||
jest.mock('~/behaviors/markdown/render_gfm');
|
||||
|
|
@ -50,13 +54,13 @@ const propsData = {
|
|||
notesFilterValue: TEST_NOTES_FILTER_VALUE,
|
||||
};
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(VueApollo);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('note_app', () => {
|
||||
let axiosMock;
|
||||
let mountComponent;
|
||||
let wrapper;
|
||||
let store;
|
||||
let pinia;
|
||||
|
||||
const initStore = (notesData = propsData.notesData) => {
|
||||
|
|
@ -72,6 +76,29 @@ describe('note_app', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const mountComponent = ({ props = {} } = {}) => {
|
||||
initStore();
|
||||
wrapper = mount(
|
||||
{
|
||||
components: {
|
||||
NotesApp,
|
||||
},
|
||||
template: `<div class="js-vue-notes-event">
|
||||
<notes-app ref="notesApp" v-bind="$attrs" />
|
||||
</div>`,
|
||||
inheritAttrs: false,
|
||||
},
|
||||
{
|
||||
propsData: {
|
||||
...propsData,
|
||||
...props,
|
||||
},
|
||||
store,
|
||||
pinia,
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const findCommentButton = () => wrapper.find('[data-testid="comment-button"]');
|
||||
|
||||
const getComponentOrder = () => {
|
||||
|
|
@ -84,39 +111,18 @@ describe('note_app', () => {
|
|||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store.commit('reset');
|
||||
$('body').attr('data-page', 'projects:merge_requests:show');
|
||||
|
||||
axiosMock = new AxiosMockAdapter(axios);
|
||||
Vue.use(VueApollo);
|
||||
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin] });
|
||||
pinia = createTestingPinia({
|
||||
plugins: [globalAccessorPlugin, syncWithVuex],
|
||||
stubActions: false,
|
||||
});
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
|
||||
store = createStore();
|
||||
|
||||
mountComponent = ({ props = {} } = {}) => {
|
||||
initStore();
|
||||
return mount(
|
||||
{
|
||||
components: {
|
||||
NotesApp,
|
||||
},
|
||||
template: `<div class="js-vue-notes-event">
|
||||
<notes-app ref="notesApp" v-bind="$attrs" />
|
||||
</div>`,
|
||||
inheritAttrs: false,
|
||||
},
|
||||
{
|
||||
propsData: {
|
||||
...propsData,
|
||||
...props,
|
||||
},
|
||||
store,
|
||||
pinia,
|
||||
},
|
||||
);
|
||||
};
|
||||
useBatchComments();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
@ -126,7 +132,7 @@ describe('note_app', () => {
|
|||
describe('render', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
|
|
@ -169,7 +175,7 @@ describe('note_app', () => {
|
|||
describe('render with comments disabled', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent({
|
||||
mountComponent({
|
||||
// why: In this integration test, previously we manually set store.state.commentsDisabled
|
||||
// This stopped working when we added `<discussion-filter>` into the component tree.
|
||||
// Let's lean into the integration scope and use a prop that "disables comments".
|
||||
|
|
@ -193,10 +199,9 @@ describe('note_app', () => {
|
|||
describe('timeline view', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
store.state.commentsDisabled = false;
|
||||
store.state.isTimelineEnabled = true;
|
||||
store.commit(types.SET_TIMELINE_VIEW, true);
|
||||
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
|
|
@ -207,7 +212,7 @@ describe('note_app', () => {
|
|||
|
||||
describe('while fetching data', () => {
|
||||
beforeEach(() => {
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
it('renders skeleton notes', () => {
|
||||
|
|
@ -226,7 +231,7 @@ describe('note_app', () => {
|
|||
describe('individual note', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises().then(() => {
|
||||
wrapper.find('.js-note-edit').trigger('click');
|
||||
});
|
||||
|
|
@ -249,7 +254,7 @@ describe('note_app', () => {
|
|||
describe('discussion note', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getDiscussionNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises().then(() => {
|
||||
wrapper.find('.js-note-edit').trigger('click');
|
||||
});
|
||||
|
|
@ -273,7 +278,7 @@ describe('note_app', () => {
|
|||
describe('new note form', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
|
|
@ -287,7 +292,7 @@ describe('note_app', () => {
|
|||
describe('edit form', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
|
|
@ -303,42 +308,35 @@ describe('note_app', () => {
|
|||
describe('emoji awards', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(HTTP_STATUS_OK, []);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
it('dispatches toggleAward after toggleAward event', () => {
|
||||
const spy = jest.spyOn(store, 'dispatch').mockImplementation(jest.fn());
|
||||
const toggleAwardEvent = new CustomEvent('toggleAward', {
|
||||
detail: {
|
||||
awardName: 'test',
|
||||
noteId: 1,
|
||||
},
|
||||
});
|
||||
const toggleAwardAction = jest.fn().mockName('toggleAward');
|
||||
wrapper.vm.$store.hotUpdate({
|
||||
actions: {
|
||||
toggleAward: toggleAwardAction,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.$parent.$el.dispatchEvent(toggleAwardEvent);
|
||||
wrapper.element.dispatchEvent(toggleAwardEvent);
|
||||
|
||||
jest.advanceTimersByTime(2);
|
||||
|
||||
expect(toggleAwardAction).toHaveBeenCalledTimes(1);
|
||||
const [, payload] = toggleAwardAction.mock.calls[0];
|
||||
|
||||
expect(payload).toEqual({
|
||||
expect(spy).toHaveBeenCalledWith('toggleAward', {
|
||||
awardName: 'test',
|
||||
noteId: 1,
|
||||
});
|
||||
spy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('mounted', () => {
|
||||
beforeEach(() => {
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
return waitForPromises();
|
||||
});
|
||||
|
||||
|
|
@ -354,14 +352,14 @@ describe('note_app', () => {
|
|||
|
||||
describe('when sort direction is desc', () => {
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store.state.discussionSortOrder = constants.DESC;
|
||||
store.state.isLoading = true;
|
||||
store.state.discussions = [mockData.discussionMock];
|
||||
store.commit(types.SET_DISCUSSIONS_SORT, { direction: constants.DESC });
|
||||
store.commit(types.SET_NOTES_LOADING_STATE, true);
|
||||
store.commit(types.ADD_NEW_NOTE, { discussion: mockData.discussionMock });
|
||||
|
||||
wrapper = shallowMount(NotesApp, {
|
||||
propsData,
|
||||
store,
|
||||
pinia,
|
||||
stubs: {
|
||||
'ordered-layout': OrderedLayout,
|
||||
},
|
||||
|
|
@ -379,13 +377,13 @@ describe('note_app', () => {
|
|||
|
||||
describe('when sort direction is asc', () => {
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store.state.isLoading = true;
|
||||
store.state.discussions = [mockData.discussionMock];
|
||||
store.commit(types.SET_NOTES_LOADING_STATE, true);
|
||||
store.commit(types.ADD_NEW_NOTE, { discussion: mockData.discussionMock });
|
||||
|
||||
wrapper = shallowMount(NotesApp, {
|
||||
propsData,
|
||||
store,
|
||||
pinia,
|
||||
stubs: {
|
||||
'ordered-layout': OrderedLayout,
|
||||
},
|
||||
|
|
@ -411,13 +409,13 @@ describe('note_app', () => {
|
|||
.fn()
|
||||
.mockResolvedValue(mockData.singleNoteResponseFactory({ urlHash, authorId }));
|
||||
|
||||
store = createStore();
|
||||
store.state.isLoading = true;
|
||||
store.state.targetNoteHash = urlHash;
|
||||
store.commit(types.SET_NOTES_LOADING_STATE, true);
|
||||
store.commit(types.SET_TARGET_NOTE_HASH, urlHash);
|
||||
|
||||
wrapper = shallowMount(NotesApp, {
|
||||
propsData,
|
||||
store,
|
||||
pinia,
|
||||
apolloProvider: createMockApollo([[noteQuery, noteQueryHandler]]),
|
||||
stubs: {
|
||||
'ordered-layout': OrderedLayout,
|
||||
|
|
@ -460,23 +458,26 @@ describe('note_app', () => {
|
|||
|
||||
describe('when multiple draft types are present', () => {
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
store.registerModule('batchComments', batchComments());
|
||||
store.state.batchComments.drafts = [
|
||||
store.commit(types.SET_NOTES_LOADING_STATE, true);
|
||||
store.commit(`batchComments/${SET_BATCH_COMMENTS_DRAFTS}`, [
|
||||
mockData.draftDiffDiscussion,
|
||||
mockData.draftReply,
|
||||
...mockData.draftComments,
|
||||
];
|
||||
store.state.isLoading = false;
|
||||
]);
|
||||
wrapper = shallowMount(NotesApp, {
|
||||
propsData,
|
||||
store,
|
||||
pinia,
|
||||
stubs: {
|
||||
OrderedLayout,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
store.commit('batchComments/reset');
|
||||
});
|
||||
|
||||
it('correctly finds only draft comments', () => {
|
||||
const drafts = wrapper.findAllComponents(DraftNote).wrappers;
|
||||
|
||||
|
|
@ -489,9 +490,8 @@ describe('note_app', () => {
|
|||
describe('fetching discussions', () => {
|
||||
describe('when note anchor is not present', () => {
|
||||
it('does not include extra query params', async () => {
|
||||
store = createStore();
|
||||
initStore();
|
||||
wrapper = shallowMount(NotesApp, { propsData, store });
|
||||
wrapper = shallowMount(NotesApp, { propsData, store, pinia });
|
||||
await waitForPromises();
|
||||
|
||||
expect(axiosMock.history.get[0].params).toEqual({ per_page: 20 });
|
||||
|
|
@ -506,7 +506,8 @@ describe('note_app', () => {
|
|||
});
|
||||
return shallowMount(NotesApp, {
|
||||
propsData,
|
||||
store: createStore(),
|
||||
store,
|
||||
pinia,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -548,7 +549,7 @@ describe('note_app', () => {
|
|||
window.mrTabs = { eventHub: notesEventHub };
|
||||
axiosMock.onAny().reply(mockData.getIndividualNoteResponse);
|
||||
trackingSpy = mockTracking(undefined, window.document, jest.spyOn);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
});
|
||||
|
||||
describe('when adding a new comment to an existing review', () => {
|
||||
|
|
@ -588,7 +589,7 @@ describe('note_app', () => {
|
|||
|
||||
it('sends quote to main reply editor', async () => {
|
||||
jest.spyOn(CopyAsGFM, 'selectionToGfm').mockReturnValueOnce('foo');
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
const replySpy = jest.spyOn(wrapper.findComponent(CommentForm).vm, 'append');
|
||||
const target = wrapper.element.querySelector('p');
|
||||
stubSelection(target);
|
||||
|
|
@ -600,7 +601,7 @@ describe('note_app', () => {
|
|||
it('sends quote to discussion reply editor', async () => {
|
||||
jest.spyOn(CopyAsGFM, 'selectionToGfm').mockReturnValueOnce('foo');
|
||||
axiosMock.onAny().reply(mockData.getDiscussionNoteResponse);
|
||||
wrapper = mountComponent();
|
||||
mountComponent();
|
||||
await waitForPromises();
|
||||
const replySpy = jest.spyOn(wrapper.findComponent(NoteableDiscussion).vm, 'showReplyForm');
|
||||
const target = wrapper.element.querySelector('.js-noteable-discussion p');
|
||||
|
|
@ -613,7 +614,6 @@ describe('note_app', () => {
|
|||
|
||||
describe('noteableType computed property', () => {
|
||||
const createComponent = (noteableType, type) => {
|
||||
store = createStore();
|
||||
return shallowMount(NotesApp, {
|
||||
propsData: {
|
||||
...propsData,
|
||||
|
|
@ -624,6 +624,7 @@ describe('note_app', () => {
|
|||
},
|
||||
},
|
||||
store,
|
||||
pinia,
|
||||
});
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,65 +1,66 @@
|
|||
import { GlButton } from '@gitlab/ui';
|
||||
import { mount } from '@vue/test-utils';
|
||||
import Vue, { nextTick } from 'vue';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import Vuex from 'vuex';
|
||||
import { PiniaVuePlugin } from 'pinia';
|
||||
import { createTestingPinia } from '@pinia/testing';
|
||||
import TimelineToggle, {
|
||||
timelineEnabledTooltip,
|
||||
timelineDisabledTooltip,
|
||||
} from '~/notes/components/timeline_toggle.vue';
|
||||
import { ASC, DESC } from '~/notes/constants';
|
||||
import createStore from '~/notes/stores';
|
||||
import { trackToggleTimelineView } from '~/notes/utils';
|
||||
import Tracking from '~/tracking';
|
||||
import { globalAccessorPlugin } from '~/pinia/plugins';
|
||||
import { useLegacyDiffs } from '~/diffs/stores/legacy_diffs';
|
||||
import { useNotes } from '~/notes/store/legacy_notes';
|
||||
|
||||
Vue.use(Vuex);
|
||||
Vue.use(PiniaVuePlugin);
|
||||
|
||||
describe('Timeline toggle', () => {
|
||||
let wrapper;
|
||||
let store;
|
||||
let pinia;
|
||||
const mockEvent = { currentTarget: { blur: jest.fn() } };
|
||||
|
||||
const createComponent = () => {
|
||||
jest.spyOn(store, 'dispatch').mockImplementation();
|
||||
jest.spyOn(Tracking, 'event').mockImplementation();
|
||||
|
||||
wrapper = mount(TimelineToggle, {
|
||||
store,
|
||||
pinia,
|
||||
});
|
||||
};
|
||||
|
||||
const findGlButton = () => wrapper.findComponent(GlButton);
|
||||
|
||||
beforeEach(() => {
|
||||
store = createStore();
|
||||
createComponent();
|
||||
pinia = createTestingPinia({ plugins: [globalAccessorPlugin], stubActions: false });
|
||||
useLegacyDiffs();
|
||||
useNotes();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
store.dispatch.mockReset();
|
||||
mockEvent.currentTarget.blur.mockReset();
|
||||
Tracking.event.mockReset();
|
||||
});
|
||||
|
||||
describe('ON state', () => {
|
||||
it('should update timeline flag in the store', () => {
|
||||
store.state.isTimelineEnabled = false;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setTimelineView', true);
|
||||
expect(useNotes().setTimelineView).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('should set sort direction to DESC if not set', () => {
|
||||
store.state.isTimelineEnabled = true;
|
||||
store.state.sortDirection = ASC;
|
||||
useNotes().discussionSortOrder = ASC;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setDiscussionSortDirection', {
|
||||
expect(useNotes().setDiscussionSortDirection).toHaveBeenCalledWith({
|
||||
direction: DESC,
|
||||
persist: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set correct UI state', async () => {
|
||||
store.state.isTimelineEnabled = true;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
await nextTick();
|
||||
expect(findGlButton().attributes('title')).toBe(timelineEnabledTooltip);
|
||||
|
|
@ -68,7 +69,7 @@ describe('Timeline toggle', () => {
|
|||
});
|
||||
|
||||
it('should track Snowplow event', async () => {
|
||||
store.state.isTimelineEnabled = true;
|
||||
createComponent();
|
||||
await nextTick();
|
||||
|
||||
findGlButton().trigger('click');
|
||||
|
|
@ -79,20 +80,24 @@ describe('Timeline toggle', () => {
|
|||
});
|
||||
|
||||
describe('OFF state', () => {
|
||||
beforeEach(() => {
|
||||
useNotes().isTimelineEnabled = true;
|
||||
});
|
||||
|
||||
it('should update timeline flag in the store', () => {
|
||||
store.state.isTimelineEnabled = true;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
expect(store.dispatch).toHaveBeenCalledWith('setTimelineView', false);
|
||||
expect(useNotes().setTimelineView).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('should NOT update sort direction', () => {
|
||||
store.state.isTimelineEnabled = false;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
expect(store.dispatch).not.toHaveBeenCalledWith();
|
||||
expect(useNotes().setDiscussionSortDirection).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set correct UI state', async () => {
|
||||
store.state.isTimelineEnabled = false;
|
||||
createComponent();
|
||||
findGlButton().vm.$emit('click', mockEvent);
|
||||
await nextTick();
|
||||
expect(findGlButton().attributes('title')).toBe(timelineDisabledTooltip);
|
||||
|
|
@ -100,9 +105,8 @@ describe('Timeline toggle', () => {
|
|||
expect(mockEvent.currentTarget.blur).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should track Snowplow event', async () => {
|
||||
store.state.isTimelineEnabled = false;
|
||||
await nextTick();
|
||||
it('should track Snowplow event', () => {
|
||||
createComponent();
|
||||
|
||||
findGlButton().trigger('click');
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export const note = {
|
|||
path: '/gitlab-org/gitlab-foss/notes/546',
|
||||
};
|
||||
|
||||
export const discussionMock = {
|
||||
export const createDiscussionMock = () => ({
|
||||
id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
|
||||
reply_id: '9e3bd2f71a01de45fd166e6719eb380ad9f270b1',
|
||||
expanded: true,
|
||||
|
|
@ -323,7 +323,9 @@ export const discussionMock = {
|
|||
resolvable: true,
|
||||
active: true,
|
||||
confidential: false,
|
||||
};
|
||||
});
|
||||
|
||||
export const discussionMock = createDiscussionMock();
|
||||
|
||||
export const loggedOutnoteableData = {
|
||||
id: '98',
|
||||
|
|
|
|||
|
|
@ -60,13 +60,15 @@ describe('Pinia plugins', () => {
|
|||
});
|
||||
};
|
||||
|
||||
const createPiniaStore = () => {
|
||||
const createSyncWithConfig = () => ({
|
||||
store: vuexStore,
|
||||
name,
|
||||
namespaced,
|
||||
});
|
||||
|
||||
const createPiniaStore = (syncWith = createSyncWithConfig()) => {
|
||||
usePiniaStore = defineStore('exampleStore', {
|
||||
syncWith: {
|
||||
store: vuexStore,
|
||||
name,
|
||||
namespaced,
|
||||
},
|
||||
syncWith,
|
||||
state() {
|
||||
return createState();
|
||||
},
|
||||
|
|
@ -113,6 +115,17 @@ describe('Pinia plugins', () => {
|
|||
setActivePinia(createPinia().use(syncWithVuex));
|
||||
},
|
||||
],
|
||||
[
|
||||
'with a non namespaced config override',
|
||||
() => {
|
||||
name = 'myStore';
|
||||
namespaced = false;
|
||||
createVuexStoreWithModule();
|
||||
createPiniaStore(undefined);
|
||||
setActivePinia(createPinia().use(syncWithVuex));
|
||||
usePiniaStore().syncWith(createSyncWithConfig());
|
||||
},
|
||||
],
|
||||
])('%s', (caseName, setupFn) => {
|
||||
beforeEach(() => {
|
||||
setupFn();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe ViteHelper, feature_category: :tooling do
|
||||
describe '#vite_page_entrypoint_path' do
|
||||
describe '#vite_page_entrypoint_paths' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:path, :action, :result) do
|
||||
|
|
@ -93,36 +93,4 @@ RSpec.describe ViteHelper, feature_category: :tooling do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#vite_origin' do
|
||||
before do
|
||||
allow(ViteRuby).to receive_message_chain(:config, :origin).and_return('origin')
|
||||
end
|
||||
|
||||
it { expect(helper.vite_origin).to eq('origin') }
|
||||
end
|
||||
|
||||
describe '#vite_hmr_ws_origin' do
|
||||
before do
|
||||
allow(ViteRuby).to receive_message_chain(:config, :host_with_port).and_return('host')
|
||||
allow(ViteRuby).to receive_message_chain(:config, :https).and_return(https)
|
||||
end
|
||||
|
||||
context 'with https' do
|
||||
let(:https) { true }
|
||||
|
||||
it 'returns wss origin' do
|
||||
expect(helper.vite_hmr_ws_origin).to eq('wss://host')
|
||||
end
|
||||
end
|
||||
|
||||
context 'without https' do
|
||||
let(:https) { false }
|
||||
|
||||
it 'returns ws origin' do
|
||||
allow(ViteRuby).to receive_message_chain(:config, :https).and_return(false)
|
||||
expect(helper.vite_hmr_ws_origin).to eq('ws://host')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -39,8 +39,8 @@ RSpec.describe Gitlab::Ci::Build::Releaser, feature_category: :continuous_integr
|
|||
release_cli_command = 'release-cli create --name "Release $CI_COMMIT_SHA" --description "Created using the release-cli $EXTRA_DESCRIPTION" --tag-name "release-$CI_COMMIT_SHA" --tag-message "Annotated tag message" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z" --milestone "m1" --milestone "m2" --milestone "m3"'
|
||||
result_for_release_cli_without_catalog_publish = "#{release_cli_command} #{release_cli_assets_links}"
|
||||
|
||||
glab_create_unix = 'glab -R $CI_PROJECT_PATH release create'
|
||||
glab_create_windows = 'glab -R $env:CI_PROJECT_PATH release create'
|
||||
glab_create_unix = 'glab release create -R $CI_PROJECT_PATH'
|
||||
glab_create_windows = 'glab release create -R $env:CI_PROJECT_PATH'
|
||||
glab_command = "\"release-$CI_COMMIT_SHA\" #{glab_assets_links} --milestone \"m1,m2,m3\" --name \"Release $CI_COMMIT_SHA\" --experimental-notes-text-or-file \"Created using the release-cli $EXTRA_DESCRIPTION\" --ref \"$CI_COMMIT_SHA\" --tag-message \"Annotated tag message\" --released-at \"2020-07-15T08:00:00Z\" --no-update --no-close-milestone"
|
||||
|
||||
warning_message = "Warning: release-cli will not be supported after 18.0. Please use glab version >= 1.53.0. Troubleshooting: http://localhost/help/user/project/releases/_index.md#gitlab-cli-version-requirement"
|
||||
|
|
@ -206,14 +206,14 @@ RSpec.describe Gitlab::Ci::Build::Releaser, feature_category: :continuous_integr
|
|||
links = { links: [{ name: 'asset1', url: 'https://example.com/assets/1', link_type: 'other', filepath: '/pretty/asset/1' }] }
|
||||
|
||||
where(:node_name, :node_value, :result) do
|
||||
:name | 'Release $CI_COMMIT_SHA' | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --name "Release $CI_COMMIT_SHA" --ref "$CI_COMMIT_SHA"'
|
||||
:description | 'Release-cli $EXTRA_DESCRIPTION' | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --experimental-notes-text-or-file "Release-cli $EXTRA_DESCRIPTION" --ref "$CI_COMMIT_SHA"'
|
||||
:tag_name | 'release-$CI_COMMIT_SHA' | 'glab -R $CI_PROJECT_PATH release create "release-$CI_COMMIT_SHA" --ref "$CI_COMMIT_SHA"'
|
||||
:tag_message | 'Annotated tag message' | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA" --tag-message "Annotated tag message"'
|
||||
:ref | '$CI_COMMIT_SHA' | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA"'
|
||||
:milestones | %w[m1 m2 m3] | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --milestone "m1,m2,m3" --ref "$CI_COMMIT_SHA"'
|
||||
:released_at | '2020-07-15T08:00:00Z' | 'glab -R $CI_PROJECT_PATH release create "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z"'
|
||||
:assets | links | "glab -R $CI_PROJECT_PATH release create \"$CI_COMMIT_TAG\" --assets-links #{links[:links].to_json.to_json} --ref \"$CI_COMMIT_SHA\""
|
||||
:name | 'Release $CI_COMMIT_SHA' | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --name "Release $CI_COMMIT_SHA" --ref "$CI_COMMIT_SHA"'
|
||||
:description | 'Release-cli $EXTRA_DESCRIPTION' | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --experimental-notes-text-or-file "Release-cli $EXTRA_DESCRIPTION" --ref "$CI_COMMIT_SHA"'
|
||||
:tag_name | 'release-$CI_COMMIT_SHA' | 'glab release create -R $CI_PROJECT_PATH "release-$CI_COMMIT_SHA" --ref "$CI_COMMIT_SHA"'
|
||||
:tag_message | 'Annotated tag message' | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA" --tag-message "Annotated tag message"'
|
||||
:ref | '$CI_COMMIT_SHA' | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA"'
|
||||
:milestones | %w[m1 m2 m3] | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --milestone "m1,m2,m3" --ref "$CI_COMMIT_SHA"'
|
||||
:released_at | '2020-07-15T08:00:00Z' | 'glab release create -R $CI_PROJECT_PATH "$CI_COMMIT_TAG" --ref "$CI_COMMIT_SHA" --released-at "2020-07-15T08:00:00Z"'
|
||||
:assets | links | "glab release create -R $CI_PROJECT_PATH \"$CI_COMMIT_TAG\" --assets-links #{links[:links].to_json.to_json} --ref \"$CI_COMMIT_SHA\""
|
||||
end
|
||||
|
||||
with_them do
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ RSpec.describe Gitlab::Ci::Build::Step, feature_category: :continuous_integratio
|
|||
let(:job) { create(:ci_build, :release_options) }
|
||||
|
||||
it 'returns glab command line' do
|
||||
expect(subject.script).to match_array([a_string_including("glab -R $CI_PROJECT_PATH release create")])
|
||||
expect(subject.script).to match_array([a_string_including("glab release create -R $CI_PROJECT_PATH")])
|
||||
end
|
||||
|
||||
context 'when the FF ci_glab_for_release is disabled' do
|
||||
|
|
@ -92,7 +92,7 @@ RSpec.describe Gitlab::Ci::Build::Step, feature_category: :continuous_integratio
|
|||
let(:job) { create(:ci_build, :release_options, pipeline: pipeline) }
|
||||
|
||||
it 'returns glab scripts with catalog publish' do
|
||||
expect(subject.script).to match_array([a_string_including("glab -R $CI_PROJECT_PATH release create")])
|
||||
expect(subject.script).to match_array([a_string_including("glab release create -R $CI_PROJECT_PATH")])
|
||||
expect(subject.script).to match_array([a_string_including("--publish-to-catalog")])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
let(:policy) { ActionDispatch::ContentSecurityPolicy.new }
|
||||
let(:lfs_enabled) { false }
|
||||
let(:proxy_download) { false }
|
||||
let(:host) { "gdk.test" }
|
||||
let(:port) { 3443 }
|
||||
|
||||
let(:csp_config) do
|
||||
{
|
||||
|
|
@ -43,6 +45,11 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
end
|
||||
|
||||
before do
|
||||
ViteRuby.configure(
|
||||
host: host,
|
||||
port: port,
|
||||
https: true
|
||||
)
|
||||
stub_lfs_setting(enabled: lfs_enabled)
|
||||
allow(LfsObjectUploader)
|
||||
.to receive(:object_store_options)
|
||||
|
|
@ -110,7 +117,78 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
|
||||
describe 'the worker-src directive' do
|
||||
it 'can be loaded from local origins' do
|
||||
expect(worker_src).to eq("'self' http://localhost/assets/ blob: data:")
|
||||
expect(worker_src).to eq("'self' http://localhost/assets/ blob: data: https://gdk.test:3443/vite-dev/")
|
||||
end
|
||||
end
|
||||
|
||||
describe 'Vite dev server' do
|
||||
using RSpec::Parameterized::TableSyntax
|
||||
|
||||
where(:https, :env) do
|
||||
[
|
||||
[true, 'development'],
|
||||
[true, 'test'],
|
||||
[false, 'development'],
|
||||
[false, 'test']
|
||||
]
|
||||
end
|
||||
|
||||
with_them do
|
||||
def protocol
|
||||
https ? 'https' : 'http'
|
||||
end
|
||||
|
||||
def ws_protocol
|
||||
https ? 'wss' : 'ws'
|
||||
end
|
||||
|
||||
def origin
|
||||
"#{protocol}://#{ViteRuby.config.host_with_port}"
|
||||
end
|
||||
|
||||
def dev_server_path
|
||||
"#{origin}/vite-dev"
|
||||
end
|
||||
|
||||
def dev_server_socket_path
|
||||
"#{ws_protocol}://#{ViteRuby.config.host_with_port}/vite-dev"
|
||||
end
|
||||
|
||||
before do
|
||||
ViteRuby.configure(
|
||||
host: host,
|
||||
port: port,
|
||||
https: https
|
||||
)
|
||||
end
|
||||
|
||||
context 'when in production' do
|
||||
before do
|
||||
stub_rails_env('production')
|
||||
end
|
||||
|
||||
it 'does not add directives' do
|
||||
expect(connect_src).not_to include(dev_server_path)
|
||||
expect(connect_src).not_to include(dev_server_socket_path)
|
||||
expect(worker_src).not_to include(dev_server_path)
|
||||
expect(style_src).not_to include(dev_server_path)
|
||||
expect(font_src).not_to include(dev_server_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when in non-production' do
|
||||
before do
|
||||
stub_rails_env(env)
|
||||
end
|
||||
|
||||
it 'adds directives' do
|
||||
expect(connect_src).to include(dev_server_path)
|
||||
expect(connect_src).to include(dev_server_socket_path)
|
||||
expect(worker_src).to include(dev_server_path)
|
||||
expect(style_src).to include(dev_server_path)
|
||||
expect(font_src).to include(dev_server_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -183,28 +261,28 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
describe 'Websocket connections' do
|
||||
it 'with insecure domain' do
|
||||
stub_config_setting(host: 'example.com', https: false)
|
||||
expect(connect_src).to eq("'self' ws://example.com")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ ws://example.com")
|
||||
end
|
||||
|
||||
it 'with secure domain' do
|
||||
stub_config_setting(host: 'example.com', https: true)
|
||||
expect(connect_src).to eq("'self' wss://example.com")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ wss://example.com")
|
||||
end
|
||||
|
||||
it 'with custom port' do
|
||||
stub_config_setting(host: 'example.com', port: '1234')
|
||||
expect(connect_src).to eq("'self' ws://example.com:1234")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ ws://example.com:1234")
|
||||
end
|
||||
|
||||
it 'with custom port and secure domain' do
|
||||
stub_config_setting(host: 'example.com', https: true, port: '1234')
|
||||
expect(connect_src).to eq("'self' wss://example.com:1234")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ wss://example.com:1234")
|
||||
end
|
||||
|
||||
it 'when port is included in HTTP_PORTS' do
|
||||
described_class::HTTP_PORTS.each do |port|
|
||||
stub_config_setting(host: 'example.com', https: true, port: port)
|
||||
expect(connect_src).to eq("'self' wss://example.com")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ wss://example.com")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -323,9 +401,9 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
|
||||
it 'does not include CDN host in CSP' do
|
||||
expect(script_src).to eq(::Gitlab::ContentSecurityPolicy::Directives.script_src)
|
||||
expect(style_src).to eq(::Gitlab::ContentSecurityPolicy::Directives.style_src)
|
||||
expect(font_src).to eq("'self'")
|
||||
expect(worker_src).to eq(::Gitlab::ContentSecurityPolicy::Directives.worker_src)
|
||||
expect(style_src).to eq("#{::Gitlab::ContentSecurityPolicy::Directives.style_src} https://gdk.test:3443/vite-dev/")
|
||||
expect(font_src).to eq("'self' https://gdk.test:3443/vite-dev/")
|
||||
expect(worker_src).to eq("#{::Gitlab::ContentSecurityPolicy::Directives.worker_src} https://gdk.test:3443/vite-dev/")
|
||||
expect(frame_src).to eq(::Gitlab::ContentSecurityPolicy::Directives.frame_src)
|
||||
end
|
||||
end
|
||||
|
|
@ -359,7 +437,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
end
|
||||
|
||||
it 'adds new sentry path to CSP' do
|
||||
expect(connect_src).to eq("'self' ws://gitlab.example.com dummy://sentry.example.com")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ ws://gitlab.example.com dummy://sentry.example.com")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -373,7 +451,7 @@ RSpec.describe Gitlab::ContentSecurityPolicy::ConfigLoader, feature_category: :s
|
|||
end
|
||||
|
||||
it 'config is backwards compatible, does not add sentry path to CSP' do
|
||||
expect(connect_src).to eq("'self' ws://gitlab.example.com")
|
||||
expect(connect_src).to eq("'self' wss://gdk.test:3443/vite-dev/ https://gdk.test:3443/vite-dev/ ws://gitlab.example.com")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ RSpec.describe Sidebars::Groups::SuperSidebarMenus::DeployMenu, feature_category
|
|||
expect(items.map(&:class).uniq).to eq([Sidebars::NilMenuItem])
|
||||
expect(items.map(&:item_id)).to eq([
|
||||
:packages_registry,
|
||||
:container_registry
|
||||
:container_registry,
|
||||
:virtual_registry
|
||||
])
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -442,7 +442,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state, feature_catego
|
|||
},
|
||||
{
|
||||
"name" => "release",
|
||||
"script" => [a_string_including("glab -R $CI_PROJECT_PATH release create")],
|
||||
"script" => [a_string_including("glab release create -R $CI_PROJECT_PATH")],
|
||||
"timeout" => 3600,
|
||||
"when" => "on_success",
|
||||
"allow_failure" => false
|
||||
|
|
|
|||
|
|
@ -282,39 +282,4 @@ RSpec.describe ApplicationController, type: :request, feature_category: :shared
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when configuring vite' do
|
||||
let(:vite_hmr_ws_origin) { 'ws://gitlab.example.com:3808' }
|
||||
let(:vite_origin) { 'http://gitlab.example.com:3808' }
|
||||
|
||||
before do
|
||||
# rubocop:disable RSpec/AnyInstanceOf -- Doesn't work with allow_next_instance_of
|
||||
allow_any_instance_of(ViteHelper)
|
||||
.to receive_messages(
|
||||
vite_enabled?: vite_enabled,
|
||||
vite_hmr_ws_origin: vite_hmr_ws_origin,
|
||||
vite_origin: vite_origin,
|
||||
universal_path_to_stylesheet: '')
|
||||
# rubocop:enable RSpec/AnyInstanceOf
|
||||
end
|
||||
|
||||
context 'when vite enabled during development' do
|
||||
let(:vite_enabled) { true }
|
||||
|
||||
it 'adds vite csp' do
|
||||
get root_path
|
||||
expect(response.headers['Content-Security-Policy']).to include("#{vite_hmr_ws_origin}/vite-dev/")
|
||||
expect(response.headers['Content-Security-Policy']).to include("#{vite_origin}/vite-dev/")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when vite is disabled' do
|
||||
let(:vite_enabled) { false }
|
||||
|
||||
it "doesn't add vite csp" do
|
||||
get root_path
|
||||
expect(response.headers['Content-Security-Policy']).not_to include('/vite-dev/')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ module NavbarStructureHelper
|
|||
)
|
||||
end
|
||||
|
||||
def insert_virtual_registry_nav
|
||||
insert_after_sub_nav_item(
|
||||
_('Package registry'),
|
||||
within: _('Deploy'),
|
||||
new_sub_nav_item_name: _('Virtual registry')
|
||||
)
|
||||
end
|
||||
|
||||
def insert_google_artifact_registry_nav
|
||||
insert_after_sub_nav_item(
|
||||
_('Container registry'),
|
||||
|
|
|
|||
292
yarn.lock
292
yarn.lock
|
|
@ -3223,181 +3223,181 @@
|
|||
dom-accessibility-api "^0.5.1"
|
||||
pretty-format "^26.4.2"
|
||||
|
||||
"@tiptap/core@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.13.0.tgz#5ad9a1f3980cee1379493b3c0c94234a2a716808"
|
||||
integrity sha512-VDwlf5+DznkrAT+vlggl9t/mPVeo3ayi4irXgLPkDfHAY8adVIx+RCek6GBChclJ8q2Iy0HZwpIYs/8L7tadqA==
|
||||
"@tiptap/core@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.14.0.tgz#9a0ffd500cc720194916475506292006d2cb69c3"
|
||||
integrity sha512-MBSMzGYRFlwYCocvx3dU7zpCBSDQ0qWByNtStaEzuBUgzCJ6wn2DP/xG0cMcLmE3Ia0VLM4nwbLOAAvBXOtylA==
|
||||
|
||||
"@tiptap/extension-blockquote@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.13.0.tgz#1c5f12f88298a43e03718f0fd81485e4589fe548"
|
||||
integrity sha512-DQjd8AUG9TE+ReFp05T9N5Z+o4dw88j+O6xgPMjs1T1u9yHUMxwjvidPRi5QqCqPiwDJcqm3bjYxebPPMHbi9w==
|
||||
"@tiptap/extension-blockquote@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.14.0.tgz#e5a660f0afc2b372da4c21545517e74d2c770b68"
|
||||
integrity sha512-AwqPP0jLYNioKxakiVw0vlfH/ceGFbV+SGoqBbPSGFPRdSbHhxHDNBlTtiThmT3N2PiVwXAD9xislJV+WY4GUA==
|
||||
|
||||
"@tiptap/extension-bold@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.13.0.tgz#ed552467b7e4c598c94e4be234644d4a6e9f1f6f"
|
||||
integrity sha512-q/Kqo1HXas+dUevP/Qice+nbxXue8ZpmYBniw9zt/JHbgwH1b6Rw7lIjLxYerdaPWj305h9ZHxLqmzDOEcQRPw==
|
||||
"@tiptap/extension-bold@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.14.0.tgz#53655a81fd11304a83cc654fc4071e519170674d"
|
||||
integrity sha512-8DWwelH55H8KtLECSIv0wh8x/F/6lpagV/pMvT+Azujad0oqK+1iAPKU/kLgjXbFSkisrpV6KSwQts5neCtfRQ==
|
||||
|
||||
"@tiptap/extension-bubble-menu@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.13.0.tgz#4ad8e4ccc45c8e7a90e7f04e099f2c23aa214a99"
|
||||
integrity sha512-y2PRg7YT8Km1e4+xEvXcKTPfEu/i44eKNjbsKojgs70kuONdhFmhWIXCeGEVAwPH8ZPH+JPam5kcW2vsihoayg==
|
||||
"@tiptap/extension-bubble-menu@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.14.0.tgz#d51752558323c89ed45ca0118265b9a9e4226166"
|
||||
integrity sha512-sN15n0RjPh+2Asvxs7l47hVEvX6c0aPempU8QQWcPUlHoGf1D/XkyHXy6GWVPSxZ5Rj5uAwgKvhHsG/FJ/YGKQ==
|
||||
dependencies:
|
||||
tippy.js "^6.3.7"
|
||||
|
||||
"@tiptap/extension-bullet-list@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.13.0.tgz#6392a0970ab39db9dffdd6d6b58f3a5689ed8ee2"
|
||||
integrity sha512-7XMNtRFCC+Co7FrwyU5gUC+4i3k83kQb1KJcYjq6Deud/2DoXNJeRcacqXr+mNePm+W71xZJ7W7ePWLlHrfHPw==
|
||||
"@tiptap/extension-bullet-list@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.14.0.tgz#9be7a09f792320a1baf33daf3dd88d149b9cd2c2"
|
||||
integrity sha512-SWnL4bP8Mm/mWN42AMQNoqYE0V6LgSBTVsHwwAki2wIUQdr9HyoAnohvHy3IME56NMwoyZyo+Mzl45wOqUxziA==
|
||||
|
||||
"@tiptap/extension-code-block-lowlight@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.13.0.tgz#088ed24a1e3ecc93c33dc698b506851ed517b97e"
|
||||
integrity sha512-8iIbOLUpkhYBgovxauDVwr52IRdMpE4hFKt1ktSvnC8jpT+I01vaWojew8qU3A/5ZVNe7EkBEXHG0B0gvzCznA==
|
||||
"@tiptap/extension-code-block-lowlight@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.14.0.tgz#2ccf83471012d8b27b6002154c0b53802061858f"
|
||||
integrity sha512-jGcVOkcThwzLdXf56zYkmB0tcB8Xy3S+ImS3kDzaccdem6qCG05JeE33K8bfPqh99OU1QqO9XdHNO9x77A2jug==
|
||||
|
||||
"@tiptap/extension-code-block@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.13.0.tgz#8c7cf37baefdca16896584cd7ba951673e480488"
|
||||
integrity sha512-HGcWmwKx4D53HY4XO0ve6lNHirpWdd91AVGVPuIkyG0wSvAzBgXy4TIgyUrHxGRE4qvNAe685RCYIA/VMBJAyg==
|
||||
"@tiptap/extension-code-block@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.14.0.tgz#7a1f85388bed7ab3f5e98f9121c7c9bebf7b7583"
|
||||
integrity sha512-LRYYZeh8U2XgfTsJ4houB9s9cVRt7PRfVa4MaCeOYKfowVOKQh67yV5oom8Azk9XrMPkPxDmMmdPAEPxeVYFvw==
|
||||
|
||||
"@tiptap/extension-code@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.13.0.tgz#67665a018703322e9ad137cb54060001e00dc48f"
|
||||
integrity sha512-TrMhcRKsxmpZyHEstMdXAjwe+3bTqsSdiLsotzV7LwO8eCyeN42xfm2yzz9h/SJGUsO1X70YhoLH8liBNif97A==
|
||||
"@tiptap/extension-code@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.14.0.tgz#cbbc58f073478e70b2217e50fb7aad23b7ace88c"
|
||||
integrity sha512-kyo02mnzqgwXayMcyRA/fHQgb+nMmQQpIt1irZwjtEoFZshA7NnY/6b5SJmRcxQ4/X4r2Y2Ha2sWmOcEkLmt4A==
|
||||
|
||||
"@tiptap/extension-document@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.13.0.tgz#cde03f03229dcbfc4a322a77e2c282640271f5c6"
|
||||
integrity sha512-jZ9NRUtJ2g67XTFgfn/6hMz+N4XJ+kG0/4GeLU2B7ZMF3UdcMWTvgyLxUNXvuLgMbCATVFTDTZiYTXgB/nPAVw==
|
||||
"@tiptap/extension-document@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.14.0.tgz#9f631caa8b9a3d5cc448ffaf32e3a22326d87ef1"
|
||||
integrity sha512-qwEgpPIJ3AgXdEtRTr88hODbXRdt14VAwLj27PTSqexB5V7Ra1Jy7iQDhqRwBCoUomVywBsWYxkSuDisSRG+9w==
|
||||
|
||||
"@tiptap/extension-dropcursor@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.13.0.tgz#cdb09cb4d59ffdb574eb56deb37b1f2aca421fb0"
|
||||
integrity sha512-nx2PjBiuOb3vBUDKGSzHtfKcWJstaHGr4dx3C+TsJ3Z8qHl4tB9Ud+c7Uaz6/DptK52Q3nn2WAK2mp/njLvXuA==
|
||||
"@tiptap/extension-dropcursor@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.14.0.tgz#8036c782790fc96c17daa1def0e3f03279745058"
|
||||
integrity sha512-FIh5cdPuoPKvZ0GqSKhzMZGixm05ac3hSgqhMNCBZmXX459qBUI9CvDl/uzSnY9koBDeLVV3HYMthWQQLSXl9A==
|
||||
|
||||
"@tiptap/extension-floating-menu@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.13.0.tgz#f9a3f785b1750b5814296a7c6d085d56d938184f"
|
||||
integrity sha512-gF14Nu61QUWWJDxOxzB679uK0W/rWcU7FTn1ll2zGt3NW2P2HheLo6qL1U5Wwxo3YwXloM8KLofdWi6vMN5RQQ==
|
||||
"@tiptap/extension-floating-menu@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.14.0.tgz#8fc400b0d47c2898552ef09bb0952bf3e6142ddb"
|
||||
integrity sha512-Khx7M7RfZlD1/T/PUlpJmao6FtEBa2L6td2hhaW1USflwGJGk0U/ud4UEqh+aZoJZrkot/EMhEvzmORF3nq+xw==
|
||||
dependencies:
|
||||
tippy.js "^6.3.7"
|
||||
|
||||
"@tiptap/extension-gapcursor@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.13.0.tgz#9a8ee0098e8fe111b26a9fdabff02b631deb5bf3"
|
||||
integrity sha512-rOYVPa+mBksgGn+o9KGzKXhPNabSUVxvOB5BBwFhc0pTD+icoNk5awcUNfKx1VcBqXE3ghUvR/EEro9PvwTtdg==
|
||||
"@tiptap/extension-gapcursor@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.14.0.tgz#cc2a9296df36879816b8cb4121f58b0a0b3c415a"
|
||||
integrity sha512-as+SqC39FRshw4Fm1XVlrdSXveiusf5xiC4nuefLmXsUxO7Yx67x8jS0/VQbxWTLHZ6R1YEW8prLtnxGmVLCAQ==
|
||||
|
||||
"@tiptap/extension-hard-break@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.13.0.tgz#087b3cda05080219444a0867e064d69643afc3ed"
|
||||
integrity sha512-1n83Uq7r/x/vJecj46ZO/a+MrAVDYFofz2J1V+N42EindNXaf/c2I9dIR8yxIXH8NT211gd1MckRM43eSSXU0w==
|
||||
"@tiptap/extension-hard-break@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.14.0.tgz#ff1ac6be4c09b85fe01d1a3782360d994b570abc"
|
||||
integrity sha512-A8c8n8881iBq3AusNqibh6Hloybr+FgYdg4Lg4jNxbbEaL0WhyLFge1bWlGVpbHXFqdv5YldMUAu6Rop3FhNvw==
|
||||
|
||||
"@tiptap/extension-heading@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.13.0.tgz#5e75f555b207de50fa1108df27df5c761f1c58fd"
|
||||
integrity sha512-85G/DrrywpMQtZYwZJFwR+2ocRdvUM3FMLO4JVIZA7DyFsMD59lAixSSdBC2yZyr2Y8uzMW/UYyJdE+gpO6k7A==
|
||||
"@tiptap/extension-heading@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.14.0.tgz#c5a9dc761712e9c87073ba8446548cbe4d403360"
|
||||
integrity sha512-vM//6G3Ox3mxPv9eilhrDqylELCc8kEP1aQ4xUuOw7vCidjNtGggOa1ERnnpV2dCa2A9E8y4FHtN4Xh29stXQg==
|
||||
|
||||
"@tiptap/extension-highlight@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.13.0.tgz#f2aac9b7efa6d3f414eddb232f46c6bb3a209ceb"
|
||||
integrity sha512-9ElpTg7VV+hcMXTTUPcq+zaNI/sNTPVNMS9GHqEGnOYABndnlsvcs1VRgAbWuZO6Hor2kKOXOOyrjY987RpyLA==
|
||||
"@tiptap/extension-highlight@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.14.0.tgz#7a40ce7113d369e38d4dfa2d625deda386164b37"
|
||||
integrity sha512-21eouZEuCBFrpGeefnnU9yJ1SH32L9gSlT9MOJXBSXCX5HFskNLdN8Q4cQSyRXSt6r5kEz1GG5a4I805/U2TMQ==
|
||||
|
||||
"@tiptap/extension-history@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.13.0.tgz#3b2089eee52694900532dfe24f9b867b3cf176b9"
|
||||
integrity sha512-0gaCTHYrsECs8ZeG3oX3fyTyvKJHuGUuCnbEBfvGVxlYdhDVyuloe+G3s3UFeVVQJKO/P5OcxvUuAQ06s29tXw==
|
||||
"@tiptap/extension-history@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.14.0.tgz#e7e29427c22845567c0fece8dffc32d2beeecfff"
|
||||
integrity sha512-/qnOHQFCEPfkb3caykqd+sqzEC2gx30EQB/mM7+5kIG7CQy7XXaGjFAEaqzE1xJ783Q2E7GVk4JxWM+3NhYSLw==
|
||||
|
||||
"@tiptap/extension-horizontal-rule@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.13.0.tgz#9d9aebe75e5d399369d7bf082fefb7f637a7efaf"
|
||||
integrity sha512-8gqYh2f7OAgEckIEoqsj98omJSPUOOWUvJRL50q70AUceMvPtVngWGKfITlDy4eYvRj5WXrNRSYrQrCU/8S8sg==
|
||||
"@tiptap/extension-horizontal-rule@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.14.0.tgz#f777c8d9eb945ef50d865a37784fd0dc6c982674"
|
||||
integrity sha512-OrKWgHOhmJtVHjPYaEJetNLiNEvrI85lTrGxzeQa+a8ACb93h4svyHe9J+LHs5pKkXDQFcpYEXJntu0LVLLiDw==
|
||||
|
||||
"@tiptap/extension-image@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.13.0.tgz#1e1b2fc47f35138c8462354f6724be96f3a67db0"
|
||||
integrity sha512-FT/Lpr81fFMfeT4Juk8c29XxUVBEgi7CR1E/w4Z5rtcQxVUNbS00QMFktuHL3Fbh/8PaUcOZtFCkZkrVP2iQmw==
|
||||
"@tiptap/extension-image@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.14.0.tgz#151c96c302b0f30dd4fe90fb5ec637531264e40e"
|
||||
integrity sha512-pYCUzZBgsxIvVGTzuW03cPz6PIrAo26xpoxqq4W090uMVoK0SgY5W5y0IqCdw4QyLkJ2/oNSFNc2EP9jVi1CcQ==
|
||||
|
||||
"@tiptap/extension-italic@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.13.0.tgz#143591ed04b5fba52bff12a80e6bf557afe9bfaf"
|
||||
integrity sha512-eI5TtUgfvwIkj576pWWgHQjRItEe7IWG+t/ZyRDMo5eTxz5b6lqsJydexmJbdL/u25kMgtwd39v7QEu0OiPcVg==
|
||||
"@tiptap/extension-italic@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.14.0.tgz#b250dc6d95ba15e73f37230f07bacb0946fc042d"
|
||||
integrity sha512-yEw2S+smoVR8DMYQMAWckVW2Sstf7z5+GBZ8zm8NMGhMKb1JFCPZUv5KTTIPnq7ZrKuuZHvjN9+Ef1dRYD8T2A==
|
||||
|
||||
"@tiptap/extension-link@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.13.0.tgz#a1bf3ada603a8a7adf9811cd376a5f308759818e"
|
||||
integrity sha512-kYCuf23Do1IWVmqDuD7V9K1nW0pEj1hUEtkGrZoGMYTcph9csYUT6gpwaflOYe66A7fVdz36gSKZjO+7XznWNg==
|
||||
"@tiptap/extension-link@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-link/-/extension-link-2.14.0.tgz#c858eb67eb9c651b804fae5d324f30431884e75f"
|
||||
integrity sha512-fsqW7eRD2xoD6xy7eFrNPAdIuZ3eicA4jKC45Vcft/Xky0DJoIehlVBLxsPbfmv3f27EBrtPkg5+msLXkLyzJA==
|
||||
dependencies:
|
||||
linkifyjs "^4.2.0"
|
||||
|
||||
"@tiptap/extension-list-item@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.13.0.tgz#77dea61478c1ce15db9801b3895c20c78df64267"
|
||||
integrity sha512-EDq4Xm/dPjvEhdnaVJlsjV/qjdNtCCFAgO7eBzHvXP14b8feVmqJdDGs6sEZznAGxDe9gjRNsDj9EABEkQK8Dg==
|
||||
"@tiptap/extension-list-item@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.14.0.tgz#da5c9d1747ece9dcbf0abdb0f85ad18857f76d35"
|
||||
integrity sha512-t1jXDPEd82sC6vZVE/12/CB52uuiydCIcRfwdh21xNgBMckToKO9S0K6XEp4ROtrKQdlIH2JDVPfpUBvVrYN8Q==
|
||||
|
||||
"@tiptap/extension-ordered-list@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.13.0.tgz#4d4c80a62ec7b32145f9fa16462ae7b1a4e73179"
|
||||
integrity sha512-krHS6lRf3R+m2eNpgHIfqI/A4CcbNoAWyQFH7HXkSuh0bgO7Aq1zi39clVcYfnI7eFGy07Lx8dQZ2kTLQ/n4Lg==
|
||||
"@tiptap/extension-ordered-list@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.14.0.tgz#53e8fae14f40c19b0c72d1afa8a772285fe021dd"
|
||||
integrity sha512-QUZcyuW9AKvSfpFHcGmbyRCqxcpY0VNf0xipEtogxbA+JDDw3ZSPqU1dUgz9wk00RahPTwNDdY5aVjdQ5N4N9Q==
|
||||
|
||||
"@tiptap/extension-paragraph@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.13.0.tgz#dc4aba50dbd5f691461fa223b47883e3b72c4425"
|
||||
integrity sha512-icWUqD6j3VCIW8RTFoknBDSOwWruraJwSwfUKadEoplRSIz81J75cmoExscO8rHMD8juhIrKGbs5WD5WJajrpQ==
|
||||
"@tiptap/extension-paragraph@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.14.0.tgz#9116a961b618c27974bb95cbfc40f7630c9e20e7"
|
||||
integrity sha512-bsQesVpgvDS2e+wr2fp59QO7rWRp2FqcJvBafwXS3Br9U5Mx3eFYryx4wC7cUnhlhUwX5pmaoA7zISgV9dZDgg==
|
||||
|
||||
"@tiptap/extension-strike@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.13.0.tgz#c95a90cb0dddf36865fb6aa6e98f03489c719322"
|
||||
integrity sha512-1j7ho8vuldtLuSHl4oQUNbVtub3P/mR8b+DbK+Fmp9UZrd5moHP9sD3yeIP4Ms/7iJhdDgLJPKAKNqmA2fsOtg==
|
||||
"@tiptap/extension-strike@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.14.0.tgz#6c6c802b1e8ffbad22ee4aec9d8068664d4dc2ea"
|
||||
integrity sha512-rD5d/IL3XPfBOrHRHxt+b+0X1jbIbWONGiad/3sX0ZYQD3PandtCWboH40r/J5tFksebuY12dVYyYQKgLpDBOQ==
|
||||
|
||||
"@tiptap/extension-subscript@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-subscript/-/extension-subscript-2.13.0.tgz#deaf6308f89cbcc7b87c1883b4703b389c1c5522"
|
||||
integrity sha512-C//oecgSn8MNle7k39dSo3z8glNO+4XHX2evZmv6s5NP+88OIZC2GeU4pwKhUFozY8MX3jBWUey1c12IDih/iQ==
|
||||
"@tiptap/extension-subscript@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-subscript/-/extension-subscript-2.14.0.tgz#b38f4dea8744ff0c698c2917bba8a5f0541a8db6"
|
||||
integrity sha512-1gQucSZ6WqhKukc8YA7ZfQzBYaVY00F6G7+trD2iWSm6EpiabaUVP0vMjuonIiujTioEwe04KmZuC9ZLbEU9dQ==
|
||||
|
||||
"@tiptap/extension-superscript@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-superscript/-/extension-superscript-2.13.0.tgz#2868aef3aa3c1a2547d387680841ead32bbcd217"
|
||||
integrity sha512-uBeyMqMgHBNDF6GlV7GNajbm7jo0DjCPzgNEKTBh1khJJIyRz4y4O+GiVzApNJmrRnYBAcWWIPbPuwpOcncf5Q==
|
||||
"@tiptap/extension-superscript@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-superscript/-/extension-superscript-2.14.0.tgz#2e3121ef2c0df2fe5027a69160a107c993da1a47"
|
||||
integrity sha512-BnsqY9TxN15KxxoX1rulL0sV0Wu3umD4Un0E90LZ5G/QRrVUeohAuOiraqRJ4GnJPVJBR2H0+7Sg5sKqYuIpnQ==
|
||||
|
||||
"@tiptap/extension-table-cell@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.13.0.tgz#5b6f4fcc5c896d32569279d7015637607ba5c131"
|
||||
integrity sha512-7AAKYz0UpJNu7KMVZL+WvPing3LINXfv4+x+hPG5dnRuPjYH4lX5fUhTKoQ9alk1QSxyppX6vB5Z+/PC7gdogw==
|
||||
"@tiptap/extension-table-cell@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.14.0.tgz#fb28edd5a9f498c7aba851e38dd6e4a7d8663a41"
|
||||
integrity sha512-DkSNAAkMI/ymPgO8y8Gv0MDVcbd2gk7xrSyicIDNoDFFXp15VasInGW8mvyM+CgvlurGB2N+PkYncPtfb4XNuQ==
|
||||
|
||||
"@tiptap/extension-table-header@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.13.0.tgz#dc631ac43b7256096fd9aeba022d5a6da309566f"
|
||||
integrity sha512-ItnZvVYBWwYXBOw2ubGF037nrw7IODuCCbhAReeaI0DNTgpLeWXEkWbfoiT8gJ1ghlnsxbTCSrj+29/GEclq7g==
|
||||
"@tiptap/extension-table-header@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.14.0.tgz#10cce2a4b3ace59ffb1734549a09fd13ecd5440d"
|
||||
integrity sha512-wX6/+t0iCo3KrqK2OjK0vbFeL76Pq+VpobGt+oM8lcxsENnsa6a0s3wdd1QEVLVPlj+WMFQggAG80Rf17+iDxA==
|
||||
|
||||
"@tiptap/extension-table-row@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.13.0.tgz#99ed273c6493bfb68d55e4c15893336d931bbbea"
|
||||
integrity sha512-rV1RVwYACDKRSfygMXVkxiBGW68Gkwop8/SiqXWB2Y/LcQlxgi+S9OsvpouDzIG3H66rqahIXTVbkSO4XuYnyw==
|
||||
"@tiptap/extension-table-row@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.14.0.tgz#5154a05d199c0728470a99079ca56aae74753a75"
|
||||
integrity sha512-a1GvCIju9xETIQu664lVQNftHqpPdRmwYp+1QzY82v3zHClso+tTLPeBSlbDdUscSmv3yZXgGML20IiOoR2l2Q==
|
||||
|
||||
"@tiptap/extension-table@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.13.0.tgz#bab797d2d63b513d9b986fbde3ba2a974a42ad92"
|
||||
integrity sha512-f5wYkfJt1T0QVdzSrUsR12Vla2IeJA0UsBgltTfHznLEMzo8asEOn7rgp/YKx4SYgaQfA9L8eqOpoz2kX6e9mA==
|
||||
"@tiptap/extension-table@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.14.0.tgz#b7541351518c2cf7d5bae72640d6d3375b29c677"
|
||||
integrity sha512-X/wH3XKxi5+G7cB+lHt3fPMWIJ30IBkzrJZYapJ8d4p2JxMNIU1Nyu+8K6204d0hF6SVWY8hvb/Jq/WgHtoCFA==
|
||||
|
||||
"@tiptap/extension-task-item@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.13.0.tgz#efe20ccbbc6f7f8787d758741df354d4b7b90bb7"
|
||||
integrity sha512-w8GYuQL82i99J/iFaJJiGm4LGL0lfGQwnb8Z0Mi4MrpCqz9Xy5WwgtgD5oDS2ym4aua3w94dpUFPJ+yfAd222Q==
|
||||
"@tiptap/extension-task-item@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.14.0.tgz#5d576c5486dba9d4475456a3f1dc4e614b6b3ac0"
|
||||
integrity sha512-MFE928s1J2ACyjOlkx52D/+r6aqz6c516C0tvnP2vzrkijFaSMNY4Xg7L1wTinzIdijh184AYQpyw7LezJa1ug==
|
||||
|
||||
"@tiptap/extension-task-list@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.13.0.tgz#931ef7ac570ec5bb928adf491173f57cb921c17d"
|
||||
integrity sha512-L8ogRow1bjkb4s/Sh42OpzN8sKWRMNDGBoD0jCpRcnRdIsOT+hPhgZfE1GLFwy6+rBYERCHrCQYO0GDBfr+IiA==
|
||||
"@tiptap/extension-task-list@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.14.0.tgz#a61ae92ac3e534ff192f57c88dd8bbb14ca9fc11"
|
||||
integrity sha512-o2VELXgkDIHS15pnF1W2OFfxZGvo9V6RcwjzCYUS0mqMF9TTbfHwddRcv4t3pifpMO3sWhspVARavJAGaP5zdQ==
|
||||
|
||||
"@tiptap/extension-text@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.13.0.tgz#491654249f608e0ea48c88abe70ac069bd32bbe8"
|
||||
integrity sha512-4VhKCxfOlBXbaRAM4/OtZnOeq+WcjJoaduo4Jrb7w1I18yqknVjOwFH3Cv+2VqIjPI8bILMWzgpxvFPKdrOllA==
|
||||
"@tiptap/extension-text@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.14.0.tgz#2b57f47917e97b6c06774b7eaf8598973536a29e"
|
||||
integrity sha512-rHny566nGZHq61zRLwQ9BPG55W/O+eDKwUJl+LhrLiVWwzpvAl9QQYixtoxJKOY48VK41PKwxe3bgDYgNs/Fhg==
|
||||
|
||||
"@tiptap/pm@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.13.0.tgz#a635940264f07898ec8c59afff7ffe3d8989379b"
|
||||
integrity sha512-CU2DEMQbYXwlnISFD0+7nQSBaqGKJHOiTnWpnAi5pOVphroEIbLPlT6AO4638MWtIR9QsOmGR8KXkQmNGA6tjQ==
|
||||
"@tiptap/pm@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/pm/-/pm-2.14.0.tgz#bc097936a6e4da90dbfe05ff55f8041ff6dda238"
|
||||
integrity sha512-cnsfaIlvTFCDtLP/A2Fd3LmpttgY0O/tuTM2fC71vetONz83wUTYT+aD9uvxdX0GkSocoh840b0TsEazbBxhpA==
|
||||
dependencies:
|
||||
prosemirror-changeset "^2.3.0"
|
||||
prosemirror-collab "^1.3.1"
|
||||
|
|
@ -3418,18 +3418,18 @@
|
|||
prosemirror-transform "^1.10.2"
|
||||
prosemirror-view "^1.37.0"
|
||||
|
||||
"@tiptap/suggestion@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.13.0.tgz#6654b060d7714b95c67a2a6d5d30b9005fa0a693"
|
||||
integrity sha512-O+Mwz4vjEURPBYrdlNdelt6Ml6dToZFE+yxXX/sl9UyUiWmej3WAytvIJguycX5RsYGO/Zgan3gk/UCMojY/Kw==
|
||||
"@tiptap/suggestion@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.14.0.tgz#3170dcf837f9261ecda7c55757ded6c99c977e7c"
|
||||
integrity sha512-AXzEw0KYIyg5id8gz5geIffnBtkZqan5MWe29rGo3gXTfKH+Ik8tWbZdnlMVheycsUCllrymDRei4zw9DqVqkQ==
|
||||
|
||||
"@tiptap/vue-2@^2.13.0":
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.13.0.tgz#6237cf2fd211619b59e70c0f0381b70d9d3718c4"
|
||||
integrity sha512-g/qJXHClUVaKxKQhPUKkww/CcRl6zyxYRz64RBxJz78ErPku/tpfvyb/bn+xpoQzmYfDVh/zKv0r7qzk9MLseQ==
|
||||
"@tiptap/vue-2@^2.14.0":
|
||||
version "2.14.0"
|
||||
resolved "https://registry.yarnpkg.com/@tiptap/vue-2/-/vue-2-2.14.0.tgz#c441eaf789848a2491d32265e2bcbb2f5f370170"
|
||||
integrity sha512-BD5cRLmQSuEQiMXQ4ngbytZa3D6y8ofc4tBRPHxXKU6bp2CA3Gec6g7LqSiIIeUv/i5r2xfWol7aajypZJL85g==
|
||||
dependencies:
|
||||
"@tiptap/extension-bubble-menu" "^2.13.0"
|
||||
"@tiptap/extension-floating-menu" "^2.13.0"
|
||||
"@tiptap/extension-bubble-menu" "^2.14.0"
|
||||
"@tiptap/extension-floating-menu" "^2.14.0"
|
||||
vue-ts-types "1.6.2"
|
||||
|
||||
"@tootallnate/once@2":
|
||||
|
|
|
|||
Loading…
Reference in New Issue