Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2023-10-25 15:12:22 +00:00
parent df65129ab4
commit f8888a274f
84 changed files with 347 additions and 451 deletions

View File

@ -1,6 +1,6 @@
include:
- project: gitlab-org/quality/pipeline-common
ref: 7.7.0
ref: 7.8.0
file:
- /ci/danger-review.yml

View File

@ -277,11 +277,7 @@ export default {
>
{{ saveText }}
</gl-button>
<gl-button
:type="cancelButtonType"
data-qa-selector="cancel_badge_button"
@click="handleCancel"
>
<gl-button :type="cancelButtonType" @click="handleCancel">
{{ __('Cancel') }}
</gl-button>
</div>

View File

@ -43,7 +43,6 @@ export default {
<div
class="board-add-new-list board gl-display-inline-block gl-h-full gl-vertical-align-top gl-white-space-normal gl-flex-shrink-0 gl-rounded-base gl-px-3"
data-testid="board-add-new-column"
data-qa-selector="board_add_new_list"
>
<div
class="board-inner gl-display-flex gl-flex-direction-column gl-relative gl-h-full gl-rounded-base gl-bg-gray-50"

View File

@ -38,7 +38,7 @@ export default {
<template #control> </template>
<template #candidate>
<div v-if="canShowCreateButton" class="gl-ml-1 gl-mr-3 gl-display-flex gl-align-items-center">
<gl-button data-qa-selector="new_board_button" @click.prevent="showDialog">
<gl-button @click.prevent="showDialog">
{{ createButtonText }}
</gl-button>
</div>

View File

@ -386,7 +386,6 @@ export default {
icon="remove"
size="small"
:aria-label="$options.i18n.deleteButton"
data-qa-selector="delete_ci_variable_button"
/>
<gl-modal
ref="modal"

View File

@ -132,7 +132,6 @@ export default {
<template>
<div
class="ci-job-component gl-display-flex gl-align-items-center gl-justify-content-space-between"
data-qa-selector="job_item_container"
>
<gl-link
v-if="hasDetails"

View File

@ -371,11 +371,7 @@ export default {
</gl-form-group>
<!--Variable List-->
<gl-form-group class="gl-mb-0" :label="$options.i18n.variables">
<div
v-for="(variable, index) in variables"
:key="`var-${index}`"
data-qa-selector="ci_variable_row_container"
>
<div v-for="(variable, index) in variables" :key="`var-${index}`">
<div
v-if="!variable.destroy"
class="gl-display-flex gl-align-items-stretch gl-flex-direction-column gl-md-flex-direction-row gl-mb-3 gl-pb-2"

View File

@ -180,17 +180,11 @@ export default {
data-confirm-btn-variant="danger"
rel="nofollow"
data-testid="trigger_revoke_button"
data-qa-selector="trigger_revoke_button"
:href="item.projectTriggerPath"
/>
</template>
</gl-table>
<div
v-else
class="gl-new-card-empty gl-px-5 gl-py-4"
data-testid="no_triggers_content"
data-qa-selector="no_triggers_content"
>
<div v-else class="gl-new-card-empty gl-px-5 gl-py-4" data-testid="no_triggers_content">
{{ s__('Pipelines|No triggers have been created yet. Add one using the form above.') }}
</div>
</div>

View File

@ -142,7 +142,6 @@ export default {
ref="freezeStartCron"
v-model="freezeStartCron"
class="gl-font-monospace!"
data-qa-selector="deploy_freeze_start_field"
:placeholder="$options.i18n.cronPlaceholder"
:state="freezeStartCronState"
autofocus
@ -160,7 +159,6 @@ export default {
id="deploy-freeze-end"
v-model="freezeEndCron"
class="gl-font-monospace!"
data-qa-selector="deploy_freeze_end_field"
:placeholder="$options.i18n.cronPlaceholder"
:state="freezeEndCronState"
trim

View File

@ -63,7 +63,7 @@ export default {
title: s__('DesignManagement|Are you sure you want to archive the selected designs?'),
actionPrimary: {
text: s__('DesignManagement|Archive designs'),
attributes: { variant: 'confirm', 'data-qa-selector': 'confirm_archiving_button' },
attributes: { variant: 'confirm', 'data-testid': 'confirm-archiving-button' },
},
actionCancel: {
text: __('Cancel'),

View File

@ -293,7 +293,6 @@ export default {
<ul
class="design-discussion bordered-box gl-relative gl-p-0 gl-list-style-none"
:class="{ 'gl-bg-blue-50': isDiscussionActive }"
data-qa-selector="design_discussion_content"
data-testid="design-discussion-content"
>
<design-note

View File

@ -129,8 +129,6 @@ export default {
this.isEditing = true;
},
extraAttrs: {
'data-testid': 'delete-note-button',
'data-qa-selector': 'delete_design_note_button',
class: 'gl-sm-display-none!',
},
},
@ -140,8 +138,6 @@ export default {
this.$emit('delete-note', this.note);
},
extraAttrs: {
'data-testid': 'delete-note-button',
'data-qa-selector': 'delete_design_note_button',
class: 'gl-text-red-500!',
},
},
@ -311,7 +307,6 @@ export default {
v-gl-tooltip.hover
icon="ellipsis_v"
category="tertiary"
data-qa-selector="design_discussion_actions_ellipsis_dropdown"
text-sr-only
:title="$options.i18n.moreActionsLabel"
:aria-label="$options.i18n.moreActionsLabel"
@ -322,12 +317,7 @@ export default {
</div>
</div>
<template v-if="!isEditing">
<div
v-safe-html="note.bodyHtml"
class="note-text md"
data-qa-selector="note_content"
data-testid="note-text"
></div>
<div v-safe-html="note.bodyHtml" class="note-text md" data-testid="note-text"></div>
<slot name="resolved-status"></slot>
</template>
<design-note-awards-list

View File

@ -221,7 +221,7 @@ export default {
class="note-textarea js-gfm-input js-autosize markdown-area"
dir="auto"
data-supports-quick-actions="false"
data-qa-selector="note_textarea"
data-testid="note-textarea"
:aria-label="__('Description')"
:placeholder="__('Write a comment…')"
@input="handleInput"
@ -243,7 +243,7 @@ export default {
variant="confirm"
type="submit"
data-track-action="click_button"
data-qa-selector="save_comment_button"
data-testid="save-comment-button"
@click="submitForm"
>
{{ buttonText }}

View File

@ -272,7 +272,7 @@ export default {
role="button"
:aria-label="$options.i18n.newCommentButtonLabel"
class="gl-absolute gl-w-full gl-h-full gl-p-0 gl-top-0 gl-left-0 gl-outline-0! btn-transparent gl-hover-cursor-crosshair"
data-qa-selector="design_image_button"
data-testid="design-image-button"
@mouseup="onAddCommentMouseup"
></button>

View File

@ -144,12 +144,17 @@ export default {
:name="icon.name"
:size="16"
:class="icon.classes"
data-qa-selector="design_status_icon"
data-testid="design-status-icon"
:data-qa-status="icon.name"
/>
</span>
</div>
<gl-intersection-observer class="gl-flex-grow-1" @appear="onAppear">
<gl-intersection-observer
class="gl-flex-grow-1"
data-testid="design-image"
:data-qa-filename="filename"
@appear="onAppear"
>
<gl-loading-icon v-if="showLoadingSpinner" size="lg" />
<gl-icon
v-else-if="showImageErrorIcon"
@ -162,8 +167,6 @@ export default {
:src="imageLink"
:alt="filename"
class="gl-display-block gl-mx-auto gl-max-w-full gl-max-h-full gl-w-auto design-img"
data-qa-selector="design_image"
:data-qa-filename="filename"
:data-testid="`design-img-${id}`"
@load="onImageLoad"
@error="onImageError"
@ -171,11 +174,13 @@ export default {
</gl-intersection-observer>
</div>
<div class="card-footer gl-display-flex gl-w-full gl-bg-white gl-py-3 gl-px-4">
<div class="gl-display-flex gl-flex-direction-column str-truncated-100">
<div
class="gl-display-flex gl-flex-direction-column str-truncated-100"
data-testid="design-file-name"
>
<span
v-gl-tooltip
class="gl-font-weight-semibold str-truncated-100"
data-qa-selector="design_file_name"
:data-testid="`design-img-filename-${id}`"
:title="filename"
>{{ filename }}</span

View File

@ -402,7 +402,7 @@ export default {
button-variant="default"
button-class="gl-mr-3"
button-size="small"
data-qa-selector="archive_button"
data-testid="archive-button"
:loading="loading"
:has-selected-designs="hasSelectedDesigns"
@delete-selected-designs="mutate()"
@ -490,7 +490,7 @@ export default {
:checked="isDesignSelected(design.filename)"
type="checkbox"
class="design-checkbox gl-absolute gl-top-4 gl-left-6 gl-ml-2"
data-qa-selector="design_checkbox"
data-testid="design-checkbox"
:data-qa-design="design.filename"
@change="changeSelectedDesigns(design.filename)"
/>
@ -506,7 +506,7 @@ export default {
:class="{ 'design-list-item': !isDesignListEmpty }"
:display-as-card="hasDesigns"
v-bind="$options.dropzoneProps"
data-qa-selector="design_dropzone_content"
data-testid="design-dropzone-content"
@change="onUploadDesign"
@error="onDesignDropzoneError"
>

View File

@ -147,12 +147,7 @@ export default {
</div>
</div>
<template v-for="(model, i) in sortedEnvironments">
<environment-item
:key="`environment-item-${i}`"
:model="model"
:table-data="tableData"
data-qa-selector="environment_item"
/>
<environment-item :key="`environment-item-${i}`" :model="model" :table-data="tableData" />
<div
v-if="shouldRenderDeployBoard(model)"
@ -185,7 +180,6 @@ export default {
:key="`environment-row-${i}-${index}`"
:model="child"
:table-data="tableData"
data-qa-selector="environment_item"
/>
<div

View File

@ -196,7 +196,7 @@ export default {
text: __('Create issue'),
action: this.createIssue,
extraAttrs: {
'data-qa-selector': 'create_issue_button',
'data-testid': 'create-issue-button',
},
};
},
@ -309,7 +309,7 @@ export default {
<div
v-if="!loadingStacktrace && stacktrace"
class="gl-my-auto gl-text-truncate"
data-qa-selector="reported_text"
data-testid="reported-text"
>
<gl-sprintf :message="__('Reported %{timeAgo} by %{reportedBy}')">
<template #reportedBy>
@ -367,7 +367,7 @@ export default {
category="primary"
variant="confirm"
:loading="issueCreationInProgress"
data-qa-selector="create_issue_button"
data-testid="create-issue-button"
@click="createIssue"
>
{{ __('Create issue') }}

View File

@ -206,7 +206,6 @@ export default {
v-gl-modal="'configure-feature-flags'"
variant="confirm"
category="secondary"
data-qa-selector="configure_feature_flags_button"
data-testid="ff-configure-button"
class="gl-mb-0 gl-mr-3"
>

View File

@ -57,7 +57,6 @@ export default {
icon="ellipsis_v"
no-caret
:data-testid="`group-${group.id}-dropdown-button`"
data-qa-selector="group_dropdown_button"
:data-qa-group-id="group.id"
>
<gl-dropdown-item

View File

@ -260,11 +260,7 @@ export default {
>
<gl-dropdown-divider />
</template>
<div
v-if="hasUserTransferLocations"
data-qa-selector="namespaces_list_users"
data-testid="user-transfer-locations"
>
<div v-if="hasUserTransferLocations" data-testid="user-transfer-locations">
<gl-dropdown-section-header>{{ $options.i18n.USERS }}</gl-dropdown-section-header>
<gl-dropdown-item
v-for="item in userTransferLocations"

View File

@ -78,7 +78,6 @@ export default {
@input="setFilter({ organization_login: $event })"
/>
<gl-search-box-by-click
data-qa-selector="githubish_import_filter_field"
name="filter"
:disabled="isNameFilterDisabled"
:value="nameFilter"

View File

@ -155,7 +155,6 @@ export default {
<slot name="actions"></slot>
<form v-if="filterable" class="gl-ml-auto" novalidate @submit.prevent>
<gl-search-box-by-click
data-qa-selector="githubish_import_filter_field"
name="filter"
:placeholder="__('Filter by name')"
autofocus

View File

@ -115,7 +115,6 @@ export default {
:search-placeholder="$options.i18n.searchPlaceholder"
:no-results-text="$options.i18n.emptySearchResult"
data-testid="project-select-dropdown"
data-qa-selector="project_select_dropdown"
class="gl-collapsible-listbox-w-full"
@search="searchTerm = $event"
@select="selectProject"

View File

@ -79,7 +79,6 @@ export default {
:autocomplete-data-sources="autocompleteDataSources"
supports-quick-actions
autofocus
data-qa-selector="description_field"
@input="$emit('input', $event)"
@keydown.meta.enter="saveIssuable"
@keydown.ctrl.enter="saveIssuable"

View File

@ -44,12 +44,7 @@ export default {
<template>
<list-item v-bind="$attrs">
<template #left-primary>
<router-link
class="gl-text-body gl-font-weight-bold"
data-testid="details-link"
data-qa-selector="registry_image_content"
:to="linkTo"
>
<router-link class="gl-text-body gl-font-weight-bold" data-testid="details-link" :to="linkTo">
{{ item.name }}
</router-link>
<clipboard-button

View File

@ -167,7 +167,7 @@ export default {
<gl-tabs>
<gl-tab :title="__('Detail')">
<div data-qa-selector="package_information_content">
<div>
<package-history :package-entity="packageEntity" :project-name="projectName" />
<terraform-installation />
</div>

View File

@ -34,7 +34,7 @@ export default {
</script>
<template>
<title-area :title="packageEntity.name" data-qa-selector="package_title">
<title-area :title="packageEntity.name">
<template #sub-header>
<gl-icon name="eye" class="gl-mr-3" />
<gl-sprintf :message="$options.i18n.packageInfo">

View File

@ -77,7 +77,6 @@ export default {
v-gl-resize-observer="checkBreakpoints"
:title="packageEntity.name"
:avatar="packageIcon"
data-qa-selector="package_title"
>
<template #sub-header>
<div data-testid="sub-header" class="gl-display-flex gl-flex-wrap gl-gap-3">

View File

@ -47,7 +47,7 @@ export default {
</script>
<template>
<div data-qa-selector="package_path" class="gl-display-flex gl-align-items-center">
<div class="gl-display-flex gl-align-items-center">
<gl-icon data-testid="base-icon" name="project" class="gl-mx-3 gl-min-w-0" />
<gl-link

View File

@ -225,12 +225,7 @@ export default {
:description="s__('BulkImport|Your imported groups and projects will appear here.')"
/>
<template v-else>
<gl-table-lite
:fields="$options.fields"
:items="historyItems"
data-qa-selector="import_history_table"
class="gl-w-full"
>
<gl-table-lite :fields="$options.fields" :items="historyItems" class="gl-w-full">
<template #cell(destination_name)="{ item }">
<gl-icon
v-gl-tooltip

View File

@ -144,12 +144,7 @@ export default {
:description="s__('BulkImport|Your imported projects will appear here.')"
/>
<template v-else>
<gl-table
:fields="$options.fields"
:items="historyItems"
data-qa-selector="import_history_table"
class="gl-w-full"
>
<gl-table :fields="$options.fields" :items="historyItems" class="gl-w-full">
<template #cell(source)="{ item }">
<template v-if="item.import_url">
<gl-link

View File

@ -311,7 +311,6 @@ export default {
variant="confirm"
class="gl-mt-5"
data-testid="save_service_desk_settings_button"
data-qa-selector="save_service_desk_settings_button"
:disabled="isTemplateSaving || !isIssueTrackerEnabled"
@click="onSaveTemplate"
>

View File

@ -84,7 +84,6 @@ export default {
id="service-desk-template-select"
:text="selectedTemplate || $options.i18n.defaultDropdownText"
:header-text="$options.i18n.defaultDropdownText"
data-qa-selector="service_desk_template_dropdown"
:block="true"
class="service-desk-template-select"
toggle-class="gl-m-0"

View File

@ -47,7 +47,6 @@ export default {
class="js-sidebar-dropdown-toggle edit-link btn gl-text-gray-900! gl-ml-auto hide-collapsed btn-default btn-sm gl-button btn-default-tertiary float-right"
href="#"
data-test-id="edit-link"
data-qa-selector="edit_link"
data-track-action="click_edit_button"
data-track-label="right_sidebar"
data-track-property="assignee"

View File

@ -58,7 +58,6 @@ export default {
type="button"
class="gl-button btn-link gl-reset-color!"
data-testid="assign-yourself"
data-qa-selector="assign_yourself_button"
@click="assignSelf"
>
{{ __('assign yourself') }}

View File

@ -58,7 +58,6 @@ export default {
<template v-for="label in sortedSelectedLabels" v-else>
<gl-label
:key="label.id"
data-qa-selector="selected_label_content"
:data-qa-label-name="label.title"
:title="label.title"
:description="label.description"

View File

@ -40,7 +40,6 @@ export default {
:width="imgSize"
:class="`s${imgSize}`"
class="avatar avatar-inline m-0"
data-qa-selector="avatar_image"
/>
<gl-icon v-if="hasMergeIcon" name="warning-solid" aria-hidden="true" class="merge-icon" />
</span>

View File

@ -71,7 +71,6 @@ export default {
variant="link"
class="gl-ml-2"
data-testid="assign-yourself"
data-qa-selector="assign_yourself_button"
@click="assignSelf"
>
<span class="gl-text-gray-500 gl-hover-text-blue-800">{{ __('assign yourself') }}</span>

View File

@ -70,7 +70,6 @@ export default {
:toggle-text="$options.i18n.createNew"
:toggle-id="$options.toggleId"
:dropdown-offset="dropdownOffset"
data-qa-selector="new_menu_toggle"
data-testid="new-menu-toggle"
@shown="dropdownOpen = true"
@hidden="dropdownOpen = false"

View File

@ -151,7 +151,6 @@ export default {
ref="deleteTagButton"
:disabled="deleteButtonDisabled"
variant="danger"
data-qa-selector="delete_tag_confirmation_button"
data-testid="delete-tag-confirmation-button"
@click="submitForm"
>{{ buttonText }}</gl-button

View File

@ -662,7 +662,6 @@ export default {
<template v-if="showAutoMergeHelperText">
<div
class="gl-ml-4 gl-text-gray-500 gl-font-sm"
data-qa-selector="auto_merge_helper_text"
data-testid="auto-merge-helper-text"
>
{{ autoMergeHelperText }}

View File

@ -284,13 +284,7 @@ export default {
>
<div v-if="userName" class="gl-display-inline-flex gl-mt-2" data-testid="assigned-users">
<span class="gl-relative gl-mr-4">
<img
:alt="userName"
:src="userImg"
:width="32"
class="avatar avatar-inline gl-m-0 s32"
data-qa-selector="avatar_image"
/>
<img :alt="userName" :src="userImg" :width="32" class="avatar avatar-inline gl-m-0 s32" />
</span>
<span class="gl-display-flex gl-flex-direction-column gl-overflow-hidden">
<strong class="dropdown-menu-user-full-name">

View File

@ -98,7 +98,6 @@ export default {
:class="$options.userColorScheme"
data-type="simple"
:data-path="blob.path"
data-qa-selector="blob_viewer_file_content"
>
<chunk
v-for="(chunk, index) in chunks"

View File

@ -54,7 +54,6 @@ export default {
label-position="left"
aria-describedby="board-labels-toggle-text"
data-testid="show-labels-toggle"
data-qa-selector="show_labels_toggle"
class="gl-flex-direction-row"
@change="setShowLabels"
/>

View File

@ -43,7 +43,7 @@ export default {
value="all_branches"
data-testid="rule_all_branches"
>
<div data-qa-selector="strategy_radio_all">{{ __('All branches') }}</div>
<div>{{ __('All branches') }}</div>
</gl-form-radio>
<!-- wildcard -->
@ -52,7 +52,7 @@ export default {
value="wildcard"
data-testid="rule_wildcard"
>
<div data-qa-selector="strategy_radio_wildcard">
<div>
{{ s__('Webhooks|Wildcard pattern') }}
</div>
</gl-form-radio>
@ -61,7 +61,6 @@ export default {
v-if="branchFilterStrategyData === 'wildcard'"
v-model="pushEventsBranchFilterData"
name="hook[push_events_branch_filter]"
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
/>
</div>
@ -85,7 +84,7 @@ export default {
value="regex"
data-testid="rule_regex"
>
<div data-qa-selector="strategy_radio_regex">
<div>
{{ s__('Webhooks|Regular expression') }}
</div>
</gl-form-radio>
@ -94,7 +93,6 @@ export default {
v-if="branchFilterStrategyData === 'regex'"
v-model="pushEventsBranchFilterData"
name="hook[push_events_branch_filter]"
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
/>
</div>

View File

@ -53,7 +53,7 @@
= render 'usage'
- if Feature.enabled?(:configure_sentry_in_application_settings)
%section.settings.as-sentry.no-animate#js-sentry-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'sentry_settings_content' } }
%section.settings.as-sentry.no-animate#js-sentry-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Sentry')

View File

@ -26,7 +26,7 @@
.settings-content
= render 'ip_limits'
%section.settings.as-packages-limits.no-animate#js-packages-limits-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'packages_limits_content' } }
%section.settings.as-packages-limits.no-animate#js-packages-limits-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Package registry rate limits')
@ -72,7 +72,7 @@
.settings-content
= render partial: 'network_rate_limits', locals: { anchor: 'js-deprecated-limits-settings', setting_fragment: 'deprecated_api' }
%section.settings.as-git-lfs-limits.no-animate#js-git-lfs-limits-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'git_lfs_limits_content' } }
%section.settings.as-git-lfs-limits.no-animate#js-git-lfs-limits-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Git LFS Rate Limits')

View File

@ -26,7 +26,7 @@
.settings-content
= render partial: 'repository_mirrors_form'
%section.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?), data: { qa_selector: 'repository_storage_settings_content' } }
%section.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded_by_default?) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= _('Repository storage')

View File

@ -4,7 +4,7 @@
.form-group
= f.label :name do
= _("Topic slug (name)")
= f.text_field :name, placeholder: _('my-topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_name_field' },
= f.text_field :name, placeholder: _('my-topic'), class: 'form-control input-lg',
required: true,
title: _('Please fill in a name for your topic.'),
autofocus: true
@ -12,7 +12,7 @@
.form-group
= f.label :title do
= _("Topic title")
= f.text_field :title, placeholder: _('My topic'), class: 'form-control input-lg', data: { qa_selector: 'topic_title_field' },
= f.text_field :title, placeholder: _('My topic'), class: 'form-control input-lg',
required: true,
title: _('Please fill in a title for your topic.')

View File

@ -1,7 +1,7 @@
- topic = local_assigns.fetch(:topic)
- title = topic.title || topic.name
%li.topic-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'topic_row_content' } }
%li.topic-row.gl-py-3.gl-align-items-center{ class: 'gl-display-flex!' }
= render Pajamas::AvatarComponent.new(topic, size: 32, alt: '')
.gl-min-w-0.gl-flex-grow-1.gl-ml-3

View File

@ -6,7 +6,7 @@
= form_tag admin_topics_path, method: :get do |f|
- search = params.fetch(:search, nil)
.search-field-holder
= search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name'), data: { qa_selector: 'topic_search_field' }
= search_field_tag :search, search, class: "form-control gl-form-input search-text-input js-search-input", autofocus: true, spellcheck: false, placeholder: _('Search by name')
= sprite_icon('search', css_class: 'search-icon')
.gl-flex-grow-1
.js-merge-topics{ data: { path: merge_admin_topics_path } }

View File

@ -17,15 +17,15 @@
= _("New project")
- c.with_body do
%ul.content-list{ class: 'gl-px-3!' }
- @projects.each_with_index do |project, idx|
%li.project-row.gl-align-items-center{ class: 'gl-display-flex!', data: { qa_selector: 'project_row_container', qa_index: idx } }
- @projects.each do |project|
%li.project-row.gl-align-items-center{ class: 'gl-display-flex!' }
.avatar-container.rect-avatar.s40.gl-flex-shrink-0
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
.gl-min-w-0.gl-flex-grow-1
.title
= link_to project_path(project), class: 'js-prefetch-document' do
%span.project-full-name{ data: { qa_selector: 'project_fullname_content' } }
%span.namespace-name{ data: { qa_selector: 'project_namespace_content' } }
%span.project-full-name
%span.namespace-name
- if project.namespace
= project.namespace.human_name
\/
@ -43,13 +43,12 @@
.controls.gl-flex-shrink-0.gl-ml-5
= render Pajamas::ButtonComponent.new(href: project_project_members_path(project),
variant: :link,
button_options: { class: 'gl-mr-2', data: { qa_selector: 'project_members_button' } }) do
button_options: { class: 'gl-mr-2' }) do
= _('View members')
= render Pajamas::ButtonComponent.new(href: edit_project_path(project),
size: :small,
button_options: { data: { qa_selector: 'project_edit_button' } }) do
size: :small) do
= _('Edit')
= render 'delete_project_button', project: project, data: { qa_selector: 'project_delete_button' }
= render 'delete_project_button', project: project
- if @projects.blank?
.nothing-here-block= _("This group has no projects yet")

View File

@ -31,8 +31,8 @@
- if group.export_file_exists?
= render Pajamas::ButtonComponent.new(href: download_export_group_path(group), button_options: { rel: 'nofollow', data: { method: :get, qa_selector: 'download_export_link' } }) do
= _('Download export')
= render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'regenerate_export_group_link' } }) do
= render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post } }) do
= _('Regenerate export')
- else
= render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post, qa_selector: 'export_group_link' } }) do
= render Pajamas::ButtonComponent.new(href: export_group_path(group), button_options: { data: { method: :post } }) do
= _('Export group')

View File

@ -1,7 +1,7 @@
- if group.root?
.form-group
= f.label _('Enabled Git access protocols'), class: 'label-bold'
= f.select :enabled_git_access_protocol, options_for_select(enabled_git_access_protocol_options_for_group(group), group.enabled_git_access_protocol), {}, class: 'form-control', data: { qa_selector: 'enabled_git_access_protocol_dropdown' }, disabled: !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
= f.select :enabled_git_access_protocol, options_for_select(enabled_git_access_protocol_options_for_group(group), group.enabled_git_access_protocol), {}, class: 'form-control', disabled: !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
- if !::Gitlab::CurrentSettings.enabled_git_access_protocol.blank?
.form-text.text-muted
= _("This setting has been configured at the instance level and cannot be overridden per group")

View File

@ -7,4 +7,4 @@
- link_start_group = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: group_access_tokens_link }
= f.gitlab_ui_checkbox_component :resource_access_token_creation_allowed,
s_('GroupSettings|Users can create %{link_start_project}project access tokens%{link_end} and %{link_start_group}group access tokens%{link_end} in this group').html_safe % { link_start_project: link_start_project, link_start_group: link_start_group, link_end: '</a>'.html_safe },
checkbox_options: { checked: group.namespace_settings.resource_access_token_creation_allowed?, data: { qa_selector: 'resource_access_token_creation_allowed_checkbox' } }
checkbox_options: { checked: group.namespace_settings.resource_access_token_creation_allowed? }

View File

@ -3,7 +3,7 @@
!!! 5
%html.html-devise-layout{ lang: I18n.locale }
= render "layouts/head"
%body.signup-page.navless{ class: "#{system_message_class} #{user_application_theme} #{client_class_list}", data: { page: body_data_page, qa_selector: 'signup_page' } }
%body.signup-page.navless{ class: "#{system_message_class} #{user_application_theme} #{client_class_list}", data: { page: body_data_page } }
= header_message
= render "layouts/init_client_detection_flags"
= render "layouts/header/logo_with_title"

View File

@ -1,5 +1,5 @@
- expanded = expanded_by_default?
%section.settings.js-service-desk-setting-wrapper.no-animate#js-service-desk{ class: ('expanded' if expanded), data: { qa_selector: 'service_desk_settings_content' } }
%section.settings.js-service-desk-setting-wrapper.no-animate#js-service-desk{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Service Desk')
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do

View File

@ -1,4 +1,4 @@
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id, qa_selector: 'issue_container', qa_issue_title: issue.title } }
%li{ id: dom_id(issue), class: issue_css_classes(issue), url: issue_path(issue), data: { labels: issue.label_ids, id: issue.id } }
.issuable-info-container
.issuable-main-info
.issue-title.title

View File

@ -1,7 +1,7 @@
- issue = local_assigns.fetch(:issue)
- if issue.time_estimate > 0
%span.issuable-estimate.d-none.d-sm-inline-block.has-tooltip{ data: { container: 'body', qa_selector: 'issuable_estimate' }, title: _('Estimate') }
%span.issuable-estimate.d-none.d-sm-inline-block.has-tooltip{ data: { container: 'body' }, title: _('Estimate') }
&nbsp;
= sprite_icon('timer', css_class: 'issue-estimate-icon')
= Gitlab::TimeTrackingFormatter.output(issue.time_estimate)

View File

@ -30,7 +30,7 @@
.form-group
= label_tag :access_level, s_("AccessTokens|Select a role"), class: "label-bold"
.select-wrapper.gl-form-input-md
= select_tag :"#{prefix}[access_level]", options_for_select(access_levels, default_access_level), class: "form-control select-control", data: { qa_selector: 'access_token_access_level' }
= select_tag :"#{prefix}[access_level]", options_for_select(access_levels, default_access_level), class: "form-control select-control"
= sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200")
.form-group

View File

@ -7,5 +7,5 @@
= link_to_member(@project, assignee, name: false, title: s_("MrList|Assigned to %{name}") % { name: assignee.name})
- if more_assignees_count > 0
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old', qa_selector: 'avatar_counter_content' }, title: _("+%{more_assignees_count} more assignees") % { more_assignees_count: more_assignees_count} }
%span{ class: 'avatar-counter has-tooltip', data: { container: 'body', placement: 'bottom', 'line-type' => 'old' }, title: _("+%{more_assignees_count} more assignees") % { more_assignees_count: more_assignees_count} }
= _("+%{more_assignees_count}") % { more_assignees_count: more_assignees_count}

View File

@ -11,7 +11,7 @@
= gl_tab_link_to page_filter_path(state: 'closed'), item_active: params[:state] == 'closed', id: 'state-closed', title: _('Filter by merge requests that are currently closed and unmerged.'), data: { state: 'closed' } do
#{issuables_state_counter_text(type, :closed, display_count)}
- else
= gl_tab_link_to page_filter_path(state: 'closed'), item_active: params[:state] == 'closed', id: 'state-closed', title: _('Filter by issues that are currently closed.'), data: { state: 'closed', qa_selector: 'closed_issues_link' } do
= gl_tab_link_to page_filter_path(state: 'closed'), item_active: params[:state] == 'closed', id: 'state-closed', title: _('Filter by issues that are currently closed.'), data: { state: 'closed' } do
#{issuables_state_counter_text(type, :closed, display_count)}
= render 'shared/issuable/nav_links/all', page_context_word: page_context_word, counter: issuables_state_counter_text(type, :all, display_count)

View File

@ -157,7 +157,7 @@
= gl_badge_tag @user.followers.count, size: :sm
- if profile_tab?(:following)
%li.js-following-tab
= link_to user_following_path, data: { target: 'div#following', action: 'following', toggle: 'tab', endpoint: user_following_path(format: :json), qa_selector: 'following_tab' } do
= link_to user_following_path, data: { target: 'div#following', action: 'following', toggle: 'tab', endpoint: user_following_path(format: :json) } do
= s_('UserProfile|Following')
= gl_badge_tag @user.followees.count, size: :sm
- if !profile_tabs.empty? && Feature.enabled?(:profile_tabs_vue, current_user)

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
class CleanupCiPipelineChatDataPipelineIdBigint < Gitlab::Database::Migration[2.1]
disable_ddl_transaction!
TABLE = :ci_pipeline_chat_data
COLUMNS = [:pipeline_id]
def up
with_lock_retries(raise_on_exhaustion: true) do
cleanup_conversion_of_integer_to_bigint(TABLE, COLUMNS) # rubocop:disable Migration/WithLockRetriesDisallowedMethod
end
end
def down
restore_conversion_of_integer_to_bigint(TABLE, COLUMNS)
add_concurrent_index(
TABLE, :pipeline_id_convert_to_bigint,
name: :index_ci_pipeline_chat_data_on_pipeline_id_convert_to_bigint,
unique: true
)
add_concurrent_foreign_key(
TABLE, :ci_pipelines,
column: :pipeline_id_convert_to_bigint,
on_delete: :cascade, validate: true, reverse_lock_order: true
)
end
end

View File

@ -0,0 +1 @@
4c90d6df75ddb93f8fd8fb89131256fc97bac990f024576fa57a3a8c6b60fee9

View File

@ -307,15 +307,6 @@ BEGIN
END;
$$;
CREATE FUNCTION trigger_239c8032a8d6() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
NEW."pipeline_id_convert_to_bigint" := NEW."pipeline_id";
RETURN NEW;
END;
$$;
CREATE FUNCTION trigger_68d7b6653c7d() RETURNS trigger
LANGUAGE plpgsql
AS $$
@ -13817,7 +13808,6 @@ ALTER SEQUENCE ci_pipeline_artifacts_id_seq OWNED BY ci_pipeline_artifacts.id;
CREATE TABLE ci_pipeline_chat_data (
id bigint NOT NULL,
pipeline_id_convert_to_bigint integer DEFAULT 0 NOT NULL,
chat_name_id integer NOT NULL,
response_url text NOT NULL,
pipeline_id bigint NOT NULL
@ -31771,8 +31761,6 @@ CREATE INDEX index_ci_pipeline_chat_data_on_chat_name_id ON ci_pipeline_chat_dat
CREATE UNIQUE INDEX index_ci_pipeline_chat_data_on_pipeline_id ON ci_pipeline_chat_data USING btree (pipeline_id);
CREATE UNIQUE INDEX index_ci_pipeline_chat_data_on_pipeline_id_convert_to_bigint ON ci_pipeline_chat_data USING btree (pipeline_id_convert_to_bigint);
CREATE INDEX index_ci_pipeline_messages_on_pipeline_id ON ci_pipeline_messages USING btree (pipeline_id);
CREATE INDEX index_ci_pipeline_messages_on_pipeline_id_convert_to_bigint ON ci_pipeline_messages USING btree (pipeline_id_convert_to_bigint);
@ -36667,8 +36655,6 @@ CREATE TRIGGER trigger_07bc3c48f407 BEFORE INSERT OR UPDATE ON ci_stages FOR EAC
CREATE TRIGGER trigger_1bd97da9c1a4 BEFORE INSERT OR UPDATE ON ci_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_1bd97da9c1a4();
CREATE TRIGGER trigger_239c8032a8d6 BEFORE INSERT OR UPDATE ON ci_pipeline_chat_data FOR EACH ROW EXECUTE FUNCTION trigger_239c8032a8d6();
CREATE TRIGGER trigger_68d7b6653c7d BEFORE INSERT OR UPDATE ON ci_sources_pipelines FOR EACH ROW EXECUTE FUNCTION trigger_68d7b6653c7d();
CREATE TRIGGER trigger_7f3d66a7d7f5 BEFORE INSERT OR UPDATE ON ci_pipeline_variables FOR EACH ROW EXECUTE FUNCTION trigger_7f3d66a7d7f5();
@ -37095,9 +37081,6 @@ ALTER TABLE ONLY approval_merge_request_rules
ALTER TABLE ONLY deploy_keys_projects
ADD CONSTRAINT fk_58a901ca7e FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE ONLY ci_pipeline_chat_data
ADD CONSTRAINT fk_5b21bde562 FOREIGN KEY (pipeline_id_convert_to_bigint) REFERENCES ci_pipelines(id) ON DELETE CASCADE;
ALTER TABLE ONLY dependency_list_exports
ADD CONSTRAINT fk_5b3d11e1ef FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL;

View File

@ -17102,8 +17102,10 @@ Total weight of open and closed descendant issues.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="epicdescendantweightsclosedissues"></a>`closedIssues` | [`Int`](#int) | Total weight of completed (closed) issues in this epic, including epic descendants. |
| <a id="epicdescendantweightsopenedissues"></a>`openedIssues` | [`Int`](#int) | Total weight of opened issues in this epic, including epic descendants. |
| <a id="epicdescendantweightsclosedissues"></a>`closedIssues` **{warning-solid}** | [`Int`](#int) | **Deprecated** in 16.6. Use `closedIssuesTotal`. |
| <a id="epicdescendantweightsclosedissuestotal"></a>`closedIssuesTotal` | [`BigInt`](#bigint) | Total weight of completed (closed) issues in this epic, including epic descendants, encoded as a string. |
| <a id="epicdescendantweightsopenedissues"></a>`openedIssues` **{warning-solid}** | [`Int`](#int) | **Deprecated** in 16.6. Use `OpenedIssuesTotal`. |
| <a id="epicdescendantweightsopenedissuestotal"></a>`openedIssuesTotal` | [`BigInt`](#bigint) | Total weight of opened issues in this epic, including epic descendants, encoded as a string. |
### `EpicHealthStatus`

View File

@ -62,22 +62,13 @@ Code Suggestions do not prevent you from writing code in your IDE.
## Supported languages
The best results from Code Suggestions are expected for languages that [Anthropic Claude](https://www.anthropic.com/product) and the [Google Vertex AI Codey APIs](https://cloud.google.com/vertex-ai/docs/generative-ai/code/code-models-overview#supported_coding_languages) directly support:
Code Suggestions support is a function of the:
- C++
- C#
- Go
- Google SQL
- Java
- JavaScript
- Kotlin
- PHP
- Python
- Ruby
- Rust
- Scala
- Swift
- TypeScript
- Underlying large language model.
- IDE used.
- Extension or plug-in support in the IDE.
For languages not listed in the following table, Code Suggestions might not function as expected.
### Supported languages in IDEs

View File

@ -148,7 +148,7 @@
"gettext-parser": "^6.0.0",
"graphql": "^15.7.2",
"graphql-tag": "^2.11.0",
"gridstack": "^9.3.0",
"gridstack": "^9.4.0",
"highlight.js": "^11.8.0",
"immer": "^9.0.15",
"ipaddr.js": "^1.9.1",

View File

@ -39,7 +39,7 @@ module Gitlab
# @option plan [Hash] Support::Helpers::PREMIUM_SELF_MANAGED
# @option plan [Hash] Support::Helpers::ULTIMATE
# @option plan [Hash] Support::Helpers::ULTIMATE_SELF_MANAGED
# @option plan [Hash] Support::Helpers::CI_MINUTES
# @option plan [Hash] Support::Helpers::COMPUTE_MINUTES
# @option plan [Hash] Support::Helpers::STORAGE
# @param users_in_license [Integer] Number of users in license
# @param license_type [Hash] Type of the license

View File

@ -13,9 +13,9 @@ module Gitlab
# Pipelines section
link :pipelines_tab
link :buy_ci_minutes
div :plan_ci_minutes
div :additional_ci_minutes
link :buy_compute_minutes
div :plan_compute_minutes
div :additional_compute_minutes
div :ci_purchase_successful_alert, text: /You have successfully purchased CI minutes/
# Storage section
@ -39,17 +39,17 @@ module Gitlab
button :confirm_member_approval, text: /^OK$/
def plan_ci_limits
plan_ci_minutes[/(\d+){2}/]
plan_compute_minutes[/(\d+){2}/]
end
def additional_ci_limits
additional_ci_minutes[/(\d+){2}/]
additional_compute_minutes[/(\d+){2}/]
end
def additional_ci_minutes_added?
def additional_compute_minutes_added?
# When opening the Usage quotas page, Seats quota tab is opened briefly even when url is to a different tab
::QA::Support::WaitForRequests.wait_for_requests
additional_ci_minutes?
additional_compute_minutes?
end
# Returns total purchased storage value once it's ready on page
@ -61,29 +61,29 @@ module Gitlab
storage_purchased[/(\d+){2}.\d+/].to_f
end
# Waits for additional CI minutes to be available on the page
def wait_for_additional_ci_minutes_available
# Waits for additional compute minutes to be available on the page
def wait_for_additional_compute_minutes_available
::QA::Support::Waiter.wait_until(
max_duration: ::QA::Support::Helpers::Zuora::ZUORA_TIMEOUT,
sleep_interval: 2,
reload_page: Chemlab.configuration.browser.session,
message: 'Expected additional CI minutes but they did not appear.'
message: 'Expected additional compute minutes but they did not appear.'
) do
additional_ci_minutes_added?
additional_compute_minutes_added?
end
end
# Waits for additional CI minutes amount to match the expected number of minutes
# Waits for additional compute minutes amount to match the expected number of minutes
#
# @param [String] minutes
def wait_for_additional_ci_minute_limits(minutes)
wait_for_additional_ci_minutes_available
def wait_for_additional_compute_minute_limits(minutes)
wait_for_additional_compute_minutes_available
::QA::Support::Waiter.wait_until(
max_duration: ::QA::Support::Helpers::Zuora::ZUORA_TIMEOUT,
sleep_interval: 2,
reload_page: Chemlab.configuration.browser.session,
message: "Expected additional CI minutes to equal #{minutes}"
message: "Expected additional compute minutes to equal #{minutes}"
) do
additional_ci_limits == minutes
end

View File

@ -125,75 +125,75 @@ module Gitlab
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +link :buy_ci_minutes+
# Clicks +buy_ci_minutes+
def buy_ci_minutes
# @note Defined as +link :buy_compute_minutes+
# Clicks +buy_compute_minutes+
def buy_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.buy_ci_minutes_element).to exist
# expect(usage_quotas.buy_compute_minutes_element).to exist
# end
# @return [Watir::Link] The raw +Link+ element
def buy_ci_minutes_element
def buy_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_buy_ci_minutes
# expect(usage_quotas).to be_buy_compute_minutes
# end
# @return [Boolean] true if the +buy_ci_minutes+ element is present on the page
def buy_ci_minutes?
# @return [Boolean] true if the +buy_compute_minutes+ element is present on the page
def buy_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :plan_ci_minutes+
# @return [String] The text content or value of +plan_ci_minutes+
def plan_ci_minutes
# @note Defined as +div :plan_compute_minutes+
# @return [String] The text content or value of +plan_compute_minutes+
def plan_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.plan_ci_minutes_element).to exist
# expect(usage_quotas.plan_compute_minutes_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def plan_ci_minutes_element
def plan_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_plan_ci_minutes
# expect(usage_quotas).to be_plan_compute_minutes
# end
# @return [Boolean] true if the +plan_ci_minutes+ element is present on the page
def plan_ci_minutes?
# @return [Boolean] true if the +plan_compute_minutes+ element is present on the page
def plan_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end
# @note Defined as +div :additional_ci_minutes+
# @return [String] The text content or value of +additional_ci_minutes+
def additional_ci_minutes
# @note Defined as +div :additional_compute_minutes+
# @return [String] The text content or value of +additional_compute_minutes+
def additional_compute_minutes
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas.additional_ci_minutes_element).to exist
# expect(usage_quotas.additional_compute_minutes_element).to exist
# end
# @return [Watir::Div] The raw +Div+ element
def additional_ci_minutes_element
def additional_compute_minutes_element
# This is a stub, used for indexing. The method is dynamically generated.
end
# @example
# Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quotas|
# expect(usage_quotas).to be_additional_ci_minutes
# expect(usage_quotas).to be_additional_compute_minutes
# end
# @return [Boolean] true if the +additional_ci_minutes+ element is present on the page
def additional_ci_minutes?
# @return [Boolean] true if the +additional_compute_minutes+ element is present on the page
def additional_compute_minutes?
# This is a stub, used for indexing. The method is dynamically generated.
end

View File

@ -23,21 +23,21 @@ module QA
end
end
def purchase_ci_minutes(quantity: 1)
def purchase_compute_minutes(quantity: 1)
Page::Group::Menu.perform(&:go_to_usage_quotas)
Gitlab::Page::Group::Settings::UsageQuotas.perform do |usage_quota|
usage_quota.pipelines_tab
usage_quota.buy_ci_minutes
usage_quota.buy_compute_minutes
end
Gitlab::Page::Subscriptions::New.perform do |ci_minutes|
ci_minutes.quantity = quantity
ci_minutes.continue_to_billing
Gitlab::Page::Subscriptions::New.perform do |compute_minutes|
compute_minutes.quantity = quantity
compute_minutes.continue_to_billing
fill_in_customer_info
fill_in_payment_info
ci_minutes.purchase
compute_minutes.purchase
end
end

View File

@ -11,49 +11,49 @@ module QA
base.class_eval do
view 'app/assets/javascripts/design_management/components/design_notes/design_discussion.vue' do
element :design_discussion_content
element 'design-discussion-content'
end
view 'app/assets/javascripts/design_management/components/design_notes/design_note.vue' do
element :note_content
element 'note-text'
end
view 'app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue' do
element :note_textarea
element :save_comment_button
element 'note-textarea'
element 'save-comment-button'
end
view 'app/assets/javascripts/design_management/components/design_overlay.vue' do
element :design_image_button
element 'design-image-button'
end
view 'app/assets/javascripts/design_management/components/list/item.vue' do
element :design_file_name
element :design_image
element :design_status_icon
element 'design-file-name'
element 'design-image'
element 'design-status-icon'
end
view 'app/assets/javascripts/design_management/pages/index.vue' do
element :archive_button
element :design_checkbox
element :design_dropzone_content
element 'archive-button'
element 'design-checkbox'
element 'design-dropzone-content'
end
view 'app/assets/javascripts/design_management/components/delete_button.vue' do
element :confirm_archiving_button
element 'confirm-archiving-button'
end
end
end
def add_annotation(note)
click_element(:design_image_button)
fill_element(:note_textarea, note)
click_element(:save_comment_button)
click_element('design-image-button')
fill_element('note-textarea', note)
click_element('save-comment-button')
# It takes a moment for the annotation to be saved.
# We'll check for the annotation in a test, but here we'll at least
# wait for the "Save comment" button to disappear
saved = has_no_element?(:save_comment_button)
saved = has_no_element?('save-comment-button')
return if saved
raise RSpec::Expectations::ExpectationNotMetError, %q(There was a problem while adding the annotation)
@ -64,16 +64,16 @@ module QA
# It accepts a `class:` option, but that only works for class attributes
# It doesn't work as a CSS selector.
# So instead we use the name attribute as a locator
within_element(:design_dropzone_content) do
within_element('design-dropzone-content') do
page.attach_file("upload_file", design_file_path, make_visible: { display: 'block' })
end
filename = ::File.basename(design_file_path)
wait_until(reload: false, sleep_interval: 1, message: "Design upload") do
image = find_element(:design_image, filename: filename)
image = find_element('design-image', filename: filename).find('img')
has_element?(:design_file_name, text: filename) && image["complete"] && image["naturalWidth"].to_i > 0
has_element?('design-file-name', text: filename) && image["complete"] && image["naturalWidth"].to_i > 0
end
end
@ -83,38 +83,38 @@ module QA
end
def click_design(filename)
click_element(:design_file_name, text: filename)
click_element('design-file-name', text: filename)
end
def select_design(filename)
click_element(:design_checkbox, design: filename)
click_element('design-checkbox', design: filename)
end
def archive_selected_designs
click_element(:archive_button)
click_element(:confirm_archiving_button)
click_element('archive-button')
click_element('confirm-archiving-button')
end
def has_annotation?(note)
within_element_by_index(:design_discussion_content, 0) do
has_element?(:note_content, text: note)
within_element_by_index('design-discussion-content', 0) do
has_element?('note-text', text: note)
end
end
def has_design?(filename)
has_element?(:design_file_name, text: filename)
has_element?('design-file-name', text: filename)
end
def has_no_design?(filename)
has_no_element?(:design_file_name, text: filename)
has_no_element?('design-file-name', text: filename)
end
def has_created_icon?
has_element?(:design_status_icon, status: 'file-addition-solid')
has_element?('design-status-icon', status: 'file-addition-solid')
end
def has_modified_icon?
has_element?(:design_status_icon, status: 'file-modified-solid')
has_element?('design-status-icon', status: 'file-modified-solid')
end
end
end

View File

@ -4,7 +4,7 @@ module QA
module Support
module Helpers
module Plan
FREE = { name: 'free', price: 0, yearly_price: 0, ci_minutes: 400 }.freeze
FREE = { name: 'free', price: 0, yearly_price: 0, compute_minutes: 400 }.freeze
PREMIUM = {
plan_id: '2c92a00d76f0d5060176f2fb0a5029ff',
@ -12,7 +12,7 @@ module QA
name: 'premium',
price: 19,
yearly_price: 228,
ci_minutes: 10000
compute_minutes: 10000
}.freeze
PREMIUM_SELF_MANAGED = {
@ -29,7 +29,7 @@ module QA
name: 'ultimate',
price: 99,
yearly_price: 1188,
ci_minutes: 50000
compute_minutes: 50000
}.freeze
ULTIMATE_SELF_MANAGED = {
@ -40,12 +40,12 @@ module QA
yearly_price: 1188
}.freeze
CI_MINUTES = {
COMPUTE_MINUTES = {
plan_id: '2c92a0086a07f4a8016a2c0a1f7b4b4c',
rate_charge_id: '2c92a0fd6a07f4c6016a2c0af07c3f21',
name: 'ci_minutes',
name: 'compute_minutes',
price: 10,
ci_minutes: 1000
compute_minutes: 1000
}.freeze
STORAGE = {

View File

@ -3,7 +3,7 @@
exports[`Design reply form component renders button text as "Comment" when creating a comment 1`] = `
<button
class="btn btn-confirm btn-md disabled gl-button gl-mr-3 gl-w-auto!"
data-qa-selector="save_comment_button"
data-testid="save-comment-button"
data-track-action="click_button"
disabled=""
type="submit"
@ -19,7 +19,7 @@ exports[`Design reply form component renders button text as "Comment" when creat
exports[`Design reply form component renders button text as "Save comment" when creating a comment 1`] = `
<button
class="btn btn-confirm btn-md disabled gl-button gl-mr-3 gl-w-auto!"
data-qa-selector="save_comment_button"
data-testid="save-comment-button"
data-track-action="click_button"
disabled=""
type="submit"

View File

@ -86,7 +86,7 @@ describe('Design overlay component', () => {
};
wrapper
.find('[data-qa-selector="design_image_button"]')
.find('[data-testid="design-image-button"]')
.trigger('mouseup', { offsetX: newCoordinates.x, offsetY: newCoordinates.y });
expect(wrapper.emitted('openCommentForm')).toEqual([

View File

@ -46,7 +46,7 @@ describe('Design management design presentation component', () => {
wrapper.element.scrollTo = jest.fn();
}
const findOverlayCommentButton = () => wrapper.find('[data-qa-selector="design_image_button"]');
const findOverlayCommentButton = () => wrapper.find('[data-testid="design-image-button"]');
/**
* Spy on $refs and mock given values

View File

@ -17,12 +17,12 @@ exports[`Design management list item component with notes renders item with mult
>
<gl-intersection-observer-stub
class="gl-flex-grow-1"
data-qa-filename="test"
data-testid="design-image"
>
<img
alt="test"
class="design-img gl-display-block gl-max-h-full gl-max-w-full gl-mx-auto gl-w-auto"
data-qa-filename="test"
data-qa-selector="design_image"
data-testid="design-img-1"
src="null"
/>
@ -33,10 +33,10 @@ exports[`Design management list item component with notes renders item with mult
>
<div
class="gl-display-flex gl-flex-direction-column str-truncated-100"
data-testid="design-file-name"
>
<span
class="gl-font-weight-semibold str-truncated-100"
data-qa-selector="design_file_name"
data-testid="design-img-filename-1"
title="test"
>
@ -82,12 +82,12 @@ exports[`Design management list item component with notes renders item with sing
>
<gl-intersection-observer-stub
class="gl-flex-grow-1"
data-qa-filename="test"
data-testid="design-image"
>
<img
alt="test"
class="design-img gl-display-block gl-max-h-full gl-max-w-full gl-mx-auto gl-w-auto"
data-qa-filename="test"
data-qa-selector="design_image"
data-testid="design-img-1"
src="null"
/>
@ -98,10 +98,10 @@ exports[`Design management list item component with notes renders item with sing
>
<div
class="gl-display-flex gl-flex-direction-column str-truncated-100"
data-testid="design-file-name"
>
<span
class="gl-font-weight-semibold str-truncated-100"
data-qa-selector="design_file_name"
data-testid="design-img-filename-1"
title="test"
>

View File

@ -190,7 +190,7 @@ describe('ErrorDetails', () => {
});
describe('unsafe chars for culprit field', () => {
const findReportedText = () => wrapper.find('[data-qa-selector="reported_text"]');
const findReportedText = () => wrapper.find('[data-testid="reported-text"]');
const culprit = '<script>console.log("surprise!")</script>';
beforeEach(() => {
store.state.details.loadingStacktrace = false;
@ -350,7 +350,7 @@ describe('ErrorDetails', () => {
it('should submit the form', () => {
window.HTMLFormElement.prototype.submit = () => {};
const submitSpy = jest.spyOn(wrapper.vm.$refs.sentryIssueForm, 'submit');
wrapper.find('[data-qa-selector="create_issue_button"]').vm.$emit('click');
wrapper.find('[data-testid="create-issue-button"]').vm.$emit('click');
expect(submitSpy).toHaveBeenCalled();
submitSpy.mockRestore();
});
@ -462,7 +462,7 @@ describe('ErrorDetails', () => {
describe('GitLab issue link', () => {
const gitlabIssuePath = 'https://gitlab.example.com/issues/1';
const findGitLabLink = () => wrapper.find(`[href="${gitlabIssuePath}"]`);
const findCreateIssueButton = () => wrapper.find('[data-qa-selector="create_issue_button"]');
const findCreateIssueButton = () => wrapper.find('[data-testid="create-issue-button"]');
const findViewIssueButton = () => wrapper.find('[data-qa-selector="view_issue_button"]');
describe('is present', () => {
@ -562,7 +562,7 @@ describe('ErrorDetails', () => {
});
it('should track create issue button click', async () => {
await wrapper.find('[data-qa-selector="create_issue_button"]').vm.$emit('click');
await wrapper.find('[data-testid="create-issue-button"]').vm.$emit('click');
expect(Tracking.event).toHaveBeenCalledWith(category, 'click_create_issue_from_error', {
extra: {
variant: integrated ? 'integrated' : 'external',

View File

@ -15,9 +15,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -26,9 +24,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -40,9 +36,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>
@ -67,9 +61,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -78,9 +70,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -92,9 +82,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>
@ -102,7 +90,6 @@ exports[`Webhook push events form editor component Different push events rules w
class="gl-ml-6"
>
<gl-form-input-stub
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
name="hook[push_events_branch_filter]"
value="foo"
@ -133,9 +120,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -144,9 +129,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -154,7 +137,6 @@ exports[`Webhook push events form editor component Different push events rules w
class="gl-ml-6"
>
<gl-form-input-stub
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
name="hook[push_events_branch_filter]"
value="foo"
@ -172,9 +154,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>
@ -199,9 +179,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -210,9 +188,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -224,9 +200,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>
@ -251,9 +225,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -262,9 +234,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -276,9 +246,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>
@ -286,7 +254,6 @@ exports[`Webhook push events form editor component Different push events rules w
class="gl-ml-6"
>
<gl-form-input-stub
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
name="hook[push_events_branch_filter]"
value=""
@ -317,9 +284,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_all_branches"
value="all_branches"
>
<div
data-qa-selector="strategy_radio_all"
>
<div>
All branches
</div>
</gl-form-radio-stub>
@ -328,9 +293,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_wildcard"
value="wildcard"
>
<div
data-qa-selector="strategy_radio_wildcard"
>
<div>
Wildcard pattern
</div>
</gl-form-radio-stub>
@ -338,7 +301,6 @@ exports[`Webhook push events form editor component Different push events rules w
class="gl-ml-6"
>
<gl-form-input-stub
data-qa-selector="webhook_branch_filter_field"
data-testid="webhook_branch_filter_field"
name="hook[push_events_branch_filter]"
value=""
@ -356,9 +318,7 @@ exports[`Webhook push events form editor component Different push events rules w
data-testid="rule_regex"
value="regex"
>
<div
data-qa-selector="strategy_radio_regex"
>
<div>
Regular expression
</div>
</gl-form-radio-stub>

View File

@ -4,7 +4,6 @@ require 'spec_helper'
RSpec.describe 'Creating the packages protection rule', :aggregate_failures, feature_category: :package_registry do
include GraphqlHelpers
using RSpec::Parameterized::TableSyntax
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user, maintainer_projects: [project]) }
@ -15,151 +14,160 @@ RSpec.describe 'Creating the packages protection rule', :aggregate_failures, fea
{
project_path: project.full_path,
package_name_pattern: package_protection_rule_attributes.package_name_pattern,
package_type: "NPM",
push_protected_up_to_access_level: "MAINTAINER"
package_type: 'NPM',
push_protected_up_to_access_level: 'MAINTAINER'
}
end
let(:mutation) do
graphql_mutation(:create_packages_protection_rule, kwargs,
<<~QUERY
clientMutationId
packageProtectionRule {
packageNamePattern
packageType
pushProtectedUpToAccessLevel
}
errors
QUERY
)
end
let(:mutation_response) { graphql_mutation_response(:create_packages_protection_rule) }
let(:mutation_response_package_protection_rule) do
graphql_data_at(:createPackagesProtectionRule, :packageProtectionRule)
end
describe 'post graphql mutation' do
subject { post_graphql_mutation(mutation, current_user: user) }
let(:mutation_response_errors) { graphql_data_at(:createPackagesProtectionRule, :errors) }
context 'without existing packages protection rule' do
it 'returns without error' do
subject
subject { post_graphql_mutation(mutation, current_user: user) }
expect_graphql_errors_to_be_empty
end
shared_examples 'a successful response' do
it 'returns without error' do
subject
it 'returns the created packages protection rule' do
expect { subject }.to change { ::Packages::Protection::Rule.count }.by(1)
expect_graphql_errors_to_be_empty
expect(Packages::Protection::Rule.where(project: project).count).to eq 1
expect(Packages::Protection::Rule.where(project: project,
package_name_pattern: kwargs[:package_name_pattern])).to exist
end
context 'when invalid fields are given' do
let(:kwargs) do
{
project_path: project.full_path,
package_name_pattern: '',
package_type: 'UNKNOWN_PACKAGE_TYPE',
push_protected_up_to_access_level: 'UNKNOWN_ACCESS_LEVEL'
}
end
it 'returns error about required argument' do
subject
expect_graphql_errors_to_include(/was provided invalid value for packageType/)
expect_graphql_errors_to_include(/pushProtectedUpToAccessLevel/)
end
end
expect_graphql_errors_to_be_empty
expect(mutation_response_errors).to be_empty
end
context 'when user does not have permission' do
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let_it_be(:anonymous) { create(:user) }
it 'returns the created packages protection rule' do
subject
where(:user) do
[ref(:developer), ref(:reporter), ref(:guest), ref(:anonymous)]
end
with_them do
it 'returns an error' do
expect { subject }.not_to change { ::Packages::Protection::Rule.count }
expect_graphql_errors_to_include(/you don't have permission to perform this action/)
end
end
expect(mutation_response_package_protection_rule).to include(
'packageNamePattern' => kwargs[:package_name_pattern],
'packageType' => kwargs[:package_type],
'pushProtectedUpToAccessLevel' => kwargs[:push_protected_up_to_access_level]
)
end
context 'with existing packages protection rule' do
let_it_be(:existing_package_protection_rule) do
create(:package_protection_rule, project: project, push_protected_up_to_access_level: Gitlab::Access::DEVELOPER)
end
it 'creates one package protection rule' do
expect { subject }.to change { ::Packages::Protection::Rule.count }.by(1)
context 'when package name pattern is slightly different' do
let(:kwargs) do
{
project_path: project.full_path,
# The field `package_name_pattern` is unique; this is why we change the value in a minimum way
package_name_pattern: "#{existing_package_protection_rule.package_name_pattern}-unique",
package_type: "NPM",
push_protected_up_to_access_level: "DEVELOPER"
}
end
expect(Packages::Protection::Rule.last).to have_attributes(
project: project,
package_name_pattern: kwargs[:package_name_pattern],
package_type: kwargs[:package_type].downcase,
push_protected_up_to_access_level: kwargs[:push_protected_up_to_access_level].downcase
)
end
end
it 'returns the created packages protection rule' do
expect { subject }.to change { ::Packages::Protection::Rule.count }.by(1)
shared_examples 'an erroneous response' do
it 'does not create one package protection rule' do
expect { subject }.not_to change { ::Packages::Protection::Rule.count }
end
end
expect(Packages::Protection::Rule.where(project: project).count).to eq 2
expect(Packages::Protection::Rule.where(project: project,
package_name_pattern: kwargs[:package_name_pattern])).to exist
end
it_behaves_like 'a successful response'
it 'returns without error' do
subject
expect_graphql_errors_to_be_empty
end
end
context 'when field `package_name_pattern` is taken' do
let(:kwargs) do
{
project_path: project.full_path,
package_name_pattern: existing_package_protection_rule.package_name_pattern,
package_type: 'NPM',
push_protected_up_to_access_level: 'MAINTAINER'
}
end
it 'returns without error' do
subject
expect(mutation_response).to include 'errors' => ['Package name pattern has already been taken']
end
it 'does not create new package protection rules' do
expect { subject }.to change { Packages::Protection::Rule.count }.by(0)
expect(Packages::Protection::Rule.where(project: project,
package_name_pattern: kwargs[:package_name_pattern],
push_protected_up_to_access_level: Gitlab::Access::MAINTAINER)).not_to exist
end
end
context 'with invalid kwargs leading to error from graphql' do
let(:kwargs) do
super().merge!(
package_name_pattern: '',
package_type: 'UNKNOWN_PACKAGE_TYPE',
push_protected_up_to_access_level: 'UNKNOWN_ACCESS_LEVEL'
)
end
context "when feature flag ':packages_protected_packages' disabled" do
before do
stub_feature_flags(packages_protected_packages: false)
it_behaves_like 'an erroneous response'
it 'returns error about required argument' do
subject
expect_graphql_errors_to_include(/was provided invalid value for packageType/)
expect_graphql_errors_to_include(/pushProtectedUpToAccessLevel/)
end
end
context 'with invalid kwargs leading to error from business model' do
let(:kwargs) { super().merge!(package_name_pattern: '') }
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject.tap { expect(mutation_response_errors).to include(/Package name pattern can't be blank/) }
end
end
context 'with existing packages protection rule' do
let_it_be(:existing_package_protection_rule) do
create(:package_protection_rule, project: project, push_protected_up_to_access_level: :maintainer)
end
let(:kwargs) { super().merge!(package_name_pattern: existing_package_protection_rule.package_name_pattern) }
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject.tap { expect(mutation_response_errors).to include(/Package name pattern has already been taken/) }
end
context 'when field `package_name_pattern` is different than existing one' do
let(:kwargs) do
# The field `package_name_pattern` is unique; this is why we change the value in a minimum way
super().merge!(package_name_pattern: "#{existing_package_protection_rule.package_name_pattern}-unique")
end
it 'does not create any package protection rules' do
expect { subject }.to change { Packages::Protection::Rule.count }.by(0)
it_behaves_like 'a successful response'
end
expect(Packages::Protection::Rule.where(project: project)).not_to exist
end
context 'when field `push_protected_up_to_access_level` is different than existing one' do
let(:kwargs) { super().merge!(push_protected_up_to_access_level: 'DEVELOPER') }
it 'returns error of disabled feature flag' do
subject.tap { expect_graphql_errors_to_include(/'packages_protected_packages' feature flag is disabled/) }
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject.tap { expect(mutation_response_errors).to include(/Package name pattern has already been taken/) }
end
end
end
context 'when user does not have permission' do
let_it_be(:anonymous) { create(:user) }
let_it_be(:developer) { create(:user).tap { |u| project.add_developer(u) } }
let_it_be(:guest) { create(:user).tap { |u| project.add_guest(u) } }
let_it_be(:reporter) { create(:user).tap { |u| project.add_reporter(u) } }
where(:user) do
[ref(:developer), ref(:reporter), ref(:guest), ref(:anonymous)]
end
with_them do
it_behaves_like 'an erroneous response'
it 'returns an error' do
subject.tap { expect_graphql_errors_to_include(/you don't have permission to perform this action/) }
end
end
end
context "when feature flag ':packages_protected_packages' disabled" do
before do
stub_feature_flags(packages_protected_packages: false)
end
it_behaves_like 'an erroneous response'
it 'returns error of disabled feature flag' do
subject.tap { expect_graphql_errors_to_include(/'packages_protected_packages' feature flag is disabled/) }
end
end
end

View File

@ -7213,10 +7213,10 @@ graphql@^15.7.2:
resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.7.2.tgz#85ab0eeb83722977151b3feb4d631b5f2ab287ef"
integrity sha512-AnnKk7hFQFmU/2I9YSQf3xw44ctnSFCfp3zE0N6W174gqe9fWG/2rKaKxROK7CcI3XtERpjEKFqts8o319Kf7A==
gridstack@^9.3.0:
version "9.3.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-9.3.0.tgz#0f86cb8aabc3249e340900a5d9505e37e6d7c85e"
integrity sha512-IamRPgMK0AyFsGefosPfz3i6ehNfbx7mVsZDumEbsGeN2BDZt15Ae6AOowl9D5I6d6c0mhQyYoifAfykefXf1g==
gridstack@^9.4.0:
version "9.4.0"
resolved "https://registry.yarnpkg.com/gridstack/-/gridstack-9.4.0.tgz#806b5f8dc2d52ac6aade5d5fc799979a167f6977"
integrity sha512-4dcXGT5IzWfLxJIj4oGZPohgYfHrbdNHenRwwApzialkG2bVd9J9NmcdZsRFX6Q4cANqR0M56yF2Km2FAQkVCw==
gzip-size@^6.0.0:
version "6.0.0"